Merge "Handle LANGUAGE NOTIFICATION command"
diff --git a/Android.mk b/Android.mk
index add14fa..915ad01 100644
--- a/Android.mk
+++ b/Android.mk
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 # enable this build only when platform library is available
-ifeq ($(TARGET_BUILD_JAVA_SUPPORT_LEVEL),platform)
+ifneq ($(TARGET_BUILD_PDK), true)
 
 LOCAL_PATH := $(call my-dir)
 
@@ -25,11 +25,17 @@
 	$(call all-logtags-files-under, src/java) \
 	$(call all-proto-files-under, proto)
 
-LOCAL_JAVA_LIBRARIES := voip-common ims-common
+LOCAL_JAVA_LIBRARIES := voip-common ims-common services
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android.hardware.radio-V1.0-java \
+    android.hardware.radio-V1.1-java \
+    android.hardware.radio.deprecated-V1.0-java \
+    android.hidl.base-V1.0-java
+
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := telephony-common
 LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors,store_unknown_fields=true,enum_style=java
+LOCAL_PROTO_JAVA_OUTPUT_PARAMS := store_unknown_fields=true,enum_style=java
 
 LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/jarjar-rules.txt
@@ -44,4 +50,4 @@
 # ============================================================
 include $(call all-makefiles-under,$(LOCAL_PATH))
 
-endif # JAVA platform
+endif # non-PDK build
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..02f7976
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,12 @@
+amitmahajan@google.com
+breadley@google.com
+fionaxu@google.com
+jackyu@google.com
+hallliu@google.com
+rgreenwalt@google.com
+tgunn@google.com
+jminjie@google.com
+mpq@google.com
+sanketpadawe@google.com
+shuoq@google.com
+refuhoo@google.com
diff --git a/jarjar-rules.txt b/jarjar-rules.txt
index 50220b4..958e2bb 100644
--- a/jarjar-rules.txt
+++ b/jarjar-rules.txt
@@ -1,2 +1,2 @@
-rule com.google.protobuf.nano.** com.android.framework.protobuf.nano.@1
+rule com.google.protobuf.nano.** com.android.internal.telephony.protobuf.nano.@1
 
diff --git a/proto/telephony.proto b/proto/telephony.proto
index 3531f39..1f1a2bb 100644
--- a/proto/telephony.proto
+++ b/proto/telephony.proto
@@ -95,13 +95,13 @@
 // Telephony related user settings
 message TelephonySettings {
 
-  // NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE
+  // NETWORK_MODE_* See ril.h PREF_NET_TYPE_XXXX
   enum RilNetworkMode {
 
     // Mode is unknown.
     NETWORK_MODE_UNKNOWN = 0;
 
-    // GSM/WCDMA (WCDMA preferred)
+    // GSM/WCDMA (WCDMA preferred). Note the following values are all off by 1.
     NETWORK_MODE_WCDMA_PREF = 1;
 
     // GSM only
@@ -236,6 +236,9 @@
   // Roaming type
   enum RoamingType {
 
+    // Unknown. The default value. Different from ROAMING_TYPE_UNKNOWN.
+    UNKNOWN = -1;
+
     // In home network
     ROAMING_TYPE_NOT_ROAMING = 0;
 
@@ -257,21 +260,26 @@
   optional TelephonyOperator data_operator = 2;
 
   // Current voice network roaming type
-  optional RoamingType voice_roaming_type = 3;
+  optional RoamingType voice_roaming_type = 3 [default = UNKNOWN];
 
   // Current data network roaming type
-  optional RoamingType data_roaming_type = 4;
+  optional RoamingType data_roaming_type = 4 [default = UNKNOWN];
 
   // Current voice radio technology
-  optional RadioAccessTechnology voice_rat = 5;
+  optional RadioAccessTechnology voice_rat = 5 [default = UNKNOWN];
 
   // Current data radio technology
-  optional RadioAccessTechnology data_rat = 6;
+  optional RadioAccessTechnology data_rat = 6 [default = UNKNOWN];
 }
 
 // Radio access families
 enum RadioAccessTechnology {
 
+  // This is the default value. Different from RAT_UNKNOWN.
+  UNKNOWN = -1;
+
+  // Airplane mode, out of service, or when the modem cannot determine
+  // the RAT.
   RAT_UNKNOWN = 0;
 
   RAT_GPRS = 1;
@@ -309,6 +317,8 @@
   RAT_TD_SCDMA = 17;
 
   RAT_IWLAN = 18;
+
+  RAT_LTE_CA = 19;
 }
 
 // The information about IMS errors
@@ -375,6 +385,7 @@
   // type is unknown.
   RIL_E_UNKNOWN = 0;
 
+  // Note the following values are all off by 1.
   RIL_E_SUCCESS = 1;
 
   // If radio did not start or is resetting
@@ -456,8 +467,12 @@
   // SS request modified to different SS request
   RIL_E_SS_MODIFIED_TO_SS = 28;
 
-  // LCE service not supported(36 in RILConstants.java)
-  RIL_E_LCE_NOT_SUPPORTED = 36;
+  // LCE service not supported(36 in RILConstants.java. This is a mistake.
+  // The value should be off by 1 ideally.)
+  RIL_E_LCE_NOT_SUPPORTED = 36 [deprecated=true];
+
+  // LCE service not supported
+  RIL_E_LCE_NOT_SUPPORTED_NEW = 37;
 }
 
 // PDP_type values in TS 27.007 section 10.1.1.
@@ -559,7 +574,7 @@
     }
 
     // Radio technology to use
-    optional RadioAccessTechnology rat = 1;
+    optional RadioAccessTechnology rat = 1 [default = UNKNOWN];
 
     // optional RIL_DataProfile
     optional RilDataProfile data_profile = 2;
@@ -574,8 +589,7 @@
   // RIL response to RilSetupDataCall
   message RilSetupDataCallResponse {
 
-    // Copy of enum RIL_DataCallFailCause defined at
-    // https://cs.corp.google.com/android/hardware/ril/include/telephony/ril.h
+    // Copy of enum RIL_DataCallFailCause defined at ril.h
     enum RilDataCallFailCause {
 
       // Failure reason is unknown.
@@ -785,14 +799,19 @@
   // RIL error code
   optional RilErrno error = 9;
 
+  // Setup data call request
   optional RilSetupDataCall setup_data_call = 10;
 
+  // Setup data call response
   optional RilSetupDataCallResponse setup_data_call_response = 11;
 
+  // Deactivate data call request
   optional RilDeactivateDataCall deactivate_data_call = 12;
 
+  // Data call stall recovery action
   optional int32 data_stall_action = 13;
 
+  // Modem restart event
   optional ModemRestart modem_restart = 14;
 
   // NITZ time in milliseconds
@@ -1011,6 +1030,13 @@
       optional CallState state = 2;
 
       optional Type type = 3;
+
+      // For possible values for a call end reason check
+      // frameworks/base/telephony/java/android/telephony/DisconnectCause.java
+      optional int32 call_end_reason = 4;
+
+      // This field is true for Conference Calls
+      optional bool is_multiparty = 5;
     }
 
     // Single Radio Voice Call Continuity(SRVCC) progress state
@@ -1083,10 +1109,10 @@
     optional ImsReasonInfo reason_info = 18;
 
     // Original access technology
-    optional RadioAccessTechnology src_access_tech = 19;
+    optional RadioAccessTechnology src_access_tech = 19 [default = UNKNOWN];
 
     // New access technology
-    optional RadioAccessTechnology target_access_tech = 20;
+    optional RadioAccessTechnology target_access_tech = 20 [default = UNKNOWN];
 
     // NITZ time in milliseconds
     optional int64 nitz_timestamp_millis = 21;
diff --git a/src/java/android/provider/Telephony.java b/src/java/android/provider/Telephony.java
deleted file mode 100644
index 943a6ca..0000000
--- a/src/java/android/provider/Telephony.java
+++ /dev/null
@@ -1,2975 +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 android.provider;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.annotation.TestApi;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.database.Cursor;
-import android.database.sqlite.SqliteWrapper;
-import android.net.Uri;
-import android.telephony.SmsMessage;
-import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
-import android.telephony.Rlog;
-import android.util.Patterns;
-
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.SmsApplication;
-
-
-import java.util.HashSet;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * The Telephony provider contains data related to phone operation, specifically SMS and MMS
- * messages and access to the APN list, including the MMSC to use.
- *
- * <p class="note"><strong>Note:</strong> These APIs are not available on all Android-powered
- * devices. If your app depends on telephony features such as for managing SMS messages, include
- * a <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}
- * </a> element in your manifest that declares the {@code "android.hardware.telephony"} hardware
- * feature. Alternatively, you can check for telephony availability at runtime using either
- * {@link android.content.pm.PackageManager#hasSystemFeature
- * hasSystemFeature(PackageManager.FEATURE_TELEPHONY)} or {@link
- * android.telephony.TelephonyManager#getPhoneType}.</p>
- *
- * <h3>Creating an SMS app</h3>
- *
- * <p>Only the default SMS app (selected by the user in system settings) is able to write to the
- * SMS Provider (the tables defined within the {@code Telephony} class) and only the default SMS
- * app receives the {@link android.provider.Telephony.Sms.Intents#SMS_DELIVER_ACTION} broadcast
- * when the user receives an SMS or the {@link
- * android.provider.Telephony.Sms.Intents#WAP_PUSH_DELIVER_ACTION} broadcast when the user
- * receives an MMS.</p>
- *
- * <p>Any app that wants to behave as the user's default SMS app must handle the following intents:
- * <ul>
- * <li>In a broadcast receiver, include an intent filter for {@link Sms.Intents#SMS_DELIVER_ACTION}
- * (<code>"android.provider.Telephony.SMS_DELIVER"</code>). The broadcast receiver must also
- * require the {@link android.Manifest.permission#BROADCAST_SMS} permission.
- * <p>This allows your app to directly receive incoming SMS messages.</p></li>
- * <li>In a broadcast receiver, include an intent filter for {@link
- * Sms.Intents#WAP_PUSH_DELIVER_ACTION}} ({@code "android.provider.Telephony.WAP_PUSH_DELIVER"})
- * with the MIME type <code>"application/vnd.wap.mms-message"</code>.
- * The broadcast receiver must also require the {@link
- * android.Manifest.permission#BROADCAST_WAP_PUSH} permission.
- * <p>This allows your app to directly receive incoming MMS messages.</p></li>
- * <li>In your activity that delivers new messages, include an intent filter for
- * {@link android.content.Intent#ACTION_SENDTO} (<code>"android.intent.action.SENDTO"
- * </code>) with schemas, <code>sms:</code>, <code>smsto:</code>, <code>mms:</code>, and
- * <code>mmsto:</code>.
- * <p>This allows your app to receive intents from other apps that want to deliver a
- * message.</p></li>
- * <li>In a service, include an intent filter for {@link
- * android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE}
- * (<code>"android.intent.action.RESPOND_VIA_MESSAGE"</code>) with schemas,
- * <code>sms:</code>, <code>smsto:</code>, <code>mms:</code>, and <code>mmsto:</code>.
- * This service must also require the {@link
- * android.Manifest.permission#SEND_RESPOND_VIA_MESSAGE} permission.
- * <p>This allows users to respond to incoming phone calls with an immediate text message
- * using your app.</p></li>
- * </ul>
- *
- * <p>Other apps that are not selected as the default SMS app can only <em>read</em> the SMS
- * Provider, but may also be notified when a new SMS arrives by listening for the {@link
- * Sms.Intents#SMS_RECEIVED_ACTION}
- * broadcast, which is a non-abortable broadcast that may be delivered to multiple apps. This
- * broadcast is intended for apps that&mdash;while not selected as the default SMS app&mdash;need to
- * read special incoming messages such as to perform phone number verification.</p>
- *
- * <p>For more information about building SMS apps, read the blog post, <a
- * href="http://android-developers.blogspot.com/2013/10/getting-your-sms-apps-ready-for-kitkat.html"
- * >Getting Your SMS Apps Ready for KitKat</a>.</p>
- *
- */
-public final class Telephony {
-    private static final String TAG = "Telephony";
-
-    /**
-     * Not instantiable.
-     * @hide
-     */
-    private Telephony() {
-    }
-
-    /**
-     * Base columns for tables that contain text-based SMSs.
-     */
-    public interface TextBasedSmsColumns {
-
-        /** Message type: all messages. */
-        public static final int MESSAGE_TYPE_ALL    = 0;
-
-        /** Message type: inbox. */
-        public static final int MESSAGE_TYPE_INBOX  = 1;
-
-        /** Message type: sent messages. */
-        public static final int MESSAGE_TYPE_SENT   = 2;
-
-        /** Message type: drafts. */
-        public static final int MESSAGE_TYPE_DRAFT  = 3;
-
-        /** Message type: outbox. */
-        public static final int MESSAGE_TYPE_OUTBOX = 4;
-
-        /** Message type: failed outgoing message. */
-        public static final int MESSAGE_TYPE_FAILED = 5;
-
-        /** Message type: queued to send later. */
-        public static final int MESSAGE_TYPE_QUEUED = 6;
-
-        /**
-         * The type of message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String TYPE = "type";
-
-        /**
-         * The thread ID of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String THREAD_ID = "thread_id";
-
-        /**
-         * The address of the other party.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ADDRESS = "address";
-
-        /**
-         * The date the message was received.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String DATE = "date";
-
-        /**
-         * The date the message was sent.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String DATE_SENT = "date_sent";
-
-        /**
-         * Has the message been read?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String READ = "read";
-
-        /**
-         * Has the message been seen by the user? The "seen" flag determines
-         * whether we need to show a notification.
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String SEEN = "seen";
-
-        /**
-         * {@code TP-Status} value for the message, or -1 if no status has been received.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String STATUS = "status";
-
-        /** TP-Status: no status received. */
-        public static final int STATUS_NONE = -1;
-        /** TP-Status: complete. */
-        public static final int STATUS_COMPLETE = 0;
-        /** TP-Status: pending. */
-        public static final int STATUS_PENDING = 32;
-        /** TP-Status: failed. */
-        public static final int STATUS_FAILED = 64;
-
-        /**
-         * The subject of the message, if present.
-         * <P>Type: TEXT</P>
-         */
-        public static final String SUBJECT = "subject";
-
-        /**
-         * The body of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String BODY = "body";
-
-        /**
-         * The ID of the sender of the conversation, if present.
-         * <P>Type: INTEGER (reference to item in {@code content://contacts/people})</P>
-         */
-        public static final String PERSON = "person";
-
-        /**
-         * The protocol identifier code.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String PROTOCOL = "protocol";
-
-        /**
-         * Is the {@code TP-Reply-Path} flag set?
-         * <P>Type: BOOLEAN</P>
-         */
-        public static final String REPLY_PATH_PRESENT = "reply_path_present";
-
-        /**
-         * The service center (SC) through which to send the message, if present.
-         * <P>Type: TEXT</P>
-         */
-        public static final String SERVICE_CENTER = "service_center";
-
-        /**
-         * Is the message locked?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String LOCKED = "locked";
-
-        /**
-         * The subscription to which the message belongs to. Its value will be
-         * < 0 if the sub id cannot be determined.
-         * <p>Type: INTEGER (long) </p>
-         */
-        public static final String SUBSCRIPTION_ID = "sub_id";
-
-        /**
-         * The MTU size of the mobile interface to which the APN connected
-         * @hide
-         */
-        public static final String MTU = "mtu";
-
-        /**
-         * Error code associated with sending or receiving this message
-         * <P>Type: INTEGER</P>
-         */
-        public static final String ERROR_CODE = "error_code";
-
-        /**
-         * The identity of the sender of a sent message. It is
-         * usually the package name of the app which sends the message.
-         * <p class="note"><strong>Note:</strong>
-         * This column is read-only. It is set by the provider and can not be changed by apps.
-         * <p>Type: TEXT</p>
-         */
-        public static final String CREATOR = "creator";
-    }
-
-    /**
-     * Contains all text-based SMS messages.
-     */
-    public static final class Sms implements BaseColumns, TextBasedSmsColumns {
-
-        /**
-         * Not instantiable.
-         * @hide
-         */
-        private Sms() {
-        }
-
-        /**
-         * Used to determine the currently configured default SMS package.
-         * @param context context of the requesting application
-         * @return package name for the default SMS package or null
-         */
-        public static String getDefaultSmsPackage(Context context) {
-            ComponentName component = SmsApplication.getDefaultSmsApplication(context, false);
-            if (component != null) {
-                return component.getPackageName();
-            }
-            return null;
-        }
-
-        /**
-         * Return cursor for table query.
-         * @hide
-         */
-        public static Cursor query(ContentResolver cr, String[] projection) {
-            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
-        }
-
-        /**
-         * Return cursor for table query.
-         * @hide
-         */
-        public static Cursor query(ContentResolver cr, String[] projection,
-                String where, String orderBy) {
-            return cr.query(CONTENT_URI, projection, where,
-                    null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        /**
-         * The {@code content://} style URL for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://sms");
-
-        /**
-         * The default sort order for this table.
-         */
-        public static final String DEFAULT_SORT_ORDER = "date DESC";
-
-        /**
-         * Add an SMS to the given URI.
-         *
-         * @param resolver the content resolver to use
-         * @param uri the URI to add the message to
-         * @param address the address of the sender
-         * @param body the body of the message
-         * @param subject the pseudo-subject of the message
-         * @param date the timestamp for the message
-         * @param read true if the message has been read, false if not
-         * @param deliveryReport true if a delivery report was requested, false if not
-         * @return the URI for the new message
-         * @hide
-         */
-        public static Uri addMessageToUri(ContentResolver resolver,
-                Uri uri, String address, String body, String subject,
-                Long date, boolean read, boolean deliveryReport) {
-            return addMessageToUri(SubscriptionManager.getDefaultSmsSubscriptionId(),
-                    resolver, uri, address, body, subject, date, read, deliveryReport, -1L);
-        }
-
-        /**
-         * Add an SMS to the given URI.
-         *
-         * @param resolver the content resolver to use
-         * @param uri the URI to add the message to
-         * @param address the address of the sender
-         * @param body the body of the message
-         * @param subject the psuedo-subject of the message
-         * @param date the timestamp for the message
-         * @param read true if the message has been read, false if not
-         * @param deliveryReport true if a delivery report was requested, false if not
-         * @param subId the subscription which the message belongs to
-         * @return the URI for the new message
-         * @hide
-         */
-        public static Uri addMessageToUri(int subId, ContentResolver resolver,
-                Uri uri, String address, String body, String subject,
-                Long date, boolean read, boolean deliveryReport) {
-            return addMessageToUri(subId, resolver, uri, address, body, subject,
-                    date, read, deliveryReport, -1L);
-        }
-
-        /**
-         * Add an SMS to the given URI with the specified thread ID.
-         *
-         * @param resolver the content resolver to use
-         * @param uri the URI to add the message to
-         * @param address the address of the sender
-         * @param body the body of the message
-         * @param subject the pseudo-subject of the message
-         * @param date the timestamp for the message
-         * @param read true if the message has been read, false if not
-         * @param deliveryReport true if a delivery report was requested, false if not
-         * @param threadId the thread_id of the message
-         * @return the URI for the new message
-         * @hide
-         */
-        public static Uri addMessageToUri(ContentResolver resolver,
-                Uri uri, String address, String body, String subject,
-                Long date, boolean read, boolean deliveryReport, long threadId) {
-            return addMessageToUri(SubscriptionManager.getDefaultSmsSubscriptionId(),
-                    resolver, uri, address, body, subject,
-                    date, read, deliveryReport, threadId);
-        }
-
-        /**
-         * Add an SMS to the given URI with thread_id specified.
-         *
-         * @param resolver the content resolver to use
-         * @param uri the URI to add the message to
-         * @param address the address of the sender
-         * @param body the body of the message
-         * @param subject the psuedo-subject of the message
-         * @param date the timestamp for the message
-         * @param read true if the message has been read, false if not
-         * @param deliveryReport true if a delivery report was requested, false if not
-         * @param threadId the thread_id of the message
-         * @param subId the subscription which the message belongs to
-         * @return the URI for the new message
-         * @hide
-         */
-        public static Uri addMessageToUri(int subId, ContentResolver resolver,
-                Uri uri, String address, String body, String subject,
-                Long date, boolean read, boolean deliveryReport, long threadId) {
-            ContentValues values = new ContentValues(8);
-            Rlog.v(TAG,"Telephony addMessageToUri sub id: " + subId);
-
-            values.put(SUBSCRIPTION_ID, subId);
-            values.put(ADDRESS, address);
-            if (date != null) {
-                values.put(DATE, date);
-            }
-            values.put(READ, read ? Integer.valueOf(1) : Integer.valueOf(0));
-            values.put(SUBJECT, subject);
-            values.put(BODY, body);
-            if (deliveryReport) {
-                values.put(STATUS, STATUS_PENDING);
-            }
-            if (threadId != -1L) {
-                values.put(THREAD_ID, threadId);
-            }
-            return resolver.insert(uri, values);
-        }
-
-        /**
-         * Move a message to the given folder.
-         *
-         * @param context the context to use
-         * @param uri the message to move
-         * @param folder the folder to move to
-         * @return true if the operation succeeded
-         * @hide
-         */
-        public static boolean moveMessageToFolder(Context context,
-                Uri uri, int folder, int error) {
-            if (uri == null) {
-                return false;
-            }
-
-            boolean markAsUnread = false;
-            boolean markAsRead = false;
-            switch(folder) {
-            case MESSAGE_TYPE_INBOX:
-            case MESSAGE_TYPE_DRAFT:
-                break;
-            case MESSAGE_TYPE_OUTBOX:
-            case MESSAGE_TYPE_SENT:
-                markAsRead = true;
-                break;
-            case MESSAGE_TYPE_FAILED:
-            case MESSAGE_TYPE_QUEUED:
-                markAsUnread = true;
-                break;
-            default:
-                return false;
-            }
-
-            ContentValues values = new ContentValues(3);
-
-            values.put(TYPE, folder);
-            if (markAsUnread) {
-                values.put(READ, 0);
-            } else if (markAsRead) {
-                values.put(READ, 1);
-            }
-            values.put(ERROR_CODE, error);
-
-            return 1 == SqliteWrapper.update(context, context.getContentResolver(),
-                            uri, values, null, null);
-        }
-
-        /**
-         * Returns true iff the folder (message type) identifies an
-         * outgoing message.
-         * @hide
-         */
-        public static boolean isOutgoingFolder(int messageType) {
-            return  (messageType == MESSAGE_TYPE_FAILED)
-                    || (messageType == MESSAGE_TYPE_OUTBOX)
-                    || (messageType == MESSAGE_TYPE_SENT)
-                    || (messageType == MESSAGE_TYPE_QUEUED);
-        }
-
-        /**
-         * Contains all text-based SMS messages in the SMS app inbox.
-         */
-        public static final class Inbox implements BaseColumns, TextBasedSmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Inbox() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri CONTENT_URI = Uri.parse("content://sms/inbox");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-
-            /**
-             * Add an SMS to the Draft box.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the pseudo-subject of the message
-             * @param date the timestamp for the message
-             * @param read true if the message has been read, false if not
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(ContentResolver resolver,
-                    String address, String body, String subject, Long date,
-                    boolean read) {
-                return addMessageToUri(SubscriptionManager.getDefaultSmsSubscriptionId(),
-                        resolver, CONTENT_URI, address, body, subject, date, read, false);
-            }
-
-            /**
-             * Add an SMS to the Draft box.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the psuedo-subject of the message
-             * @param date the timestamp for the message
-             * @param read true if the message has been read, false if not
-             * @param subId the subscription which the message belongs to
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(int subId, ContentResolver resolver,
-                    String address, String body, String subject, Long date, boolean read) {
-                return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
-                        subject, date, read, false);
-            }
-        }
-
-        /**
-         * Contains all sent text-based SMS messages in the SMS app.
-         */
-        public static final class Sent implements BaseColumns, TextBasedSmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Sent() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri CONTENT_URI = Uri.parse("content://sms/sent");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-
-            /**
-             * Add an SMS to the Draft box.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the pseudo-subject of the message
-             * @param date the timestamp for the message
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(ContentResolver resolver,
-                    String address, String body, String subject, Long date) {
-                return addMessageToUri(SubscriptionManager.getDefaultSmsSubscriptionId(),
-                        resolver, CONTENT_URI, address, body, subject, date, true, false);
-            }
-
-            /**
-             * Add an SMS to the Draft box.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the psuedo-subject of the message
-             * @param date the timestamp for the message
-             * @param subId the subscription which the message belongs to
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(int subId, ContentResolver resolver,
-                    String address, String body, String subject, Long date) {
-                return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
-                        subject, date, true, false);
-            }
-        }
-
-        /**
-         * Contains all sent text-based SMS messages in the SMS app.
-         */
-        public static final class Draft implements BaseColumns, TextBasedSmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Draft() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri CONTENT_URI = Uri.parse("content://sms/draft");
-
-           /**
-            * @hide
-            */
-            public static Uri addMessage(ContentResolver resolver,
-                    String address, String body, String subject, Long date) {
-                return addMessageToUri(SubscriptionManager.getDefaultSmsSubscriptionId(),
-                        resolver, CONTENT_URI, address, body, subject, date, true, false);
-            }
-
-            /**
-             * Add an SMS to the Draft box.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the psuedo-subject of the message
-             * @param date the timestamp for the message
-             * @param subId the subscription which the message belongs to
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(int subId, ContentResolver resolver,
-                    String address, String body, String subject, Long date) {
-                return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
-                        subject, date, true, false);
-            }
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-        }
-
-        /**
-         * Contains all pending outgoing text-based SMS messages.
-         */
-        public static final class Outbox implements BaseColumns, TextBasedSmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Outbox() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri CONTENT_URI = Uri.parse("content://sms/outbox");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-
-            /**
-             * Add an SMS to the outbox.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the pseudo-subject of the message
-             * @param date the timestamp for the message
-             * @param deliveryReport whether a delivery report was requested for the message
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(ContentResolver resolver,
-                    String address, String body, String subject, Long date,
-                    boolean deliveryReport, long threadId) {
-                return addMessageToUri(SubscriptionManager.getDefaultSmsSubscriptionId(),
-                        resolver, CONTENT_URI, address, body, subject, date,
-                        true, deliveryReport, threadId);
-            }
-
-            /**
-             * Add an SMS to the Out box.
-             *
-             * @param resolver the content resolver to use
-             * @param address the address of the sender
-             * @param body the body of the message
-             * @param subject the psuedo-subject of the message
-             * @param date the timestamp for the message
-             * @param deliveryReport whether a delivery report was requested for the message
-             * @param subId the subscription which the message belongs to
-             * @return the URI for the new message
-             * @hide
-             */
-            public static Uri addMessage(int subId, ContentResolver resolver,
-                    String address, String body, String subject, Long date,
-                    boolean deliveryReport, long threadId) {
-                return addMessageToUri(subId, resolver, CONTENT_URI, address, body,
-                        subject, date, true, deliveryReport, threadId);
-            }
-        }
-
-        /**
-         * Contains all sent text-based SMS messages in the SMS app.
-         */
-        public static final class Conversations
-                implements BaseColumns, TextBasedSmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Conversations() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri CONTENT_URI = Uri.parse("content://sms/conversations");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-
-            /**
-             * The first 45 characters of the body of the message.
-             * <P>Type: TEXT</P>
-             */
-            public static final String SNIPPET = "snippet";
-
-            /**
-             * The number of messages in the conversation.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String MESSAGE_COUNT = "msg_count";
-        }
-
-        /**
-         * Contains constants for SMS related Intents that are broadcast.
-         */
-        public static final class Intents {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Intents() {
-            }
-
-            /**
-             * Set by BroadcastReceiver to indicate that the message was handled
-             * successfully.
-             */
-            public static final int RESULT_SMS_HANDLED = 1;
-
-            /**
-             * Set by BroadcastReceiver to indicate a generic error while
-             * processing the message.
-             */
-            public static final int RESULT_SMS_GENERIC_ERROR = 2;
-
-            /**
-             * Set by BroadcastReceiver to indicate insufficient memory to store
-             * the message.
-             */
-            public static final int RESULT_SMS_OUT_OF_MEMORY = 3;
-
-            /**
-             * Set by BroadcastReceiver to indicate that the message, while
-             * possibly valid, is of a format or encoding that is not
-             * supported.
-             */
-            public static final int RESULT_SMS_UNSUPPORTED = 4;
-
-            /**
-             * Set by BroadcastReceiver to indicate a duplicate incoming message.
-             */
-            public static final int RESULT_SMS_DUPLICATED = 5;
-
-            /**
-             * Activity action: Ask the user to change the default
-             * SMS application. This will show a dialog that asks the
-             * user whether they want to replace the current default
-             * SMS application with the one specified in
-             * {@link #EXTRA_PACKAGE_NAME}.
-             */
-            @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-            public static final String ACTION_CHANGE_DEFAULT =
-                    "android.provider.Telephony.ACTION_CHANGE_DEFAULT";
-
-            /**
-             * The PackageName string passed in as an
-             * extra for {@link #ACTION_CHANGE_DEFAULT}
-             *
-             * @see #ACTION_CHANGE_DEFAULT
-             */
-            public static final String EXTRA_PACKAGE_NAME = "package";
-
-            /**
-             * Broadcast Action: A new text-based SMS message has been received
-             * by the device. This intent will only be delivered to the default
-             * sms app. That app is responsible for writing the message and notifying
-             * the user. The intent will have the following extra values:</p>
-             *
-             * <ul>
-             *   <li><em>"pdus"</em> - An Object[] of byte[]s containing the PDUs
-             *   that make up the message.</li>
-             *   <li><em>"format"</em> - A String describing the format of the PDUs. It can
-             *   be either "3gpp" or "3gpp2".</li>
-             *   <li><em>"subscription"</em> - An optional long value of the subscription id which
-             *   received the message.</li>
-             *   <li><em>"slot"</em> - An optional int value of the SIM slot containing the
-             *   subscription.</li>
-             *   <li><em>"phone"</em> - An optional int value of the phone id associated with the
-             *   subscription.</li>
-             *   <li><em>"errorCode"</em> - An optional int error code associated with receiving
-             *   the message.</li>
-             * </ul>
-             *
-             * <p>The extra values can be extracted using
-             * {@link #getMessagesFromIntent(Intent)}.</p>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p class="note"><strong>Note:</strong>
-             * The broadcast receiver that filters for this intent must declare
-             * {@link android.Manifest.permission#BROADCAST_SMS} as a required permission in
-             * the <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code
-             * <receiver>}</a> tag.
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SMS_DELIVER_ACTION =
-                    "android.provider.Telephony.SMS_DELIVER";
-
-            /**
-             * Broadcast Action: A new text-based SMS message has been received
-             * by the device. This intent will be delivered to all registered
-             * receivers as a notification. These apps are not expected to write the
-             * message or notify the user. The intent will have the following extra
-             * values:</p>
-             *
-             * <ul>
-             *   <li><em>"pdus"</em> - An Object[] of byte[]s containing the PDUs
-             *   that make up the message.</li>
-             * </ul>
-             *
-             * <p>The extra values can be extracted using
-             * {@link #getMessagesFromIntent(Intent)}.</p>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SMS_RECEIVED_ACTION =
-                    "android.provider.Telephony.SMS_RECEIVED";
-
-            /**
-             * Broadcast Action: A new data based SMS message has been received
-             * by the device. This intent will be delivered to all registered
-             * receivers as a notification. The intent will have the following extra
-             * values:</p>
-             *
-             * <ul>
-             *   <li><em>"pdus"</em> - An Object[] of byte[]s containing the PDUs
-             *   that make up the message.</li>
-             * </ul>
-             *
-             * <p>The extra values can be extracted using
-             * {@link #getMessagesFromIntent(Intent)}.</p>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String DATA_SMS_RECEIVED_ACTION =
-                    "android.intent.action.DATA_SMS_RECEIVED";
-
-            /**
-             * Broadcast Action: A new WAP PUSH message has been received by the
-             * device. This intent will only be delivered to the default
-             * sms app. That app is responsible for writing the message and notifying
-             * the user. The intent will have the following extra values:</p>
-             *
-             * <ul>
-             *   <li><em>"transactionId"</em> - (Integer) The WAP transaction ID</li>
-             *   <li><em>"pduType"</em> - (Integer) The WAP PDU type</li>
-             *   <li><em>"header"</em> - (byte[]) The header of the message</li>
-             *   <li><em>"data"</em> - (byte[]) The data payload of the message</li>
-             *   <li><em>"contentTypeParameters" </em>
-             *   -(HashMap&lt;String,String&gt;) Any parameters associated with the content type
-             *   (decoded from the WSP Content-Type header)</li>
-             *   <li><em>"subscription"</em> - An optional long value of the subscription id which
-             *   received the message.</li>
-             *   <li><em>"slot"</em> - An optional int value of the SIM slot containing the
-             *   subscription.</li>
-             *   <li><em>"phone"</em> - An optional int value of the phone id associated with the
-             *   subscription.</li>
-             * </ul>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>The contentTypeParameters extra value is map of content parameters keyed by
-             * their names.</p>
-             *
-             * <p>If any unassigned well-known parameters are encountered, the key of the map will
-             * be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter.  If
-             * a parameter has No-Value the value in the map will be null.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_MMS} or
-             * {@link android.Manifest.permission#RECEIVE_WAP_PUSH} (depending on WAP PUSH type) to
-             * receive.</p>
-             *
-             * <p class="note"><strong>Note:</strong>
-             * The broadcast receiver that filters for this intent must declare
-             * {@link android.Manifest.permission#BROADCAST_WAP_PUSH} as a required permission in
-             * the <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code
-             * <receiver>}</a> tag.
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String WAP_PUSH_DELIVER_ACTION =
-                    "android.provider.Telephony.WAP_PUSH_DELIVER";
-
-            /**
-             * Broadcast Action: A new WAP PUSH message has been received by the
-             * device. This intent will be delivered to all registered
-             * receivers as a notification. These apps are not expected to write the
-             * message or notify the user. The intent will have the following extra
-             * values:</p>
-             *
-             * <ul>
-             *   <li><em>"transactionId"</em> - (Integer) The WAP transaction ID</li>
-             *   <li><em>"pduType"</em> - (Integer) The WAP PDU type</li>
-             *   <li><em>"header"</em> - (byte[]) The header of the message</li>
-             *   <li><em>"data"</em> - (byte[]) The data payload of the message</li>
-             *   <li><em>"contentTypeParameters"</em>
-             *   - (HashMap&lt;String,String&gt;) Any parameters associated with the content type
-             *   (decoded from the WSP Content-Type header)</li>
-             * </ul>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>The contentTypeParameters extra value is map of content parameters keyed by
-             * their names.</p>
-             *
-             * <p>If any unassigned well-known parameters are encountered, the key of the map will
-             * be 'unassigned/0x...', where '...' is the hex value of the unassigned parameter.  If
-             * a parameter has No-Value the value in the map will be null.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_MMS} or
-             * {@link android.Manifest.permission#RECEIVE_WAP_PUSH} (depending on WAP PUSH type) to
-             * receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String WAP_PUSH_RECEIVED_ACTION =
-                    "android.provider.Telephony.WAP_PUSH_RECEIVED";
-
-            /**
-             * Broadcast Action: A new Cell Broadcast message has been received
-             * by the device. The intent will have the following extra
-             * values:</p>
-             *
-             * <ul>
-             *   <li><em>"message"</em> - An SmsCbMessage object containing the broadcast message
-             *   data. This is not an emergency alert, so ETWS and CMAS data will be null.</li>
-             * </ul>
-             *
-             * <p>The extra values can be extracted using
-             * {@link #getMessagesFromIntent(Intent)}.</p>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SMS_CB_RECEIVED_ACTION =
-                    "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)
-            @TestApi
-            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>
-             *
-             * <ul>
-             *   <li><em>"message"</em> - An SmsCbMessage object containing the broadcast message
-             *   data, including ETWS or CMAS warning notification info if present.</li>
-             * </ul>
-             *
-             * <p>The extra values can be extracted using
-             * {@link #getMessagesFromIntent(Intent)}.</p>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_EMERGENCY_BROADCAST} to
-             * receive.</p>
-             * @removed
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SMS_EMERGENCY_CB_RECEIVED_ACTION =
-                    "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
-
-            /**
-             * Broadcast Action: A new CDMA SMS has been received containing Service Category
-             * Program Data (updates the list of enabled broadcast channels). The intent will
-             * have the following extra values:</p>
-             *
-             * <ul>
-             *   <li><em>"operations"</em> - An array of CdmaSmsCbProgramData objects containing
-             *   the service category operations (add/delete/clear) to perform.</li>
-             * </ul>
-             *
-             * <p>The extra values can be extracted using
-             * {@link #getMessagesFromIntent(Intent)}.</p>
-             *
-             * <p>If a BroadcastReceiver encounters an error while processing
-             * this intent it should set the result code appropriately.</p>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION =
-                    "android.provider.Telephony.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED";
-
-            /**
-             * Broadcast Action: The SIM storage for SMS messages is full.  If
-             * space is not freed, messages targeted for the SIM (class 2) may
-             * not be saved.
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SIM_FULL_ACTION =
-                    "android.provider.Telephony.SIM_FULL";
-
-            /**
-             * Broadcast Action: An incoming SMS has been rejected by the
-             * telephony framework.  This intent is sent in lieu of any
-             * of the RECEIVED_ACTION intents.  The intent will have the
-             * following extra value:</p>
-             *
-             * <ul>
-             *   <li><em>"result"</em> - An int result code, e.g. {@link #RESULT_SMS_OUT_OF_MEMORY}
-             *   indicating the error returned to the network.</li>
-             * </ul>
-             *
-             * <p>Requires {@link android.Manifest.permission#RECEIVE_SMS} to receive.</p>
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String SMS_REJECTED_ACTION =
-                "android.provider.Telephony.SMS_REJECTED";
-
-            /**
-             * Broadcast Action: An incoming MMS has been downloaded. The intent is sent to all
-             * users, except for secondary users where SMS has been disabled and to managed
-             * profiles.
-             * @hide
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String MMS_DOWNLOADED_ACTION =
-                "android.provider.Telephony.MMS_DOWNLOADED";
-
-            /**
-             * Broadcast action: When the default SMS package changes,
-             * the previous default SMS package and the new default SMS
-             * package are sent this broadcast to notify them of the change.
-             * A boolean is specified in {@link #EXTRA_IS_DEFAULT_SMS_APP} to
-             * indicate whether the package is the new default SMS package.
-            */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String ACTION_DEFAULT_SMS_PACKAGE_CHANGED =
-                            "android.provider.action.DEFAULT_SMS_PACKAGE_CHANGED";
-
-            /**
-             * The IsDefaultSmsApp boolean passed as an
-             * extra for {@link #ACTION_DEFAULT_SMS_PACKAGE_CHANGED} to indicate whether the
-             * SMS app is becoming the default SMS app or is no longer the default.
-             *
-             * @see #ACTION_DEFAULT_SMS_PACKAGE_CHANGED
-             */
-            public static final String EXTRA_IS_DEFAULT_SMS_APP =
-                    "android.provider.extra.IS_DEFAULT_SMS_APP";
-
-            /**
-             * Broadcast action: When a change is made to the SmsProvider or
-             * MmsProvider by a process other than the default SMS application,
-             * this intent is broadcast to the default SMS application so it can
-             * re-sync or update the change. The uri that was used to call the provider
-             * can be retrieved from the intent with getData(). The actual affected uris
-             * (which would depend on the selection specified) are not included.
-            */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String ACTION_EXTERNAL_PROVIDER_CHANGE =
-                          "android.provider.action.EXTERNAL_PROVIDER_CHANGE";
-
-            /**
-             * Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
-             * {@link #DATA_SMS_RECEIVED_ACTION} intent.
-             *
-             * @param intent the intent to read from
-             * @return an array of SmsMessages for the PDUs
-             */
-            public static SmsMessage[] getMessagesFromIntent(Intent intent) {
-                Object[] messages;
-                try {
-                    messages = (Object[]) intent.getSerializableExtra("pdus");
-                }
-                catch (ClassCastException e) {
-                    Rlog.e(TAG, "getMessagesFromIntent: " + e);
-                    return null;
-                }
-
-                if (messages == null) {
-                    Rlog.e(TAG, "pdus does not exist in the intent");
-                    return null;
-                }
-
-                String format = intent.getStringExtra("format");
-                int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
-                        SubscriptionManager.getDefaultSmsSubscriptionId());
-
-                Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
-
-                int pduCount = messages.length;
-                SmsMessage[] msgs = new SmsMessage[pduCount];
-
-                for (int i = 0; i < pduCount; i++) {
-                    byte[] pdu = (byte[]) messages[i];
-                    msgs[i] = SmsMessage.createFromPdu(pdu, format);
-                    if (msgs[i] != null) msgs[i].setSubId(subId);
-                }
-                return msgs;
-            }
-        }
-    }
-
-    /**
-     * Base columns for tables that contain MMSs.
-     */
-    public interface BaseMmsColumns extends BaseColumns {
-
-        /** Message box: all messages. */
-        public static final int MESSAGE_BOX_ALL    = 0;
-        /** Message box: inbox. */
-        public static final int MESSAGE_BOX_INBOX  = 1;
-        /** Message box: sent messages. */
-        public static final int MESSAGE_BOX_SENT   = 2;
-        /** Message box: drafts. */
-        public static final int MESSAGE_BOX_DRAFTS = 3;
-        /** Message box: outbox. */
-        public static final int MESSAGE_BOX_OUTBOX = 4;
-        /** Message box: failed. */
-        public static final int MESSAGE_BOX_FAILED = 5;
-
-        /**
-         * The thread ID of the message.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String THREAD_ID = "thread_id";
-
-        /**
-         * The date the message was received.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String DATE = "date";
-
-        /**
-         * The date the message was sent.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String DATE_SENT = "date_sent";
-
-        /**
-         * The box which the message belongs to, e.g. {@link #MESSAGE_BOX_INBOX}.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MESSAGE_BOX = "msg_box";
-
-        /**
-         * Has the message been read?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String READ = "read";
-
-        /**
-         * Has the message been seen by the user? The "seen" flag determines
-         * whether we need to show a new message notification.
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String SEEN = "seen";
-
-        /**
-         * Does the message have only a text part (can also have a subject) with
-         * no picture, slideshow, sound, etc. parts?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String TEXT_ONLY = "text_only";
-
-        /**
-         * The {@code Message-ID} of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MESSAGE_ID = "m_id";
-
-        /**
-         * The subject of the message, if present.
-         * <P>Type: TEXT</P>
-         */
-        public static final String SUBJECT = "sub";
-
-        /**
-         * The character set of the subject, if present.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String SUBJECT_CHARSET = "sub_cs";
-
-        /**
-         * The {@code Content-Type} of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String CONTENT_TYPE = "ct_t";
-
-        /**
-         * The {@code Content-Location} of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String CONTENT_LOCATION = "ct_l";
-
-        /**
-         * The expiry time of the message.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String EXPIRY = "exp";
-
-        /**
-         * The class of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MESSAGE_CLASS = "m_cls";
-
-        /**
-         * The type of the message defined by MMS spec.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MESSAGE_TYPE = "m_type";
-
-        /**
-         * The version of the specification that this message conforms to.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MMS_VERSION = "v";
-
-        /**
-         * The size of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MESSAGE_SIZE = "m_size";
-
-        /**
-         * The priority of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String PRIORITY = "pri";
-
-        /**
-         * The {@code read-report} of the message.
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String READ_REPORT = "rr";
-
-        /**
-         * Is read report allowed?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String REPORT_ALLOWED = "rpt_a";
-
-        /**
-         * The {@code response-status} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String RESPONSE_STATUS = "resp_st";
-
-        /**
-         * The {@code status} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String STATUS = "st";
-
-        /**
-         * The {@code transaction-id} of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String TRANSACTION_ID = "tr_id";
-
-        /**
-         * The {@code retrieve-status} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String RETRIEVE_STATUS = "retr_st";
-
-        /**
-         * The {@code retrieve-text} of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String RETRIEVE_TEXT = "retr_txt";
-
-        /**
-         * The character set of the retrieve-text.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String RETRIEVE_TEXT_CHARSET = "retr_txt_cs";
-
-        /**
-         * The {@code read-status} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String READ_STATUS = "read_status";
-
-        /**
-         * The {@code content-class} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CONTENT_CLASS = "ct_cls";
-
-        /**
-         * The {@code delivery-report} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String DELIVERY_REPORT = "d_rpt";
-
-        /**
-         * The {@code delivery-time-token} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String DELIVERY_TIME_TOKEN = "d_tm_tok";
-
-        /**
-         * The {@code delivery-time} of the message.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String DELIVERY_TIME = "d_tm";
-
-        /**
-         * The {@code response-text} of the message.
-         * <P>Type: TEXT</P>
-         */
-        public static final String RESPONSE_TEXT = "resp_txt";
-
-        /**
-         * The {@code sender-visibility} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String SENDER_VISIBILITY = "s_vis";
-
-        /**
-         * The {@code reply-charging} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLY_CHARGING = "r_chg";
-
-        /**
-         * The {@code reply-charging-deadline-token} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLY_CHARGING_DEADLINE_TOKEN = "r_chg_dl_tok";
-
-        /**
-         * The {@code reply-charging-deadline} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLY_CHARGING_DEADLINE = "r_chg_dl";
-
-        /**
-         * The {@code reply-charging-id} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLY_CHARGING_ID = "r_chg_id";
-
-        /**
-         * The {@code reply-charging-size} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLY_CHARGING_SIZE = "r_chg_sz";
-
-        /**
-         * The {@code previously-sent-by} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String PREVIOUSLY_SENT_BY = "p_s_by";
-
-        /**
-         * The {@code previously-sent-date} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String PREVIOUSLY_SENT_DATE = "p_s_d";
-
-        /**
-         * The {@code store} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String STORE = "store";
-
-        /**
-         * The {@code mm-state} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MM_STATE = "mm_st";
-
-        /**
-         * The {@code mm-flags-token} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MM_FLAGS_TOKEN = "mm_flg_tok";
-
-        /**
-         * The {@code mm-flags} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MM_FLAGS = "mm_flg";
-
-        /**
-         * The {@code store-status} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String STORE_STATUS = "store_st";
-
-        /**
-         * The {@code store-status-text} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String STORE_STATUS_TEXT = "store_st_txt";
-
-        /**
-         * The {@code stored} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String STORED = "stored";
-
-        /**
-         * The {@code totals} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String TOTALS = "totals";
-
-        /**
-         * The {@code mbox-totals} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MBOX_TOTALS = "mb_t";
-
-        /**
-         * The {@code mbox-totals-token} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MBOX_TOTALS_TOKEN = "mb_t_tok";
-
-        /**
-         * The {@code quotas} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String QUOTAS = "qt";
-
-        /**
-         * The {@code mbox-quotas} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MBOX_QUOTAS = "mb_qt";
-
-        /**
-         * The {@code mbox-quotas-token} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MBOX_QUOTAS_TOKEN = "mb_qt_tok";
-
-        /**
-         * The {@code message-count} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String MESSAGE_COUNT = "m_cnt";
-
-        /**
-         * The {@code start} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String START = "start";
-
-        /**
-         * The {@code distribution-indicator} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String DISTRIBUTION_INDICATOR = "d_ind";
-
-        /**
-         * The {@code element-descriptor} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String ELEMENT_DESCRIPTOR = "e_des";
-
-        /**
-         * The {@code limit} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String LIMIT = "limit";
-
-        /**
-         * The {@code recommended-retrieval-mode} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String RECOMMENDED_RETRIEVAL_MODE = "r_r_mod";
-
-        /**
-         * The {@code recommended-retrieval-mode-text} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String RECOMMENDED_RETRIEVAL_MODE_TEXT = "r_r_mod_txt";
-
-        /**
-         * The {@code status-text} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String STATUS_TEXT = "st_txt";
-
-        /**
-         * The {@code applic-id} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String APPLIC_ID = "apl_id";
-
-        /**
-         * The {@code reply-applic-id} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLY_APPLIC_ID = "r_apl_id";
-
-        /**
-         * The {@code aux-applic-id} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String AUX_APPLIC_ID = "aux_apl_id";
-
-        /**
-         * The {@code drm-content} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String DRM_CONTENT = "drm_c";
-
-        /**
-         * The {@code adaptation-allowed} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String ADAPTATION_ALLOWED = "adp_a";
-
-        /**
-         * The {@code replace-id} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String REPLACE_ID = "repl_id";
-
-        /**
-         * The {@code cancel-id} of the message.
-         * <P>Type: TEXT</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String CANCEL_ID = "cl_id";
-
-        /**
-         * The {@code cancel-status} of the message.
-         * <P>Type: INTEGER</P>
-         * @deprecated this column is no longer supported.
-         * @hide
-         */
-        @Deprecated
-        public static final String CANCEL_STATUS = "cl_st";
-
-        /**
-         * Is the message locked?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String LOCKED = "locked";
-
-        /**
-         * The subscription to which the message belongs to. Its value will be
-         * < 0 if the sub id cannot be determined.
-         * <p>Type: INTEGER (long)</p>
-         */
-        public static final String SUBSCRIPTION_ID = "sub_id";
-
-        /**
-         * The identity of the sender of a sent message. It is
-         * usually the package name of the app which sends the message.
-         * <p class="note"><strong>Note:</strong>
-         * This column is read-only. It is set by the provider and can not be changed by apps.
-         * <p>Type: TEXT</p>
-         */
-        public static final String CREATOR = "creator";
-    }
-
-    /**
-     * Columns for the "canonical_addresses" table used by MMS and SMS.
-     */
-    public interface CanonicalAddressesColumns extends BaseColumns {
-        /**
-         * An address used in MMS or SMS.  Email addresses are
-         * converted to lower case and are compared by string
-         * equality.  Other addresses are compared using
-         * PHONE_NUMBERS_EQUAL.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ADDRESS = "address";
-    }
-
-    /**
-     * Columns for the "threads" table used by MMS and SMS.
-     */
-    public interface ThreadsColumns extends BaseColumns {
-
-        /**
-         * The date at which the thread was created.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String DATE = "date";
-
-        /**
-         * A string encoding of the recipient IDs of the recipients of
-         * the message, in numerical order and separated by spaces.
-         * <P>Type: TEXT</P>
-         */
-        public static final String RECIPIENT_IDS = "recipient_ids";
-
-        /**
-         * The message count of the thread.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MESSAGE_COUNT = "message_count";
-
-        /**
-         * Indicates whether all messages of the thread have been read.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String READ = "read";
-
-        /**
-         * The snippet of the latest message in the thread.
-         * <P>Type: TEXT</P>
-         */
-        public static final String SNIPPET = "snippet";
-
-        /**
-         * The charset of the snippet.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String SNIPPET_CHARSET = "snippet_cs";
-
-        /**
-         * Type of the thread, either {@link Threads#COMMON_THREAD} or
-         * {@link Threads#BROADCAST_THREAD}.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String TYPE = "type";
-
-        /**
-         * Indicates whether there is a transmission error in the thread.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String ERROR = "error";
-
-        /**
-         * Indicates whether this thread contains any attachments.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String HAS_ATTACHMENT = "has_attachment";
-
-        /**
-         * If the thread is archived
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String ARCHIVED = "archived";
-    }
-
-    /**
-     * Helper functions for the "threads" table used by MMS and SMS.
-     */
-    public static final class Threads implements ThreadsColumns {
-
-        private static final String[] ID_PROJECTION = { BaseColumns._ID };
-
-        /**
-         * Private {@code content://} style URL for this table. Used by
-         * {@link #getOrCreateThreadId(android.content.Context, java.util.Set)}.
-         */
-        private static final Uri THREAD_ID_CONTENT_URI = Uri.parse(
-                "content://mms-sms/threadID");
-
-        /**
-         * The {@code content://} style URL for this table, by conversation.
-         */
-        public static final Uri CONTENT_URI = Uri.withAppendedPath(
-                MmsSms.CONTENT_URI, "conversations");
-
-        /**
-         * The {@code content://} style URL for this table, for obsolete threads.
-         */
-        public static final Uri OBSOLETE_THREADS_URI = Uri.withAppendedPath(
-                CONTENT_URI, "obsolete");
-
-        /** Thread type: common thread. */
-        public static final int COMMON_THREAD    = 0;
-
-        /** Thread type: broadcast thread. */
-        public static final int BROADCAST_THREAD = 1;
-
-        /**
-         * Not instantiable.
-         * @hide
-         */
-        private Threads() {
-        }
-
-        /**
-         * This is a single-recipient version of {@code getOrCreateThreadId}.
-         * It's convenient for use with SMS messages.
-         * @param context the context object to use.
-         * @param recipient the recipient to send to.
-         */
-        public static long getOrCreateThreadId(Context context, String recipient) {
-            Set<String> recipients = new HashSet<String>();
-
-            recipients.add(recipient);
-            return getOrCreateThreadId(context, recipients);
-        }
-
-        /**
-         * Given the recipients list and subject of an unsaved message,
-         * return its thread ID.  If the message starts a new thread,
-         * allocate a new thread ID.  Otherwise, use the appropriate
-         * existing thread ID.
-         *
-         * <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>
-         */
-        public static long getOrCreateThreadId(
-                Context context, Set<String> recipients) {
-            Uri.Builder uriBuilder = THREAD_ID_CONTENT_URI.buildUpon();
-
-            for (String recipient : recipients) {
-                if (Mms.isEmailAddress(recipient)) {
-                    recipient = Mms.extractAddrSpec(recipient);
-                }
-
-                uriBuilder.appendQueryParameter("recipient", recipient);
-            }
-
-            Uri uri = uriBuilder.build();
-            //if (DEBUG) Rlog.v(TAG, "getOrCreateThreadId uri: " + uri);
-
-            Cursor cursor = SqliteWrapper.query(context, context.getContentResolver(),
-                    uri, ID_PROJECTION, null, null, null);
-            if (cursor != null) {
-                try {
-                    if (cursor.moveToFirst()) {
-                        return cursor.getLong(0);
-                    } else {
-                        Rlog.e(TAG, "getOrCreateThreadId returned no rows!");
-                    }
-                } finally {
-                    cursor.close();
-                }
-            }
-
-            Rlog.e(TAG, "getOrCreateThreadId failed with " + recipients.size() + " recipients");
-            throw new IllegalArgumentException("Unable to find or allocate a thread ID.");
-        }
-    }
-
-    /**
-     * Contains all MMS messages.
-     */
-    public static final class Mms implements BaseMmsColumns {
-
-        /**
-         * Not instantiable.
-         * @hide
-         */
-        private Mms() {
-        }
-
-        /**
-         * The {@code content://} URI for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://mms");
-
-        /**
-         * Content URI for getting MMS report requests.
-         */
-        public static final Uri REPORT_REQUEST_URI = Uri.withAppendedPath(
-                                            CONTENT_URI, "report-request");
-
-        /**
-         * Content URI for getting MMS report status.
-         */
-        public static final Uri REPORT_STATUS_URI = Uri.withAppendedPath(
-                                            CONTENT_URI, "report-status");
-
-        /**
-         * The default sort order for this table.
-         */
-        public static final String DEFAULT_SORT_ORDER = "date DESC";
-
-        /**
-         * Regex pattern for names and email addresses.
-         * <ul>
-         *     <li><em>mailbox</em> = {@code name-addr}</li>
-         *     <li><em>name-addr</em> = {@code [display-name] angle-addr}</li>
-         *     <li><em>angle-addr</em> = {@code [CFWS] "<" addr-spec ">" [CFWS]}</li>
-         * </ul>
-         * @hide
-         */
-        public static final Pattern NAME_ADDR_EMAIL_PATTERN =
-                Pattern.compile("\\s*(\"[^\"]*\"|[^<>\"]+)\\s*<([^<>]+)>\\s*");
-
-        /**
-         * Helper method to query this table.
-         * @hide
-         */
-        public static Cursor query(
-                ContentResolver cr, String[] projection) {
-            return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
-        }
-
-        /**
-         * Helper method to query this table.
-         * @hide
-         */
-        public static Cursor query(
-                ContentResolver cr, String[] projection,
-                String where, String orderBy) {
-            return cr.query(CONTENT_URI, projection,
-                    where, null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        /**
-         * Helper method to extract email address from address string.
-         * @hide
-         */
-        public static String extractAddrSpec(String address) {
-            Matcher match = NAME_ADDR_EMAIL_PATTERN.matcher(address);
-
-            if (match.matches()) {
-                return match.group(2);
-            }
-            return address;
-        }
-
-        /**
-         * Is the specified address an email address?
-         *
-         * @param address the input address to test
-         * @return true if address is an email address; false otherwise.
-         * @hide
-         */
-        public static boolean isEmailAddress(String address) {
-            if (TextUtils.isEmpty(address)) {
-                return false;
-            }
-
-            String s = extractAddrSpec(address);
-            Matcher match = Patterns.EMAIL_ADDRESS.matcher(s);
-            return match.matches();
-        }
-
-        /**
-         * Is the specified number a phone number?
-         *
-         * @param number the input number to test
-         * @return true if number is a phone number; false otherwise.
-         * @hide
-         */
-        public static boolean isPhoneNumber(String number) {
-            if (TextUtils.isEmpty(number)) {
-                return false;
-            }
-
-            Matcher match = Patterns.PHONE.matcher(number);
-            return match.matches();
-        }
-
-        /**
-         * Contains all MMS messages in the MMS app inbox.
-         */
-        public static final class Inbox implements BaseMmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Inbox() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri
-                    CONTENT_URI = Uri.parse("content://mms/inbox");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-        }
-
-        /**
-         * Contains all MMS messages in the MMS app sent folder.
-         */
-        public static final class Sent implements BaseMmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Sent() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri
-                    CONTENT_URI = Uri.parse("content://mms/sent");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-        }
-
-        /**
-         * Contains all MMS messages in the MMS app drafts folder.
-         */
-        public static final class Draft implements BaseMmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Draft() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri
-                    CONTENT_URI = Uri.parse("content://mms/drafts");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-        }
-
-        /**
-         * Contains all MMS messages in the MMS app outbox.
-         */
-        public static final class Outbox implements BaseMmsColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Outbox() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri
-                    CONTENT_URI = Uri.parse("content://mms/outbox");
-
-            /**
-             * The default sort order for this table.
-             */
-            public static final String DEFAULT_SORT_ORDER = "date DESC";
-        }
-
-        /**
-         * Contains address information for an MMS message.
-         */
-        public static final class Addr implements BaseColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Addr() {
-            }
-
-            /**
-             * The ID of MM which this address entry belongs to.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String MSG_ID = "msg_id";
-
-            /**
-             * The ID of contact entry in Phone Book.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String CONTACT_ID = "contact_id";
-
-            /**
-             * The address text.
-             * <P>Type: TEXT</P>
-             */
-            public static final String ADDRESS = "address";
-
-            /**
-             * Type of address: must be one of {@code PduHeaders.BCC},
-             * {@code PduHeaders.CC}, {@code PduHeaders.FROM}, {@code PduHeaders.TO}.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String TYPE = "type";
-
-            /**
-             * Character set of this entry (MMS charset value).
-             * <P>Type: INTEGER</P>
-             */
-            public static final String CHARSET = "charset";
-        }
-
-        /**
-         * Contains message parts.
-         */
-        public static final class Part implements BaseColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Part() {
-            }
-
-            /**
-             * The identifier of the message which this part belongs to.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String MSG_ID = "mid";
-
-            /**
-             * The order of the part.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String SEQ = "seq";
-
-            /**
-             * The content type of the part.
-             * <P>Type: TEXT</P>
-             */
-            public static final String CONTENT_TYPE = "ct";
-
-            /**
-             * The name of the part.
-             * <P>Type: TEXT</P>
-             */
-            public static final String NAME = "name";
-
-            /**
-             * The charset of the part.
-             * <P>Type: TEXT</P>
-             */
-            public static final String CHARSET = "chset";
-
-            /**
-             * The file name of the part.
-             * <P>Type: TEXT</P>
-             */
-            public static final String FILENAME = "fn";
-
-            /**
-             * The content disposition of the part.
-             * <P>Type: TEXT</P>
-             */
-            public static final String CONTENT_DISPOSITION = "cd";
-
-            /**
-             * The content ID of the part.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String CONTENT_ID = "cid";
-
-            /**
-             * The content location of the part.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String CONTENT_LOCATION = "cl";
-
-            /**
-             * The start of content-type of the message.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String CT_START = "ctt_s";
-
-            /**
-             * The type of content-type of the message.
-             * <P>Type: TEXT</P>
-             */
-            public static final String CT_TYPE = "ctt_t";
-
-            /**
-             * The location (on filesystem) of the binary data of the part.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String _DATA = "_data";
-
-            /**
-             * The message text.
-             * <P>Type: TEXT</P>
-             */
-            public static final String TEXT = "text";
-        }
-
-        /**
-         * Message send rate table.
-         */
-        public static final class Rate {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Rate() {
-            }
-
-            /**
-             * The {@code content://} style URL for this table.
-             */
-            public static final Uri CONTENT_URI = Uri.withAppendedPath(
-                    Mms.CONTENT_URI, "rate");
-
-            /**
-             * When a message was successfully sent.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String SENT_TIME = "sent_time";
-        }
-
-        /**
-         * Intents class.
-         */
-        public static final class Intents {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private Intents() {
-            }
-
-            /**
-             * Indicates that the contents of specified URIs were changed.
-             * The application which is showing or caching these contents
-             * should be updated.
-             */
-            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-            public static final String CONTENT_CHANGED_ACTION
-                    = "android.intent.action.CONTENT_CHANGED";
-
-            /**
-             * An extra field which stores the URI of deleted contents.
-             */
-            public static final String DELETED_CONTENTS = "deleted_contents";
-        }
-    }
-
-    /**
-     * Contains all MMS and SMS messages.
-     */
-    public static final class MmsSms implements BaseColumns {
-
-        /**
-         * Not instantiable.
-         * @hide
-         */
-        private MmsSms() {
-        }
-
-        /**
-         * The column to distinguish SMS and MMS messages in query results.
-         */
-        public static final String TYPE_DISCRIMINATOR_COLUMN =
-                "transport_type";
-
-        /**
-         * The {@code content://} style URL for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://mms-sms/");
-
-        /**
-         * The {@code content://} style URL for this table, by conversation.
-         */
-        public static final Uri CONTENT_CONVERSATIONS_URI = Uri.parse(
-                "content://mms-sms/conversations");
-
-        /**
-         * The {@code content://} style URL for this table, by phone number.
-         */
-        public static final Uri CONTENT_FILTER_BYPHONE_URI = Uri.parse(
-                "content://mms-sms/messages/byphone");
-
-        /**
-         * The {@code content://} style URL for undelivered messages in this table.
-         */
-        public static final Uri CONTENT_UNDELIVERED_URI = Uri.parse(
-                "content://mms-sms/undelivered");
-
-        /**
-         * The {@code content://} style URL for draft messages in this table.
-         */
-        public static final Uri CONTENT_DRAFT_URI = Uri.parse(
-                "content://mms-sms/draft");
-
-        /**
-         * The {@code content://} style URL for locked messages in this table.
-         */
-        public static final Uri CONTENT_LOCKED_URI = Uri.parse(
-                "content://mms-sms/locked");
-
-        /**
-         * Pass in a query parameter called "pattern" which is the text to search for.
-         * The sort order is fixed to be: {@code thread_id ASC, date DESC}.
-         */
-        public static final Uri SEARCH_URI = Uri.parse(
-                "content://mms-sms/search");
-
-        // Constants for message protocol types.
-
-        /** SMS protocol type. */
-        public static final int SMS_PROTO = 0;
-
-        /** MMS protocol type. */
-        public static final int MMS_PROTO = 1;
-
-        // Constants for error types of pending messages.
-
-        /** Error type: no error. */
-        public static final int NO_ERROR                      = 0;
-
-        /** Error type: generic transient error. */
-        public static final int ERR_TYPE_GENERIC              = 1;
-
-        /** Error type: SMS protocol transient error. */
-        public static final int ERR_TYPE_SMS_PROTO_TRANSIENT  = 2;
-
-        /** Error type: MMS protocol transient error. */
-        public static final int ERR_TYPE_MMS_PROTO_TRANSIENT  = 3;
-
-        /** Error type: transport failure. */
-        public static final int ERR_TYPE_TRANSPORT_FAILURE    = 4;
-
-        /** Error type: permanent error (along with all higher error values). */
-        public static final int ERR_TYPE_GENERIC_PERMANENT    = 10;
-
-        /** Error type: SMS protocol permanent error. */
-        public static final int ERR_TYPE_SMS_PROTO_PERMANENT  = 11;
-
-        /** Error type: MMS protocol permanent error. */
-        public static final int ERR_TYPE_MMS_PROTO_PERMANENT  = 12;
-
-        /**
-         * Contains pending messages info.
-         */
-        public static final class PendingMessages implements BaseColumns {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private PendingMessages() {
-            }
-
-            public static final Uri CONTENT_URI = Uri.withAppendedPath(
-                    MmsSms.CONTENT_URI, "pending");
-
-            /**
-             * The type of transport protocol (MMS or SMS).
-             * <P>Type: INTEGER</P>
-             */
-            public static final String PROTO_TYPE = "proto_type";
-
-            /**
-             * The ID of the message to be sent or downloaded.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String MSG_ID = "msg_id";
-
-            /**
-             * The type of the message to be sent or downloaded.
-             * This field is only valid for MM. For SM, its value is always set to 0.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String MSG_TYPE = "msg_type";
-
-            /**
-             * The type of the error code.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String ERROR_TYPE = "err_type";
-
-            /**
-             * The error code of sending/retrieving process.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String ERROR_CODE = "err_code";
-
-            /**
-             * How many times we tried to send or download the message.
-             * <P>Type: INTEGER</P>
-             */
-            public static final String RETRY_INDEX = "retry_index";
-
-            /**
-             * The time to do next retry.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String DUE_TIME = "due_time";
-
-            /**
-             * The time we last tried to send or download the message.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String LAST_TRY = "last_try";
-
-            /**
-             * The subscription to which the message belongs to. Its value will be
-             * < 0 if the sub id cannot be determined.
-             * <p>Type: INTEGER (long) </p>
-             */
-            public static final String SUBSCRIPTION_ID = "pending_sub_id";
-        }
-
-        /**
-         * Words table used by provider for full-text searches.
-         * @hide
-         */
-        public static final class WordsTable {
-
-            /**
-             * Not instantiable.
-             * @hide
-             */
-            private WordsTable() {}
-
-            /**
-             * Primary key.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String ID = "_id";
-
-            /**
-             * Source row ID.
-             * <P>Type: INTEGER (long)</P>
-             */
-            public static final String SOURCE_ROW_ID = "source_id";
-
-            /**
-             * Table ID (either 1 or 2).
-             * <P>Type: INTEGER</P>
-             */
-            public static final String TABLE_ID = "table_to_use";
-
-            /**
-             * The words to index.
-             * <P>Type: TEXT</P>
-             */
-            public static final String INDEXED_TEXT = "index_text";
-        }
-    }
-
-    /**
-     * Carriers class contains information about APNs, including MMSC information.
-     */
-    public static final class Carriers implements BaseColumns {
-
-        /**
-         * Not instantiable.
-         * @hide
-         */
-        private Carriers() {}
-
-        /**
-         * The {@code content://} style URL for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://telephony/carriers");
-
-        /**
-         * The default sort order for this table.
-         */
-        public static final String DEFAULT_SORT_ORDER = "name ASC";
-
-        /**
-         * Entry name.
-         * <P>Type: TEXT</P>
-         */
-        public static final String NAME = "name";
-
-        /**
-         * APN name.
-         * <P>Type: TEXT</P>
-         */
-        public static final String APN = "apn";
-
-        /**
-         * Proxy address.
-         * <P>Type: TEXT</P>
-         */
-        public static final String PROXY = "proxy";
-
-        /**
-         * Proxy port.
-         * <P>Type: TEXT</P>
-         */
-        public static final String PORT = "port";
-
-        /**
-         * MMS proxy address.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MMSPROXY = "mmsproxy";
-
-        /**
-         * MMS proxy port.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MMSPORT = "mmsport";
-
-        /**
-         * Server address.
-         * <P>Type: TEXT</P>
-         */
-        public static final String SERVER = "server";
-
-        /**
-         * APN username.
-         * <P>Type: TEXT</P>
-         */
-        public static final String USER = "user";
-
-        /**
-         * APN password.
-         * <P>Type: TEXT</P>
-         */
-        public static final String PASSWORD = "password";
-
-        /**
-         * MMSC URL.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MMSC = "mmsc";
-
-        /**
-         * Mobile Country Code (MCC).
-         * <P>Type: TEXT</P>
-         */
-        public static final String MCC = "mcc";
-
-        /**
-         * Mobile Network Code (MNC).
-         * <P>Type: TEXT</P>
-         */
-        public static final String MNC = "mnc";
-
-        /**
-         * Numeric operator ID (as String). Usually {@code MCC + MNC}.
-         * <P>Type: TEXT</P>
-         */
-        public static final String NUMERIC = "numeric";
-
-        /**
-         * Authentication type.
-         * <P>Type:  INTEGER</P>
-         */
-        public static final String AUTH_TYPE = "authtype";
-
-        /**
-         * Comma-delimited list of APN types.
-         * <P>Type: TEXT</P>
-         */
-        public static final String TYPE = "type";
-
-        /**
-         * The protocol to use to connect to this APN.
-         *
-         * One of the {@code PDP_type} values in TS 27.007 section 10.1.1.
-         * For example: {@code IP}, {@code IPV6}, {@code IPV4V6}, or {@code PPP}.
-         * <P>Type: TEXT</P>
-         */
-        public static final String PROTOCOL = "protocol";
-
-        /**
-         * The protocol to use to connect to this APN when roaming.
-         * The syntax is the same as protocol.
-         * <P>Type: TEXT</P>
-         */
-        public static final String ROAMING_PROTOCOL = "roaming_protocol";
-
-        /**
-         * Is this the current APN?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String CURRENT = "current";
-
-        /**
-         * Is this APN enabled?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String CARRIER_ENABLED = "carrier_enabled";
-
-        /**
-         * Radio Access Technology info.
-         * To check what values are allowed, refer to {@link android.telephony.ServiceState}.
-         * This should be spread to other technologies,
-         * but is currently only used for LTE (14) and eHRPD (13).
-         * <P>Type: INTEGER</P>
-         */
-        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>
-         */
-        public static final String MVNO_TYPE = "mvno_type";
-
-        /**
-         * MVNO data.
-         * Use the following examples.
-         * <ul>
-         *     <li>SPN: A MOBILE, BEN NL, ...</li>
-         *     <li>IMSI: 302720x94, 2060188, ...</li>
-         *     <li>GID: 4E, 33, ...</li>
-         * </ul>
-         * <P>Type: TEXT</P>
-         */
-        public static final String MVNO_MATCH_DATA = "mvno_match_data";
-
-        /**
-         * The subscription to which the APN belongs to
-         * <p>Type: INTEGER (long) </p>
-         */
-        public static final String SUBSCRIPTION_ID = "sub_id";
-
-        /**
-         * The profile_id to which the APN saved in modem
-         * <p>Type: INTEGER</p>
-         *@hide
-         */
-        public static final String PROFILE_ID = "profile_id";
-
-        /**
-         * Is the apn setting to be set in modem
-         * <P>Type: INTEGER (boolean)</P>
-         *@hide
-         */
-        public static final String MODEM_COGNITIVE = "modem_cognitive";
-
-        /**
-         * The max connections of this apn
-         * <p>Type: INTEGER</p>
-         *@hide
-         */
-        public static final String MAX_CONNS = "max_conns";
-
-        /**
-         * The wait time for retry of the apn
-         * <p>Type: INTEGER</p>
-         *@hide
-         */
-        public static final String WAIT_TIME = "wait_time";
-
-        /**
-         * The time to limit max connection for the apn
-         * <p>Type: INTEGER</p>
-         *@hide
-         */
-        public static final String MAX_CONNS_TIME = "max_conns_time";
-
-        /**
-         * The MTU size of the mobile interface to  which the APN connected
-         * <p>Type: INTEGER </p>
-         * @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";
-
-        /**
-         * Is this APN visible to the user?
-         * <p>Type: INTEGER (boolean) </p>
-         * @hide
-         */
-        public static final String USER_VISIBLE = "user_visible";
-
-        /**
-         * 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;
-    }
-
-    /**
-     * Contains received SMS cell broadcast messages.
-     * @hide
-     */
-    public static final class CellBroadcasts implements BaseColumns {
-
-        /**
-         * Not instantiable.
-         * @hide
-         */
-        private CellBroadcasts() {}
-
-        /**
-         * The {@code content://} URI for this table.
-         */
-        public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
-
-        /**
-         * Message geographical scope.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String GEOGRAPHICAL_SCOPE = "geo_scope";
-
-        /**
-         * Message serial number.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String SERIAL_NUMBER = "serial_number";
-
-        /**
-         * PLMN of broadcast sender. {@code SERIAL_NUMBER + PLMN + LAC + CID} uniquely identifies
-         * a broadcast for duplicate detection purposes.
-         * <P>Type: TEXT</P>
-         */
-        public static final String PLMN = "plmn";
-
-        /**
-         * Location Area (GSM) or Service Area (UMTS) of broadcast sender. Unused for CDMA.
-         * Only included if Geographical Scope of message is not PLMN wide (01).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String LAC = "lac";
-
-        /**
-         * Cell ID of message sender (GSM/UMTS). Unused for CDMA. Only included when the
-         * Geographical Scope of message is cell wide (00 or 11).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CID = "cid";
-
-        /**
-         * Message code. <em>OBSOLETE: merged into SERIAL_NUMBER.</em>
-         * <P>Type: INTEGER</P>
-         */
-        public static final String V1_MESSAGE_CODE = "message_code";
-
-        /**
-         * Message identifier. <em>OBSOLETE: renamed to SERVICE_CATEGORY.</em>
-         * <P>Type: INTEGER</P>
-         */
-        public static final String V1_MESSAGE_IDENTIFIER = "message_id";
-
-        /**
-         * Service category (GSM/UMTS: message identifier; CDMA: service category).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String SERVICE_CATEGORY = "service_category";
-
-        /**
-         * Message language code.
-         * <P>Type: TEXT</P>
-         */
-        public static final String LANGUAGE_CODE = "language";
-
-        /**
-         * Message body.
-         * <P>Type: TEXT</P>
-         */
-        public static final String MESSAGE_BODY = "body";
-
-        /**
-         * Message delivery time.
-         * <P>Type: INTEGER (long)</P>
-         */
-        public static final String DELIVERY_TIME = "date";
-
-        /**
-         * Has the message been viewed?
-         * <P>Type: INTEGER (boolean)</P>
-         */
-        public static final String MESSAGE_READ = "read";
-
-        /**
-         * Message format (3GPP or 3GPP2).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MESSAGE_FORMAT = "format";
-
-        /**
-         * Message priority (including emergency).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String MESSAGE_PRIORITY = "priority";
-
-        /**
-         * ETWS warning type (ETWS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String ETWS_WARNING_TYPE = "etws_warning_type";
-
-        /**
-         * CMAS message class (CMAS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CMAS_MESSAGE_CLASS = "cmas_message_class";
-
-        /**
-         * CMAS category (CMAS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CMAS_CATEGORY = "cmas_category";
-
-        /**
-         * CMAS response type (CMAS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CMAS_RESPONSE_TYPE = "cmas_response_type";
-
-        /**
-         * CMAS severity (CMAS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CMAS_SEVERITY = "cmas_severity";
-
-        /**
-         * CMAS urgency (CMAS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CMAS_URGENCY = "cmas_urgency";
-
-        /**
-         * CMAS certainty (CMAS alerts only).
-         * <P>Type: INTEGER</P>
-         */
-        public static final String CMAS_CERTAINTY = "cmas_certainty";
-
-        /** The default sort order for this table. */
-        public static final String DEFAULT_SORT_ORDER = DELIVERY_TIME + " DESC";
-
-        /**
-         * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
-         */
-        public static final String[] QUERY_COLUMNS = {
-                _ID,
-                GEOGRAPHICAL_SCOPE,
-                PLMN,
-                LAC,
-                CID,
-                SERIAL_NUMBER,
-                SERVICE_CATEGORY,
-                LANGUAGE_CODE,
-                MESSAGE_BODY,
-                DELIVERY_TIME,
-                MESSAGE_READ,
-                MESSAGE_FORMAT,
-                MESSAGE_PRIORITY,
-                ETWS_WARNING_TYPE,
-                CMAS_MESSAGE_CLASS,
-                CMAS_CATEGORY,
-                CMAS_RESPONSE_TYPE,
-                CMAS_SEVERITY,
-                CMAS_URGENCY,
-                CMAS_CERTAINTY
-        };
-    }
-}
diff --git a/src/java/android/telephony/SmsCbCmasInfo.java b/src/java/android/telephony/SmsCbCmasInfo.java
deleted file mode 100644
index c912924..0000000
--- a/src/java/android/telephony/SmsCbCmasInfo.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Contains CMAS warning notification Type 1 elements for a {@link SmsCbMessage}.
- * Supported values for each element are defined in TIA-1149-0-1 (CMAS over CDMA) and
- * 3GPP TS 23.041 (for GSM/UMTS).
- *
- * {@hide}
- */
-public class SmsCbCmasInfo implements Parcelable {
-
-    // CMAS message class (in GSM/UMTS message identifier or CDMA service category).
-
-    /** Presidential-level alert (Korean Public Alert System Class 0 message). */
-    public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0x00;
-
-    /** Extreme threat to life and property (Korean Public Alert System Class 1 message). */
-    public static final int CMAS_CLASS_EXTREME_THREAT = 0x01;
-
-    /** Severe threat to life and property (Korean Public Alert System Class 1 message). */
-    public static final int CMAS_CLASS_SEVERE_THREAT = 0x02;
-
-    /** Child abduction emergency (AMBER Alert). */
-    public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 0x03;
-
-    /** CMAS test message. */
-    public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 0x04;
-
-    /** CMAS exercise. */
-    public static final int CMAS_CLASS_CMAS_EXERCISE = 0x05;
-
-    /** CMAS category for operator defined use. */
-    public static final int CMAS_CLASS_OPERATOR_DEFINED_USE = 0x06;
-
-    /** CMAS category for warning types that are reserved for future extension. */
-    public static final int CMAS_CLASS_UNKNOWN = -1;
-
-    // CMAS alert category (in CDMA type 1 elements record).
-
-    /** CMAS alert category: Geophysical including landslide. */
-    public static final int CMAS_CATEGORY_GEO = 0x00;
-
-    /** CMAS alert category: Meteorological including flood. */
-    public static final int CMAS_CATEGORY_MET = 0x01;
-
-    /** CMAS alert category: General emergency and public safety. */
-    public static final int CMAS_CATEGORY_SAFETY = 0x02;
-
-    /** CMAS alert category: Law enforcement, military, homeland/local/private security. */
-    public static final int CMAS_CATEGORY_SECURITY = 0x03;
-
-    /** CMAS alert category: Rescue and recovery. */
-    public static final int CMAS_CATEGORY_RESCUE = 0x04;
-
-    /** CMAS alert category: Fire suppression and rescue. */
-    public static final int CMAS_CATEGORY_FIRE = 0x05;
-
-    /** CMAS alert category: Medical and public health. */
-    public static final int CMAS_CATEGORY_HEALTH = 0x06;
-
-    /** CMAS alert category: Pollution and other environmental. */
-    public static final int CMAS_CATEGORY_ENV = 0x07;
-
-    /** CMAS alert category: Public and private transportation. */
-    public static final int CMAS_CATEGORY_TRANSPORT = 0x08;
-
-    /** CMAS alert category: Utility, telecom, other non-transport infrastructure. */
-    public static final int CMAS_CATEGORY_INFRA = 0x09;
-
-    /** CMAS alert category: Chem, bio, radiological, nuclear, high explosive threat or attack. */
-    public static final int CMAS_CATEGORY_CBRNE = 0x0a;
-
-    /** CMAS alert category: Other events. */
-    public static final int CMAS_CATEGORY_OTHER = 0x0b;
-
-    /**
-     * CMAS alert category is unknown. The category is only available for CDMA broadcasts
-     * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
-     */
-    public static final int CMAS_CATEGORY_UNKNOWN = -1;
-
-    // CMAS response type (in CDMA type 1 elements record).
-
-    /** CMAS response type: Take shelter in place. */
-    public static final int CMAS_RESPONSE_TYPE_SHELTER = 0x00;
-
-    /** CMAS response type: Evacuate (Relocate). */
-    public static final int CMAS_RESPONSE_TYPE_EVACUATE = 0x01;
-
-    /** CMAS response type: Make preparations. */
-    public static final int CMAS_RESPONSE_TYPE_PREPARE = 0x02;
-
-    /** CMAS response type: Execute a pre-planned activity. */
-    public static final int CMAS_RESPONSE_TYPE_EXECUTE = 0x03;
-
-    /** CMAS response type: Attend to information sources. */
-    public static final int CMAS_RESPONSE_TYPE_MONITOR = 0x04;
-
-    /** CMAS response type: Avoid hazard. */
-    public static final int CMAS_RESPONSE_TYPE_AVOID = 0x05;
-
-    /** CMAS response type: Evaluate the information in this message (not for public warnings). */
-    public static final int CMAS_RESPONSE_TYPE_ASSESS = 0x06;
-
-    /** CMAS response type: No action recommended. */
-    public static final int CMAS_RESPONSE_TYPE_NONE = 0x07;
-
-    /**
-     * CMAS response type is unknown. The response type is only available for CDMA broadcasts
-     * containing a type 1 elements record, so GSM and UMTS broadcasts always return unknown.
-     */
-    public static final int CMAS_RESPONSE_TYPE_UNKNOWN = -1;
-
-    // 4-bit CMAS severity (in GSM/UMTS message identifier or CDMA type 1 elements record).
-
-    /** CMAS severity type: Extraordinary threat to life or property. */
-    public static final int CMAS_SEVERITY_EXTREME = 0x0;
-
-    /** CMAS severity type: Significant threat to life or property. */
-    public static final int CMAS_SEVERITY_SEVERE = 0x1;
-
-    /**
-     * CMAS alert severity is unknown. The severity is available for CDMA warning alerts
-     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
-     * Presidential-level alert class (Korean Public Alert System Class 0).
-     */
-    public static final int CMAS_SEVERITY_UNKNOWN = -1;
-
-    // CMAS urgency (in GSM/UMTS message identifier or CDMA type 1 elements record).
-
-    /** CMAS urgency type: Responsive action should be taken immediately. */
-    public static final int CMAS_URGENCY_IMMEDIATE = 0x0;
-
-    /** CMAS urgency type: Responsive action should be taken within the next hour. */
-    public static final int CMAS_URGENCY_EXPECTED = 0x1;
-
-    /**
-     * CMAS alert urgency is unknown. The urgency is available for CDMA warning alerts
-     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
-     * Presidential-level alert class (Korean Public Alert System Class 0).
-     */
-    public static final int CMAS_URGENCY_UNKNOWN = -1;
-
-    // CMAS certainty (in GSM/UMTS message identifier or CDMA type 1 elements record).
-
-    /** CMAS certainty type: Determined to have occurred or to be ongoing. */
-    public static final int CMAS_CERTAINTY_OBSERVED = 0x0;
-
-    /** CMAS certainty type: Likely (probability > ~50%). */
-    public static final int CMAS_CERTAINTY_LIKELY = 0x1;
-
-    /**
-     * CMAS alert certainty is unknown. The certainty is available for CDMA warning alerts
-     * containing a type 1 elements record and for all GSM and UMTS alerts except for the
-     * Presidential-level alert class (Korean Public Alert System Class 0).
-     */
-    public static final int CMAS_CERTAINTY_UNKNOWN = -1;
-
-    /** CMAS message class. */
-    private final int mMessageClass;
-
-    /** CMAS category. */
-    private final int mCategory;
-
-    /** CMAS response type. */
-    private final int mResponseType;
-
-    /** CMAS severity. */
-    private final int mSeverity;
-
-    /** CMAS urgency. */
-    private final int mUrgency;
-
-    /** CMAS certainty. */
-    private final int mCertainty;
-
-    /** Create a new SmsCbCmasInfo object with the specified values. */
-    public SmsCbCmasInfo(int messageClass, int category, int responseType, int severity,
-            int urgency, int certainty) {
-        mMessageClass = messageClass;
-        mCategory = category;
-        mResponseType = responseType;
-        mSeverity = severity;
-        mUrgency = urgency;
-        mCertainty = certainty;
-    }
-
-    /** Create a new SmsCbCmasInfo object from a Parcel. */
-    SmsCbCmasInfo(Parcel in) {
-        mMessageClass = in.readInt();
-        mCategory = in.readInt();
-        mResponseType = in.readInt();
-        mSeverity = in.readInt();
-        mUrgency = in.readInt();
-        mCertainty = in.readInt();
-    }
-
-    /**
-     * Flatten this object into a Parcel.
-     *
-     * @param dest  The Parcel in which the object should be written.
-     * @param flags Additional flags about how the object should be written (ignored).
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mMessageClass);
-        dest.writeInt(mCategory);
-        dest.writeInt(mResponseType);
-        dest.writeInt(mSeverity);
-        dest.writeInt(mUrgency);
-        dest.writeInt(mCertainty);
-    }
-
-    /**
-     * Returns the CMAS message class, e.g. {@link #CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT}.
-     * @return one of the {@code CMAS_CLASS} values
-     */
-    public int getMessageClass() {
-        return mMessageClass;
-    }
-
-    /**
-     * Returns the CMAS category, e.g. {@link #CMAS_CATEGORY_GEO}.
-     * @return one of the {@code CMAS_CATEGORY} values
-     */
-    public int getCategory() {
-        return mCategory;
-    }
-
-    /**
-     * Returns the CMAS response type, e.g. {@link #CMAS_RESPONSE_TYPE_SHELTER}.
-     * @return one of the {@code CMAS_RESPONSE_TYPE} values
-     */
-    public int getResponseType() {
-        return mResponseType;
-    }
-
-    /**
-     * Returns the CMAS severity, e.g. {@link #CMAS_SEVERITY_EXTREME}.
-     * @return one of the {@code CMAS_SEVERITY} values
-     */
-    public int getSeverity() {
-        return mSeverity;
-    }
-
-    /**
-     * Returns the CMAS urgency, e.g. {@link #CMAS_URGENCY_IMMEDIATE}.
-     * @return one of the {@code CMAS_URGENCY} values
-     */
-    public int getUrgency() {
-        return mUrgency;
-    }
-
-    /**
-     * Returns the CMAS certainty, e.g. {@link #CMAS_CERTAINTY_OBSERVED}.
-     * @return one of the {@code CMAS_CERTAINTY} values
-     */
-    public int getCertainty() {
-        return mCertainty;
-    }
-
-    @Override
-    public String toString() {
-        return "SmsCbCmasInfo{messageClass=" + mMessageClass + ", category=" + mCategory
-                + ", responseType=" + mResponseType + ", severity=" + mSeverity
-                + ", urgency=" + mUrgency + ", certainty=" + mCertainty + '}';
-    }
-
-    /**
-     * Describe the kinds of special objects contained in the marshalled representation.
-     * @return a bitmask indicating this Parcelable contains no special objects
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Creator for unparcelling objects. */
-    public static final Parcelable.Creator<SmsCbCmasInfo>
-            CREATOR = new Parcelable.Creator<SmsCbCmasInfo>() {
-        @Override
-        public SmsCbCmasInfo createFromParcel(Parcel in) {
-            return new SmsCbCmasInfo(in);
-        }
-
-        @Override
-        public SmsCbCmasInfo[] newArray(int size) {
-            return new SmsCbCmasInfo[size];
-        }
-    };
-}
diff --git a/src/java/android/telephony/SmsCbEtwsInfo.java b/src/java/android/telephony/SmsCbEtwsInfo.java
deleted file mode 100644
index 14e02de..0000000
--- a/src/java/android/telephony/SmsCbEtwsInfo.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.format.Time;
-
-import com.android.internal.telephony.uicc.IccUtils;
-
-import java.util.Arrays;
-
-/**
- * Contains information elements for a GSM or UMTS ETWS warning notification.
- * Supported values for each element are defined in 3GPP TS 23.041.
- *
- * {@hide}
- */
-public class SmsCbEtwsInfo implements Parcelable {
-
-    /** ETWS warning type for earthquake. */
-    public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
-
-    /** ETWS warning type for tsunami. */
-    public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
-
-    /** ETWS warning type for earthquake and tsunami. */
-    public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
-
-    /** ETWS warning type for test messages. */
-    public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 0x03;
-
-    /** ETWS warning type for other emergency types. */
-    public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 0x04;
-
-    /** Unknown ETWS warning type. */
-    public static final int ETWS_WARNING_TYPE_UNKNOWN = -1;
-
-    /** One of the ETWS warning type constants defined in this class. */
-    private final int mWarningType;
-
-    /** Whether or not to activate the emergency user alert tone and vibration. */
-    private final boolean mEmergencyUserAlert;
-
-    /** Whether or not to activate a popup alert. */
-    private final boolean mActivatePopup;
-
-    /** Whether ETWS primary message or not/ */
-    private final boolean mPrimary;
-
-    /**
-     * 50-byte security information (ETWS primary notification for GSM only). As of Release 10,
-     * 3GPP TS 23.041 states that the UE shall ignore the ETWS primary notification timestamp
-     * and digital signature if received. Therefore it is treated as a raw byte array and
-     * parceled with the broadcast intent if present, but the timestamp is only computed if an
-     * application asks for the individual components.
-     */
-    private final byte[] mWarningSecurityInformation;
-
-    /** Create a new SmsCbEtwsInfo object with the specified values. */
-    public SmsCbEtwsInfo(int warningType, boolean emergencyUserAlert, boolean activatePopup,
-                boolean primary, byte[] warningSecurityInformation) {
-        mWarningType = warningType;
-        mEmergencyUserAlert = emergencyUserAlert;
-        mActivatePopup = activatePopup;
-        mPrimary = primary;
-        mWarningSecurityInformation = warningSecurityInformation;
-    }
-
-    /** Create a new SmsCbEtwsInfo object from a Parcel. */
-    SmsCbEtwsInfo(Parcel in) {
-        mWarningType = in.readInt();
-        mEmergencyUserAlert = (in.readInt() != 0);
-        mActivatePopup = (in.readInt() != 0);
-        mPrimary = (in.readInt() != 0);
-        mWarningSecurityInformation = in.createByteArray();
-    }
-
-    /**
-     * Flatten this object into a Parcel.
-     *
-     * @param dest  The Parcel in which the object should be written.
-     * @param flags Additional flags about how the object should be written (ignored).
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mWarningType);
-        dest.writeInt(mEmergencyUserAlert ? 1 : 0);
-        dest.writeInt(mActivatePopup ? 1 : 0);
-        dest.writeInt(mPrimary ? 1 : 0);
-        dest.writeByteArray(mWarningSecurityInformation);
-    }
-
-    /**
-     * Returns the ETWS warning type.
-     * @return a warning type such as {@link #ETWS_WARNING_TYPE_EARTHQUAKE}
-     */
-    public int getWarningType() {
-        return mWarningType;
-    }
-
-    /**
-     * Returns the ETWS emergency user alert flag.
-     * @return true to notify terminal to activate emergency user alert; false otherwise
-     */
-    public boolean isEmergencyUserAlert() {
-        return mEmergencyUserAlert;
-    }
-
-    /**
-     * Returns the ETWS activate popup flag.
-     * @return true to notify terminal to activate display popup; false otherwise
-     */
-    public boolean isPopupAlert() {
-        return mActivatePopup;
-    }
-
-    /**
-     * Returns the ETWS format flag.
-     * @return true if the message is primary message, otherwise secondary message
-     */
-    public boolean isPrimary() {
-        return mPrimary;
-    }
-
-    /**
-     * Returns the Warning-Security-Information timestamp (GSM primary notifications only).
-     * As of Release 10, 3GPP TS 23.041 states that the UE shall ignore this value if received.
-     * @return a UTC timestamp in System.currentTimeMillis() format, or 0 if not present
-     */
-    public long getPrimaryNotificationTimestamp() {
-        if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 7) {
-            return 0;
-        }
-
-        int year = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[0]);
-        int month = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[1]);
-        int day = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[2]);
-        int hour = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[3]);
-        int minute = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[4]);
-        int second = IccUtils.gsmBcdByteToInt(mWarningSecurityInformation[5]);
-
-        // For the timezone, the most significant bit of the
-        // least significant nibble is the sign byte
-        // (meaning the max range of this field is 79 quarter-hours,
-        // which is more than enough)
-
-        byte tzByte = mWarningSecurityInformation[6];
-
-        // Mask out sign bit.
-        int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
-
-        timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
-
-        Time time = new Time(Time.TIMEZONE_UTC);
-
-        // We only need to support years above 2000.
-        time.year = year + 2000;
-        time.month = month - 1;
-        time.monthDay = day;
-        time.hour = hour;
-        time.minute = minute;
-        time.second = second;
-
-        // Timezone offset is in quarter hours.
-        return time.toMillis(true) - timezoneOffset * 15 * 60 * 1000;
-    }
-
-    /**
-     * Returns the digital signature (GSM primary notifications only). As of Release 10,
-     * 3GPP TS 23.041 states that the UE shall ignore this value if received.
-     * @return a byte array containing a copy of the primary notification digital signature
-     */
-    public byte[] getPrimaryNotificationSignature() {
-        if (mWarningSecurityInformation == null || mWarningSecurityInformation.length < 50) {
-            return null;
-        }
-        return Arrays.copyOfRange(mWarningSecurityInformation, 7, 50);
-    }
-
-    @Override
-    public String toString() {
-        return "SmsCbEtwsInfo{warningType=" + mWarningType + ", emergencyUserAlert="
-                + mEmergencyUserAlert + ", activatePopup=" + mActivatePopup + '}';
-    }
-
-    /**
-     * Describe the kinds of special objects contained in the marshalled representation.
-     * @return a bitmask indicating this Parcelable contains no special objects
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /** Creator for unparcelling objects. */
-    public static final Creator<SmsCbEtwsInfo> CREATOR = new Creator<SmsCbEtwsInfo>() {
-        @Override
-        public SmsCbEtwsInfo createFromParcel(Parcel in) {
-            return new SmsCbEtwsInfo(in);
-        }
-
-        @Override
-        public SmsCbEtwsInfo[] newArray(int size) {
-            return new SmsCbEtwsInfo[size];
-        }
-    };
-}
diff --git a/src/java/android/telephony/SmsCbLocation.java b/src/java/android/telephony/SmsCbLocation.java
deleted file mode 100644
index 6eb72a8..0000000
--- a/src/java/android/telephony/SmsCbLocation.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright (C) 2012 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 android.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Represents the location and geographical scope of a cell broadcast message.
- * For GSM/UMTS, the Location Area and Cell ID are set when the broadcast
- * geographical scope is cell wide or Location Area wide. For CDMA, the
- * broadcast geographical scope is always PLMN wide.
- *
- * @hide
- */
-public class SmsCbLocation implements Parcelable {
-
-    /** The PLMN. Note that this field may be an empty string, but isn't allowed to be null. */
-    private final String mPlmn;
-
-    private final int mLac;
-    private final int mCid;
-
-    /**
-     * Construct an empty location object. This is used for some test cases, and for
-     * cell broadcasts saved in older versions of the database without location info.
-     */
-    public SmsCbLocation() {
-        mPlmn = "";
-        mLac = -1;
-        mCid = -1;
-    }
-
-    /**
-     * Construct a location object for the PLMN. This class is immutable, so
-     * the same object can be reused for multiple broadcasts.
-     */
-    public SmsCbLocation(String plmn) {
-        mPlmn = plmn;
-        mLac = -1;
-        mCid = -1;
-    }
-
-    /**
-     * Construct a location object for the PLMN, LAC, and Cell ID. This class is immutable, so
-     * the same object can be reused for multiple broadcasts.
-     */
-    public SmsCbLocation(String plmn, int lac, int cid) {
-        mPlmn = plmn;
-        mLac = lac;
-        mCid = cid;
-    }
-
-    /**
-     * Initialize the object from a Parcel.
-     */
-    public SmsCbLocation(Parcel in) {
-        mPlmn = in.readString();
-        mLac = in.readInt();
-        mCid = in.readInt();
-    }
-
-    /**
-     * Returns the MCC/MNC of the network as a String.
-     * @return the PLMN identifier (MCC+MNC) as a String
-     */
-    public String getPlmn() {
-        return mPlmn;
-    }
-
-    /**
-     * Returns the GSM location area code, or UMTS service area code.
-     * @return location area code, -1 if unknown, 0xffff max legal value
-     */
-    public int getLac() {
-        return mLac;
-    }
-
-    /**
-     * Returns the GSM or UMTS cell ID.
-     * @return gsm cell id, -1 if unknown, 0xffff max legal value
-     */
-    public int getCid() {
-        return mCid;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = mPlmn.hashCode();
-        hash = hash * 31 + mLac;
-        hash = hash * 31 + mCid;
-        return hash;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (o == this) {
-            return true;
-        }
-        if (o == null || !(o instanceof SmsCbLocation)) {
-            return false;
-        }
-        SmsCbLocation other = (SmsCbLocation) o;
-        return mPlmn.equals(other.mPlmn) && mLac == other.mLac && mCid == other.mCid;
-    }
-
-    @Override
-    public String toString() {
-        return '[' + mPlmn + ',' + mLac + ',' + mCid + ']';
-    }
-
-    /**
-     * Test whether this location is within the location area of the specified object.
-     *
-     * @param area the location area to compare with this location
-     * @return true if this location is contained within the specified location area
-     */
-    public boolean isInLocationArea(SmsCbLocation area) {
-        if (mCid != -1 && mCid != area.mCid) {
-            return false;
-        }
-        if (mLac != -1 && mLac != area.mLac) {
-            return false;
-        }
-        return mPlmn.equals(area.mPlmn);
-    }
-
-    /**
-     * Test whether this location is within the location area of the CellLocation.
-     *
-     * @param plmn the PLMN to use for comparison
-     * @param lac the Location Area (GSM) or Service Area (UMTS) to compare with
-     * @param cid the Cell ID to compare with
-     * @return true if this location is contained within the specified PLMN, LAC, and Cell ID
-     */
-    public boolean isInLocationArea(String plmn, int lac, int cid) {
-        if (!mPlmn.equals(plmn)) {
-            return false;
-        }
-
-        if (mLac != -1 && mLac != lac) {
-            return false;
-        }
-
-        if (mCid != -1 && mCid != cid) {
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Flatten this object into a Parcel.
-     *
-     * @param dest  The Parcel in which the object should be written.
-     * @param flags Additional flags about how the object should be written (ignored).
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mPlmn);
-        dest.writeInt(mLac);
-        dest.writeInt(mCid);
-    }
-
-    public static final Parcelable.Creator<SmsCbLocation> CREATOR
-            = new Parcelable.Creator<SmsCbLocation>() {
-        @Override
-        public SmsCbLocation createFromParcel(Parcel in) {
-            return new SmsCbLocation(in);
-        }
-
-        @Override
-        public SmsCbLocation[] newArray(int size) {
-            return new SmsCbLocation[size];
-        }
-    };
-
-    /**
-     * Describe the kinds of special objects contained in the marshalled representation.
-     * @return a bitmask indicating this Parcelable contains no special objects
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/src/java/android/telephony/SmsCbMessage.java b/src/java/android/telephony/SmsCbMessage.java
deleted file mode 100644
index 046bf8c..0000000
--- a/src/java/android/telephony/SmsCbMessage.java
+++ /dev/null
@@ -1,382 +0,0 @@
-/*
- * Copyright (C) 2010 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 android.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Parcelable object containing a received cell broadcast message. There are four different types
- * of Cell Broadcast messages:
- *
- * <ul>
- * <li>opt-in informational broadcasts, e.g. news, weather, stock quotes, sports scores</li>
- * <li>cell information messages, broadcast on channel 50, indicating the current cell name for
- *  roaming purposes (required to display on the idle screen in Brazil)</li>
- * <li>emergency broadcasts for the Japanese Earthquake and Tsunami Warning System (ETWS)</li>
- * <li>emergency broadcasts for the American Commercial Mobile Alert Service (CMAS)</li>
- * </ul>
- *
- * <p>There are also four different CB message formats: GSM, ETWS Primary Notification (GSM only),
- * UMTS, and CDMA. Some fields are only applicable for some message formats. Other fields were
- * unified under a common name, avoiding some names, such as "Message Identifier", that refer to
- * two completely different concepts in 3GPP and CDMA.
- *
- * <p>The GSM/UMTS Message Identifier field is available via {@link #getServiceCategory}, the name
- * of the equivalent field in CDMA. In both cases the service category is a 16-bit value, but 3GPP
- * and 3GPP2 have completely different meanings for the respective values. For ETWS and CMAS, the
- * application should
- *
- * <p>The CDMA Message Identifier field is available via {@link #getSerialNumber}, which is used
- * to detect the receipt of a duplicate message to be discarded. In CDMA, the message ID is
- * unique to the current PLMN. In GSM/UMTS, there is a 16-bit serial number containing a 2-bit
- * Geographical Scope field which indicates whether the 10-bit message code and 4-bit update number
- * are considered unique to the PLMN, to the current cell, or to the current Location Area (or
- * Service Area in UMTS). The relevant values are concatenated into a single String which will be
- * unique if the messages are not duplicates.
- *
- * <p>The SMS dispatcher does not detect duplicate messages. However, it does concatenate the
- * pages of a GSM multi-page cell broadcast into a single SmsCbMessage object.
- *
- * <p>Interested applications with {@code RECEIVE_SMS_PERMISSION} can register to receive
- * {@code SMS_CB_RECEIVED_ACTION} broadcast intents for incoming non-emergency broadcasts.
- * Only system applications such as the CellBroadcastReceiver may receive notifications for
- * emergency broadcasts (ETWS and CMAS). This is intended to prevent any potential for delays or
- * interference with the immediate display of the alert message and playing of the alert sound and
- * vibration pattern, which could be caused by poorly written or malicious non-system code.
- *
- * @hide
- */
-public class SmsCbMessage implements Parcelable {
-
-    protected static final String LOG_TAG = "SMSCB";
-
-    /** Cell wide geographical scope with immediate display (GSM/UMTS only). */
-    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
-
-    /** PLMN wide geographical scope (GSM/UMTS and all CDMA broadcasts). */
-    public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
-
-    /** Location / service area wide geographical scope (GSM/UMTS only). */
-    public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
-
-    /** Cell wide geographical scope (GSM/UMTS only). */
-    public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
-
-    /** GSM or UMTS format cell broadcast. */
-    public static final int MESSAGE_FORMAT_3GPP = 1;
-
-    /** CDMA format cell broadcast. */
-    public static final int MESSAGE_FORMAT_3GPP2 = 2;
-
-    /** Normal message priority. */
-    public static final int MESSAGE_PRIORITY_NORMAL = 0;
-
-    /** Interactive message priority. */
-    public static final int MESSAGE_PRIORITY_INTERACTIVE = 1;
-
-    /** Urgent message priority. */
-    public static final int MESSAGE_PRIORITY_URGENT = 2;
-
-    /** Emergency message priority. */
-    public static final int MESSAGE_PRIORITY_EMERGENCY = 3;
-
-    /** Format of this message (for interpretation of service category values). */
-    private final int mMessageFormat;
-
-    /** Geographical scope of broadcast. */
-    private final int mGeographicalScope;
-
-    /**
-     * Serial number of broadcast (message identifier for CDMA, geographical scope + message code +
-     * update number for GSM/UMTS). The serial number plus the location code uniquely identify
-     * a cell broadcast for duplicate detection.
-     */
-    private final int mSerialNumber;
-
-    /**
-     * Location identifier for this message. It consists of the current operator MCC/MNC as a
-     * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
-     * message is not binary 01, the Location Area is included for comparison. If the GS is
-     * 00 or 11, the Cell ID is also included. LAC and Cell ID are -1 if not specified.
-     */
-    private final SmsCbLocation mLocation;
-
-    /**
-     * 16-bit CDMA service category or GSM/UMTS message identifier. For ETWS and CMAS warnings,
-     * the information provided by the category is also available via {@link #getEtwsWarningInfo()}
-     * or {@link #getCmasWarningInfo()}.
-     */
-    private final int mServiceCategory;
-
-    /** Message language, as a two-character string, e.g. "en". */
-    private final String mLanguage;
-
-    /** Message body, as a String. */
-    private final String mBody;
-
-    /** Message priority (including emergency priority). */
-    private final int mPriority;
-
-    /** ETWS warning notification information (ETWS warnings only). */
-    private final SmsCbEtwsInfo mEtwsWarningInfo;
-
-    /** CMAS warning notification information (CMAS warnings only). */
-    private final SmsCbCmasInfo mCmasWarningInfo;
-
-    /**
-     * Create a new SmsCbMessage with the specified data.
-     */
-    public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
-            SmsCbLocation location, int serviceCategory, String language, String body,
-            int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo) {
-        mMessageFormat = messageFormat;
-        mGeographicalScope = geographicalScope;
-        mSerialNumber = serialNumber;
-        mLocation = location;
-        mServiceCategory = serviceCategory;
-        mLanguage = language;
-        mBody = body;
-        mPriority = priority;
-        mEtwsWarningInfo = etwsWarningInfo;
-        mCmasWarningInfo = cmasWarningInfo;
-    }
-
-    /** Create a new SmsCbMessage object from a Parcel. */
-    public SmsCbMessage(Parcel in) {
-        mMessageFormat = in.readInt();
-        mGeographicalScope = in.readInt();
-        mSerialNumber = in.readInt();
-        mLocation = new SmsCbLocation(in);
-        mServiceCategory = in.readInt();
-        mLanguage = in.readString();
-        mBody = in.readString();
-        mPriority = in.readInt();
-        int type = in.readInt();
-        switch (type) {
-            case 'E':
-                // unparcel ETWS warning information
-                mEtwsWarningInfo = new SmsCbEtwsInfo(in);
-                mCmasWarningInfo = null;
-                break;
-
-            case 'C':
-                // unparcel CMAS warning information
-                mEtwsWarningInfo = null;
-                mCmasWarningInfo = new SmsCbCmasInfo(in);
-                break;
-
-            default:
-                mEtwsWarningInfo = null;
-                mCmasWarningInfo = null;
-        }
-    }
-
-    /**
-     * Flatten this object into a Parcel.
-     *
-     * @param dest  The Parcel in which the object should be written.
-     * @param flags Additional flags about how the object should be written (ignored).
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mMessageFormat);
-        dest.writeInt(mGeographicalScope);
-        dest.writeInt(mSerialNumber);
-        mLocation.writeToParcel(dest, flags);
-        dest.writeInt(mServiceCategory);
-        dest.writeString(mLanguage);
-        dest.writeString(mBody);
-        dest.writeInt(mPriority);
-        if (mEtwsWarningInfo != null) {
-            // parcel ETWS warning information
-            dest.writeInt('E');
-            mEtwsWarningInfo.writeToParcel(dest, flags);
-        } else if (mCmasWarningInfo != null) {
-            // parcel CMAS warning information
-            dest.writeInt('C');
-            mCmasWarningInfo.writeToParcel(dest, flags);
-        } else {
-            // no ETWS or CMAS warning information
-            dest.writeInt('0');
-        }
-    }
-
-    public static final Parcelable.Creator<SmsCbMessage> CREATOR
-            = new Parcelable.Creator<SmsCbMessage>() {
-        @Override
-        public SmsCbMessage createFromParcel(Parcel in) {
-            return new SmsCbMessage(in);
-        }
-
-        @Override
-        public SmsCbMessage[] newArray(int size) {
-            return new SmsCbMessage[size];
-        }
-    };
-
-    /**
-     * Return the geographical scope of this message (GSM/UMTS only).
-     *
-     * @return Geographical scope
-     */
-    public int getGeographicalScope() {
-        return mGeographicalScope;
-    }
-
-    /**
-     * Return the broadcast serial number of broadcast (message identifier for CDMA, or
-     * geographical scope + message code + update number for GSM/UMTS). The serial number plus
-     * the location code uniquely identify a cell broadcast for duplicate detection.
-     *
-     * @return the 16-bit CDMA message identifier or GSM/UMTS serial number
-     */
-    public int getSerialNumber() {
-        return mSerialNumber;
-    }
-
-    /**
-     * Return the location identifier for this message, consisting of the MCC/MNC as a
-     * 5 or 6-digit decimal string. In addition, for GSM/UMTS, if the Geographical Scope of the
-     * message is not binary 01, the Location Area is included. If the GS is 00 or 11, the
-     * cell ID is also included. The {@link SmsCbLocation} object includes a method to test
-     * if the location is included within another location area or within a PLMN and CellLocation.
-     *
-     * @return the geographical location code for duplicate message detection
-     */
-    public SmsCbLocation getLocation() {
-        return mLocation;
-    }
-
-    /**
-     * Return the 16-bit CDMA service category or GSM/UMTS message identifier. The interpretation
-     * of the category is radio technology specific. For ETWS and CMAS warnings, the information
-     * provided by the category is available via {@link #getEtwsWarningInfo()} or
-     * {@link #getCmasWarningInfo()} in a radio technology independent format.
-     *
-     * @return the radio technology specific service category
-     */
-    public int getServiceCategory() {
-        return mServiceCategory;
-    }
-
-    /**
-     * Get the ISO-639-1 language code for this message, or null if unspecified
-     *
-     * @return Language code
-     */
-    public String getLanguageCode() {
-        return mLanguage;
-    }
-
-    /**
-     * Get the body of this message, or null if no body available
-     *
-     * @return Body, or null
-     */
-    public String getMessageBody() {
-        return mBody;
-    }
-
-    /**
-     * Get the message format ({@link #MESSAGE_FORMAT_3GPP} or {@link #MESSAGE_FORMAT_3GPP2}).
-     * @return an integer representing 3GPP or 3GPP2 message format
-     */
-    public int getMessageFormat() {
-        return mMessageFormat;
-    }
-
-    /**
-     * Get the message priority. Normal broadcasts return {@link #MESSAGE_PRIORITY_NORMAL}
-     * and emergency broadcasts return {@link #MESSAGE_PRIORITY_EMERGENCY}. CDMA also may return
-     * {@link #MESSAGE_PRIORITY_INTERACTIVE} or {@link #MESSAGE_PRIORITY_URGENT}.
-     * @return an integer representing the message priority
-     */
-    public int getMessagePriority() {
-        return mPriority;
-    }
-
-    /**
-     * If this is an ETWS warning notification then this method will return an object containing
-     * the ETWS warning type, the emergency user alert flag, and the popup flag. If this is an
-     * ETWS primary notification (GSM only), there will also be a 7-byte timestamp and 43-byte
-     * digital signature. As of Release 10, 3GPP TS 23.041 states that the UE shall ignore the
-     * ETWS primary notification timestamp and digital signature if received.
-     *
-     * @return an SmsCbEtwsInfo object, or null if this is not an ETWS warning notification
-     */
-    public SmsCbEtwsInfo getEtwsWarningInfo() {
-        return mEtwsWarningInfo;
-    }
-
-    /**
-     * If this is a CMAS warning notification then this method will return an object containing
-     * the CMAS message class, category, response type, severity, urgency and certainty.
-     * The message class is always present. Severity, urgency and certainty are present for CDMA
-     * warning notifications containing a type 1 elements record and for GSM and UMTS warnings
-     * except for the Presidential-level alert category. Category and response type are only
-     * available for CDMA notifications containing a type 1 elements record.
-     *
-     * @return an SmsCbCmasInfo object, or null if this is not a CMAS warning notification
-     */
-    public SmsCbCmasInfo getCmasWarningInfo() {
-        return mCmasWarningInfo;
-    }
-
-    /**
-     * Return whether this message is an emergency (PWS) message type.
-     * @return true if the message is a public warning notification; false otherwise
-     */
-    public boolean isEmergencyMessage() {
-        return mPriority == MESSAGE_PRIORITY_EMERGENCY;
-    }
-
-    /**
-     * Return whether this message is an ETWS warning alert.
-     * @return true if the message is an ETWS warning notification; false otherwise
-     */
-    public boolean isEtwsMessage() {
-        return mEtwsWarningInfo != null;
-    }
-
-    /**
-     * Return whether this message is a CMAS warning alert.
-     * @return true if the message is a CMAS warning notification; false otherwise
-     */
-    public boolean isCmasMessage() {
-        return mCmasWarningInfo != null;
-    }
-
-    @Override
-    public String toString() {
-        return "SmsCbMessage{geographicalScope=" + mGeographicalScope + ", serialNumber="
-                + mSerialNumber + ", location=" + mLocation + ", serviceCategory="
-                + mServiceCategory + ", language=" + mLanguage + ", body=" + mBody
-                + ", priority=" + mPriority
-                + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
-                + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "") + '}';
-    }
-
-    /**
-     * Describe the kinds of special objects contained in the marshalled representation.
-     * @return a bitmask indicating this Parcelable contains no special objects
-     */
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-}
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
deleted file mode 100644
index 8063364..0000000
--- a/src/java/android/telephony/SmsManager.java
+++ /dev/null
@@ -1,1662 +0,0 @@
-/*
- * Copyright (C) 2008 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 android.telephony;
-
-import android.app.ActivityThread;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
-import android.content.ContentValues;
-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;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.telephony.IMms;
-import com.android.internal.telephony.ISms;
-import com.android.internal.telephony.SmsRawData;
-import com.android.internal.telephony.uicc.IccConstants;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/*
- * TODO(code review): Curious question... Why are a lot of these
- * methods not declared as static, since they do not seem to require
- * any local object state?  Presumably this cannot be changed without
- * interfering with the API...
- */
-
-/**
- * Manages SMS operations such as sending data, text, and pdu SMS messages.
- * Get this object by calling the static method {@link #getDefault()}.
- *
- * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19)
- * and higher, see {@link android.provider.Telephony}.
- */
-public final class SmsManager {
-    private static final String TAG = "SmsManager";
-    /**
-     * A psuedo-subId that represents the default subId at any given time. The actual subId it
-     * represents changes as the default subId is changed.
-     */
-    private static final int DEFAULT_SUBSCRIPTION_ID = -1002;
-
-    /** Singleton object constructed during class initialization. */
-    private static final SmsManager sInstance = new SmsManager(DEFAULT_SUBSCRIPTION_ID);
-    private static final Object sLockObject = new Object();
-
-    /** @hide */
-    public static final int CELL_BROADCAST_RAN_TYPE_GSM = 0;
-    /** @hide */
-    public static final int CELL_BROADCAST_RAN_TYPE_CDMA = 1;
-
-    private static final Map<Integer, SmsManager> sSubInstances =
-            new ArrayMap<Integer, SmsManager>();
-
-    /** A concrete subscription id, or the pseudo DEFAULT_SUBSCRIPTION_ID */
-    private int mSubId;
-
-    /*
-     * Key for the various carrier-dependent configuration values.
-     * Some of the values are used by the system in processing SMS or MMS messages. Others
-     * are provided for the convenience of SMS applications.
-     */
-
-    /**
-     * 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 =
-            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 = 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 = 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)
-     */
-    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 = 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 = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL;
-    /**
-     * Whether multipart SMS is enabled (boolean type)
-     */
-    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 =
-            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 =
-            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 =
-            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 =
-            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 =
-            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 = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT;
-    /**
-     * Max MMS image width (int type)
-     */
-    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 = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT;
-    /**
-     * Limit of recipients of MMS messages (int type)
-     */
-    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 = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT;
-    /**
-     * Max alias character count (int type)
-     */
-    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)
-     */
-    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 =
-            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 =
-            CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT;
-    /**
-     * Max message subject length (int type)
-     */
-    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 = 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 = 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 = 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 = 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 = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING;
-    /**
-     * Email gateway number (String type)
-     */
-    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 = 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)
-     */
-    public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS =
-            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;
-    /**
-     * If true, add "Connection: close" header to MMS HTTP requests so the connection
-     * is immediately closed (disabling keep-alive). (Boolean type)
-     * @hide
-     */
-    public static final String MMS_CONFIG_CLOSE_CONNECTION =
-            CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL;
-
-    /*
-     * Forwarded constants from SimDialogActivity.
-     */
-    private static String DIALOG_TYPE_KEY = "dialog_type";
-    private static final int SMS_PICK = 2;
-
-    /**
-     * Send a text based SMS.
-     *
-     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
-     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
-     *
-     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
-     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
-     * writes messages sent using this method to the SMS Provider (the default SMS app is always
-     * responsible for writing its sent messages to the SMS Provider). For information about
-     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
-     *
-     *
-     * @param destinationAddress the address to send the message to
-     * @param scAddress is the service center address or null to use
-     *  the current default SMSC
-     * @param text the body of the message to send
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK</code> for success,
-     *  or one of these errors:<br>
-     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *  <code>RESULT_ERROR_NULL_PDU</code><br>
-     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
-     *  the extra "errorCode" containing a radio technology specific value,
-     *  generally only useful for troubleshooting.<br>
-     *  The per-application based SMS control checks sentIntent. If sentIntent
-     *  is NULL the caller will be checked against all unknown applications,
-     *  which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is delivered to the recipient.  The
-     *  raw pdu of the status report is in the extended data ("pdu").
-     *
-     * @throws IllegalArgumentException if destinationAddress or text are empty
-     */
-    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");
-        }
-
-        if (TextUtils.isEmpty(text)) {
-            throw new IllegalArgumentException("Invalid message body");
-        }
-
-        try {
-            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.
-     *
-     * @param persistMessage whether to persist the sent message in the SMS app. the caller must be
-     * the Phone process if set to false.
-     *
-     * @hide
-     */
-    public void sendTextMessageWithSelfPermissions(
-            String destinationAddress, String scAddress, String text,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean persistMessage) {
-        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, persistMessage);
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-    }
-
-    /**
-     * Inject an SMS PDU into the android application framework.
-     *
-     * The caller should have carrier privileges.
-     * @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)
-     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully received by the
-     *  android application framework, or failed. This intent is broadcasted at
-     *  the same time an SMS received from radio is acknowledged back.
-     *  The result code will be <code>RESULT_SMS_HANDLED</code> for success, or
-     *  <code>RESULT_SMS_GENERIC_ERROR</code> for error.
-     *
-     * @throws IllegalArgumentException if format is not one of 3gpp and 3gpp2.
-     */
-    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
-        if (!format.equals(SmsMessage.FORMAT_3GPP) && !format.equals(SmsMessage.FORMAT_3GPP2)) {
-            // Format must be either 3gpp or 3gpp2.
-            throw new IllegalArgumentException(
-                    "Invalid pdu format. format must be either 3gpp or 3gpp2");
-        }
-        try {
-            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
-            if (iccISms != null) {
-                iccISms.injectSmsPduForSubscriber(
-                        getSubscriptionId(), pdu, format, receivedIntent);
-            }
-        } catch (RemoteException ex) {
-          // ignore it
-        }
-    }
-
-    /**
-     * Divide a message text into several fragments, none bigger than
-     * the maximum SMS message size.
-     *
-     * @param text the original message.  Must not be null.
-     * @return an <code>ArrayList</code> of strings that, in order,
-     *   comprise the original message
-     *
-     * @throws IllegalArgumentException if text is null
-     */
-    public ArrayList<String> divideMessage(String text) {
-        if (null == text) {
-            throw new IllegalArgumentException("text is null");
-        }
-        return SmsMessage.fragmentText(text);
-    }
-
-    /**
-     * Send a multi-part text based SMS.  The callee should have already
-     * divided the message into correctly sized parts by calling
-     * <code>divideMessage</code>.
-     *
-     * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
-     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
-     *
-     * <p class="note"><strong>Note:</strong> Beginning with Android 4.4 (API level 19), if
-     * <em>and only if</em> an app is not selected as the default SMS app, the system automatically
-     * writes messages sent using this method to the SMS Provider (the default SMS app is always
-     * responsible for writing its sent messages to the SMS Provider). For information about
-     * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p>
-     *
-     * @param destinationAddress the address to send the message to
-     * @param scAddress is the service center address or null to use
-     *   the current default SMSC
-     * @param parts an <code>ArrayList</code> of strings that, in order,
-     *   comprise the original message
-     * @param sentIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been sent.
-     *   The result code will be <code>Activity.RESULT_OK</code> for success,
-     *   or one of these errors:<br>
-     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *   <code>RESULT_ERROR_NULL_PDU</code><br>
-     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
-     *   the extra "errorCode" containing a radio technology specific value,
-     *   generally only useful for troubleshooting.<br>
-     *   The per-application based SMS control checks sentIntent. If sentIntent
-     *   is NULL the caller will be checked against all unknown applications,
-     *   which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been delivered
-     *   to the recipient.  The raw pdu of the status report is in the
-     *   extended data ("pdu").
-     *
-     * @throws IllegalArgumentException if destinationAddress or data are empty
-     */
-    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");
-        }
-        if (parts == null || parts.size() < 1) {
-            throw new IllegalArgumentException("Invalid message body");
-        }
-
-        if (parts.size() > 1) {
-            try {
-                ISms iccISms = getISmsServiceOrThrow();
-                iccISms.sendMultipartTextForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
-                        destinationAddress, scAddress, parts,
-                        sentIntents, deliveryIntents, persistMessageForCarrierApp);
-            } catch (RemoteException ex) {
-                // ignore it
-            }
-        } else {
-            PendingIntent sentIntent = null;
-            PendingIntent deliveryIntent = null;
-            if (sentIntents != null && sentIntents.size() > 0) {
-                sentIntent = sentIntents.get(0);
-            }
-            if (deliveryIntents != null && deliveryIntents.size() > 0) {
-                deliveryIntent = deliveryIntents.get(0);
-            }
-            sendTextMessage(destinationAddress, scAddress, parts.get(0),
-                    sentIntent, deliveryIntent);
-        }
-    }
-
-    /**
-     * 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
-     * {@link android.Manifest.permission#SEND_SMS} permission.</p>
-     *
-     * @param destinationAddress the address to send the message to
-     * @param scAddress is the service center address or null to use
-     *  the current default SMSC
-     * @param destinationPort the port to deliver the message to
-     * @param data the body of the message to send
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK</code> for success,
-     *  or one of these errors:<br>
-     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *  <code>RESULT_ERROR_NULL_PDU</code><br>
-     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
-     *  the extra "errorCode" containing a radio technology specific value,
-     *  generally only useful for troubleshooting.<br>
-     *  The per-application based SMS control checks sentIntent. If sentIntent
-     *  is NULL the caller will be checked against all unknown applications,
-     *  which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is delivered to the recipient.  The
-     *  raw pdu of the status report is in the extended data ("pdu").
-     *
-     * @throws IllegalArgumentException if destinationAddress or data are empty
-     */
-    public void sendDataMessage(
-            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.sendDataForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
-                    destinationAddress, scAddress, destinationPort & 0xFFFF,
-                    data, sentIntent, deliveryIntent);
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-    }
-
-    /**
-     * 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.
-     *
-     * @return the SmsManager associated with the default subscription id
-     */
-    public static SmsManager getDefault() {
-        return sInstance;
-    }
-
-    /**
-     * Get the the instance of the SmsManager associated with a particular subscription id
-     *
-     * @param subId an SMS subscription id, typically accessed using
-     *   {@link android.telephony.SubscriptionManager}
-     * @return the instance of the SmsManager associated with subId
-     */
-    public static SmsManager getSmsManagerForSubscriptionId(int subId) {
-        // TODO(shri): Add javadoc link once SubscriptionManager is made public api
-        synchronized(sLockObject) {
-            SmsManager smsManager = sSubInstances.get(subId);
-            if (smsManager == null) {
-                smsManager = new SmsManager(subId);
-                sSubInstances.put(subId, smsManager);
-            }
-            return smsManager;
-        }
-    }
-
-    private SmsManager(int subId) {
-        mSubId = subId;
-    }
-
-    /**
-     * Get the associated subscription id. If the instance was returned by {@link #getDefault()},
-     * then this method may return different values at different points in time (if the user
-     * changes the default subscription id). It will return < 0 if the default subscription id
-     * cannot be determined.
-     *
-     * Additionally, to support legacy applications that are not multi-SIM aware,
-     * if the following are true:
-     *     - We are using a multi-SIM device
-     *     - A default SMS SIM has not been selected
-     *     - At least one SIM subscription is available
-     * then ask the user to set the default SMS SIM.
-     *
-     * @return associated subscription id
-     */
-    public int getSubscriptionId() {
-        final int subId = (mSubId == DEFAULT_SUBSCRIPTION_ID)
-                ? getDefaultSmsSubscriptionId() : mSubId;
-        boolean isSmsSimPickActivityNeeded = false;
-        final Context context = ActivityThread.currentApplication().getApplicationContext();
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                isSmsSimPickActivityNeeded = iccISms.isSmsSimPickActivityNeeded(subId);
-            }
-        } catch (RemoteException ex) {
-            Log.e(TAG, "Exception in getSubscriptionId");
-        }
-
-        if (isSmsSimPickActivityNeeded) {
-            Log.d(TAG, "getSubscriptionId isSmsSimPickActivityNeeded is true");
-            // ask the user for a default SMS SIM.
-            Intent intent = new Intent();
-            intent.setClassName("com.android.settings",
-                    "com.android.settings.sim.SimDialogActivity");
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK);
-            try {
-                context.startActivity(intent);
-            } catch (ActivityNotFoundException anfe) {
-                // If Settings is not installed, only log the error as we do not want to break
-                // legacy applications.
-                Log.e(TAG, "Unable to launch Settings application.");
-            }
-        }
-
-        return subId;
-    }
-
-    /**
-     * Returns the ISms service, or throws an UnsupportedOperationException if
-     * the service does not exist.
-     */
-    private static ISms getISmsServiceOrThrow() {
-        ISms iccISms = getISmsService();
-        if (iccISms == null) {
-            throw new UnsupportedOperationException("Sms is not supported");
-        }
-        return iccISms;
-    }
-
-    private static ISms getISmsService() {
-        return ISms.Stub.asInterface(ServiceManager.getService("isms"));
-    }
-
-    /**
-     * Copy a raw SMS PDU to the ICC.
-     * ICC (Integrated Circuit Card) is the card of the device.
-     * For example, this can be the SIM or USIM for GSM.
-     *
-     * @param smsc the SMSC for this message, or NULL for the default SMSC
-     * @param pdu the raw PDU to store
-     * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
-     *               STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
-     * @return true for success
-     *
-     * @throws IllegalArgumentException if pdu is NULL
-     * {@hide}
-     */
-    public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
-        boolean success = false;
-
-        if (null == pdu) {
-            throw new IllegalArgumentException("pdu is NULL");
-        }
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.copyMessageToIccEfForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
-                        status, pdu, smsc);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Delete the specified message from the ICC.
-     * ICC (Integrated Circuit Card) is the card of the device.
-     * For example, this can be the SIM or USIM for GSM.
-     *
-     * @param messageIndex is the record index of the message on ICC
-     * @return true for success
-     *
-     * {@hide}
-     */
-    public boolean
-    deleteMessageFromIcc(int messageIndex) {
-        boolean success = false;
-        byte[] pdu = new byte[IccConstants.SMS_RECORD_LENGTH-1];
-        Arrays.fill(pdu, (byte)0xff);
-
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
-                        messageIndex, STATUS_ON_ICC_FREE, pdu);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Update the specified message on the ICC.
-     * ICC (Integrated Circuit Card) is the card of the device.
-     * For example, this can be the SIM or USIM for GSM.
-     *
-     * @param messageIndex record index of message to update
-     * @param newStatus new message status (STATUS_ON_ICC_READ,
-     *                  STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT,
-     *                  STATUS_ON_ICC_UNSENT, STATUS_ON_ICC_FREE)
-     * @param pdu the raw PDU to store
-     * @return true for success
-     *
-     * {@hide}
-     */
-    public boolean updateMessageOnIcc(int messageIndex, int newStatus, byte[] pdu) {
-        boolean success = false;
-
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.updateMessageOnIccEfForSubscriber(getSubscriptionId(),
-                        ActivityThread.currentPackageName(),
-                        messageIndex, newStatus, pdu);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Retrieves all messages currently stored on ICC.
-     * ICC (Integrated Circuit Card) is the card of the device.
-     * For example, this can be the SIM or USIM for GSM.
-     *
-     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects
-     *
-     * {@hide}
-     */
-    public ArrayList<SmsMessage> getAllMessagesFromIcc() {
-        List<SmsRawData> records = null;
-
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                records = iccISms.getAllMessagesFromIccEfForSubscriber(
-                        getSubscriptionId(),
-                        ActivityThread.currentPackageName());
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return createMessageListFromRawRecords(records);
-    }
-
-    /**
-     * Enable reception of cell broadcast (SMS-CB) messages with the given
-     * message identifier and RAN type. The RAN type specify this message ID
-     * belong to 3GPP (GSM) or 3GPP2(CDMA).Note that if two different clients
-     * enable the same message identifier, they must both disable it for the device to stop
-     * receiving those messages. All received messages will be broadcast in an
-     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
-     * Note: This call is blocking, callers may want to avoid calling it from
-     * the main thread of an application.
-     *
-     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
-     * or C.R1001-G (3GPP2)
-     * @param ranType as defined in class SmsManager, the value can be one of these:
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
-     * @return true if successful, false otherwise
-     * @see #disableCellBroadcast(int, int)
-     *
-     * {@hide}
-     */
-    public boolean enableCellBroadcast(int messageIdentifier, int ranType) {
-        boolean success = false;
-
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.enableCellBroadcastForSubscriber(
-                        getSubscriptionId(), messageIdentifier, ranType);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Disable reception of cell broadcast (SMS-CB) messages with the given
-     * message identifier and RAN type. The RAN type specify this message ID
-     * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients
-     * enable the same message identifier, they must both disable it for the
-     * device to stop receiving those messages.
-     * Note: This call is blocking, callers may want to avoid calling it from
-     * the main thread of an application.
-     *
-     * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP)
-     * or C.R1001-G (3GPP2)
-     * @param ranType as defined in class SmsManager, the value can be one of these:
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
-     * @return true if successful, false otherwise
-     *
-     * @see #enableCellBroadcast(int, int)
-     *
-     * {@hide}
-     */
-    public boolean disableCellBroadcast(int messageIdentifier, int ranType) {
-        boolean success = false;
-
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.disableCellBroadcastForSubscriber(
-                        getSubscriptionId(), messageIdentifier, ranType);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Enable reception of cell broadcast (SMS-CB) messages with the given
-     * message identifier range and RAN type. The RAN type specify this message ID
-     * belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different clients enable
-     * the same message identifier, they must both disable it for the device to stop
-     * receiving those messages. All received messages will be broadcast in an
-     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
-     * Note: This call is blocking, callers may want to avoid calling it from
-     * the main thread of an application.
-     *
-     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
-     * or C.R1001-G (3GPP2)
-     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
-     * or C.R1001-G (3GPP2)
-     * @param ranType as defined in class SmsManager, the value can be one of these:
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
-     * @return true if successful, false otherwise
-     * @see #disableCellBroadcastRange(int, int, int)
-     *
-     * @throws IllegalArgumentException if endMessageId < startMessageId
-     * {@hide}
-     */
-    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) {
-        boolean success = false;
-
-        if (endMessageId < startMessageId) {
-            throw new IllegalArgumentException("endMessageId < startMessageId");
-        }
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(),
-                        startMessageId, endMessageId, ranType);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Disable reception of cell broadcast (SMS-CB) messages with the given
-     * message identifier range and RAN type. The RAN type specify this message
-     * ID range belong to 3GPP (GSM) or 3GPP2(CDMA). Note that if two different
-     * clients enable the same message identifier, they must both disable it for
-     * the device to stop receiving those messages.
-     * Note: This call is blocking, callers may want to avoid calling it from
-     * the main thread of an application.
-     *
-     * @param startMessageId first message identifier as specified in TS 23.041 (3GPP)
-     * or C.R1001-G (3GPP2)
-     * @param endMessageId last message identifier as specified in TS 23.041 (3GPP)
-     * or C.R1001-G (3GPP2)
-     * @param ranType as defined in class SmsManager, the value can be one of these:
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_GSM
-     *    android.telephony.SmsMessage.CELL_BROADCAST_RAN_TYPE_CDMA
-     * @return true if successful, false otherwise
-     *
-     * @see #enableCellBroadcastRange(int, int, int)
-     *
-     * @throws IllegalArgumentException if endMessageId < startMessageId
-     * {@hide}
-     */
-    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType) {
-        boolean success = false;
-
-        if (endMessageId < startMessageId) {
-            throw new IllegalArgumentException("endMessageId < startMessageId");
-        }
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                success = iccISms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(),
-                        startMessageId, endMessageId, ranType);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-
-        return success;
-    }
-
-    /**
-     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
-     * records returned by <code>getAllMessagesFromIcc()</code>
-     *
-     * @param records SMS EF records, returned by
-     *   <code>getAllMessagesFromIcc</code>
-     * @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
-     */
-    private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
-        ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
-        if (records != null) {
-            int count = records.size();
-            for (int i = 0; i < count; i++) {
-                SmsRawData data = records.get(i);
-                // List contains all records, including "free" records (null)
-                if (data != null) {
-                    SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes());
-                    if (sms != null) {
-                        messages.add(sms);
-                    }
-                }
-            }
-        }
-        return messages;
-    }
-
-    /**
-     * SMS over IMS is supported if IMS is registered and SMS is supported
-     * on IMS.
-     *
-     * @return true if SMS over IMS is supported, false otherwise
-     *
-     * @see #getImsSmsFormat()
-     *
-     * @hide
-     */
-    public boolean isImsSmsSupported() {
-        boolean boSupported = false;
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                boSupported = iccISms.isImsSmsSupportedForSubscriber(getSubscriptionId());
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return boSupported;
-    }
-
-    /**
-     * Gets SMS format supported on IMS.  SMS over IMS format is
-     * either 3GPP or 3GPP2.
-     *
-     * @return SmsMessage.FORMAT_3GPP,
-     *         SmsMessage.FORMAT_3GPP2
-     *      or SmsMessage.FORMAT_UNKNOWN
-     *
-     * @see #isImsSmsSupported()
-     *
-     * @hide
-     */
-    public String getImsSmsFormat() {
-        String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
-        try {
-            ISms iccISms = getISmsService();
-            if (iccISms != null) {
-                format = iccISms.getImsSmsFormatForSubscriber(getSubscriptionId());
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return format;
-    }
-
-    /**
-     * Get default sms subscription id
-     *
-     * @return the default SMS subscription id
-     */
-    public static int getDefaultSmsSubscriptionId() {
-        ISms iccISms = null;
-        try {
-            iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
-            return iccISms.getPreferredSmsSubscription();
-        } catch (RemoteException ex) {
-            return -1;
-        } catch (NullPointerException ex) {
-            return -1;
-        }
-    }
-
-    /**
-     * Get SMS prompt property,  enabled or not
-     *
-     * @return true if enabled, false otherwise
-     * @hide
-     */
-    public boolean isSMSPromptEnabled() {
-        ISms iccISms = null;
-        try {
-            iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
-            return iccISms.isSMSPromptEnabled();
-        } catch (RemoteException ex) {
-            return false;
-        } catch (NullPointerException ex) {
-            return false;
-        }
-    }
-
-    // see SmsMessage.getStatusOnIcc
-
-    /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
-    static public final int STATUS_ON_ICC_FREE      = 0;
-
-    /** Received and read (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
-    static public final int STATUS_ON_ICC_READ      = 1;
-
-    /** Received and unread (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
-    static public final int STATUS_ON_ICC_UNREAD    = 3;
-
-    /** Stored and sent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
-    static public final int STATUS_ON_ICC_SENT      = 5;
-
-    /** Stored and unsent (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
-    static public final int STATUS_ON_ICC_UNSENT    = 7;
-
-    // SMS send failure result codes
-
-    /** Generic failure cause */
-    static public final int RESULT_ERROR_GENERIC_FAILURE    = 1;
-    /** Failed because radio was explicitly turned off */
-    static public final int RESULT_ERROR_RADIO_OFF          = 2;
-    /** Failed because no pdu provided */
-    static public final int RESULT_ERROR_NULL_PDU           = 3;
-    /** Failed because service is currently unavailable */
-    static public final int RESULT_ERROR_NO_SERVICE         = 4;
-    /** Failed because we reached the sending queue limit.  {@hide} */
-    static public final int RESULT_ERROR_LIMIT_EXCEEDED     = 5;
-    /** Failed because FDN is enabled. {@hide} */
-    static public final int RESULT_ERROR_FDN_CHECK_FAILURE  = 6;
-
-    static private final String PHONE_PACKAGE_NAME = "com.android.phone";
-
-    /**
-     * Send an MMS message
-     *
-     * @param context application context
-     * @param contentUri the content Uri from which the message pdu will be read
-     * @param locationUrl the optional location url where message should be sent to
-     * @param configOverrides the carrier-specific messaging configuration values to override for
-     *  sending the message.
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully sent, or failed
-     * @throws IllegalArgumentException if contentUri is empty
-     */
-    public void sendMultimediaMessage(Context context, Uri contentUri, String locationUrl,
-            Bundle configOverrides, PendingIntent sentIntent) {
-        if (contentUri == null) {
-            throw new IllegalArgumentException("Uri contentUri null");
-        }
-        try {
-            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms == null) {
-                return;
-            }
-
-            iMms.sendMessage(getSubscriptionId(), ActivityThread.currentPackageName(), contentUri,
-                    locationUrl, configOverrides, sentIntent);
-        } catch (RemoteException e) {
-            // Ignore it
-        }
-    }
-
-    /**
-     * Download an MMS message from carrier by a given location URL
-     *
-     * @param context application context
-     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
-     *  from the MMS WAP push notification
-     * @param contentUri the content uri to which the downloaded pdu will be written
-     * @param configOverrides the carrier-specific messaging configuration values to override for
-     *  downloading the message.
-     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is downloaded, or the download is failed
-     * @throws IllegalArgumentException if locationUrl or contentUri is empty
-     */
-    public void downloadMultimediaMessage(Context context, String locationUrl, Uri contentUri,
-            Bundle configOverrides, PendingIntent downloadedIntent) {
-        if (TextUtils.isEmpty(locationUrl)) {
-            throw new IllegalArgumentException("Empty MMS location URL");
-        }
-        if (contentUri == null) {
-            throw new IllegalArgumentException("Uri contentUri null");
-        }
-        try {
-            final IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms == null) {
-                return;
-            }
-            iMms.downloadMessage(
-                    getSubscriptionId(), ActivityThread.currentPackageName(), locationUrl,
-                    contentUri, configOverrides, downloadedIntent);
-        } catch (RemoteException e) {
-            // Ignore it
-        }
-    }
-
-    // MMS send/download failure result codes
-    public static final int MMS_ERROR_UNSPECIFIED = 1;
-    public static final int MMS_ERROR_INVALID_APN = 2;
-    public static final int MMS_ERROR_UNABLE_CONNECT_MMS = 3;
-    public static final int MMS_ERROR_HTTP_FAILURE = 4;
-    public static final int MMS_ERROR_IO_ERROR = 5;
-    public static final int MMS_ERROR_RETRY = 6;
-    public static final int MMS_ERROR_CONFIGURATION_ERROR = 7;
-    public static final int MMS_ERROR_NO_DATA_NETWORK = 8;
-
-    /** Intent extra name for MMS sending result data in byte array type */
-    public static final String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA";
-    /** Intent extra name for HTTP status code for MMS HTTP failure in integer type */
-    public static final String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS";
-
-    /**
-     * Import a text message into system's SMS store
-     *
-     * Only default SMS apps can import SMS
-     *
-     * @param address the destination(source) address of the sent(received) message
-     * @param type the type of the message
-     * @param text the message text
-     * @param timestampMillis the message timestamp in milliseconds
-     * @param seen if the message is seen
-     * @param read if the message is read
-     * @return the message URI, null if failed
-     * @hide
-     */
-    public Uri importTextMessage(String address, int type, String text, long timestampMillis,
-            boolean seen, boolean read) {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.importTextMessage(ActivityThread.currentPackageName(),
-                        address, type, text, timestampMillis, seen, read);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return null;
-    }
-
-    /** Represents the received SMS message for importing {@hide} */
-    public static final int SMS_TYPE_INCOMING = 0;
-    /** Represents the sent SMS message for importing {@hide} */
-    public static final int SMS_TYPE_OUTGOING = 1;
-
-    /**
-     * Import a multimedia message into system's MMS store. Only the following PDU type is
-     * supported: Retrieve.conf, Send.req, Notification.ind, Delivery.ind, Read-Orig.ind
-     *
-     * Only default SMS apps can import MMS
-     *
-     * @param contentUri the content uri from which to read the PDU of the message to import
-     * @param messageId the optional message id. Use null if not specifying
-     * @param timestampSecs the optional message timestamp. Use -1 if not specifying
-     * @param seen if the message is seen
-     * @param read if the message is read
-     * @return the message URI, null if failed
-     * @throws IllegalArgumentException if pdu is empty
-     * {@hide}
-     */
-    public Uri importMultimediaMessage(Uri contentUri, String messageId, long timestampSecs,
-            boolean seen, boolean read) {
-        if (contentUri == null) {
-            throw new IllegalArgumentException("Uri contentUri null");
-        }
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.importMultimediaMessage(ActivityThread.currentPackageName(),
-                        contentUri, messageId, timestampSecs, seen, read);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return null;
-    }
-
-    /**
-     * Delete a system stored SMS or MMS message
-     *
-     * Only default SMS apps can delete system stored SMS and MMS messages
-     *
-     * @param messageUri the URI of the stored message
-     * @return true if deletion is successful, false otherwise
-     * @throws IllegalArgumentException if messageUri is empty
-     * {@hide}
-     */
-    public boolean deleteStoredMessage(Uri messageUri) {
-        if (messageUri == null) {
-            throw new IllegalArgumentException("Empty message URI");
-        }
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.deleteStoredMessage(ActivityThread.currentPackageName(), messageUri);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return false;
-    }
-
-    /**
-     * Delete a system stored SMS or MMS thread
-     *
-     * Only default SMS apps can delete system stored SMS and MMS conversations
-     *
-     * @param conversationId the ID of the message conversation
-     * @return true if deletion is successful, false otherwise
-     * {@hide}
-     */
-    public boolean deleteStoredConversation(long conversationId) {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.deleteStoredConversation(
-                        ActivityThread.currentPackageName(), conversationId);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return false;
-    }
-
-    /**
-     * Update the status properties of a system stored SMS or MMS message, e.g.
-     * the read status of a message, etc.
-     *
-     * @param messageUri the URI of the stored message
-     * @param statusValues a list of status properties in key-value pairs to update
-     * @return true if update is successful, false otherwise
-     * @throws IllegalArgumentException if messageUri is empty
-     * {@hide}
-     */
-    public boolean updateStoredMessageStatus(Uri messageUri, ContentValues statusValues) {
-        if (messageUri == null) {
-            throw new IllegalArgumentException("Empty message URI");
-        }
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.updateStoredMessageStatus(ActivityThread.currentPackageName(),
-                        messageUri, statusValues);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return false;
-    }
-
-    /** Message status property: whether the message has been seen. 1 means seen, 0 not {@hide} */
-    public static final String MESSAGE_STATUS_SEEN = "seen";
-    /** Message status property: whether the message has been read. 1 means read, 0 not {@hide} */
-    public static final String MESSAGE_STATUS_READ = "read";
-
-    /**
-     * Archive or unarchive a stored conversation
-     *
-     * @param conversationId the ID of the message conversation
-     * @param archived true to archive the conversation, false to unarchive
-     * @return true if update is successful, false otherwise
-     * {@hide}
-     */
-    public boolean archiveStoredConversation(long conversationId, boolean archived) {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.archiveStoredConversation(ActivityThread.currentPackageName(),
-                        conversationId, archived);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return false;
-    }
-
-    /**
-     * Add a text message draft to system SMS store
-     *
-     * Only default SMS apps can add SMS draft
-     *
-     * @param address the destination address of message
-     * @param text the body of the message to send
-     * @return the URI of the stored draft message
-     * {@hide}
-     */
-    public Uri addTextMessageDraft(String address, String text) {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.addTextMessageDraft(ActivityThread.currentPackageName(), address, text);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return null;
-    }
-
-    /**
-     * Add a multimedia message draft to system MMS store
-     *
-     * Only default SMS apps can add MMS draft
-     *
-     * @param contentUri the content uri from which to read the PDU data of the draft MMS
-     * @return the URI of the stored draft message
-     * @throws IllegalArgumentException if pdu is empty
-     * {@hide}
-     */
-    public Uri addMultimediaMessageDraft(Uri contentUri) {
-        if (contentUri == null) {
-            throw new IllegalArgumentException("Uri contentUri null");
-        }
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.addMultimediaMessageDraft(ActivityThread.currentPackageName(),
-                        contentUri);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return null;
-    }
-
-    /**
-     * Send a system stored text message.
-     *
-     * You can only send a failed text message or a draft text message.
-     *
-     * @param messageUri the URI of the stored message
-     * @param scAddress is the service center address or null to use the current default SMSC
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully sent, or failed.
-     *  The result code will be <code>Activity.RESULT_OK</code> for success,
-     *  or one of these errors:<br>
-     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *  <code>RESULT_ERROR_NULL_PDU</code><br>
-     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
-     *  the extra "errorCode" containing a radio technology specific value,
-     *  generally only useful for troubleshooting.<br>
-     *  The per-application based SMS control checks sentIntent. If sentIntent
-     *  is NULL the caller will be checked against all unknown applications,
-     *  which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is delivered to the recipient.  The
-     *  raw pdu of the status report is in the extended data ("pdu").
-     *
-     * @throws IllegalArgumentException if messageUri is empty
-     * {@hide}
-     */
-    public void sendStoredTextMessage(Uri messageUri, String scAddress, PendingIntent sentIntent,
-            PendingIntent deliveryIntent) {
-        if (messageUri == null) {
-            throw new IllegalArgumentException("Empty message URI");
-        }
-        try {
-            ISms iccISms = getISmsServiceOrThrow();
-            iccISms.sendStoredText(
-                    getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
-                    scAddress, sentIntent, deliveryIntent);
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-    }
-
-    /**
-     * Send a system stored multi-part text message.
-     *
-     * You can only send a failed text message or a draft text message.
-     * The provided <code>PendingIntent</code> lists should match the part number of the
-     * divided text of the stored message by using <code>divideMessage</code>
-     *
-     * @param messageUri the URI of the stored message
-     * @param scAddress is the service center address or null to use
-     *   the current default SMSC
-     * @param sentIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been sent.
-     *   The result code will be <code>Activity.RESULT_OK</code> for success,
-     *   or one of these errors:<br>
-     *   <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
-     *   <code>RESULT_ERROR_RADIO_OFF</code><br>
-     *   <code>RESULT_ERROR_NULL_PDU</code><br>
-     *   For <code>RESULT_ERROR_GENERIC_FAILURE</code> each sentIntent may include
-     *   the extra "errorCode" containing a radio technology specific value,
-     *   generally only useful for troubleshooting.<br>
-     *   The per-application based SMS control checks sentIntent. If sentIntent
-     *   is NULL the caller will be checked against all unknown applications,
-     *   which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntents if not null, an <code>ArrayList</code> of
-     *   <code>PendingIntent</code>s (one for each message part) that is
-     *   broadcast when the corresponding message part has been delivered
-     *   to the recipient.  The raw pdu of the status report is in the
-     *   extended data ("pdu").
-     *
-     * @throws IllegalArgumentException if messageUri is empty
-     * {@hide}
-     */
-    public void sendStoredMultipartTextMessage(Uri messageUri, String scAddress,
-            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
-        if (messageUri == null) {
-            throw new IllegalArgumentException("Empty message URI");
-        }
-        try {
-            ISms iccISms = getISmsServiceOrThrow();
-            iccISms.sendStoredMultipartText(
-                    getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
-                    scAddress, sentIntents, deliveryIntents);
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-    }
-
-    /**
-     * Send a system stored MMS message
-     *
-     * This is used for sending a previously sent, but failed-to-send, message or
-     * for sending a text message that has been stored as a draft.
-     *
-     * @param messageUri the URI of the stored message
-     * @param configOverrides the carrier-specific messaging configuration values to override for
-     *  sending the message.
-     * @param sentIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully sent, or failed
-     * @throws IllegalArgumentException if messageUri is empty
-     * {@hide}
-     */
-    public void sendStoredMultimediaMessage(Uri messageUri, Bundle configOverrides,
-            PendingIntent sentIntent) {
-        if (messageUri == null) {
-            throw new IllegalArgumentException("Empty message URI");
-        }
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                iMms.sendStoredMessage(
-                        getSubscriptionId(), ActivityThread.currentPackageName(), messageUri,
-                        configOverrides, sentIntent);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-    }
-
-    /**
-     * Turns on/off the flag to automatically write sent/received SMS/MMS messages into system
-     *
-     * When this flag is on, all SMS/MMS sent/received are stored by system automatically
-     * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
-     * automatically
-     *
-     * This flag can only be changed by default SMS apps
-     *
-     * @param enabled Whether to enable message auto persisting
-     * {@hide}
-     */
-    public void setAutoPersisting(boolean enabled) {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                iMms.setAutoPersisting(ActivityThread.currentPackageName(), enabled);
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-    }
-
-    /**
-     * Get the value of the flag to automatically write sent/received SMS/MMS messages into system
-     *
-     * When this flag is on, all SMS/MMS sent/received are stored by system automatically
-     * When this flag is off, only SMS/MMS sent by non-default SMS apps are stored by system
-     * automatically
-     *
-     * @return the current value of the auto persist flag
-     * {@hide}
-     */
-    public boolean getAutoPersisting() {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.getAutoPersisting();
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        return false;
-    }
-
-    /**
-     * Get carrier-dependent configuration values.
-     *
-     * @return bundle key/values pairs of configuration values
-     */
-    public Bundle getCarrierConfigValues() {
-        try {
-            IMms iMms = IMms.Stub.asInterface(ServiceManager.getService("imms"));
-            if (iMms != null) {
-                return iMms.getCarrierConfigValues(getSubscriptionId());
-            }
-        } catch (RemoteException ex) {
-            // ignore it
-        }
-        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.putBoolean(MMS_CONFIG_CLOSE_CONNECTION,
-                config.getBoolean(MMS_CONFIG_CLOSE_CONNECTION));
-        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
deleted file mode 100644
index 73e1f1a..0000000
--- a/src/java/android/telephony/SmsMessage.java
+++ /dev/null
@@ -1,890 +0,0 @@
-/*
- * Copyright (C) 2008 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 android.telephony;
-
-import android.os.Binder;
-import android.os.Parcel;
-import android.content.res.Resources;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.SmsMessageBase.SubmitPduBase;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
-
-import java.lang.Math;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
-
-
-/**
- * A Short Message Service message.
- * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent
- */
-public class SmsMessage {
-    private static final String LOG_TAG = "SmsMessage";
-
-    /**
-     * SMS Class enumeration.
-     * See TS 23.038.
-     *
-     */
-    public enum MessageClass{
-        UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3;
-    }
-
-    /** User data text encoding code unit size */
-    public static final int ENCODING_UNKNOWN = 0;
-    public static final int ENCODING_7BIT = 1;
-    public static final int ENCODING_8BIT = 2;
-    public static final int ENCODING_16BIT = 3;
-    /**
-     * @hide This value is not defined in global standard. Only in Korea, this is used.
-     */
-    public static final int ENCODING_KSC5601 = 4;
-
-    /** The maximum number of payload bytes per message */
-    public static final int MAX_USER_DATA_BYTES = 140;
-
-    /**
-     * The maximum number of payload bytes per message if a user data header
-     * is present.  This assumes the header only contains the
-     * CONCATENATED_8_BIT_REFERENCE element.
-     */
-    public static final int MAX_USER_DATA_BYTES_WITH_HEADER = 134;
-
-    /** The maximum number of payload septets per message */
-    public static final int MAX_USER_DATA_SEPTETS = 160;
-
-    /**
-     * The maximum number of payload septets per message if a user data header
-     * is present.  This assumes the header only contains the
-     * CONCATENATED_8_BIT_REFERENCE element.
-     */
-    public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153;
-
-    /**
-     * Indicates a 3GPP format SMS message.
-     * @hide pending API council approval
-     */
-    public static final String FORMAT_3GPP = "3gpp";
-
-    /**
-     * Indicates a 3GPP2 format SMS message.
-     * @hide pending API council approval
-     */
-    public static final String FORMAT_3GPP2 = "3gpp2";
-
-    /** Contains actual SmsMessage. Only public for debugging and for framework layer.
-     *
-     * @hide
-     */
-    public SmsMessageBase mWrappedSmsMessage;
-
-    /** Indicates the subId
-     *
-     * @hide
-     */
-    private int mSubId = 0;
-
-    /** set Subscription information
-     *
-     * @hide
-     */
-    public void setSubId(int subId) {
-        mSubId = subId;
-    }
-
-    /** get Subscription information
-     *
-     * @hide
-     */
-    public int getSubId() {
-        return mSubId;
-    }
-
-    public static class SubmitPdu {
-
-        public byte[] encodedScAddress; // Null if not applicable.
-        public byte[] encodedMessage;
-
-        @Override
-        public String toString() {
-            return "SubmitPdu: encodedScAddress = "
-                    + Arrays.toString(encodedScAddress)
-                    + ", encodedMessage = "
-                    + Arrays.toString(encodedMessage);
-        }
-
-        /**
-         * @hide
-         */
-        protected SubmitPdu(SubmitPduBase spb) {
-            this.encodedMessage = spb.encodedMessage;
-            this.encodedScAddress = spb.encodedScAddress;
-        }
-
-    }
-
-    private SmsMessage(SmsMessageBase smb) {
-        mWrappedSmsMessage = smb;
-    }
-
-    /**
-     * 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.
-     * @deprecated Use {@link #createFromPdu(byte[], String)} instead.
-     */
-    @Deprecated
-    public static SmsMessage createFromPdu(byte[] pdu) {
-         SmsMessage message = null;
-
-        // cdma(3gpp2) vs gsm(3gpp) format info was not given,
-        // guess from active voice phone type
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-        String format = (PHONE_TYPE_CDMA == activePhone) ?
-                SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
-        message = createFromPdu(pdu, format);
-
-        if (null == message || null == message.mWrappedSmsMessage) {
-            // decoding pdu failed based on activePhone type, must be other format
-            format = (PHONE_TYPE_CDMA == activePhone) ?
-                    SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2;
-            message = createFromPdu(pdu, format);
-        }
-        return message;
-    }
-
-    /**
-     * Create an SmsMessage from a raw PDU with the specified message format. The
-     * 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
-     * {@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;
-
-        if (SmsConstants.FORMAT_3GPP2.equals(format)) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
-        } else if (SmsConstants.FORMAT_3GPP.equals(format)) {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu);
-        } else {
-            Rlog.e(LOG_TAG, "createFromPdu(): unsupported message format " + format);
-            return null;
-        }
-
-        if (wrappedMessage != null) {
-            return new SmsMessage(wrappedMessage);
-        } else {
-            Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
-            return null;
-        }
-    }
-
-    /**
-     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
-     * +CMT unsolicited response (PDU mode, of course)
-     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
-     *
-     * Only public for debugging and for RIL
-     *
-     * {@hide}
-     */
-    public static SmsMessage newFromCMT(String[] lines) {
-        // received SMS in 3GPP format
-        SmsMessageBase wrappedMessage =
-                com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines);
-
-        if (wrappedMessage != null) {
-            return new SmsMessage(wrappedMessage);
-        } else {
-            Rlog.e(LOG_TAG, "newFromCMT(): wrappedMessage is null");
-            return null;
-        }
-    }
-
-    /** @hide */
-    public static SmsMessage newFromParcel(Parcel p) {
-        // received SMS in 3GPP2 format
-        SmsMessageBase wrappedMessage =
-                com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p);
-
-        return new SmsMessage(wrappedMessage);
-    }
-
-    /**
-     * Create an SmsMessage from an SMS EF record.
-     *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by SmsManager.getAllMessagesFromSim + 1.
-     * @param data Record data.
-     * @return An SmsMessage representing the record.
-     *
-     * @hide
-     */
-    public static SmsMessage createFromEfRecord(int index, byte[] data) {
-        SmsMessageBase wrappedMessage;
-
-        if (isCdmaVoice()) {
-            wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
-                    index, data);
-        } else {
-            wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
-                    index, data);
-        }
-
-        if (wrappedMessage != null) {
-            return new SmsMessage(wrappedMessage);
-        } else {
-            Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null");
-            return null;
-        }
-    }
-
-    /**
-     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
-     * length in bytes (not hex chars) less the SMSC header
-     *
-     * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices.
-     * We should probably deprecate it and remove the obsolete test case.
-     */
-    public static int getTPLayerLengthForPDU(String pdu) {
-        if (isCdmaVoice()) {
-            return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
-        } else {
-            return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
-        }
-    }
-
-    /*
-     * TODO(cleanup): It would make some sense if the result of
-     * preprocessing a message to determine the proper encoding (i.e.
-     * the resulting data structure from calculateLength) could be
-     * passed as an argument to the actual final encoding function.
-     * This would better ensure that the logic behind size calculation
-     * actually matched the encoding.
-     */
-
-    /**
-     * 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 if true, characters that are not part of the
-     *         radio-specific 7-bit encoding are counted as single
-     *         space chars.  If false, and if the messageBody contains
-     *         non-7-bit encodable characters, length is calculated
-     *         using a 16-bit encoding.
-     * @return an int[4] with int[0] being the number of SMS's
-     *         required, int[1] the number of code units used, and
-     *         int[2] is the number of code units remaining until the
-     *         next message. int[3] is an indicator of the encoding
-     *         code unit size (see the ENCODING_* definitions in SmsConstants)
-     */
-    public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
-        // this function is for MO SMS
-        TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?
-            com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly,
-                    true) :
-            com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
-        int ret[] = new int[4];
-        ret[0] = ted.msgCount;
-        ret[1] = ted.codeUnitCount;
-        ret[2] = ted.codeUnitsRemaining;
-        ret[3] = ted.codeUnitSize;
-        return ret;
-    }
-
-    /**
-     * Divide a message text into several fragments, none bigger than
-     * the maximum SMS message text size.
-     *
-     * @param text text, must not be null.
-     * @return an <code>ArrayList</code> of strings that, in order,
-     *   comprise the original msg text
-     *
-     * @hide
-     */
-    public static ArrayList<String> fragmentText(String text) {
-        // This function is for MO SMS
-        TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?
-            com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false, true) :
-            com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
-
-        // TODO(cleanup): The code here could be rolled into the logic
-        // below cleanly if these MAX_* constants were defined more
-        // flexibly...
-
-        int limit;
-        if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
-            int udhLength;
-            if (ted.languageTable != 0 && ted.languageShiftTable != 0) {
-                udhLength = GsmAlphabet.UDH_SEPTET_COST_TWO_SHIFT_TABLES;
-            } else if (ted.languageTable != 0 || ted.languageShiftTable != 0) {
-                udhLength = GsmAlphabet.UDH_SEPTET_COST_ONE_SHIFT_TABLE;
-            } else {
-                udhLength = 0;
-            }
-
-            if (ted.msgCount > 1) {
-                udhLength += GsmAlphabet.UDH_SEPTET_COST_CONCATENATED_MESSAGE;
-            }
-
-            if (udhLength != 0) {
-                udhLength += GsmAlphabet.UDH_SEPTET_COST_LENGTH;
-            }
-
-            limit = SmsConstants.MAX_USER_DATA_SEPTETS - udhLength;
-        } else {
-            if (ted.msgCount > 1) {
-                limit = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
-                // If EMS is not supported, break down EMS into single segment SMS
-                // and add page info " x/y".
-                // In the case of UCS2 encoding, 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).
-                // Make sure total number of segments is less than 10.
-                if (!hasEmsSupport() && ted.msgCount < 10) {
-                    limit -= 2;
-                }
-            } else {
-                limit = SmsConstants.MAX_USER_DATA_BYTES;
-            }
-        }
-
-        String newMsgBody = null;
-        Resources r = Resources.getSystem();
-        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
-            newMsgBody  = Sms7BitEncodingTranslator.translate(text);
-        }
-        if (TextUtils.isEmpty(newMsgBody)) {
-            newMsgBody = text;
-        }
-        int pos = 0;  // Index in code units.
-        int textLen = newMsgBody.length();
-        ArrayList<String> result = new ArrayList<String>(ted.msgCount);
-        while (pos < textLen) {
-            int nextPos = 0;  // Counts code units.
-            if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
-                if (useCdmaFormatForMoSms() && ted.msgCount == 1) {
-                    // For a singleton CDMA message, the encoding must be ASCII...
-                    nextPos = pos + Math.min(limit, textLen - pos);
-                } else {
-                    // For multi-segment messages, CDMA 7bit equals GSM 7bit encoding (EMS mode).
-                    nextPos = GsmAlphabet.findGsmSeptetLimitIndex(newMsgBody, pos, limit,
-                            ted.languageTable, ted.languageShiftTable);
-                }
-            } else {  // Assume unicode.
-                nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody);
-            }
-            if ((nextPos <= pos) || (nextPos > textLen)) {
-                Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
-                          nextPos + " >= " + textLen + ")");
-                break;
-            }
-            result.add(newMsgBody.substring(pos, nextPos));
-            pos = nextPos;
-        }
-        return result;
-    }
-
-    /**
-     * Calculates the number of SMS's required to encode the message body and
-     * the number of characters remaining until the next message, given the
-     * current encoding.
-     *
-     * @param messageBody the message to encode
-     * @param use7bitOnly if true, characters that are not part of the radio
-     *         specific (GSM / CDMA) alphabet encoding are converted to as a
-     *         single space characters. If false, a messageBody containing
-     *         non-GSM or non-CDMA alphabet characters are encoded using
-     *         16-bit encoding.
-     * @return an int[4] with int[0] being the number of SMS's required, int[1]
-     *         the number of code units used, and int[2] is the number of code
-     *         units remaining until the next message. int[3] is the encoding
-     *         type that should be used for the message.
-     */
-    public static int[] calculateLength(String messageBody, boolean use7bitOnly) {
-        return calculateLength((CharSequence)messageBody, use7bitOnly);
-    }
-
-    /*
-     * TODO(cleanup): It looks like there is now no useful reason why
-     * apps should generate pdus themselves using these routines,
-     * instead of handing the raw data to SMSDispatcher (and thereby
-     * have the phone process do the encoding).  Moreover, CDMA now
-     * has shared state (in the form of the msgId system property)
-     * which can only be modified by the phone process, and hence
-     * makes the output of these routines incorrect.  Since they now
-     * serve no purpose, they should probably just return null
-     * directly, and be deprecated.  Going further in that direction,
-     * the above parsers of serialized pdu data should probably also
-     * be gotten rid of, hiding all but the necessarily visible
-     * structured data from client apps.  A possible concern with
-     * doing this is that apps may be using these routines to generate
-     * pdus that are then sent elsewhere, some network server, for
-     * example, and that always returning null would thereby break
-     * otherwise useful apps.
-     */
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message.
-     * This method will not attempt to use any GSM national language 7 bit encodings.
-     *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, String message, boolean statusReportRequested) {
-        SubmitPduBase spb;
-
-        if (useCdmaFormatForMoSms()) {
-            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
-                    destinationAddress, message, statusReportRequested, null);
-        } else {
-            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
-                    destinationAddress, message, statusReportRequested);
-        }
-
-        return new SubmitPdu(spb);
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
-     * This method will not attempt to use any GSM national language 7 bit encodings.
-     *
-     * @param scAddress Service Centre address. null == use default
-     * @param destinationAddress the address of the destination for the message
-     * @param destinationPort the port to deliver the message to at the
-     *        destination
-     * @param data the data for the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, short destinationPort, byte[] data,
-            boolean statusReportRequested) {
-        SubmitPduBase spb;
-
-        if (useCdmaFormatForMoSms()) {
-            spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
-                    destinationAddress, destinationPort, data, statusReportRequested);
-        } else {
-            spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
-                    destinationAddress, destinationPort, data, statusReportRequested);
-        }
-
-        return new SubmitPdu(spb);
-    }
-
-    /**
-     * Returns the address of the SMS service center that relayed this message
-     * or null if there is none.
-     */
-    public String getServiceCenterAddress() {
-        return mWrappedSmsMessage.getServiceCenterAddress();
-    }
-
-    /**
-     * Returns the originating address (sender) of this SMS message in String
-     * form or null if unavailable
-     */
-    public String getOriginatingAddress() {
-        return mWrappedSmsMessage.getOriginatingAddress();
-    }
-
-    /**
-     * Returns the originating address, or email from address if this message
-     * was from an email gateway. Returns null if originating address
-     * unavailable.
-     */
-    public String getDisplayOriginatingAddress() {
-        return mWrappedSmsMessage.getDisplayOriginatingAddress();
-    }
-
-    /**
-     * Returns the message body as a String, if it exists and is text based.
-     * @return message body is there is one, otherwise null
-     */
-    public String getMessageBody() {
-        return mWrappedSmsMessage.getMessageBody();
-    }
-
-    /**
-     * Returns the class of this message.
-     */
-    public MessageClass getMessageClass() {
-        switch(mWrappedSmsMessage.getMessageClass()) {
-            case CLASS_0: return MessageClass.CLASS_0;
-            case CLASS_1: return MessageClass.CLASS_1;
-            case CLASS_2: return MessageClass.CLASS_2;
-            case CLASS_3: return MessageClass.CLASS_3;
-            default: return MessageClass.UNKNOWN;
-
-        }
-    }
-
-    /**
-     * Returns the message body, or email message body if this message was from
-     * an email gateway. Returns null if message body unavailable.
-     */
-    public String getDisplayMessageBody() {
-        return mWrappedSmsMessage.getDisplayMessageBody();
-    }
-
-    /**
-     * Unofficial convention of a subject line enclosed in parens empty string
-     * if not present
-     */
-    public String getPseudoSubject() {
-        return mWrappedSmsMessage.getPseudoSubject();
-    }
-
-    /**
-     * Returns the service centre timestamp in currentTimeMillis() format
-     */
-    public long getTimestampMillis() {
-        return mWrappedSmsMessage.getTimestampMillis();
-    }
-
-    /**
-     * Returns true if message is an email.
-     *
-     * @return true if this message came through an email gateway and email
-     *         sender / subject / parsed body are available
-     */
-    public boolean isEmail() {
-        return mWrappedSmsMessage.isEmail();
-    }
-
-     /**
-     * @return if isEmail() is true, body of the email sent through the gateway.
-     *         null otherwise
-     */
-    public String getEmailBody() {
-        return mWrappedSmsMessage.getEmailBody();
-    }
-
-    /**
-     * @return if isEmail() is true, email from address of email sent through
-     *         the gateway. null otherwise
-     */
-    public String getEmailFrom() {
-        return mWrappedSmsMessage.getEmailFrom();
-    }
-
-    /**
-     * Get protocol identifier.
-     */
-    public int getProtocolIdentifier() {
-        return mWrappedSmsMessage.getProtocolIdentifier();
-    }
-
-    /**
-     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
-     * SMS
-     */
-    public boolean isReplace() {
-        return mWrappedSmsMessage.isReplace();
-    }
-
-    /**
-     * Returns true for CPHS MWI toggle message.
-     *
-     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
-     *         B.4.2
-     */
-    public boolean isCphsMwiMessage() {
-        return mWrappedSmsMessage.isCphsMwiMessage();
-    }
-
-    /**
-     * returns true if this message is a CPHS voicemail / message waiting
-     * indicator (MWI) clear message
-     */
-    public boolean isMWIClearMessage() {
-        return mWrappedSmsMessage.isMWIClearMessage();
-    }
-
-    /**
-     * returns true if this message is a CPHS voicemail / message waiting
-     * indicator (MWI) set message
-     */
-    public boolean isMWISetMessage() {
-        return mWrappedSmsMessage.isMWISetMessage();
-    }
-
-    /**
-     * returns true if this message is a "Message Waiting Indication Group:
-     * Discard Message" notification and should not be stored.
-     */
-    public boolean isMwiDontStore() {
-        return mWrappedSmsMessage.isMwiDontStore();
-    }
-
-    /**
-     * returns the user data section minus the user data header if one was
-     * present.
-     */
-    public byte[] getUserData() {
-        return mWrappedSmsMessage.getUserData();
-    }
-
-    /**
-     * Returns the raw PDU for the message.
-     *
-     * @return the raw PDU for the message.
-     */
-    public byte[] getPdu() {
-        return mWrappedSmsMessage.getPdu();
-    }
-
-    /**
-     * Returns the status of the message on the SIM (read, unread, sent, unsent).
-     *
-     * @return the status of the message on the SIM.  These are:
-     *         SmsManager.STATUS_ON_SIM_FREE
-     *         SmsManager.STATUS_ON_SIM_READ
-     *         SmsManager.STATUS_ON_SIM_UNREAD
-     *         SmsManager.STATUS_ON_SIM_SEND
-     *         SmsManager.STATUS_ON_SIM_UNSENT
-     * @deprecated Use getStatusOnIcc instead.
-     */
-    @Deprecated public int getStatusOnSim() {
-        return mWrappedSmsMessage.getStatusOnIcc();
-    }
-
-    /**
-     * Returns the status of the message on the ICC (read, unread, sent, unsent).
-     *
-     * @return the status of the message on the ICC.  These are:
-     *         SmsManager.STATUS_ON_ICC_FREE
-     *         SmsManager.STATUS_ON_ICC_READ
-     *         SmsManager.STATUS_ON_ICC_UNREAD
-     *         SmsManager.STATUS_ON_ICC_SEND
-     *         SmsManager.STATUS_ON_ICC_UNSENT
-     */
-    public int getStatusOnIcc() {
-        return mWrappedSmsMessage.getStatusOnIcc();
-    }
-
-    /**
-     * Returns the record index of the message on the SIM (1-based index).
-     * @return the record index of the message on the SIM, or -1 if this
-     *         SmsMessage was not created from a SIM SMS EF record.
-     * @deprecated Use getIndexOnIcc instead.
-     */
-    @Deprecated public int getIndexOnSim() {
-        return mWrappedSmsMessage.getIndexOnIcc();
-    }
-
-    /**
-     * Returns the record index of the message on the ICC (1-based index).
-     * @return the record index of the message on the ICC, or -1 if this
-     *         SmsMessage was not created from a ICC SMS EF record.
-     */
-    public int getIndexOnIcc() {
-        return mWrappedSmsMessage.getIndexOnIcc();
-    }
-
-    /**
-     * GSM:
-     * For an SMS-STATUS-REPORT message, this returns the status field from
-     * the status report.  This field indicates the status of a previously
-     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
-     * description of values.
-     * CDMA:
-     * For not interfering with status codes from GSM, the value is
-     * shifted to the bits 31-16.
-     * The value is composed of an error class (bits 25-24) and a status code (bits 23-16).
-     * Possible codes are described in C.S0015-B, v2.0, 4.5.21.
-     *
-     * @return 0 indicates the previously sent message was received.
-     *         See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21
-     *         for a description of other possible values.
-     */
-    public int getStatus() {
-        return mWrappedSmsMessage.getStatus();
-    }
-
-    /**
-     * Return true iff the message is a SMS-STATUS-REPORT message.
-     */
-    public boolean isStatusReportMessage() {
-        return mWrappedSmsMessage.isStatusReportMessage();
-    }
-
-    /**
-     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
-     * this message.
-     */
-    public boolean isReplyPathPresent() {
-        return mWrappedSmsMessage.isReplyPathPresent();
-    }
-
-    /**
-     * Determines whether or not to use CDMA format for MO SMS.
-     * If SMS over IMS is supported, then format is based on IMS SMS format,
-     * otherwise format is based on current phone type.
-     *
-     * @return true if Cdma format should be used for MO SMS, false otherwise.
-     */
-    private static boolean useCdmaFormatForMoSms() {
-        if (!SmsManager.getDefault().isImsSmsSupported()) {
-            // use Voice technology to determine SMS format.
-            return isCdmaVoice();
-        }
-        // IMS is registered with SMS support, check the SMS format supported
-        return (SmsConstants.FORMAT_3GPP2.equals(SmsManager.getDefault().getImsSmsFormat()));
-    }
-
-    /**
-     * Determines whether or not to current phone type is cdma.
-     *
-     * @return true if current phone type is cdma, false otherwise.
-     */
-    private static boolean isCdmaVoice() {
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-        return (PHONE_TYPE_CDMA == activePhone);
-    }
-
-    /**
-     * Decide if the carrier supports long SMS.
-     * {@hide}
-     */
-    public static boolean hasEmsSupport() {
-        if (!isNoEmsSupportConfigListExisted()) {
-            return true;
-        }
-
-        String simOperator;
-        String gid;
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
-            gid = TelephonyManager.getDefault().getGroupIdLevel1();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        if (!TextUtils.isEmpty(simOperator)) {
-            for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
-                if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
-                        (TextUtils.isEmpty(currentConfig.mGid1) ||
-                                (!TextUtils.isEmpty(currentConfig.mGid1) &&
-                                        currentConfig.mGid1.equalsIgnoreCase(gid)))) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Check where to add " x/y" in each SMS segment, begin or end.
-     * {@hide}
-     */
-    public static boolean shouldAppendPageNumberAsPrefix() {
-        if (!isNoEmsSupportConfigListExisted()) {
-            return false;
-        }
-
-        String simOperator;
-        String gid;
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            simOperator = TelephonyManager.getDefault().getSimOperatorNumeric();
-            gid = TelephonyManager.getDefault().getGroupIdLevel1();
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-
-        for (NoEmsSupportConfig currentConfig : mNoEmsSupportConfigList) {
-            if (simOperator.startsWith(currentConfig.mOperatorNumber) &&
-                (TextUtils.isEmpty(currentConfig.mGid1) ||
-                (!TextUtils.isEmpty(currentConfig.mGid1)
-                && currentConfig.mGid1.equalsIgnoreCase(gid)))) {
-                return currentConfig.mIsPrefix;
-            }
-        }
-        return false;
-    }
-
-    private static class NoEmsSupportConfig {
-        String mOperatorNumber;
-        String mGid1;
-        boolean mIsPrefix;
-
-        public NoEmsSupportConfig(String[] config) {
-            mOperatorNumber = config[0];
-            mIsPrefix = "prefix".equals(config[1]);
-            mGid1 = config.length > 2 ? config[2] : null;
-        }
-
-        @Override
-        public String toString() {
-            return "NoEmsSupportConfig { mOperatorNumber = " + mOperatorNumber
-                    + ", mIsPrefix = " + mIsPrefix + ", mGid1 = " + mGid1 + " }";
-        }
-    }
-
-    private static NoEmsSupportConfig[] mNoEmsSupportConfigList = null;
-    private static boolean mIsNoEmsSupportConfigListLoaded = false;
-
-    private static boolean isNoEmsSupportConfigListExisted() {
-        if (!mIsNoEmsSupportConfigListLoaded) {
-            Resources r = Resources.getSystem();
-            if (r != null) {
-                String[] listArray = r.getStringArray(
-                        com.android.internal.R.array.no_ems_support_sim_operators);
-                if ((listArray != null) && (listArray.length > 0)) {
-                    mNoEmsSupportConfigList = new NoEmsSupportConfig[listArray.length];
-                    for (int i=0; i<listArray.length; i++) {
-                        mNoEmsSupportConfigList[i] = new NoEmsSupportConfig(listArray[i].split(";"));
-                    }
-                }
-                mIsNoEmsSupportConfigListLoaded = true;
-            }
-        }
-
-        if (mNoEmsSupportConfigList != null && mNoEmsSupportConfigList.length != 0) {
-            return true;
-        }
-
-        return false;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/AppSmsManager.java b/src/java/com/android/internal/telephony/AppSmsManager.java
new file mode 100644
index 0000000..f2a783f
--- /dev/null
+++ b/src/java/com/android/internal/telephony/AppSmsManager.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.SmsMessage;
+import android.util.ArrayMap;
+import android.util.Base64;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.security.SecureRandom;
+import java.util.Map;
+
+
+/**
+ *  Manager for app specific incoming SMS requests. This can be used to implement SMS based
+ *  communication channels (e.g. for SMS based phone number verification) without needing the
+ *  {@link Manifest.permission#RECEIVE_SMS} permission.
+ *
+ *  {@link #createAppSpecificSmsRequest} allows an application to provide a {@link PendingIntent}
+ *  that is triggered when an incoming SMS is received that contains the provided token.
+ */
+public class AppSmsManager {
+    private static final String LOG_TAG = "AppSmsManager";
+
+    private final SecureRandom mRandom;
+    private final Context mContext;
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private final Map<String, AppRequestInfo> mTokenMap;
+    @GuardedBy("mLock")
+    private final Map<String, AppRequestInfo> mPackageMap;
+
+    public AppSmsManager(Context context) {
+        mRandom = new SecureRandom();
+        mTokenMap = new ArrayMap<>();
+        mPackageMap = new ArrayMap<>();
+        mContext = context;
+    }
+
+    /**
+     * Create an app specific incoming SMS request for the the calling package.
+     *
+     * This method returns a token that if included in a subsequent incoming SMS message the
+     * {@link Intents.SMS_RECEIVED_ACTION} intent will be delivered only to the calling package and
+     * will not require the application have the {@link Manifest.permission#RECEIVE_SMS} permission.
+     *
+     * An app can only have one request at a time, if the app already has a request it will be
+     * dropped and the new one will be added.
+     *
+     * @return Token to include in an SMS to have it delivered directly to the app.
+     */
+    public String createAppSpecificSmsToken(String callingPkg, PendingIntent intent) {
+        // Check calling uid matches callingpkg.
+        AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        appOps.checkPackage(Binder.getCallingUid(), callingPkg);
+
+        // Generate a nonce to store the request under.
+        String token = generateNonce();
+        synchronized (mLock) {
+            // Only allow one request in flight from a package.
+            if (mPackageMap.containsKey(callingPkg)) {
+                removeRequestLocked(mPackageMap.get(callingPkg));
+            }
+            // Store state.
+            AppRequestInfo info = new AppRequestInfo(callingPkg, intent, token);
+            addRequestLocked(info);
+        }
+        return token;
+    }
+
+    /**
+     * Handle an incoming SMS_DELIVER_ACTION intent if it is an app-only SMS.
+     */
+    public boolean handleSmsReceivedIntent(Intent intent) {
+        // Sanity check the action.
+        if (intent.getAction() != Intents.SMS_DELIVER_ACTION) {
+            Log.wtf(LOG_TAG, "Got intent with incorrect action: " + intent.getAction());
+            return false;
+        }
+
+        synchronized (mLock) {
+            AppRequestInfo info = findAppRequestInfoSmsIntentLocked(intent);
+            if (info == null) {
+                // The message didn't contain a token -- nothing to do.
+                return false;
+            }
+            try {
+                Intent fillIn = new Intent();
+                fillIn.putExtras(intent.getExtras());
+                info.pendingIntent.send(mContext, 0, fillIn);
+            } catch (PendingIntent.CanceledException e) {
+                // The pending intent is canceled, send this SMS as normal.
+                removeRequestLocked(info);
+                return false;
+            }
+
+            removeRequestLocked(info);
+            return true;
+        }
+    }
+
+    private AppRequestInfo findAppRequestInfoSmsIntentLocked(Intent intent) {
+        SmsMessage[] messages = Intents.getMessagesFromIntent(intent);
+        if (messages == null) {
+            return null;
+        }
+        StringBuilder fullMessageBuilder = new StringBuilder();
+        for (SmsMessage message : messages) {
+            if (message.getMessageBody() == null) {
+                continue;
+            }
+            fullMessageBuilder.append(message.getMessageBody());
+        }
+
+        String fullMessage = fullMessageBuilder.toString();
+
+        // Look for any tokens in the full message.
+        for (String token : mTokenMap.keySet()) {
+            if (fullMessage.contains(token)) {
+                return mTokenMap.get(token);
+            }
+        }
+        return null;
+    }
+
+    private String generateNonce() {
+        byte[] bytes = new byte[8];
+        mRandom.nextBytes(bytes);
+        return Base64.encodeToString(bytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
+    }
+
+    private void removeRequestLocked(AppRequestInfo info) {
+        mTokenMap.remove(info.token);
+        mPackageMap.remove(info.packageName);
+    }
+
+    private void addRequestLocked(AppRequestInfo info) {
+        mTokenMap.put(info.token, info);
+        mPackageMap.put(info.packageName, info);
+    }
+
+    private final class AppRequestInfo {
+        public final String packageName;
+        public final PendingIntent pendingIntent;
+        public final String token;
+
+        AppRequestInfo(String packageName, PendingIntent pendingIntent, String token) {
+            this.packageName = packageName;
+            this.pendingIntent = pendingIntent;
+            this.token = token;
+        }
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index df91ceb..5e09c35 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -18,16 +18,13 @@
 package com.android.internal.telephony;
 
 import android.content.Context;
-import android.os.Message;
-import android.os.RegistrantList;
-import android.os.Registrant;
-import android.os.Handler;
 import android.os.AsyncResult;
-import android.telephony.RadioAccessFamily;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
 import android.telephony.TelephonyManager;
 
-import com.android.internal.telephony.RadioCapability;
-
 /**
  * {@hide}
  */
@@ -43,8 +40,8 @@
     protected RegistrantList mOffOrNotAvailRegistrants = new RegistrantList();
     protected RegistrantList mNotAvailRegistrants = new RegistrantList();
     protected RegistrantList mCallStateRegistrants = new RegistrantList();
-    protected RegistrantList mVoiceNetworkStateRegistrants = new RegistrantList();
-    protected RegistrantList mDataNetworkStateRegistrants = new RegistrantList();
+    protected RegistrantList mNetworkStateRegistrants = new RegistrantList();
+    protected RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
     protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList();
     protected RegistrantList mImsNetworkStateChangedRegistrants = new RegistrantList();
     protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList();
@@ -74,6 +71,8 @@
     protected RegistrantList mPhoneRadioCapabilityChangedRegistrants =
             new RegistrantList();
     protected RegistrantList mPcoDataRegistrants = new RegistrantList();
+    protected RegistrantList mCarrierInfoForImsiEncryptionRegistrants = new RegistrantList();
+    protected RegistrantList mRilNetworkScanResultRegistrants = new RegistrantList();
 
 
     protected Registrant mGsmSmsRegistrant;
@@ -237,27 +236,27 @@
     }
 
     @Override
-    public void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj) {
+    public void registerForNetworkStateChanged(Handler h, int what, Object obj) {
         Registrant r = new Registrant (h, what, obj);
 
-        mVoiceNetworkStateRegistrants.add(r);
+        mNetworkStateRegistrants.add(r);
     }
 
     @Override
-    public void unregisterForVoiceNetworkStateChanged(Handler h) {
-        mVoiceNetworkStateRegistrants.remove(h);
+    public void unregisterForNetworkStateChanged(Handler h) {
+        mNetworkStateRegistrants.remove(h);
     }
 
     @Override
-    public void registerForDataNetworkStateChanged(Handler h, int what, Object obj) {
+    public void registerForDataCallListChanged(Handler h, int what, Object obj) {
         Registrant r = new Registrant (h, what, obj);
 
-        mDataNetworkStateRegistrants.add(r);
+        mDataCallListChangedRegistrants.add(r);
     }
 
     @Override
-    public void unregisterForDataNetworkStateChanged(Handler h) {
-        mDataNetworkStateRegistrants.remove(h);
+    public void unregisterForDataCallListChanged(Handler h) {
+        mDataCallListChangedRegistrants.remove(h);
     }
 
     @Override
@@ -732,6 +731,17 @@
         mHardwareConfigChangeRegistrants.remove(h);
     }
 
+    @Override
+    public void registerForNetworkScanResult(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+        mRilNetworkScanResultRegistrants.add(r);
+    }
+
+    @Override
+    public void unregisterForNetworkScanResult(Handler h) {
+        mRilNetworkScanResultRegistrants.remove(h);
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -785,7 +795,6 @@
 
             if (mState.isAvailable() && !oldState.isAvailable()) {
                 mAvailRegistrants.notifyRegistrants();
-                onRadioAvailable();
             }
 
             if (!mState.isAvailable() && oldState.isAvailable()) {
@@ -804,9 +813,6 @@
         }
     }
 
-    protected void onRadioAvailable() {
-    }
-
     /**
      * {@inheritDoc}
      */
@@ -912,4 +918,14 @@
     public void unregisterForPcoData(Handler h) {
         mPcoDataRegistrants.remove(h);
     }
+
+    @Override
+    public void registerForCarrierInfoForImsiEncryption(Handler h, int what, Object obj) {
+        mCarrierInfoForImsiEncryptionRegistrants.add(new Registrant(h, what, obj));
+    }
+
+    @Override
+    public void unregisterForCarrierInfoForImsiEncryption(Handler h) {
+        mCarrierInfoForImsiEncryptionRegistrants.remove(h);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CallFailCause.java b/src/java/com/android/internal/telephony/CallFailCause.java
index c597927..ed39b4d 100644
--- a/src/java/com/android/internal/telephony/CallFailCause.java
+++ b/src/java/com/android/internal/telephony/CallFailCause.java
@@ -31,6 +31,7 @@
     // Unassigned/Unobtainable number
     int UNOBTAINABLE_NUMBER = 1;
 
+    int OPERATOR_DETERMINED_BARRING = 8;
     int NORMAL_CLEARING     = 16;
     // Busy Tone
     int USER_BUSY           = 17;
@@ -52,12 +53,17 @@
     int ACM_LIMIT_EXCEEDED = 68;
     int CALL_BARRED        = 240;
     int FDN_BLOCKED        = 241;
+    int IMEI_NOT_ACCEPTED  = 243;
 
     // Stk Call Control
     int DIAL_MODIFIED_TO_USSD = 244;
     int DIAL_MODIFIED_TO_SS   = 245;
     int DIAL_MODIFIED_TO_DIAL = 246;
 
+    //Emergency Redial
+    int EMERGENCY_TEMP_FAILURE = 325;
+    int EMERGENCY_PERM_FAILURE = 326;
+
     int CDMA_LOCKED_UNTIL_POWER_CYCLE  = 1000;
     int CDMA_DROP                      = 1001;
     int CDMA_INTERCEPT                 = 1002;
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 4016217..2775fe6 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -1493,6 +1493,7 @@
      * <code>obj.result</code> will be an "MmiCode" object
      */
     public void registerForMmiComplete(Handler h, int what, Object obj){
+        Rlog.d(LOG_TAG, "registerForMmiComplete");
         mMmiCompleteRegistrants.addUnique(h, what, obj);
     }
 
@@ -2317,7 +2318,7 @@
                     mMmiInitiateRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_MMI_COMPLETE:
-                    if (VDBG) Rlog.d(LOG_TAG, " handleMessage (EVENT_MMI_COMPLETE)");
+                    Rlog.d(LOG_TAG, "CallManager: handleMessage (EVENT_MMI_COMPLETE)");
                     mMmiCompleteRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                     break;
                 case EVENT_ECM_TIMER_RESET:
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 57cbeb8..8429146 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -26,7 +26,8 @@
     /** 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 static final int ERROR_OUT_OF_SERVICE = 1;
+    public static final int ERROR_POWER_OFF = 2;
 
     public
     CallStateException()
diff --git a/src/java/com/android/internal/telephony/CallTracker.java b/src/java/com/android/internal/telephony/CallTracker.java
index ad64a4a..23874e2 100644
--- a/src/java/com/android/internal/telephony/CallTracker.java
+++ b/src/java/com/android/internal/telephony/CallTracker.java
@@ -16,10 +16,13 @@
 
 package com.android.internal.telephony;
 
+import android.content.Context;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
 import android.text.TextUtils;
 
 import java.io.FileDescriptor;
@@ -203,8 +206,20 @@
         if (dialNumber == null) {
             return dialNumber;
         }
-        String[] convertMaps = phone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.dial_string_replace);
+        String[] convertMaps = null;
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle bundle = configManager.getConfig();
+        if (bundle != null) {
+            convertMaps =
+                    bundle.getStringArray(CarrierConfigManager.KEY_DIAL_STRING_REPLACE_STRING_ARRAY);
+        }
+        if (convertMaps == null) {
+            // By default no replacement is necessary
+            log("convertNumberIfNecessary convertMaps is null");
+            return dialNumber;
+        }
+
         log("convertNumberIfNecessary Roaming"
             + " convertMaps.length " + convertMaps.length
             + " dialNumber.length() " + dialNumber.length());
@@ -214,39 +229,30 @@
         }
 
         String[] entry;
-        String[] tmpArray;
         String outNumber = "";
-        boolean needConvert = false;
         for(String convertMap : convertMaps) {
             log("convertNumberIfNecessary: " + convertMap);
+            // entry format is  "dialStringToReplace:dialStringReplacement"
             entry = convertMap.split(":");
-            if (entry.length > 1) {
-                tmpArray = entry[1].split(",");
-                if (!TextUtils.isEmpty(entry[0]) && dialNumber.equals(entry[0])) {
-                    if (tmpArray.length >= 2 && !TextUtils.isEmpty(tmpArray[1])) {
-                        if (compareGid1(phone, tmpArray[1])) {
-                            needConvert = true;
-                        }
-                    } else if (outNumber.isEmpty()) {
-                        needConvert = true;
-                    }
-
-                    if (needConvert) {
-                        if(!TextUtils.isEmpty(tmpArray[0]) && tmpArray[0].endsWith("MDN")) {
-                            String mdn = phone.getLine1Number();
-                            if (!TextUtils.isEmpty(mdn) ) {
-                                if (mdn.startsWith("+")) {
-                                    outNumber = mdn;
-                                } else {
-                                    outNumber = tmpArray[0].substring(0, tmpArray[0].length() -3)
-                                            + mdn;
-                                }
+            if (entry != null && entry.length > 1) {
+                String dsToReplace = entry[0];
+                String dsReplacement = entry[1];
+                if (!TextUtils.isEmpty(dsToReplace) && dialNumber.equals(dsToReplace)) {
+                    // Needs to be converted
+                    if (!TextUtils.isEmpty(dsReplacement) && dsReplacement.endsWith("MDN")) {
+                        String mdn = phone.getLine1Number();
+                        if (!TextUtils.isEmpty(mdn)) {
+                            if (mdn.startsWith("+")) {
+                                outNumber = mdn;
+                            } else {
+                                outNumber = dsReplacement.substring(0, dsReplacement.length() -3)
+                                        + mdn;
                             }
-                        } else {
-                            outNumber = tmpArray[0];
                         }
-                        needConvert = false;
+                    } else {
+                        outNumber = dsReplacement;
                     }
+                    break;
                 }
             }
         }
@@ -290,6 +296,14 @@
     public abstract PhoneConstants.State getState();
     protected abstract void log(String msg);
 
+    /**
+     * Called when the call tracker should attempt to reconcile its calls against its underlying
+     * phone implementation and cleanup any stale calls.
+     */
+    public void cleanupCalls() {
+        // no base implementation
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("CallTracker:");
         pw.println(" mPendingOperations=" + mPendingOperations);
diff --git a/src/java/com/android/internal/telephony/CarrierActionAgent.java b/src/java/com/android/internal/telephony/CarrierActionAgent.java
new file mode 100644
index 0000000..d67636b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierActionAgent.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2016 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.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Registrant;
+import android.os.RegistrantList;
+import android.provider.Settings;
+import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Carrier Action Agent(CAA) paired with
+ * {@link com.android.internal.telephony.CarrierSignalAgent CarrierSignalAgent},
+ * serves as an agent to dispatch carrier actions from carrier apps to different telephony modules,
+ * {@link android.telephony.TelephonyManager#carrierActionSetRadioEnabled(int, boolean)
+ * carrierActionSetRadioEnabled} for example.
+ *
+ * CAA supports dynamic registration where different telephony modules could listen for a specific
+ * carrier action event and implement their own handler. CCA will dispatch the event to all
+ * interested parties and maintain the received action states internally for future inspection.
+ * Each CarrierActionAgent is associated with a phone object.
+ */
+public class CarrierActionAgent extends Handler {
+    private static final String LOG_TAG = "CarrierActionAgent";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+    /** A list of carrier actions */
+    public static final int CARRIER_ACTION_SET_METERED_APNS_ENABLED      = 0;
+    public static final int CARRIER_ACTION_SET_RADIO_ENABLED             = 1;
+    public static final int CARRIER_ACTION_RESET                         = 2;
+    public static final int EVENT_APM_SETTINGS_CHANGED                   = 3;
+    public static final int EVENT_MOBILE_DATA_SETTINGS_CHANGED           = 4;
+    public static final int EVENT_DATA_ROAMING_OFF                       = 5;
+    public static final int EVENT_SIM_STATE_CHANGED                      = 6;
+
+    /** Member variables */
+    private final Phone mPhone;
+    /** registrant list per carrier action */
+    private RegistrantList mMeteredApnEnableRegistrants = new RegistrantList();
+    private RegistrantList mRadioEnableRegistrants = new RegistrantList();
+    /** local log for carrier actions */
+    private LocalLog mMeteredApnEnabledLog = new LocalLog(10);
+    private LocalLog mRadioEnabledLog = new LocalLog(10);
+    /** carrier actions, true by default */
+    private Boolean mCarrierActionOnMeteredApnEnabled = true;
+    private Boolean mCarrierActionOnRadioEnabled = true;
+    /** content observer for APM change */
+    private final SettingsObserver mSettingsObserver;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final String iccState = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+            if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)){
+                if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(iccState)) {
+                    sendEmptyMessage(CARRIER_ACTION_RESET);
+                    String mobileData = Settings.Global.MOBILE_DATA;
+                    if (TelephonyManager.getDefault().getSimCount() != 1) {
+                        mobileData = mobileData + mPhone.getSubId();
+                    }
+                    mSettingsObserver.observe(Settings.Global.getUriFor(mobileData),
+                            EVENT_MOBILE_DATA_SETTINGS_CHANGED);
+                    mSettingsObserver.observe(
+                            Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+                            EVENT_APM_SETTINGS_CHANGED);
+                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
+                    sendEmptyMessage(CARRIER_ACTION_RESET);
+                    mSettingsObserver.unobserve();
+                }
+            }
+        }
+    };
+
+    /** Constructor */
+    public CarrierActionAgent(Phone phone) {
+        mPhone = phone;
+        mPhone.getContext().registerReceiver(mReceiver,
+                new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED));
+        mSettingsObserver = new SettingsObserver(mPhone.getContext(), this);
+        if (DBG) log("Creating CarrierActionAgent");
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
+                mCarrierActionOnMeteredApnEnabled = (boolean) msg.obj;
+                log("SET_METERED_APNS_ENABLED: " + mCarrierActionOnMeteredApnEnabled);
+                mMeteredApnEnabledLog.log("SET_METERED_APNS_ENABLED: "
+                        + mCarrierActionOnMeteredApnEnabled);
+                mMeteredApnEnableRegistrants.notifyRegistrants(
+                        new AsyncResult(null, mCarrierActionOnMeteredApnEnabled, null));
+                break;
+            case CARRIER_ACTION_SET_RADIO_ENABLED:
+                mCarrierActionOnRadioEnabled = (boolean) msg.obj;
+                log("SET_RADIO_ENABLED: " + mCarrierActionOnRadioEnabled);
+                mRadioEnabledLog.log("SET_RADIO_ENABLED: " + mCarrierActionOnRadioEnabled);
+                mRadioEnableRegistrants.notifyRegistrants(
+                        new AsyncResult(null, mCarrierActionOnRadioEnabled, null));
+                break;
+            case CARRIER_ACTION_RESET:
+                log("CARRIER_ACTION_RESET");
+                carrierActionReset();
+                break;
+            case EVENT_APM_SETTINGS_CHANGED:
+                log("EVENT_APM_SETTINGS_CHANGED");
+                if ((Settings.Global.getInt(mPhone.getContext().getContentResolver(),
+                        Settings.Global.AIRPLANE_MODE_ON, 0) != 0)) {
+                    carrierActionReset();
+                }
+                break;
+            case EVENT_MOBILE_DATA_SETTINGS_CHANGED:
+                log("EVENT_MOBILE_DATA_SETTINGS_CHANGED");
+                if (!mPhone.getDataEnabled()) carrierActionReset();
+                break;
+            case EVENT_DATA_ROAMING_OFF:
+                log("EVENT_DATA_ROAMING_OFF");
+                // reset carrier actions when exit roaming state.
+                carrierActionReset();
+                break;
+            case EVENT_SIM_STATE_CHANGED:
+                String iccState = (String) msg.obj;
+                if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(iccState)) {
+                    log("EVENT_SIM_STATE_CHANGED status: " + iccState);
+                    carrierActionReset();
+                    String mobileData = Settings.Global.MOBILE_DATA;
+                    if (TelephonyManager.getDefault().getSimCount() != 1) {
+                        mobileData = mobileData + mPhone.getSubId();
+                    }
+                    mSettingsObserver.observe(Settings.Global.getUriFor(mobileData),
+                            EVENT_MOBILE_DATA_SETTINGS_CHANGED);
+                    mSettingsObserver.observe(
+                            Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+                            EVENT_APM_SETTINGS_CHANGED);
+                    if (mPhone.getServiceStateTracker() != null) {
+                        mPhone.getServiceStateTracker().registerForDataRoamingOff(
+                                this, EVENT_DATA_ROAMING_OFF, null, false);
+                    }
+                } else if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(iccState)) {
+                    log("EVENT_SIM_STATE_CHANGED status: " + iccState);
+                    carrierActionReset();
+                    mSettingsObserver.unobserve();
+                    if (mPhone.getServiceStateTracker() != null) {
+                        mPhone.getServiceStateTracker().unregisterForDataRoamingOff(this);
+                    }
+                }
+                break;
+            default:
+                loge("Unknown carrier action: " + msg.what);
+        }
+    }
+
+    /**
+     * Return current carrier action values
+     */
+    public Object getCarrierActionValue(int action) {
+        Object val = getCarrierAction(action);
+        if (val == null) {
+            throw new IllegalArgumentException("invalid carrier action: " + action);
+        }
+        return val;
+    }
+
+    /**
+     * Action set from carrier app to enable/disable radio
+     */
+    public void carrierActionSetRadioEnabled(boolean enabled) {
+        sendMessage(obtainMessage(CARRIER_ACTION_SET_RADIO_ENABLED, enabled));
+    }
+
+    /**
+     * Action set from carrier app to enable/disable metered APNs
+     */
+    public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
+        sendMessage(obtainMessage(CARRIER_ACTION_SET_METERED_APNS_ENABLED, enabled));
+    }
+
+    private void carrierActionReset() {
+        carrierActionSetMeteredApnsEnabled(true);
+        carrierActionSetRadioEnabled(true);
+        // notify configured carrier apps for reset
+        mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(
+                new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET));
+    }
+
+    private RegistrantList getRegistrantsFromAction(int action) {
+        switch (action) {
+            case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
+                return mMeteredApnEnableRegistrants;
+            case CARRIER_ACTION_SET_RADIO_ENABLED:
+                return mRadioEnableRegistrants;
+            default:
+                loge("Unsupported action: " + action);
+                return null;
+        }
+    }
+
+    private Object getCarrierAction(int action) {
+        switch (action) {
+            case CARRIER_ACTION_SET_METERED_APNS_ENABLED:
+                return mCarrierActionOnMeteredApnEnabled;
+            case CARRIER_ACTION_SET_RADIO_ENABLED:
+                return mCarrierActionOnRadioEnabled;
+            default:
+                loge("Unsupported action: " + action);
+                return null;
+        }
+    }
+
+    /**
+     * Register with CAA for a specific event.
+     * @param action which carrier action registrant is interested in
+     * @param notifyNow if carrier action has once set, notify registrant right after
+     *                  registering, so that registrants will get the latest carrier action.
+     */
+    public void registerForCarrierAction(int action, Handler h, int what, Object obj,
+                                         boolean notifyNow) {
+        Object carrierAction = getCarrierAction(action);
+        if (carrierAction == null) {
+            throw new IllegalArgumentException("invalid carrier action: " + action);
+        }
+        RegistrantList list = getRegistrantsFromAction(action);
+        Registrant r = new Registrant(h, what, obj);
+        list.add(r);
+        if (notifyNow) {
+            r.notifyRegistrant(new AsyncResult(null, carrierAction, null));
+        }
+    }
+
+    /**
+     * Unregister with CAA for a specific event. Callers will no longer be notified upon such event.
+     * @param action which carrier action caller is no longer interested in
+     */
+    public void unregisterForCarrierAction(Handler h, int action) {
+        RegistrantList list = getRegistrantsFromAction(action);
+        if (list == null) {
+            throw new IllegalArgumentException("invalid carrier action: " + action);
+        }
+        list.remove(h);
+    }
+
+    @VisibleForTesting
+    public ContentObserver getContentObserver() {
+        return mSettingsObserver;
+    }
+
+    private void log(String s) {
+        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+
+    private void logv(String s) {
+        Rlog.v(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        pw.println(" mCarrierActionOnMeteredApnsEnabled Log:");
+        ipw.increaseIndent();
+        mMeteredApnEnabledLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+
+        pw.println(" mCarrierActionOnRadioEnabled Log:");
+        ipw.increaseIndent();
+        mRadioEnabledLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
new file mode 100644
index 0000000..96dd8c4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.telephony.ImsiEncryptionInfo;
+
+ /**
+ * This class provides methods to retreive information from the CarrierKeyProvider.
+ */
+public class CarrierInfoManager {
+    private static final String TAG = "CarrierInfoManager";
+
+    /**
+     * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * @param keyType whether the key is being used for WLAN or ePDG.
+     * @return ImsiEncryptionInfo which contains the information including the public key to be
+     *         used for encryption.
+     */
+    public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+        //TODO implementation will be done in subsequent CL.
+        return null;
+    }
+
+    /**
+     * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * This includes the public key and the key identifier. This information will be stored in the
+     * device keystore.
+     * @param imsiEncryptionInfo which includes the Key Type, the Public Key
+     *        {@link java.security.PublicKey} and the Key Identifier.
+     *        The keyIdentifier Attribute value pair that helps a server locate
+     *        the private key to decrypt the permanent identity.
+     */
+    public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        //TODO implementation will be done in subsequent CL.
+        return;
+    }
+}
+
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
index 3123ccb..ab010cc 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -28,6 +28,8 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.carrier.CarrierService;
 import android.telephony.SubscriptionManager;
@@ -48,6 +50,12 @@
 public class CarrierServiceBindHelper {
     private static final String LOG_TAG = "CarrierSvcBindHelper";
 
+    /**
+     * How long to linger a binding after an app loses carrier privileges, as long as no new
+     * binding comes in to take its place.
+     */
+    private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds
+
     private Context mContext;
     private AppBinding[] mBindings;
     private String[] mLastSimState;
@@ -70,6 +78,7 @@
     };
 
     private static final int EVENT_REBIND = 0;
+    private static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1;
 
     private Handler mHandler = new Handler() {
         @Override
@@ -83,6 +92,10 @@
                     log("Rebinding if necessary for phoneId: " + binding.getPhoneId());
                     binding.rebind();
                     break;
+                case EVENT_PERFORM_IMMEDIATE_UNBIND:
+                    binding = (AppBinding) msg.obj;
+                    binding.performImmediateUnbind();
+                    break;
             }
         }
     };
@@ -129,6 +142,7 @@
         private long lastUnbindMillis;
         private String carrierPackage;
         private String carrierServiceClass;
+        private long mUnbindScheduledUptimeMillis = -1;
 
         public AppBinding(int phoneId) {
             this.phoneId = phoneId;
@@ -158,15 +172,16 @@
 
             if (carrierPackageNames == null || carrierPackageNames.size() <= 0) {
                 log("No carrier app for: " + phoneId);
-                unbind();
+                // Unbind after a delay in case this is a temporary blip in carrier privileges.
+                unbind(false /* immediate */);
                 return;
             }
 
             log("Found carrier app: " + carrierPackageNames);
             String candidateCarrierPackage = carrierPackageNames.get(0);
-            // If we are binding to a different package, unbind from the current one.
+            // If we are binding to a different package, unbind immediately from the current one.
             if (!TextUtils.equals(carrierPackage, candidateCarrierPackage)) {
-                unbind();
+                unbind(true /* immediate */);
             }
 
             // Look up the carrier service
@@ -187,15 +202,17 @@
             if (metadata == null ||
                 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
                 log("Carrier app does not want a long lived binding");
-                unbind();
+                unbind(true /* immediate */);
                 return;
             }
 
             if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) {
-                // Unbind if the carrier service component has changed.
-                unbind();
+                // Unbind immediately if the carrier service component has changed.
+                unbind(true /* immediate */);
             } else if (connection != null) {
-                // Component is unchanged and connection is up - do nothing.
+                // Component is unchanged and connection is up - do nothing, but cancel any
+                // scheduled unbinds.
+                cancelScheduledUnbind();
                 return;
             }
 
@@ -212,8 +229,9 @@
 
             String error;
             try {
-                if (mContext.bindService(carrierService, connection, Context.BIND_AUTO_CREATE |
-                            Context.BIND_FOREGROUND_SERVICE)) {
+                if (mContext.bindServiceAsUser(carrierService, connection,
+                        Context.BIND_AUTO_CREATE |  Context.BIND_FOREGROUND_SERVICE,
+                        mHandler, Process.myUserHandle())) {
                     return;
                 }
 
@@ -224,19 +242,46 @@
 
             log("Unable to bind to " + carrierPackage + " for phone " + phoneId +
                 ". Error: " + error);
-            unbind();
+            unbind(true /* immediate */);
         }
 
-        void unbind() {
+        /**
+         * Release the binding.
+         *
+         * @param immediate whether the binding should be released immediately or after a short
+         *                  delay. This should be true unless the reason for the unbind is that no
+         *                  app has carrier privileges, in which case it is useful to delay
+         *                  unbinding in case this is a temporary SIM blip.
+         */
+        void unbind(boolean immediate) {
             if (connection == null) {
+                // Already fully unbound.
                 return;
             }
 
+            // Only let the binding linger if a delayed unbind is requested *and* the connection is
+            // currently active. If the connection is down, unbind immediately as the app is likely
+            // not running anyway and it may be a permanent disconnection (e.g. the app was
+            // disabled).
+            if (immediate || !connection.connected) {
+                cancelScheduledUnbind();
+                performImmediateUnbind();
+            } else if (mUnbindScheduledUptimeMillis == -1) {
+                long currentUptimeMillis = SystemClock.uptimeMillis();
+                mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS;
+                log("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis");
+                mHandler.sendMessageAtTime(
+                        mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, this),
+                        mUnbindScheduledUptimeMillis);
+            }
+        }
+
+        private void performImmediateUnbind() {
             // Log debug information
             unbindCount++;
             lastUnbindMillis = System.currentTimeMillis();
 
-            // Clear package state now that no binding is present.
+            // Clear package state now that no binding is desired.
             carrierPackage = null;
             carrierServiceClass = null;
 
@@ -244,6 +289,12 @@
             log("Unbinding from carrier app");
             mContext.unbindService(connection);
             connection = null;
+            mUnbindScheduledUptimeMillis = -1;
+        }
+
+        private void cancelScheduledUnbind() {
+            mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND);
+            mUnbindScheduledUptimeMillis = -1;
         }
 
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -253,6 +304,7 @@
             pw.println("  lastBindStartMillis: " + lastBindStartMillis);
             pw.println("  unbindCount: " + unbindCount);
             pw.println("  lastUnbindMillis: " + lastUnbindMillis);
+            pw.println("  mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis);
             pw.println();
         }
     }
@@ -322,7 +374,7 @@
                 }
                 if (appBindingPackage == null || isBindingForPackage) {
                     if (forceUnbind) {
-                        appBinding.unbind();
+                        appBinding.unbind(true /* immediate */);
                     }
                     appBinding.rebind();
                 }
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
new file mode 100644
index 0000000..ab52c4a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.PendingIntent;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.util.NotificationChannelController;
+
+/**
+ * This contains Carrier specific logic based on the states/events
+ * managed in ServiceStateTracker.
+ * {@hide}
+ */
+public class CarrierServiceStateTracker extends Handler {
+    private static final String LOG_TAG = "CSST";
+    protected static final int CARRIER_EVENT_BASE = 100;
+    protected static final int CARRIER_EVENT_VOICE_REGISTRATION = CARRIER_EVENT_BASE + 1;
+    protected static final int CARRIER_EVENT_VOICE_DEREGISTRATION = CARRIER_EVENT_BASE + 2;
+    protected static final int CARRIER_EVENT_DATA_REGISTRATION = CARRIER_EVENT_BASE + 3;
+    protected static final int CARRIER_EVENT_DATA_DEREGISTRATION = CARRIER_EVENT_BASE + 4;
+    private static final int SHOW_NOTIFICATION = 200;
+    private static final int NOTIFICATION_ID = 1000;
+    private static final int UNINITIALIZED_DELAY_VALUE = -1;
+    private int mDelay = UNINITIALIZED_DELAY_VALUE;
+    private Phone mPhone;
+    private boolean mIsPhoneRegistered = false;
+    private ServiceStateTracker mSST;
+
+    public CarrierServiceStateTracker(Phone phone, ServiceStateTracker sst) {
+        this.mPhone = phone;
+        this.mSST = sst;
+        phone.getContext().registerReceiver(mBroadcastReceiver, new IntentFilter(
+                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case CARRIER_EVENT_VOICE_REGISTRATION:
+            case CARRIER_EVENT_DATA_REGISTRATION:
+                mIsPhoneRegistered = true;
+                handleConfigChanges();
+                break;
+            case CARRIER_EVENT_VOICE_DEREGISTRATION:
+            case CARRIER_EVENT_DATA_DEREGISTRATION:
+                if (isGlobalModeOrRadioOffOrAirplaneMode()) {
+                    break;
+                }
+                mIsPhoneRegistered = false;
+                handleConfigChanges();
+                break;
+            case SHOW_NOTIFICATION:
+                sendNotification();
+                break;
+        }
+    }
+
+    /**
+     * Returns true if the preferred network is set to 'Global' or the radio is off or in
+     * Airplane Mode else returns false.
+     */
+    private boolean isGlobalModeOrRadioOffOrAirplaneMode() {
+        Context context = mPhone.getContext();
+        int preferredNetworkSetting = -1;
+        int airplaneMode = -1;
+        int subId = mPhone.getSubId();
+        try {
+            preferredNetworkSetting =
+                    android.provider.Settings.Global.getInt(context.getContentResolver(),
+                            android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId,
+                            Phone.PREFERRED_NT_MODE);
+            airplaneMode = Settings.Global.getInt(context.getContentResolver(),
+                    Settings.Global.AIRPLANE_MODE_ON, 0);
+        } catch (Exception e) {
+            Rlog.e(LOG_TAG, "Unable to get PREFERRED_NETWORK_MODE.");
+            return true;
+        }
+        return ((preferredNetworkSetting == RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA) ||
+                !mSST.isRadioOn() || (airplaneMode != 0));
+    }
+
+    /**
+     * Contains logic to decide when to create/cancel notifications.
+     */
+    private void handleConfigChanges() {
+
+        if (mDelay == UNINITIALIZED_DELAY_VALUE) {
+            cancelNotification();
+            return;
+        }
+        // send a notification if the device is registerd to a network.
+        if (mIsPhoneRegistered) {
+            cancelNotification();
+            Rlog.i(LOG_TAG, "canceling all notifications. ");
+        } else {
+            Message notificationMsg;
+            notificationMsg = obtainMessage(SHOW_NOTIFICATION, null);
+            Rlog.i(LOG_TAG, "starting timer for notifications. ");
+            sendMessageDelayed(notificationMsg, mDelay);
+        }
+    }
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+                    context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
+            mDelay = b.getInt(CarrierConfigManager.KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT);
+            Rlog.i(LOG_TAG, "reading time to delay notification: " + mDelay);
+            handleConfigChanges();
+        }
+    };
+
+    /**
+     * Post a notification to the NotificationManager for changing network type.
+     */
+    private void sendNotification() {
+        Context context = mPhone.getContext();
+
+        Rlog.i(LOG_TAG, "w/values: " + "," + mIsPhoneRegistered + "," + mDelay
+                + "," + isGlobalModeOrRadioOffOrAirplaneMode() + "," + mSST.isRadioOn());
+
+        // exit if the network preference is set to Global or if the phone is registered.
+        if (isGlobalModeOrRadioOffOrAirplaneMode() || mIsPhoneRegistered) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager)
+                context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+
+        Intent notificationIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
+        PendingIntent settingsIntent = PendingIntent.getActivity(context, 0, notificationIntent,
+                PendingIntent.FLAG_ONE_SHOT);
+
+        CharSequence title =
+                context.getText(com.android.internal.R.string.NetworkPreferenceSwitchTitle);
+        CharSequence details =
+                context.getText(com.android.internal.R.string.NetworkPreferenceSwitchSummary);
+
+
+        Notification mNotification = new Notification.Builder(context)
+                .setWhen(System.currentTimeMillis())
+                .setAutoCancel(true)
+                .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+                .setContentTitle(title)
+                .setColor(context.getResources().getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setStyle(new Notification.BigTextStyle().bigText(details))
+                .setContentText(details)
+                .setContentIntent(settingsIntent)
+                .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
+                .build();
+
+        notificationManager.notify(NOTIFICATION_ID, mNotification);
+    }
+
+    /**
+     * Cancel notifications if a registration is pending or has been sent.
+     */
+    private void cancelNotification() {
+        Context context = mPhone.getContext();
+        mIsPhoneRegistered = true;
+        NotificationManager notificationManager = (NotificationManager)
+                context.getSystemService(Context.NOTIFICATION_SERVICE);
+        notificationManager.cancel(NOTIFICATION_ID);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
new file mode 100644
index 0000000..f3bc1fd
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierServicesSmsFilter.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.service.carrier.CarrierMessagingService;
+import android.service.carrier.ICarrierMessagingCallback;
+import android.service.carrier.ICarrierMessagingService;
+import android.service.carrier.MessagePdu;
+import android.telephony.CarrierMessagingServiceManager;
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.uicc.UiccCard;
+import com.android.internal.telephony.uicc.UiccController;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Filters incoming SMS with carrier services.
+ * <p> A new instance must be created for filtering each message.
+ */
+public class CarrierServicesSmsFilter {
+    protected static final boolean DBG = true;
+
+    private final Context mContext;
+    private final Phone mPhone;
+    private final byte[][] mPdus;
+    private final int mDestPort;
+    private final String mPduFormat;
+    private final CarrierServicesSmsFilterCallbackInterface mCarrierServicesSmsFilterCallback;
+    private final String mLogTag;
+
+    @VisibleForTesting
+    public CarrierServicesSmsFilter(
+            Context context,
+            Phone phone,
+            byte[][] pdus,
+            int destPort,
+            String pduFormat,
+            CarrierServicesSmsFilterCallbackInterface carrierServicesSmsFilterCallback,
+            String logTag) {
+        mContext = context;
+        mPhone = phone;
+        mPdus = pdus;
+        mDestPort = destPort;
+        mPduFormat = pduFormat;
+        mCarrierServicesSmsFilterCallback = carrierServicesSmsFilterCallback;
+        mLogTag = logTag;
+    }
+
+    /**
+     * @return {@code true} if the SMS was handled by carrier services.
+     */
+    @VisibleForTesting
+    public boolean filter() {
+        Optional<String> carrierAppForFiltering = getCarrierAppPackageForFiltering();
+        List<String> smsFilterPackages = new ArrayList<>();
+        if (carrierAppForFiltering.isPresent()) {
+            smsFilterPackages.add(carrierAppForFiltering.get());
+        }
+        String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+                new Intent(CarrierMessagingService.SERVICE_INTERFACE));
+        if (carrierImsPackage != null) {
+            smsFilterPackages.add(carrierImsPackage);
+        }
+        FilterAggregator filterAggregator = new FilterAggregator(smsFilterPackages.size());
+        for (String smsFilterPackage : smsFilterPackages) {
+            filterWithPackage(smsFilterPackage, filterAggregator);
+        }
+        boolean handled = smsFilterPackages.size() > 0;
+        return handled;
+    }
+
+    private Optional<String> getCarrierAppPackageForFiltering() {
+        List<String> carrierPackages = null;
+        UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
+        if (card != null) {
+            carrierPackages = card.getCarrierPackageNamesForIntent(
+                    mContext.getPackageManager(),
+                    new Intent(CarrierMessagingService.SERVICE_INTERFACE));
+        } else {
+            Rlog.e(mLogTag, "UiccCard not initialized.");
+        }
+        if (carrierPackages != null && carrierPackages.size() == 1) {
+            log("Found carrier package.");
+            return Optional.of(carrierPackages.get(0));
+        }
+
+        // It is possible that carrier app is not present as a CarrierPackage, but instead as a
+        // system app
+        List<String> systemPackages =
+                getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
+
+        if (systemPackages != null && systemPackages.size() == 1) {
+            log("Found system package.");
+            return Optional.of(systemPackages.get(0));
+        }
+        logv("Unable to find carrier package: " + carrierPackages
+                + ", nor systemPackages: " + systemPackages);
+        return Optional.empty();
+    }
+
+    private void filterWithPackage(String packageName, FilterAggregator filterAggregator) {
+        CarrierSmsFilter smsFilter = new CarrierSmsFilter(mPdus, mDestPort, mPduFormat);
+        CarrierSmsFilterCallback smsFilterCallback =
+                new CarrierSmsFilterCallback(filterAggregator, smsFilter);
+        smsFilter.filterSms(packageName, smsFilterCallback);
+    }
+
+    private List<String> getSystemAppForIntent(Intent intent) {
+        List<String> packages = new ArrayList<String>();
+        PackageManager packageManager = mContext.getPackageManager();
+        List<ResolveInfo> receivers = packageManager.queryIntentServices(intent, 0);
+        String carrierFilterSmsPerm = "android.permission.CARRIER_FILTER_SMS";
+
+        for (ResolveInfo info : receivers) {
+            if (info.serviceInfo == null) {
+                loge("Can't get service information from " + info);
+                continue;
+            }
+            String packageName = info.serviceInfo.packageName;
+            if (packageManager.checkPermission(carrierFilterSmsPerm, packageName)
+                    == packageManager.PERMISSION_GRANTED) {
+                packages.add(packageName);
+                if (DBG) log("getSystemAppForIntent: added package " + packageName);
+            }
+        }
+        return packages;
+    }
+
+    private void log(String message) {
+        Rlog.d(mLogTag, message);
+    }
+
+    private void loge(String message) {
+        Rlog.e(mLogTag, message);
+    }
+
+    private void logv(String message) {
+        Rlog.e(mLogTag, message);
+    }
+
+    /**
+     * Result of filtering SMS is returned in this callback.
+     */
+    @VisibleForTesting
+    public interface CarrierServicesSmsFilterCallbackInterface {
+        void onFilterComplete(int result);
+    }
+
+    /**
+     * Asynchronously binds to the carrier messaging service, and filters out the message if
+     * instructed to do so by the carrier messaging service. A new instance must be used for every
+     * message.
+     */
+    private final class CarrierSmsFilter extends CarrierMessagingServiceManager {
+        private final byte[][] mPdus;
+        private final int mDestPort;
+        private final String mSmsFormat;
+        // Instantiated in filterSms.
+        private volatile CarrierSmsFilterCallback mSmsFilterCallback;
+
+        CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat) {
+            mPdus = pdus;
+            mDestPort = destPort;
+            mSmsFormat = smsFormat;
+        }
+
+        /**
+         * Attempts to bind to a {@link ICarrierMessagingService}. Filtering is initiated
+         * asynchronously once the service is ready using {@link #onServiceReady}.
+         */
+        void filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback) {
+            mSmsFilterCallback = smsFilterCallback;
+            if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
+                loge("bindService() for carrier messaging service failed");
+                smsFilterCallback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+            } else {
+                logv("bindService() for carrier messaging service succeeded");
+            }
+        }
+
+        /**
+         * Invokes the {@code carrierMessagingService} to filter messages. The filtering result is
+         * delivered to {@code smsFilterCallback}.
+         */
+        @Override
+        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
+            try {
+                carrierMessagingService.filterSms(
+                        new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
+                        mPhone.getSubId(), mSmsFilterCallback);
+            } catch (RemoteException e) {
+                loge("Exception filtering the SMS: " + e);
+                mSmsFilterCallback.onFilterComplete(
+                        CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
+            }
+        }
+    }
+
+    /**
+     * A callback used to notify the platform of the carrier messaging app filtering result. Once
+     * the result is ready, the carrier messaging service connection is disposed.
+     */
+    private final class CarrierSmsFilterCallback extends ICarrierMessagingCallback.Stub {
+        private final FilterAggregator mFilterAggregator;
+        private final CarrierMessagingServiceManager mCarrierMessagingServiceManager;
+
+        CarrierSmsFilterCallback(FilterAggregator filterAggregator,
+                                 CarrierMessagingServiceManager carrierMessagingServiceManager) {
+            mFilterAggregator = filterAggregator;
+            mCarrierMessagingServiceManager = carrierMessagingServiceManager;
+        }
+
+        /**
+         * This method should be called only once.
+         */
+        @Override
+        public void onFilterComplete(int result) {
+            mCarrierMessagingServiceManager.disposeConnection(mContext);
+            mFilterAggregator.onFilterComplete(result);
+        }
+
+        @Override
+        public void onSendSmsComplete(int result, int messageRef) {
+            loge("Unexpected onSendSmsComplete call with result: " + result);
+        }
+
+        @Override
+        public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
+            loge("Unexpected onSendMultipartSmsComplete call with result: " + result);
+        }
+
+        @Override
+        public void onSendMmsComplete(int result, byte[] sendConfPdu) {
+            loge("Unexpected onSendMmsComplete call with result: " + result);
+        }
+
+        @Override
+        public void onDownloadMmsComplete(int result) {
+            loge("Unexpected onDownloadMmsComplete call with result: " + result);
+        }
+    }
+
+    private final class FilterAggregator {
+        private final Object mFilterLock = new Object();
+        private int mNumPendingFilters;
+        private int mFilterResult;
+
+        FilterAggregator(int numFilters) {
+            mNumPendingFilters = numFilters;
+            mFilterResult = CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT;
+        }
+
+        void onFilterComplete(int result) {
+            synchronized (mFilterLock) {
+                mNumPendingFilters--;
+                combine(result);
+                if (mNumPendingFilters == 0) {
+                    // Calling identity was the CarrierMessagingService in this callback, change it
+                    // back to ours.
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        mCarrierServicesSmsFilterCallback.onFilterComplete(mFilterResult);
+                    } finally {
+                        // return back to the CarrierMessagingService, restore the calling identity.
+                        Binder.restoreCallingIdentity(token);
+                    }
+                }
+            }
+        }
+
+        private void combine(int result) {
+            mFilterResult = mFilterResult | result;
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierSignalAgent.java b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
index 19a508c..c6958cf 100644
--- a/src/java/com/android/internal/telephony/CarrierSignalAgent.java
+++ b/src/java/com/android/internal/telephony/CarrierSignalAgent.java
@@ -16,144 +16,275 @@
 package com.android.internal.telephony;
 
 import android.content.ActivityNotFoundException;
+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.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.Log;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
+
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY;
 
 /**
  * This class act as an CarrierSignalling Agent.
- * it load registered carrier signalling receivers from Carrier Config and cache the result to avoid
+ * it load registered carrier signalling receivers from carrier config, cache the result to avoid
  * repeated polling and send the intent to the interested receivers.
- * each CarrierSignalAgent is associated with a phone object.
+ * Each CarrierSignalAgent is associated with a phone object.
  */
 public class CarrierSignalAgent {
 
-    private static final String LOG_TAG = "CarrierSignalAgent";
+    private static final String LOG_TAG = CarrierSignalAgent.class.getSimpleName();
     private static final boolean DBG = true;
+    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+    private static final boolean WAKE = true;
+    private static final boolean NO_WAKE = false;
+
+    /** delimiters for parsing config of the form: pakName./receiverName : signal1, signal2,..*/
+    private static final String COMPONENT_NAME_DELIMITER = "\\s*:\\s*";
+    private static final String CARRIER_SIGNAL_DELIMITER = "\\s*,\\s*";
 
     /** Member variables */
     private final Phone mPhone;
+
     /**
-     * This is a map of intent action -> string array of carrier signal receiver names which are
-     * interested in this intent action
+     * This is a map of intent action -> set of component name of statically registered
+     * carrier signal receivers(wakeup receivers).
+     * Those intents are declared in the Manifest files, aiming to wakeup broadcast receivers.
+     * Carrier apps should be careful when configuring the wake signal list to avoid unnecessary
+     * wakeup. Note we use Set as the entry value to compare config directly regardless of element
+     * order.
+     * @see CarrierConfigManager#KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY
      */
-    private final HashMap<String, String[]> mCachedCarrierSignalReceiverNames =
-            new HashMap<>();
+    private Map<String, Set<ComponentName>> mCachedWakeSignalConfigs = new HashMap<>();
+
     /**
-     * This is a map of intent action -> carrier config key of signal receiver names which are
-     * interested in this intent action
+     * This is a map of intent action -> set of component name of dynamically registered
+     * carrier signal receivers(non-wakeup receivers). Those intents will not wake up the apps.
+     * Note Carrier apps should avoid configuring no wake signals in there Manifest files.
+     * Note we use Set as the entry value to compare config directly regardless of element order.
+     * @see CarrierConfigManager#KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY
      */
-    private final Map<String, String> mIntentToCarrierConfigKeyMap =
-            new HashMap<String, String>() {{
-                put(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED,
-                        CarrierConfigManager.KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY);
-                put(TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
-                        CarrierConfigManager.KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY);
-                put(TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
-                        CarrierConfigManager.KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY);
-            }};
+    private Map<String, Set<ComponentName>> mCachedNoWakeSignalConfigs = new HashMap<>();
+
+    /**
+     * This is a list of supported signals from CarrierSignalAgent
+     */
+    private final Set<String> mCarrierSignalList = new HashSet<>(Arrays.asList(
+            TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE,
+            TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED,
+            TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+            TelephonyIntents.ACTION_CARRIER_SIGNAL_RESET));
+
+    private final LocalLog mErrorLocalLog = new LocalLog(20);
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (DBG) log("CarrierSignalAgent receiver action: " + action);
+            if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+                loadCarrierConfig();
+            }
+        }
+    };
 
     /** Constructor */
     public CarrierSignalAgent(Phone phone) {
         mPhone = phone;
+        loadCarrierConfig();
+        // reload configurations on CARRIER_CONFIG_CHANGED
+        mPhone.getContext().registerReceiver(mReceiver,
+                new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
     }
 
     /**
-     * Read carrier signalling receiver name from CarrierConfig based on the intent type
-     * @return array of receiver Name: the package (a String) name / the class (a String) name
+     * load carrier config and cached the results into a hashMap action -> array list of components.
      */
-    private String[] getCarrierSignalReceiverName(String intentAction) {
-        String receiverType = mIntentToCarrierConfigKeyMap.get(intentAction);
-        if(receiverType == null) {
-            return null;
+    private void loadCarrierConfig() {
+        CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle b = null;
+        if (configManager != null) {
+            b = configManager.getConfig();
         }
-        String[] receiverNames = mCachedCarrierSignalReceiverNames.get(intentAction);
-        // In case of cache miss, we need to look up/load from carrier config.
-        if (!mCachedCarrierSignalReceiverNames.containsKey(intentAction)) {
-            CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
-                    .getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            PersistableBundle b = null;
-            if (configManager != null) {
-                b = configManager.getConfig();
+        if (b != null) {
+            synchronized (mCachedWakeSignalConfigs) {
+                log("Loading carrier config: " + KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+                Map<String, Set<ComponentName>> config = parseAndCache(
+                        b.getStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+                // In some rare cases, up-to-date config could be fetched with delay and all signals
+                // have already been delivered the receivers from the default carrier config.
+                // To handle this raciness, we should notify those receivers (from old configs)
+                // and reset carrier actions. This should be done before cached Config got purged
+                // and written with the up-to-date value, Otherwise those receivers from the
+                // old config might lingers without properly clean-up.
+                if (!mCachedWakeSignalConfigs.isEmpty()
+                        && !config.equals(mCachedWakeSignalConfigs)) {
+                    if (VDBG) log("carrier config changed, reset receivers from old config");
+                    mPhone.getCarrierActionAgent().sendEmptyMessage(
+                            CarrierActionAgent.CARRIER_ACTION_RESET);
+                }
+                mCachedWakeSignalConfigs = config;
             }
-            if (b != null) {
-                receiverNames = b.getStringArray(receiverType);
-                if(receiverNames!=null) {
-                    for(String name: receiverNames) {
-                        Rlog.d("loadCarrierSignalReceiverNames: ", name);
+
+            synchronized (mCachedNoWakeSignalConfigs) {
+                log("Loading carrier config: "
+                        + KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY);
+                Map<String, Set<ComponentName>> config = parseAndCache(
+                        b.getStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY));
+                if (!mCachedNoWakeSignalConfigs.isEmpty()
+                        && !config.equals(mCachedNoWakeSignalConfigs)) {
+                    if (VDBG) log("carrier config changed, reset receivers from old config");
+                    mPhone.getCarrierActionAgent().sendEmptyMessage(
+                            CarrierActionAgent.CARRIER_ACTION_RESET);
+                }
+                mCachedNoWakeSignalConfigs = config;
+            }
+        }
+    }
+
+    /**
+     * Parse each config with the form {pakName./receiverName : signal1, signal2,.} and cached the
+     * result internally to avoid repeated polling
+     * @see #CARRIER_SIGNAL_DELIMITER
+     * @see #COMPONENT_NAME_DELIMITER
+     * @param configs raw information from carrier config
+     */
+    private Map<String, Set<ComponentName>> parseAndCache(String[] configs) {
+        Map<String, Set<ComponentName>> newCachedWakeSignalConfigs = new HashMap<>();
+        if (!ArrayUtils.isEmpty(configs)) {
+            for (String config : configs) {
+                if (!TextUtils.isEmpty(config)) {
+                    String[] splitStr = config.trim().split(COMPONENT_NAME_DELIMITER, 2);
+                    if (splitStr.length == 2) {
+                        ComponentName componentName = ComponentName
+                                .unflattenFromString(splitStr[0]);
+                        if (componentName == null) {
+                            loge("Invalid component name: " + splitStr[0]);
+                            continue;
+                        }
+                        String[] signals = splitStr[1].split(CARRIER_SIGNAL_DELIMITER);
+                        for (String s : signals) {
+                            if (!mCarrierSignalList.contains(s)) {
+                                loge("Invalid signal name: " + s);
+                                continue;
+                            }
+                            Set<ComponentName> componentList = newCachedWakeSignalConfigs.get(s);
+                            if (componentList == null) {
+                                componentList = new HashSet<>();
+                                newCachedWakeSignalConfigs.put(s, componentList);
+                            }
+                            componentList.add(componentName);
+                            if (VDBG) {
+                                logv("Add config " + "{signal: " + s
+                                        + " componentName: " + componentName + "}");
+                            }
+                        }
+                    } else {
+                        loge("invalid config format: " + config);
                     }
                 }
             }
-            mCachedCarrierSignalReceiverNames.put(intentAction, receiverNames);
         }
-        return receiverNames;
+        return newCachedWakeSignalConfigs;
     }
 
     /**
-     * Check if there are registered carrier broadcast receivers to handle any registered intents.
+     * Check if there are registered carrier broadcast receivers to handle the passing intent
      */
-    public boolean hasRegisteredCarrierSignalReceivers() {
-        for(String intent : mIntentToCarrierConfigKeyMap.keySet()) {
-            if(!ArrayUtils.isEmpty(getCarrierSignalReceiverName(intent))) {
-                return true;
-            }
-        }
-        return false;
+    public boolean hasRegisteredReceivers(String action) {
+        return mCachedWakeSignalConfigs.containsKey(action)
+                || mCachedNoWakeSignalConfigs.containsKey(action);
     }
 
-    public boolean notifyCarrierSignalReceivers(Intent intent) {
-        // Read a list of broadcast receivers from carrier config manager
-        // which are interested on certain intent type
-        String[] receiverName = getCarrierSignalReceiverName(intent.getAction());
-        if (receiverName == null) {
-            loge("Carrier receiver name is null");
-            return false;
-        }
+    /**
+     * Broadcast the intents explicitly.
+     * Some sanity check will be applied before broadcasting.
+     * - for non-wakeup(runtime) receivers, make sure the intent is not declared in their manifests
+     * and apply FLAG_EXCLUDE_STOPPED_PACKAGES to avoid wake-up
+     * - for wakeup(manifest) receivers, make sure there are matched receivers with registered
+     * intents.
+     *
+     * @param intent intent which signals carrier apps
+     * @param receivers a list of component name for broadcast receivers.
+     *                  Those receivers could either be statically declared in Manifest or
+     *                  registered during run-time.
+     * @param wakeup true indicate wakeup receivers otherwise non-wakeup receivers
+     */
+    private void broadcast(Intent intent, Set<ComponentName> receivers, boolean wakeup) {
         final PackageManager packageManager = mPhone.getContext().getPackageManager();
-        boolean ret = false;
+        for (ComponentName name : receivers) {
+            Intent signal = new Intent(intent);
+            signal.setComponent(name);
 
-        for(String name : receiverName) {
-            ComponentName componentName = ComponentName.unflattenFromString(name);
-            if (componentName == null) {
-                loge("Carrier receiver name could not be parsed");
-                return false;
-            }
-            intent.setComponent(componentName);
-            // Check if broadcast receiver is available
-            if (packageManager.queryBroadcastReceivers(intent,
+            if (wakeup && packageManager.queryBroadcastReceivers(signal,
                     PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
-                loge("Carrier signal receiver is configured, but not available: " + name);
-                break;
+                loge("Carrier signal receivers are configured but unavailable: "
+                        + signal.getComponent());
+                return;
+            }
+            if (!wakeup && !packageManager.queryBroadcastReceivers(signal,
+                    PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
+                loge("Runtime signals shouldn't be configured in Manifest: "
+                        + signal.getComponent());
+                return;
             }
 
-            intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mPhone.getSubId());
-            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            signal.putExtra(PhoneConstants.SUBSCRIPTION_KEY, mPhone.getSubId());
+            signal.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            if (!wakeup) signal.setFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
 
             try {
-                mPhone.getContext().sendBroadcast(intent);
-                if (DBG) log("send Intent to carrier signal receiver with action: " +
-                        intent.getAction());
-                ret = true;
+                mPhone.getContext().sendBroadcast(signal);
+                if (DBG) {
+                    log("Sending signal " + signal.getAction() + ((signal.getComponent() != null)
+                            ? " to the carrier signal receiver: " + signal.getComponent() : ""));
+                }
             } catch (ActivityNotFoundException e) {
-                loge("sendBroadcast failed: " + e);
+                loge("Send broadcast failed: " + e);
+            }
+        }
+    }
+
+    /**
+     * Match the intent against cached tables to find a list of registered carrier signal
+     * receivers and broadcast the intent.
+     * @param intent broadcasting intent, it could belong to wakeup, non-wakeup signal list or both
+     *
+     */
+    public void notifyCarrierSignalReceivers(Intent intent) {
+        Set<ComponentName> receiverSet;
+
+        synchronized (mCachedWakeSignalConfigs) {
+            receiverSet = mCachedWakeSignalConfigs.get(intent.getAction());
+            if (!ArrayUtils.isEmpty(receiverSet)) {
+                broadcast(intent, receiverSet, WAKE);
             }
         }
 
-        return ret;
-    }
-
-    /* Clear cached receiver names */
-    public void reset() {
-        mCachedCarrierSignalReceiverNames.clear();
+        synchronized (mCachedNoWakeSignalConfigs) {
+            receiverSet = mCachedNoWakeSignalConfigs.get(intent.getAction());
+            if (!ArrayUtils.isEmpty(receiverSet)) {
+                broadcast(intent, receiverSet, NO_WAKE);
+            }
+        }
     }
 
     private void log(String s) {
@@ -161,6 +292,33 @@
     }
 
     private void loge(String s) {
+        mErrorLocalLog.log(s);
         Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
     }
+
+    private void logv(String s) {
+        Rlog.v(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        pw.println("mCachedWakeSignalConfigs:");
+        ipw.increaseIndent();
+        for (Map.Entry<String, Set<ComponentName>> entry : mCachedWakeSignalConfigs.entrySet()) {
+            pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue());
+        }
+        ipw.decreaseIndent();
+
+        pw.println("mCachedNoWakeSignalConfigs:");
+        ipw.increaseIndent();
+        for (Map.Entry<String, Set<ComponentName>> entry : mCachedNoWakeSignalConfigs.entrySet()) {
+            pw.println("signal: " + entry.getKey() + " componentName list: " + entry.getValue());
+        }
+        ipw.decreaseIndent();
+
+        pw.println("error log:");
+        ipw.increaseIndent();
+        mErrorLocalLog.dump(fd, pw, args);
+        ipw.decreaseIndent();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierSmsUtils.java b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
new file mode 100644
index 0000000..a64aea7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierSmsUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+
+import java.util.List;
+
+/**
+ * This is a basic utility class for common Carrier SMS Functions
+ */
+public class CarrierSmsUtils {
+    protected static final boolean VDBG = false;
+    protected static final String TAG = CarrierSmsUtils.class.getSimpleName();
+
+    private static final String CARRIER_IMS_PACKAGE_KEY =
+            CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING;
+
+    /** Return a Carrier-overridden IMS package, if it exists and is a CarrierSmsFilter
+     *
+     * @param context calling context
+     * @param phone object from telephony
+     * @param intent that should match a CarrierSmsFilter
+     * @return the name of the IMS CarrierService package
+     */
+    @Nullable
+    public static String getCarrierImsPackageForIntent(
+            Context context, Phone phone, Intent intent) {
+
+        String carrierImsPackage = getCarrierImsPackage(context, phone);
+        if (carrierImsPackage == null) {
+            if (VDBG) Rlog.v(TAG, "No CarrierImsPackage override found");
+            return null;
+        }
+
+        PackageManager packageManager = context.getPackageManager();
+        List<ResolveInfo> receivers = packageManager.queryIntentServices(intent, 0);
+        for (ResolveInfo info : receivers) {
+            if (info.serviceInfo == null) {
+                Rlog.e(TAG, "Can't get service information from " + info);
+                continue;
+            }
+
+            if (carrierImsPackage.equals(info.serviceInfo.packageName)) {
+                return carrierImsPackage;
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private static String getCarrierImsPackage(Context context, Phone phone) {
+        CarrierConfigManager cm = (CarrierConfigManager) context.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        if (cm == null) {
+            Rlog.e(TAG, "Failed to retrieve CarrierConfigManager");
+            return null;
+        }
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            PersistableBundle config = cm.getConfigForSubId(phone.getSubId());
+            if (config == null) {
+                if (VDBG) Rlog.v(TAG, "No CarrierConfig for subId:" + phone.getSubId());
+                return null;
+            }
+            return config.getString(CARRIER_IMS_PACKAGE_KEY, null);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private CarrierSmsUtils() {}
+}
diff --git a/src/java/com/android/internal/telephony/CellBroadcastHandler.java b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
index 5cb329a..4227148 100644
--- a/src/java/com/android/internal/telephony/CellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/CellBroadcastHandler.java
@@ -16,16 +16,20 @@
 
 package com.android.internal.telephony;
 
+import static android.provider.Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG;
+
 import android.Manifest;
 import android.app.Activity;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Message;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Telephony;
-import android.telephony.SubscriptionManager;
 import android.telephony.SmsCbMessage;
+import android.telephony.SubscriptionManager;
 
 /**
  * Dispatch new Cell Broadcasts to receivers. Acquires a private wakelock until the broadcast
@@ -82,16 +86,38 @@
         if (message.isEmergencyMessage()) {
             log("Dispatching emergency SMS CB, SmsCbMessage is: " + message);
             intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+            // Explicitly send the intent to the default cell broadcast receiver.
+            intent.setPackage(mContext.getResources().getString(
+                    com.android.internal.R.string.config_defaultCellBroadcastReceiverPkg));
             receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
             appOp = AppOpsManager.OP_RECEIVE_EMERGECY_SMS;
         } else {
             log("Dispatching SMS CB, SmsCbMessage is: " + message);
             intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
+            // Send implicit intent since there are various 3rd party carrier apps listen to
+            // this intent.
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             receiverPermission = Manifest.permission.RECEIVE_SMS;
             appOp = AppOpsManager.OP_RECEIVE_SMS;
         }
+
         intent.putExtra("message", message);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
+
+        if (Build.IS_DEBUGGABLE) {
+            // Send additional broadcast intent to the specified package. This is only for sl4a
+            // automation tests.
+            final String additionalPackage = Settings.Secure.getString(
+                    mContext.getContentResolver(), CMAS_ADDITIONAL_BROADCAST_PKG);
+            if (additionalPackage != null) {
+                Intent additionalIntent = new Intent(intent);
+                additionalIntent.setPackage(additionalPackage);
+                mContext.sendOrderedBroadcastAsUser(additionalIntent, UserHandle.ALL,
+                        receiverPermission, appOp, null, getHandler(), Activity.RESULT_OK, null,
+                        null);
+            }
+        }
+
         mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, receiverPermission, appOp,
                 mReceiver, getHandler(), Activity.RESULT_OK, null, null);
     }
diff --git a/src/java/com/android/internal/telephony/ClientWakelockAccountant.java b/src/java/com/android/internal/telephony/ClientWakelockAccountant.java
new file mode 100644
index 0000000..c47faab
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ClientWakelockAccountant.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2016 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.telephony.ClientRequestStats;
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
+
+public class ClientWakelockAccountant {
+    public static final String LOG_TAG = "ClientWakelockAccountant: ";
+
+    @VisibleForTesting
+    public ClientRequestStats mRequestStats = new ClientRequestStats();
+    @VisibleForTesting
+    public ArrayList<RilWakelockInfo> mPendingRilWakelocks = new ArrayList<>();
+
+    @VisibleForTesting
+    public ClientWakelockAccountant(String callingPackage) {
+        mRequestStats.setCallingPackage(callingPackage);
+    }
+
+    @VisibleForTesting
+    public void startAttributingWakelock(int request,
+            int token, int concurrentRequests, long time) {
+
+        RilWakelockInfo wlInfo = new RilWakelockInfo(request, token, concurrentRequests, time);
+        synchronized (mPendingRilWakelocks) {
+            mPendingRilWakelocks.add(wlInfo);
+        }
+    }
+
+    @VisibleForTesting
+    public void stopAttributingWakelock(int request, int token, long time) {
+        RilWakelockInfo wlInfo = removePendingWakelock(request, token);
+        if (wlInfo != null) {
+            completeRequest(wlInfo, time);
+        }
+    }
+
+    @VisibleForTesting
+    public void stopAllPendingRequests(long time) {
+        synchronized (mPendingRilWakelocks) {
+            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
+                completeRequest(wlInfo, time);
+            }
+            mPendingRilWakelocks.clear();
+        }
+    }
+
+    @VisibleForTesting
+    public void changeConcurrentRequests(int concurrentRequests, long time) {
+        synchronized (mPendingRilWakelocks) {
+            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
+                wlInfo.updateConcurrentRequests(concurrentRequests, time);
+            }
+        }
+    }
+
+    private void completeRequest(RilWakelockInfo wlInfo, long time) {
+        wlInfo.setResponseTime(time);
+        synchronized (mRequestStats) {
+            mRequestStats.addCompletedWakelockTime(wlInfo.getWakelockTimeAttributedToClient());
+            mRequestStats.incrementCompletedRequestsCount();
+            mRequestStats.updateRequestHistograms(wlInfo.getRilRequestSent(),
+                    (int) wlInfo.getWakelockTimeAttributedToClient());
+        }
+    }
+
+    @VisibleForTesting
+    public int getPendingRequestCount() {
+        return mPendingRilWakelocks.size();
+    }
+
+    @VisibleForTesting
+    public synchronized long updatePendingRequestWakelockTime(long uptime) {
+        long totalPendingWakelockTime = 0;
+        synchronized (mPendingRilWakelocks) {
+            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
+                wlInfo.updateTime(uptime);
+                totalPendingWakelockTime += wlInfo.getWakelockTimeAttributedToClient();
+            }
+        }
+        synchronized (mRequestStats) {
+            mRequestStats.setPendingRequestsCount(getPendingRequestCount());
+            mRequestStats.setPendingRequestsWakelockTime(totalPendingWakelockTime);
+        }
+        return totalPendingWakelockTime;
+    }
+
+    private RilWakelockInfo removePendingWakelock(int request, int token) {
+        RilWakelockInfo result = null;
+        synchronized (mPendingRilWakelocks) {
+            for (RilWakelockInfo wlInfo : mPendingRilWakelocks) {
+                if ((wlInfo.getTokenNumber() == token) &&
+                    (wlInfo.getRilRequestSent() == request)) {
+                    result = wlInfo;
+                }
+            }
+            if( result != null ) {
+                mPendingRilWakelocks.remove(result);
+            }
+        }
+        if(result == null) {
+            Rlog.w(LOG_TAG, "Looking for Request<" + request + "," + token + "> in "
+                + mPendingRilWakelocks);
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "ClientWakelockAccountant{" +
+                "mRequestStats=" + mRequestStats +
+                ", mPendingRilWakelocks=" + mPendingRilWakelocks +
+                '}';
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ClientWakelockTracker.java b/src/java/com/android/internal/telephony/ClientWakelockTracker.java
new file mode 100644
index 0000000..5bec60b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ClientWakelockTracker.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 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.SystemClock;
+import android.telephony.ClientRequestStats;
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ClientWakelockTracker {
+    public static final String LOG_TAG = "ClientWakelockTracker";
+    @VisibleForTesting
+    public HashMap<String, ClientWakelockAccountant> mClients =
+        new HashMap<String, ClientWakelockAccountant>();
+    @VisibleForTesting
+    public ArrayList<ClientWakelockAccountant> mActiveClients = new ArrayList<>();
+
+    @VisibleForTesting
+    public void startTracking(String clientId, int requestId, int token, int numRequestsInQueue) {
+        ClientWakelockAccountant client = getClientWakelockAccountant(clientId);
+        long uptime = SystemClock.uptimeMillis();
+        client.startAttributingWakelock(requestId, token, numRequestsInQueue, uptime);
+        updateConcurrentRequests(numRequestsInQueue, uptime);
+        synchronized (mActiveClients) {
+            if (!mActiveClients.contains(client)) {
+                mActiveClients.add(client);
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public void stopTracking(String clientId, int requestId, int token, int numRequestsInQueue) {
+        ClientWakelockAccountant client = getClientWakelockAccountant(clientId);
+        long uptime = SystemClock.uptimeMillis();
+        client.stopAttributingWakelock(requestId, token, uptime);
+        if(client.getPendingRequestCount() == 0) {
+            synchronized (mActiveClients) {
+                mActiveClients.remove(client);
+            }
+        }
+        updateConcurrentRequests(numRequestsInQueue, uptime);
+    }
+
+    @VisibleForTesting
+    public void stopTrackingAll() {
+        long uptime = SystemClock.uptimeMillis();
+        synchronized (mActiveClients) {
+            for (ClientWakelockAccountant client : mActiveClients) {
+                client.stopAllPendingRequests(uptime);
+            }
+            mActiveClients.clear();
+        }
+    }
+
+    List<ClientRequestStats> getClientRequestStats() {
+        List<ClientRequestStats> list;
+        long uptime = SystemClock.uptimeMillis();
+        synchronized (mClients) {
+            list = new ArrayList<>(mClients.size());
+            for (String key :  mClients.keySet()) {
+                ClientWakelockAccountant client = mClients.get(key);
+                client.updatePendingRequestWakelockTime(uptime);
+                list.add(new ClientRequestStats(client.mRequestStats));
+            }
+        }
+        return list;
+    }
+
+    private ClientWakelockAccountant getClientWakelockAccountant(String clientId) {
+        ClientWakelockAccountant client;
+        synchronized (mClients) {
+            if (mClients.containsKey(clientId)) {
+                client = mClients.get(clientId);
+            } else {
+                client = new ClientWakelockAccountant(clientId);
+                mClients.put(clientId, client);
+            }
+        }
+        return client;
+    }
+
+    private void updateConcurrentRequests(int numRequestsInQueue, long time) {
+        if(numRequestsInQueue != 0) {
+            synchronized (mActiveClients) {
+                for (ClientWakelockAccountant cI : mActiveClients) {
+                    cI.changeConcurrentRequests(numRequestsInQueue, time);
+                }
+            }
+        }
+    }
+
+    public boolean isClientActive(String clientId) {
+        ClientWakelockAccountant client = getClientWakelockAccountant(clientId);
+        synchronized (mActiveClients) {
+            if (mActiveClients.contains(client)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    void dumpClientRequestTracker() {
+        Rlog.d(RIL.RILJ_LOG_TAG, "-------mClients---------------");
+        synchronized (mClients) {
+            for (String key : mClients.keySet()) {
+                Rlog.d(RIL.RILJ_LOG_TAG, "Client : " + key);
+                Rlog.d(RIL.RILJ_LOG_TAG, mClients.get(key).toString());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index f23905a..524b813 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -16,19 +16,21 @@
 
 package com.android.internal.telephony;
 
+import android.os.Handler;
+import android.os.Message;
+import android.os.WorkSource;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.ClientRequestStats;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
+
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
-import com.android.internal.telephony.RadioCapability;
 import com.android.internal.telephony.uicc.IccCardStatus;
 
-import android.os.Message;
-import android.os.Handler;
-import android.service.carrier.CarrierIdentifier;
-
 import java.util.List;
 
-
 /**
  * {@hide}
  */
@@ -190,10 +192,14 @@
 
     void registerForCallStateChanged(Handler h, int what, Object obj);
     void unregisterForCallStateChanged(Handler h);
-    void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj);
-    void unregisterForVoiceNetworkStateChanged(Handler h);
-    void registerForDataNetworkStateChanged(Handler h, int what, Object obj);
-    void unregisterForDataNetworkStateChanged(Handler h);
+    /** Register for network state changed event */
+    void registerForNetworkStateChanged(Handler h, int what, Object obj);
+    /** Unregister from network state changed event */
+    void unregisterForNetworkStateChanged(Handler h);
+    /** Register for data call list changed event */
+    void registerForDataCallListChanged(Handler h, int what, Object obj);
+    /** Unregister from data call list changed event */
+    void unregisterForDataCallListChanged(Handler h);
 
     /** InCall voice privacy notifications */
     void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj);
@@ -1279,12 +1285,29 @@
     /**
      * Queries the currently available networks
      *
-     * ((AsyncResult)response.obj).result  is a List of NetworkInfo objects
+     * ((AsyncResult)response.obj).result is a List of NetworkInfo objects
      */
     void getAvailableNetworks(Message response);
 
-    void getBasebandVersion (Message response);
+    /**
+     * Starts a radio network scan
+     *
+     * ((AsyncResult)response.obj).result is a NetworkScanResult object
+     */
+    void startNetworkScan(NetworkScanRequest nsr, Message response);
 
+    /**
+     * Stops the ongoing network scan
+     *
+     * ((AsyncResult)response.obj).result is a NetworkScanResult object
+     *
+     */
+    void stopNetworkScan(Message response);
+
+    /**
+     * Gets the baseband version
+     */
+    void getBasebandVersion(Message response);
 
     /**
      * (AsyncResult)response.obj).result will be an Integer representing
@@ -1384,8 +1407,9 @@
      * Query neighboring cell ids
      *
      * @param response s callback message to cell ids
+     * @param workSource calling WorkSource
      */
-    void getNeighboringCids(Message response);
+    default void getNeighboringCids(Message response, WorkSource workSource){}
 
     /**
      * Request to enable/disable network state change notifications when
@@ -1428,6 +1452,18 @@
 
     void invokeOemRilRequestRaw(byte[] data, Message response);
 
+    /**
+     * Sends carrier specific information to the vendor ril that can be used to
+     * encrypt the IMSI and IMPI.
+     *
+     * @param publicKey the public key of the carrier used to encrypt IMSI/IMPI.
+     * @param keyIdentifier the key identifier is optional information that is carrier
+     *        specific.
+     * @param response callback message
+     */
+    void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+                                         Message response);
+
     void invokeOemRilRequestStrings(String[] strings, Message response);
 
     /**
@@ -1599,26 +1635,17 @@
      *
      * @param radioTechnology
      *            Radio technology to use. Values is one of RIL_RADIO_TECHNOLOGY_*
-     * @param profile
-     *            Profile Number. Values is one of DATA_PROFILE_*
-     * @param apn
-     *            the APN to connect to if radio technology is GSM/UMTS.
-     *            Otherwise null for CDMA.
-     * @param user
-     *            the username for APN, or NULL
-     * @param password
-     *            the password for APN, or NULL
-     * @param authType
-     *            the PAP / CHAP auth type. Values is one of SETUP_DATA_AUTH_*
-     * @param protocol
-     *            one of the PDP_type values in TS 27.007 section 10.1.1.
-     *            For example, "IP", "IPV6", "IPV4V6", or "PPP".
+     * @param dataProfile
+     *            Data profile for data call setup
+     * @param isRoaming
+     *            Device is roaming or not
+     * @param allowRoaming
+     *            Flag indicating data roaming is enabled or not
      * @param result
      *            Callback message
      */
-    public void setupDataCall(int radioTechnology, int profile,
-            String apn, String user, String password, int authType,
-            String protocol, Message result);
+    void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                       boolean allowRoaming, Message result);
 
     /**
      * Deactivate packet data connection
@@ -1727,8 +1754,9 @@
      * AsyncResult.result is a of Collection<CellInfo>
      *
      * @param result is sent back to handler and result.obj is a AsyncResult
+     * @param workSource calling WorkSource
      */
-    void getCellInfoList(Message result);
+    default void getCellInfoList(Message result, WorkSource workSource) {}
 
     /**
      * Sets the minimum time in milli-seconds between when RIL_UNSOL_CELL_INFO_LIST
@@ -1744,8 +1772,9 @@
      * @param response.obj is AsyncResult ar when sent to associated handler
      *                        ar.exception carries exception on failure or null on success
      *                        otherwise the error.
+     * @param workSource calling WorkSource
      */
-    void setCellInfoListRate(int rateInMillis, Message response);
+    default void setCellInfoListRate(int rateInMillis, Message response, WorkSource workSource){}
 
     /**
      * Fires when RIL_UNSOL_CELL_INFO_LIST is received from the RIL.
@@ -1756,33 +1785,26 @@
     /**
      * Set Initial Attach Apn
      *
-     * @param apn
-     *            the APN to connect to if radio technology is GSM/UMTS.
-     * @param protocol
-     *            one of the PDP_type values in TS 27.007 section 10.1.1.
-     *            For example, "IP", "IPV6", "IPV4V6", or "PPP".
-     * @param authType
-     *            authentication protocol used for this PDP context
-     *            (None: 0, PAP: 1, CHAP: 2, PAP&CHAP: 3)
-     * @param username
-     *            the username for APN, or NULL
-     * @param password
-     *            the password for APN, or NULL
+     * @param dataProfile
+     *            data profile for initial APN attach
+     * @param isRoaming
+     *            indicating the device is roaming or not
      * @param result
      *            callback message contains the information of SUCCESS/FAILURE
      */
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-            String password, Message result);
+    void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result);
 
     /**
      * Set data profiles in modem
      *
      * @param dps
      *            Array of the data profiles set to modem
+     * @param isRoaming
+     *            Indicating if the device is roaming or not
      * @param result
      *            callback message contains the information of SUCCESS/FAILURE
      */
-    public void setDataProfile(DataProfile[] dps, Message result);
+    void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result);
 
     /**
      * Notifiy that we are testing an emergency call
@@ -1795,10 +1817,11 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
+     * @param p2 P2 parameter (described in ISO 7816-4).
      * @param response Callback message. response.obj will be an int [1] with
      *            element [0] set to the id of the logical channel.
      */
-    public void iccOpenLogicalChannel(String AID, Message response);
+    public void iccOpenLogicalChannel(String AID, int p2, Message response);
 
     /**
      * Close a previously opened logical channel to the SIM.
@@ -2052,4 +2075,68 @@
      * @param h handler to be removed
      */
     public void unregisterForPcoData(Handler h);
+
+    /**
+     * Send the updated device state
+     *
+     * @param stateType Device state type
+     * @param state True if enabled, otherwise disabled
+     * @param result callback message contains the information of SUCCESS/FAILURE
+     */
+    void sendDeviceState(int stateType, boolean state, Message result);
+
+    /**
+     * Send the device state to the modem
+     *
+     * @param filter unsolicited response filter. See DeviceStateMonitor.UnsolicitedResponseFilter
+     * @param result callback message contains the information of SUCCESS/FAILURE
+     */
+    void setUnsolResponseFilter(int filter, Message result);
+
+    /**
+     * Set SIM card power up or down
+     *
+     * @param state  State of SIM (power down, power up, pass through)
+     * - {@link android.telephony.TelephonyManager#CARD_POWER_DOWN}
+     * - {@link android.telephony.TelephonyManager#CARD_POWER_UP}
+     * - {@link android.telephony.TelephonyManager#CARD_POWER_UP_PASS_THROUGH}
+     * @param result callback message contains the information of SUCCESS/FAILURE
+     */
+    void setSimCardPower(int state, Message result);
+
+    /**
+     * Register for unsolicited Carrier Public Key.
+     *
+     * @param h Handler for notificaiton message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    void registerForCarrierInfoForImsiEncryption(Handler h, int what, Object obj);
+
+    /**
+     * DeRegister for unsolicited Carrier Public Key.
+     *
+     * @param h Handler for notificaiton message.
+     */
+    void unregisterForCarrierInfoForImsiEncryption(Handler h);
+
+    /**
+     * Register for unsolicited Network Scan result.
+     *
+     * @param h Handler for notificaiton message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    void registerForNetworkScanResult(Handler h, int what, Object obj);
+
+    /**
+     * DeRegister for unsolicited Network Scan result.
+     *
+     * @param h Handler for notificaiton message.
+     */
+    void unregisterForNetworkScanResult(Handler h);
+
+    default public List<ClientRequestStats> getClientRequestStats() {
+        return null;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 5073aa1..74ee5c5 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -34,6 +34,7 @@
  * {@hide}
  */
 public abstract class Connection {
+    private static final String TAG = "Connection";
 
     public interface PostDialListener {
         void onPostDialWait();
@@ -101,6 +102,8 @@
         public void onCallPullFailed(Connection externalConnection);
         public void onHandoverToWifiFailed();
         public void onConnectionEvent(String event, Bundle extras);
+        public void onRttModifyRequestReceived();
+        public void onRttModifyResponseReceived(int status);
     }
 
     /**
@@ -136,6 +139,10 @@
         public void onHandoverToWifiFailed() {}
         @Override
         public void onConnectionEvent(String event, Bundle extras) {}
+        @Override
+        public void onRttModifyRequestReceived() {}
+        @Override
+        public void onRttModifyResponseReceived(int status) {}
     }
 
     public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -305,6 +312,15 @@
     }
 
     /**
+     * Sets the Connection connect time in {@link SystemClock#elapsedRealtime()} format.
+     *
+     * @param connectTimeReal the new connect time.
+     */
+    public void setConnectTimeReal(long connectTimeReal) {
+        mConnectTimeReal = connectTimeReal;
+    }
+
+    /**
      * Connection connect time in elapsedRealtime() format.
      * For outgoing calls: Begins at (DIALING|ALERTING) -> ACTIVE transition.
      * For incoming calls: Begins at (INCOMING|WAITING) -> ACTIVE transition.
@@ -502,7 +518,9 @@
     }
 
     protected final void clearPostDialListeners() {
-        mPostDialListeners.clear();
+        if (mPostDialListeners != null) {
+            mPostDialListeners.clear();
+        }
     }
 
     protected final void notifyPostDialListeners() {
@@ -625,6 +643,7 @@
         mOrigConnection = c.getOrigConnection();
         mPostDialString = c.mPostDialString;
         mNextPostDialChar = c.mNextPostDialChar;
+        mPostDialState = c.mPostDialState;
     }
 
     /**
@@ -809,6 +828,16 @@
     public void setConnectionExtras(Bundle extras) {
         if (extras != null) {
             mExtras = new Bundle(extras);
+
+            int previousCount = mExtras.size();
+            // Prevent vendors from passing in extras other than primitive types and android API
+            // parcelables.
+            mExtras = mExtras.filterValues();
+            int filteredCount = mExtras.size();
+            if (filteredCount != previousCount) {
+                Rlog.i(TAG, "setConnectionExtras: filtering " + (previousCount - filteredCount)
+                        + " invalid extras.");
+            }
         } else {
             mExtras = null;
         }
@@ -1004,6 +1033,18 @@
     public void pullExternalCall() {
     }
 
+    public void onRttModifyRequestReceived() {
+        for (Listener l : mListeners) {
+            l.onRttModifyRequestReceived();
+        }
+    }
+
+    public void onRttModifyResponseReceived(int status) {
+        for (Listener l : mListeners) {
+            l.onRttModifyResponseReceived(status);
+        }
+    }
+
     /**
      *
      */
diff --git a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
index 0c23cf8..504bf1d 100644
--- a/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/DefaultPhoneNotifier.java
@@ -22,19 +22,14 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.CellInfo;
+import android.telephony.PreciseCallState;
 import android.telephony.Rlog;
-import android.telephony.VoLteServiceState;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-import android.telephony.PreciseCallState;
-import android.telephony.DisconnectCause;
+import android.telephony.VoLteServiceState;
 
-import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.ITelephonyRegistry;
-import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneConstantConversions;
 
 import java.util.List;
 
@@ -64,7 +59,8 @@
         try {
             if (mRegistry != null) {
                   mRegistry.notifyCallStateForPhoneId(phoneId, subId,
-                        convertCallState(sender.getState()), incomingNumber);
+                        PhoneConstantConversions.convertCallState(
+                            sender.getState()), incomingNumber);
             }
         } catch (RemoteException ex) {
             // system process is dead
@@ -182,15 +178,14 @@
         try {
             if (mRegistry != null) {
                 mRegistry.notifyDataConnectionForSubscriber(subId,
-                    convertDataState(state),
-                    sender.isDataConnectivityPossible(apnType), reason,
-                    sender.getActiveApnHost(apnType),
-                    apnType,
-                    linkProperties,
-                    networkCapabilities,
-                    ((telephony!=null) ? telephony.getDataNetworkType(subId) :
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN),
-                    roaming);
+                    PhoneConstantConversions.convertDataState(state),
+                        sender.isDataAllowed(), reason,
+                        sender.getActiveApnHost(apnType),
+                        apnType,
+                        linkProperties,
+                        networkCapabilities,
+                        ((telephony != null) ? telephony.getDataNetworkType(subId) :
+                                TelephonyManager.NETWORK_TYPE_UNKNOWN), roaming);
             }
         } catch (RemoteException ex) {
             // system process is dead
@@ -294,75 +289,31 @@
     }
 
     @Override
-    public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+    public void notifyDataActivationStateChanged(Phone sender, int activationState) {
         try {
-            mRegistry.notifyOemHookRawEventForSubscriber(subId, rawData);
+            mRegistry.notifySimActivationStateChangedForPhoneId(sender.getPhoneId(),
+                    sender.getSubId(), PhoneConstants.SIM_ACTIVATION_TYPE_DATA, activationState);
         } catch (RemoteException ex) {
             // system process is dead
         }
     }
 
-    /**
-     * Convert the {@link PhoneConstants.State} enum into the TelephonyManager.CALL_STATE_*
-     * constants for the public API.
-     */
-    public static int convertCallState(PhoneConstants.State state) {
-        switch (state) {
-            case RINGING:
-                return TelephonyManager.CALL_STATE_RINGING;
-            case OFFHOOK:
-                return TelephonyManager.CALL_STATE_OFFHOOK;
-            default:
-                return TelephonyManager.CALL_STATE_IDLE;
+    @Override
+    public void notifyVoiceActivationStateChanged(Phone sender, int activationState) {
+        try {
+            mRegistry.notifySimActivationStateChangedForPhoneId(sender.getPhoneId(),
+                    sender.getSubId(), PhoneConstants.SIM_ACTIVATION_TYPE_VOICE, activationState);
+        } catch (RemoteException ex) {
+            // system process is dead
         }
     }
 
-    /**
-     * Convert the TelephonyManager.CALL_STATE_* constants into the
-     * {@link PhoneConstants.State} enum for the public API.
-     */
-    public static PhoneConstants.State convertCallState(int state) {
-        switch (state) {
-            case TelephonyManager.CALL_STATE_RINGING:
-                return PhoneConstants.State.RINGING;
-            case TelephonyManager.CALL_STATE_OFFHOOK:
-                return PhoneConstants.State.OFFHOOK;
-            default:
-                return PhoneConstants.State.IDLE;
-        }
-    }
-
-    /**
-     * Convert the {@link PhoneConstants.DataState} enum into the TelephonyManager.DATA_* constants
-     * for the public API.
-     */
-    public static int convertDataState(PhoneConstants.DataState state) {
-        switch (state) {
-            case CONNECTING:
-                return TelephonyManager.DATA_CONNECTING;
-            case CONNECTED:
-                return TelephonyManager.DATA_CONNECTED;
-            case SUSPENDED:
-                return TelephonyManager.DATA_SUSPENDED;
-            default:
-                return TelephonyManager.DATA_DISCONNECTED;
-        }
-    }
-
-    /**
-     * Convert the TelephonyManager.DATA_* constants into {@link PhoneConstants.DataState} enum
-     * for the public API.
-     */
-    public static PhoneConstants.DataState convertDataState(int state) {
-        switch (state) {
-            case TelephonyManager.DATA_CONNECTING:
-                return PhoneConstants.DataState.CONNECTING;
-            case TelephonyManager.DATA_CONNECTED:
-                return PhoneConstants.DataState.CONNECTED;
-            case TelephonyManager.DATA_SUSPENDED:
-                return PhoneConstants.DataState.SUSPENDED;
-            default:
-                return PhoneConstants.DataState.DISCONNECTED;
+    @Override
+    public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+        try {
+            mRegistry.notifyOemHookRawEventForSubscriber(subId, rawData);
+        } catch (RemoteException ex) {
+            // system process is dead
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/DeviceStateMonitor.java b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
new file mode 100644
index 0000000..73ab075
--- /dev/null
+++ b/src/java/com/android/internal/telephony/DeviceStateMonitor.java
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
+import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
+import static android.hardware.radio.V1_0.DeviceStateType.POWER_SAVE_MODE;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.display.DisplayManager;
+import android.hardware.radio.V1_0.IndicationFilter;
+import android.net.ConnectivityManager;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.telephony.Rlog;
+import android.util.LocalLog;
+import android.view.Display;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * The device state monitor monitors the device state such as charging state, power saving sate,
+ * and then passes down the information to the radio modem for the modem to perform its own
+ * proprietary power saving strategy. Device state monitor also turns off the unsolicited
+ * response from the modem when the device does not need to receive it, for example, device's
+ * screen is off and does not have activities like tethering, remote display, etc...This effectively
+ * prevents the CPU from waking up by those unnecessary unsolicited responses such as signal
+ * strength update.
+ */
+public class DeviceStateMonitor extends Handler {
+    protected static final boolean DBG = false;      /* STOPSHIP if true */
+    protected static final String TAG = DeviceStateMonitor.class.getSimpleName();
+
+    private static final int EVENT_RIL_CONNECTED                = 0;
+    private static final int EVENT_SCREEN_STATE_CHANGED         = 1;
+    private static final int EVENT_POWER_SAVE_MODE_CHANGED      = 2;
+    private static final int EVENT_CHARGING_STATE_CHANGED       = 3;
+    private static final int EVENT_TETHERING_STATE_CHANGED      = 4;
+
+    private final Phone mPhone;
+
+    private final LocalLog mLocalLog = new LocalLog(100);
+
+    /**
+     * Flag for wifi/usb/bluetooth tethering turned on or not
+     */
+    private boolean mIsTetheringOn;
+
+    /**
+     * Screen state provided by Display Manager. True indicates one of the screen is on, otherwise
+     * all off.
+     */
+    private boolean mIsScreenOn;
+
+    /**
+     * Indicating the device is plugged in and is supplying sufficient power that the battery level
+     * is going up (or the battery is fully charged). See BatteryManager.isCharging() for the
+     * details
+     */
+    private boolean mIsCharging;
+
+    /**
+     * Flag for device power save mode. See PowerManager.isPowerSaveMode() for the details.
+     * Note that it is not possible both mIsCharging and mIsPowerSaveOn are true at the same time.
+     * The system will automatically end power save mode when the device starts charging.
+     */
+    private boolean mIsPowerSaveOn;
+
+    /**
+     * Low data expected mode. True indicates low data traffic is expected, for example, when the
+     * device is idle (e.g. screen is off and not doing tethering in the background). Note this
+     * doesn't mean no data is expected.
+     */
+    private boolean mIsLowDataExpected;
+
+    /**
+     * The unsolicited response filter. See IndicationFilter defined in types.hal for the definition
+     * of each bit.
+     */
+    private int mUnsolicitedResponseFilter = IndicationFilter.ALL;
+
+    private final DisplayManager.DisplayListener mDisplayListener =
+            new DisplayManager.DisplayListener() {
+                @Override
+                public void onDisplayAdded(int displayId) { }
+
+                @Override
+                public void onDisplayRemoved(int displayId) { }
+
+                @Override
+                public void onDisplayChanged(int displayId) {
+                    boolean screenOn = isScreenOn();
+                    Message msg = obtainMessage(EVENT_SCREEN_STATE_CHANGED);
+                    msg.arg1 = screenOn ? 1 : 0;
+                    sendMessage(msg);
+                }
+            };
+
+    /**
+     * Device state broadcast receiver
+     */
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            log("received: " + intent, true);
+
+            Message msg;
+            switch (intent.getAction()) {
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+                    msg = obtainMessage(EVENT_POWER_SAVE_MODE_CHANGED);
+                    msg.arg1 = isPowerSaveModeOn() ? 1 : 0;
+                    log("Power Save mode " + ((msg.arg1 == 1) ? "on" : "off"), true);
+                    break;
+                case BatteryManager.ACTION_CHARGING:
+                    msg = obtainMessage(EVENT_CHARGING_STATE_CHANGED);
+                    msg.arg1 = 1;   // charging
+                    break;
+                case BatteryManager.ACTION_DISCHARGING:
+                    msg = obtainMessage(EVENT_CHARGING_STATE_CHANGED);
+                    msg.arg1 = 0;   // not charging
+                    break;
+                case ConnectivityManager.ACTION_TETHER_STATE_CHANGED:
+                    ArrayList<String> activeTetherIfaces = intent.getStringArrayListExtra(
+                            ConnectivityManager.EXTRA_ACTIVE_TETHER);
+
+                    boolean isTetheringOn = activeTetherIfaces != null
+                            && activeTetherIfaces.size() > 0;
+                    log("Tethering " + (isTetheringOn ? "on" : "off"), true);
+                    msg = obtainMessage(EVENT_TETHERING_STATE_CHANGED);
+                    msg.arg1 = isTetheringOn ? 1 : 0;
+                    break;
+                default:
+                    log("Unexpected broadcast intent: " + intent, false);
+                    return;
+            }
+            sendMessage(msg);
+        }
+    };
+
+    /**
+     * Device state monitor constructor. Note that each phone object should have its own device
+     * state monitor, meaning there will be two device monitors on the multi-sim device.
+     *
+     * @param phone Phone object
+     */
+    public DeviceStateMonitor(Phone phone) {
+        mPhone = phone;
+        DisplayManager dm = (DisplayManager) phone.getContext().getSystemService(
+                Context.DISPLAY_SERVICE);
+        dm.registerDisplayListener(mDisplayListener, null);
+
+        mIsPowerSaveOn = isPowerSaveModeOn();
+        mIsCharging = isDeviceCharging();
+        mIsScreenOn = isScreenOn();
+        // Assuming tethering is always off after boot up.
+        mIsTetheringOn = false;
+        mIsLowDataExpected = false;
+
+        log("DeviceStateMonitor mIsPowerSaveOn=" + mIsPowerSaveOn + ",mIsScreenOn="
+                + mIsScreenOn + ",mIsCharging=" + mIsCharging, false);
+
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
+        filter.addAction(BatteryManager.ACTION_CHARGING);
+        filter.addAction(BatteryManager.ACTION_DISCHARGING);
+        filter.addAction(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        mPhone.getContext().registerReceiver(mBroadcastReceiver, filter, null, mPhone);
+
+        mPhone.mCi.registerForRilConnected(this, EVENT_RIL_CONNECTED, null);
+    }
+
+    /**
+     * @return True if low data is expected
+     */
+    private boolean isLowDataExpected() {
+        return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
+    }
+
+    /**
+     * @return True if signal strength update should be turned off.
+     */
+    private boolean shouldTurnOffSignalStrength() {
+        return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn);
+    }
+
+    /**
+     * @return True if full network update should be turned off. Only significant changes will
+     * trigger the network update unsolicited response.
+     */
+    private boolean shouldTurnOffFullNetworkUpdate() {
+        return mIsPowerSaveOn || (!mIsCharging && !mIsScreenOn && !mIsTetheringOn);
+    }
+
+    /**
+     * @return True if data dormancy status update should be turned off.
+     */
+    private boolean shouldTurnOffDormancyUpdate() {
+        return mIsPowerSaveOn || (!mIsCharging && !mIsTetheringOn && !mIsScreenOn);
+    }
+
+    /**
+     * Message handler
+     *
+     * @param msg The message
+     */
+    @Override
+    public void handleMessage(Message msg) {
+        log("handleMessage msg=" + msg, false);
+        switch (msg.what) {
+            case EVENT_RIL_CONNECTED:
+                onRilConnected();
+                break;
+            default:
+                updateDeviceState(msg.what, msg.arg1 != 0);
+        }
+    }
+
+    /**
+     * Update the device and send the information to the modem.
+     *
+     * @param eventType Device state event type
+     * @param state True if enabled/on, otherwise disabled/off.
+     */
+    private void updateDeviceState(int eventType, boolean state) {
+        switch (eventType) {
+            case EVENT_SCREEN_STATE_CHANGED:
+                if (mIsScreenOn == state) return;
+                mIsScreenOn = state;
+                break;
+            case EVENT_CHARGING_STATE_CHANGED:
+                if (mIsCharging == state) return;
+                mIsCharging = state;
+                sendDeviceState(CHARGING_STATE, mIsCharging);
+                break;
+            case EVENT_TETHERING_STATE_CHANGED:
+                if (mIsTetheringOn == state) return;
+                mIsTetheringOn = state;
+                break;
+            case EVENT_POWER_SAVE_MODE_CHANGED:
+                if (mIsPowerSaveOn == state) return;
+                mIsPowerSaveOn = state;
+                sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
+                break;
+            default:
+                return;
+        }
+
+        if (mIsLowDataExpected != isLowDataExpected()) {
+            mIsLowDataExpected = !mIsLowDataExpected;
+            sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
+        }
+
+        int newFilter = 0;
+        if (!shouldTurnOffSignalStrength()) {
+            newFilter |= IndicationFilter.SIGNAL_STRENGTH;
+        }
+
+        if (!shouldTurnOffFullNetworkUpdate()) {
+            newFilter |= IndicationFilter.FULL_NETWORK_STATE;
+        }
+
+        if (!shouldTurnOffDormancyUpdate()) {
+            newFilter |= IndicationFilter.DATA_CALL_DORMANCY_CHANGED;
+        }
+
+        setUnsolResponseFilter(newFilter, false);
+    }
+
+    /**
+     * Called when RIL is connected during boot up or reconnected after modem restart.
+     *
+     * When modem crashes, if the user turns the screen off before RIL reconnects, device
+     * state and filter cannot be sent to modem. Resend the state here so that modem
+     * has the correct state (to stop signal strength reporting, etc).
+     */
+    private void onRilConnected() {
+        log("RIL connected.", true);
+        sendDeviceState(CHARGING_STATE, mIsCharging);
+        sendDeviceState(LOW_DATA_EXPECTED, mIsLowDataExpected);
+        sendDeviceState(POWER_SAVE_MODE, mIsPowerSaveOn);
+        setUnsolResponseFilter(mUnsolicitedResponseFilter, true);
+    }
+
+    /**
+     * Convert the device state type into string
+     *
+     * @param type Device state type
+     * @return The converted string
+     */
+    private String deviceTypeToString(int type) {
+        switch (type) {
+            case CHARGING_STATE: return "CHARGING_STATE";
+            case LOW_DATA_EXPECTED: return "LOW_DATA_EXPECTED";
+            case POWER_SAVE_MODE: return "POWER_SAVE_MODE";
+            default: return "UNKNOWN";
+        }
+    }
+
+    /**
+     * Send the device state to the modem.
+     *
+     * @param type Device state type. See DeviceStateType defined in types.hal.
+     * @param state True if enabled/on, otherwise disabled/off
+     */
+    private void sendDeviceState(int type, boolean state) {
+        log("send type: " + deviceTypeToString(type) + ", state=" + state, true);
+        mPhone.mCi.sendDeviceState(type, state, null);
+    }
+
+    /**
+     * Turn on/off the unsolicited response from the modem.
+     *
+     * @param newFilter See UnsolicitedResponseFilter in types.hal for the definition of each bit.
+     * @param force Always set the filter when true.
+     */
+    private void setUnsolResponseFilter(int newFilter, boolean force) {
+        if (force || newFilter != mUnsolicitedResponseFilter) {
+            log("old filter: " + mUnsolicitedResponseFilter + ", new filter: " + newFilter, true);
+            mPhone.mCi.setUnsolResponseFilter(newFilter, null);
+            mUnsolicitedResponseFilter = newFilter;
+        }
+    }
+
+    /**
+     * @return True if the device is currently in power save mode.
+     * See {@link android.os.BatteryManager#isPowerSaveMode BatteryManager.isPowerSaveMode()}.
+     */
+    private boolean isPowerSaveModeOn() {
+        final PowerManager pm = (PowerManager) mPhone.getContext().getSystemService(
+                Context.POWER_SERVICE);
+        return pm.isPowerSaveMode();
+    }
+
+    /**
+     * @return Return true if the battery is currently considered to be charging. This means that
+     * the device is plugged in and is supplying sufficient power that the battery level is
+     * going up (or the battery is fully charged).
+     * See {@link android.os.BatteryManager#isCharging BatteryManager.isCharging()}.
+     */
+    private boolean isDeviceCharging() {
+        final BatteryManager bm = (BatteryManager) mPhone.getContext().getSystemService(
+                Context.BATTERY_SERVICE);
+        return bm.isCharging();
+    }
+
+    /**
+     * @return True if one the device's screen (e.g. main screen, wifi display, HDMI display, or
+     *         Android auto, etc...) is on.
+     */
+    private boolean isScreenOn() {
+        // Note that we don't listen to Intent.SCREEN_ON and Intent.SCREEN_OFF because they are no
+        // longer adequate for monitoring the screen state since they are not sent in cases where
+        // the screen is turned off transiently such as due to the proximity sensor.
+        final DisplayManager dm = (DisplayManager) mPhone.getContext().getSystemService(
+                Context.DISPLAY_SERVICE);
+        Display[] displays = dm.getDisplays();
+
+        if (displays != null) {
+            for (Display display : displays) {
+                // Anything other than STATE_ON is treated as screen off, such as STATE_DOZE,
+                // STATE_DOZE_SUSPEND, etc...
+                if (display.getState() == Display.STATE_ON) {
+                    log("Screen " + Display.typeToString(display.getType()) + " on", true);
+                    return true;
+                }
+            }
+            log("Screens all off", true);
+            return false;
+        }
+
+        log("No displays found", true);
+        return false;
+    }
+
+    /**
+     * @param msg Debug message
+     * @param logIntoLocalLog True if log into the local log
+     */
+    private void log(String msg, boolean logIntoLocalLog) {
+        if (DBG) Rlog.d(TAG, msg);
+        if (logIntoLocalLog) {
+            mLocalLog.log(msg);
+        }
+    }
+
+    /**
+     * Print the DeviceStateMonitor into the given stream.
+     *
+     * @param fd The raw file descriptor that the dump is being sent to.
+     * @param pw A PrintWriter to which the dump is to be set.
+     * @param args Additional arguments to the dump request.
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.increaseIndent();
+        ipw.println("mIsTetheringOn=" + mIsTetheringOn);
+        ipw.println("mIsScreenOn=" + mIsScreenOn);
+        ipw.println("mIsCharging=" + mIsCharging);
+        ipw.println("mIsPowerSaveOn=" + mIsPowerSaveOn);
+        ipw.println("mIsLowDataExpected=" + mIsLowDataExpected);
+        ipw.println("mUnsolicitedResponseFilter=" + mUnsolicitedResponseFilter);
+        ipw.println("Local logs:");
+        ipw.increaseIndent();
+        mLocalLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+        ipw.decreaseIndent();
+        ipw.flush();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 6500a43..5960051 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -24,9 +24,11 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
@@ -38,6 +40,7 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 
@@ -66,7 +69,8 @@
     private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call
 
     //***** Instance Variables
-    private GsmCdmaConnection mConnections[];
+    @VisibleForTesting
+    public GsmCdmaConnection[] mConnections;
     private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
     private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
 
@@ -88,6 +92,8 @@
 
     public PhoneConstants.State mState = PhoneConstants.State.IDLE;
 
+    private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
+
     // Following member variables are for CDMA only
     private RegistrantList mCallWaitingRegistrants = new RegistrantList();
     private boolean mPendingCallInEcm;
@@ -164,6 +170,11 @@
         if (mPhone.isPhoneTypeGsm()) {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_GSM];
             mCi.unregisterForCallWaitingInfo(this);
+            // Prior to phone switch to GSM, if CDMA has any emergency call
+            // data will be in disabled state, after switching to GSM enable data.
+            if (mIsInEmergencyCall) {
+                mPhone.mDcTracker.setInternalDataEnabled(true);
+            }
         } else {
             mConnections = new GsmCdmaConnection[MAX_CONNECTIONS_CDMA];
             mPendingCallInEcm = false;
@@ -178,10 +189,9 @@
     private void reset() {
         Rlog.d(LOG_TAG, "reset");
 
-        clearDisconnected();
-
         for (GsmCdmaConnection gsmCdmaConnection : mConnections) {
             if (gsmCdmaConnection != null) {
+                gsmCdmaConnection.onDisconnect(DisconnectCause.ERROR_UNSPECIFIED);
                 gsmCdmaConnection.dispose();
             }
         }
@@ -192,7 +202,7 @@
 
         mConnections = null;
         mPendingMO = null;
-        mState = PhoneConstants.State.IDLE;
+        clearDisconnected();
     }
 
     @Override
@@ -303,6 +313,8 @@
         mPendingMO = new GsmCdmaConnection(mPhone, checkForTestEmergencyNumber(dialString),
                 this, mForegroundCall, isEmergencyCall);
         mHangupPendingMO = false;
+        mMetrics.writeRilDial(mPhone.getPhoneId(), mPendingMO, clirMode, uusInfo);
+
 
         if ( mPendingMO.getAddress() == null || mPendingMO.getAddress().length() == 0
                 || mPendingMO.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) {
@@ -394,8 +406,7 @@
             dialString = convertNumberIfNecessary(mPhone, dialString);
         }
 
-        String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
-        boolean isPhoneInEcmMode = inEcm.equals("true");
+        boolean isPhoneInEcmMode = mPhone.isInEcm();
         boolean isEmergencyCall =
                 PhoneNumberUtils.isLocalEmergencyNumber(mPhone.getContext(), dialString);
 
@@ -462,9 +473,17 @@
             mPendingMO = new GsmCdmaConnection(mPhone,
                     checkForTestEmergencyNumber(dialString), this, mForegroundCall,
                     mIsInEmergencyCall);
-            // Some network need a empty flash before sending the normal one
-            m3WayCallFlashDelay = mPhone.getContext().getResources()
-                    .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
+            // Some networks need an empty flash before sending the normal one
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            PersistableBundle bundle = configManager.getConfig();
+            if (bundle != null) {
+                m3WayCallFlashDelay =
+                        bundle.getInt(CarrierConfigManager.KEY_CDMA_3WAYCALL_FLASH_DELAY_INT);
+            } else {
+                // The default 3-way call flash delay is 0s
+                m3WayCallFlashDelay = 0;
+            }
             if (m3WayCallFlashDelay > 0) {
                 mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
             } else {
@@ -715,7 +734,7 @@
         }
         if (mState != oldState) {
             mPhone.notifyPhoneStateChanged();
-            TelephonyMetrics.getInstance().writePhoneState(mPhone.getPhoneId(), mState);
+            mMetrics.writePhoneState(mPhone.getPhoneId(), mState);
         }
     }
 
@@ -863,9 +882,6 @@
                     // Connection missing in CLCC response that we were
                     // tracking.
                     mDroppedDuringPoll.add(conn);
-                    // Dropped connections are removed from the CallTracker
-                    // list but kept in the GsmCdmaCall list
-                    mConnections[i] = null;
                 } else {
                     // This case means the RIL has no more active call anymore and
                     // we need to clean up the foregroundCall and ringingCall.
@@ -892,12 +908,10 @@
                     }
                     // If emergency call is not going through while dialing
                     checkAndEnableDataCallAfterEmergencyCallDropped();
-
-                    // Dropped connections are removed from the CallTracker
-                    // list but kept in the Call list
-                    mConnections[i] = null;
-
                 }
+                // Dropped connections are removed from the CallTracker
+                // list but kept in the Call list
+                mConnections[i] = null;
             } else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
                 // Connection in CLCC response does not match what
                 // we were tracking. Assume dropped call and new call
@@ -987,6 +1001,7 @@
         // clear the "local hangup" and "missed/rejected call"
         // cases from the "dropped during poll" list
         // These cases need no "last call fail" reason
+        ArrayList<GsmCdmaConnection> locallyDisconnectedConnections = new ArrayList<>();
         for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
             GsmCdmaConnection conn = mDroppedDuringPoll.get(i);
             //CDMA
@@ -1008,11 +1023,13 @@
                 mDroppedDuringPoll.remove(i);
                 hasAnyCallDisconnected |= conn.onDisconnect(cause);
                 wasDisconnected = true;
+                locallyDisconnectedConnections.add(conn);
             } else if (conn.mCause == DisconnectCause.LOCAL
                     || conn.mCause == DisconnectCause.INVALID_NUMBER) {
                 mDroppedDuringPoll.remove(i);
                 hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
                 wasDisconnected = true;
+                locallyDisconnectedConnections.add(conn);
             }
 
             if (!isPhoneTypeGsm() && wasDisconnected && unknownConnectionAppeared
@@ -1021,6 +1038,9 @@
                 newUnknownConnectionCdma = null;
             }
         }
+        if (locallyDisconnectedConnections.size() > 0) {
+            mMetrics.writeRilCallList(mPhone.getPhoneId(), locallyDisconnectedConnections);
+        }
 
         /* Disconnect any pending Handover connections */
         for (Iterator<Connection> it = mHandoverConnections.iterator();
@@ -1033,6 +1053,7 @@
             } else {
                 hoConnection.onDisconnect(DisconnectCause.NOT_VALID);
             }
+            // TODO: Do we need to update these hoConnections in Metrics ?
             it.remove();
         }
 
@@ -1071,6 +1092,7 @@
 
         if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
             mPhone.notifyPreciseCallStateChanged();
+            updateMetrics(mConnections);
         }
 
         // If all handover connections are mapped during this poll process clean it up
@@ -1083,6 +1105,14 @@
         //dumpState();
     }
 
+    private void updateMetrics(GsmCdmaConnection[] connections) {
+        ArrayList<GsmCdmaConnection> activeConnections = new ArrayList<>();
+        for (GsmCdmaConnection conn : connections) {
+            if (conn != null) activeConnections.add(conn);
+        }
+        mMetrics.writeRilCallList(mPhone.getPhoneId(), activeConnections);
+    }
+
     private void handleRadioNotAvailable() {
         // handlePollCalls will clear out its
         // call list when it gets the CommandException
@@ -1153,6 +1183,7 @@
             return;
         } else {
             try {
+                mMetrics.writeRilHangup(mPhone.getPhoneId(), conn, conn.getGsmCdmaIndex());
                 mCi.hangupConnection (conn.getGsmCdmaIndex(), obtainCompleteMessage());
             } catch (CallStateException ex) {
                 // Ignore "connection not found"
@@ -1201,6 +1232,7 @@
 
         if (call == mRingingCall) {
             if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
+            logHangupEvent(call);
             mCi.hangupWaitingOrBackground(obtainCompleteMessage());
         } else if (call == mForegroundCall) {
             if (call.isDialingOrAlerting()) {
@@ -1214,6 +1246,7 @@
                 log("hangup all conns in active/background call, without affecting ringing call");
                 hangupAllConnections(call);
             } else {
+                logHangupEvent(call);
                 hangupForegroundResumeBackground();
             }
         } else if (call == mBackgroundCall) {
@@ -1234,8 +1267,24 @@
         mPhone.notifyPreciseCallStateChanged();
     }
 
+    private void logHangupEvent(GsmCdmaCall call) {
+        int count = call.mConnections.size();
+        for (int i = 0; i < count; i++) {
+            GsmCdmaConnection cn = (GsmCdmaConnection) call.mConnections.get(i);
+            int call_index;
+            try {
+                call_index = cn.getGsmCdmaIndex();
+            } catch (CallStateException ex) {
+                call_index = -1;
+            }
+            mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, call_index);
+        }
+        if (VDBG) Rlog.v(LOG_TAG, "logHangupEvent logged " + count + " Connections ");
+    }
+
     public void hangupWaitingOrBackground() {
         if (Phone.DEBUG_PHONE) log("hangupWaitingOrBackground");
+        logHangupEvent(mBackgroundCall);
         mCi.hangupWaitingOrBackground(obtainCompleteMessage());
     }
 
@@ -1250,6 +1299,7 @@
         for (int i = 0; i < count; i++) {
             GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
             if (!cn.mDisconnected && cn.getGsmCdmaIndex() == index) {
+                mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex());
                 mCi.hangupConnection(index, obtainCompleteMessage());
                 return;
             }
@@ -1264,6 +1314,7 @@
             for (int i = 0; i < count; i++) {
                 GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
                 if (!cn.mDisconnected) {
+                    mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex());
                     mCi.hangupConnection(cn.getGsmCdmaIndex(), obtainCompleteMessage());
                 }
             }
@@ -1341,11 +1392,14 @@
 
             case EVENT_CONFERENCE_RESULT:
                 if (isPhoneTypeGsm()) {
-                    // The conference merge failed, so notify listeners.  Ultimately this bubbles up
-                    // to Telecom, which will inform the InCall UI of the failure.
-                    Connection connection = mForegroundCall.getLatestConnection();
-                    if (connection != null) {
-                        connection.onConferenceMergeFailed();
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        // The conference merge failed, so notify listeners.  Ultimately this
+                        // bubbles up to Telecom, which will inform the InCall UI of the failure.
+                        Connection connection = mForegroundCall.getLatestConnection();
+                        if (connection != null) {
+                            connection.onConferenceMergeFailed();
+                        }
                     }
                 }
                 // fall through
@@ -1418,6 +1472,7 @@
                 updatePhoneState();
 
                 mPhone.notifyPreciseCallStateChanged();
+                mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll);
                 mDroppedDuringPoll.clear();
             break;
 
@@ -1513,11 +1568,11 @@
     private void checkAndEnableDataCallAfterEmergencyCallDropped() {
         if (mIsInEmergencyCall) {
             mIsInEmergencyCall = false;
-            String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false");
+            boolean inEcm = mPhone.isInEcm();
             if (Phone.DEBUG_PHONE) {
                 log("checkAndEnableDataCallAfterEmergencyCallDropped,inEcm=" + inEcm);
             }
-            if (inEcm.compareTo("false") == 0) {
+            if (!inEcm) {
                 // Re-initiate data connection
                 mPhone.mDcTracker.setInternalDataEnabled(true);
                 mPhone.notifyEmergencyCallRegistrants(false);
@@ -1626,4 +1681,13 @@
                 MAX_CONNECTIONS_PER_CALL_GSM :
                 MAX_CONNECTIONS_PER_CALL_CDMA;
     }
+
+    /**
+     * Called to force the call tracker to cleanup any stale calls.  Does this by triggering
+     * {@code GET_CURRENT_CALLS} on the RIL.
+     */
+    @Override
+    public void cleanupCalls() {
+        pollCallsWhenSafe();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index 1530ada..f126600 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -26,16 +26,15 @@
 import android.os.SystemClock;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
-import android.telephony.Rlog;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
-import com.android.internal.telephony.uicc.UiccCardApplication;
-import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
+import com.android.internal.telephony.uicc.UiccCardApplication;
 
 /**
  * {@hide}
@@ -226,11 +225,15 @@
         releaseAllWakeLocks();
     }
 
-    static boolean
-    equalsHandlesNulls (Object a, Object b) {
+    static boolean equalsHandlesNulls(Object a, Object b) {
         return (a == null) ? (b == null) : a.equals (b);
     }
 
+    static boolean
+    equalsBaseDialString (String a, String b) {
+        return (a == null) ? (b == null) : (b != null && a.startsWith (b));
+    }
+
     //CDMA
     /**
      * format original dial string
@@ -438,15 +441,24 @@
             case CallFailCause.BEARER_NOT_AVAIL:
                 return DisconnectCause.CONGESTION;
 
+            case CallFailCause.EMERGENCY_TEMP_FAILURE:
+                return DisconnectCause.EMERGENCY_TEMP_FAILURE;
+            case CallFailCause.EMERGENCY_PERM_FAILURE:
+                return DisconnectCause.EMERGENCY_PERM_FAILURE;
+
             case CallFailCause.ACM_LIMIT_EXCEEDED:
                 return DisconnectCause.LIMIT_EXCEEDED;
 
+            case CallFailCause.OPERATOR_DETERMINED_BARRING:
             case CallFailCause.CALL_BARRED:
                 return DisconnectCause.CALL_BARRED;
 
             case CallFailCause.FDN_BLOCKED:
                 return DisconnectCause.FDN_BLOCKED;
 
+            case CallFailCause.IMEI_NOT_ACCEPTED:
+                return DisconnectCause.IMEI_NOT_ACCEPTED;
+
             case CallFailCause.UNOBTAINABLE_NUMBER:
                 return DisconnectCause.UNOBTAINABLE_NUMBER;
 
@@ -609,8 +621,8 @@
             if (Phone.DEBUG_PHONE) log("update: mOrigConnection is not null");
         } else {
             log(" mNumberConverted " + mNumberConverted);
-            if (!equalsHandlesNulls(mAddress, dc.number) && (!mNumberConverted
-                    || !equalsHandlesNulls(mConvertedNumber, dc.number))) {
+            if (!equalsBaseDialString(mAddress, dc.number) && (!mNumberConverted
+                    || !equalsBaseDialString(mConvertedNumber, dc.number))) {
                 if (Phone.DEBUG_PHONE) log("update: phone # changed!");
                 mAddress = dc.number;
                 changed = true;
@@ -804,13 +816,13 @@
     protected void finalize()
     {
         /**
-         * It is understood that This finializer is not guaranteed
+         * It is understood that This finalizer is not guaranteed
          * to be called and the release lock call is here just in
          * case there is some path that doesn't call onDisconnect
          * and or onConnectedInOrOut.
          */
-        if (mPartialWakeLock.isHeld()) {
-            Rlog.e(LOG_TAG, "[GsmCdmaConn] UNEXPECTED; mPartialWakeLock is held when finalizing.");
+        if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) {
+            Rlog.e(LOG_TAG, "UNEXPECTED; mPartialWakeLock is held when finalizing.");
         }
         clearPostDialListeners();
         releaseWakeLock();
@@ -935,33 +947,37 @@
         notifyPostDialListeners();
     }
 
-    private void
-    createWakeLock(Context context) {
+    private void createWakeLock(Context context) {
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
     }
 
-    private void
-    acquireWakeLock() {
-        log("acquireWakeLock");
-        mPartialWakeLock.acquire();
-    }
-
-    private void
-    releaseWakeLock() {
-        synchronized(mPartialWakeLock) {
-            if (mPartialWakeLock.isHeld()) {
-                log("releaseWakeLock");
-                mPartialWakeLock.release();
+    private void acquireWakeLock() {
+        if (mPartialWakeLock != null) {
+            synchronized (mPartialWakeLock) {
+                log("acquireWakeLock");
+                mPartialWakeLock.acquire();
             }
         }
     }
 
-    private void
-    releaseAllWakeLocks() {
-        synchronized(mPartialWakeLock) {
-            while (mPartialWakeLock.isHeld()) {
-                mPartialWakeLock.release();
+    private void releaseWakeLock() {
+        if (mPartialWakeLock != null) {
+            synchronized (mPartialWakeLock) {
+                if (mPartialWakeLock.isHeld()) {
+                    log("releaseWakeLock");
+                    mPartialWakeLock.release();
+                }
+            }
+        }
+    }
+
+    private void releaseAllWakeLocks() {
+        if (mPartialWakeLock != null) {
+            synchronized (mPartialWakeLock) {
+                while (mPartialWakeLock.isHeld()) {
+                    mPartialWakeLock.release();
+                }
             }
         }
     }
@@ -982,8 +998,7 @@
     // This function is to find the next PAUSE character index if
     // multiple pauses in a row. Otherwise it finds the next non PAUSE or
     // non WAIT character index.
-    private static int
-    findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
+    private static int findNextPCharOrNonPOrNonWCharIndex(String phoneNumber, int currIndex) {
         boolean wMatched = isWait(phoneNumber.charAt(currIndex));
         int index = currIndex + 1;
         int length = phoneNumber.length();
@@ -1010,12 +1025,12 @@
         return index;
     }
 
-    //CDMA
+    // CDMA
     // This function returns either PAUSE or WAIT character to append.
     // It is based on the next non PAUSE/WAIT character in the phoneNumber and the
     // index for the current PAUSE/WAIT character
-    private static char
-    findPOrWCharToAppend(String phoneNumber, int currPwIndex, int nextNonPwCharIndex) {
+    private static char findPOrWCharToAppend(String phoneNumber, int currPwIndex,
+                                             int nextNonPwCharIndex) {
         char c = phoneNumber.charAt(currPwIndex);
         char ret;
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 1b04339..7fce87c 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -16,7 +16,19 @@
 
 package com.android.internal.telephony;
 
-import android.app.ActivityManagerNative;
+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;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
+import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -33,42 +45,34 @@
 import android.os.PowerManager;
 import android.os.Registrant;
 import android.os.RegistrantList;
+import android.os.ResultReceiver;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.WorkSource;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Telephony;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
-
+import android.telephony.UssdResponse;
 import android.telephony.cdma.CdmaCellLocation;
 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;
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaMmiCode;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
-import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.gsm.GsmMmiCode;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -76,13 +80,13 @@
 import com.android.internal.telephony.uicc.IccException;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccVmNotSupportedException;
+import com.android.internal.telephony.uicc.IsimRecords;
+import com.android.internal.telephony.uicc.IsimUiccRecords;
 import com.android.internal.telephony.uicc.RuimRecords;
 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;
-import com.android.internal.telephony.uicc.IsimRecords;
-import com.android.internal.telephony.uicc.IsimUiccRecords;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -91,7 +95,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-
 /**
  * {@hide}
  */
@@ -129,6 +132,7 @@
     private String mMeid;
     // string to define how the carrier specifies its own ota sp number
     private String mCarrierOtaSpNumSchema;
+
     // A runnable which is used to automatically exit from Ecm after a period of time.
     private Runnable mExitEcmRunnable = new Runnable() {
         @Override
@@ -152,6 +156,7 @@
     public ServiceStateTracker mSST;
     private ArrayList <MmiCode> mPendingMMIs = new ArrayList<MmiCode>();
     private IccPhoneBookInterfaceManager mIccPhoneBookIntManager;
+    private DeviceStateMonitor mDeviceStateMonitor;
 
     private int mPrecisePhoneType;
 
@@ -182,8 +187,6 @@
 
     private int mRilVersion;
     private boolean mBroadcastEmergencyCallStateChanges = false;
-    // flag to indicate if emergency call end broadcast should be sent
-    boolean mSendEmergencyCallEnd = true;
 
     // Constructors
 
@@ -206,6 +209,7 @@
         // DcTracker uses SST so needs to be created after it is instantiated
         mDcTracker = mTelephonyComponentFactory.makeDcTracker(this);
         mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
+        mDeviceStateMonitor = mTelephonyComponentFactory.makeDeviceStateMonitor(this);
         logd("GsmCdmaPhone: constructor: sub = " + mPhoneId);
     }
 
@@ -276,7 +280,7 @@
             tm.setPhoneType(getPhoneId(), PhoneConstants.PHONE_TYPE_GSM);
             mIccCardProxy.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
         } else {
-            mCdmaSubscriptionSource = CdmaSubscriptionSourceManager.SUBSCRIPTION_SOURCE_UNKNOWN;
+            mCdmaSubscriptionSource = mCdmaSSM.getCdmaSubscriptionSource();
             // This is needed to handle phone process crashes
             mIsPhoneInEcmState = getInEcmMode();
             if (mIsPhoneInEcmState) {
@@ -382,7 +386,7 @@
     @Override
     protected void finalize() {
         if(DBG) logd("GsmCdmaPhone finalized");
-        if (mWakeLock.isHeld()) {
+        if (mWakeLock != null && mWakeLock.isHeld()) {
             Rlog.e(LOG_TAG, "UNEXPECTED; mWakeLock is held when finalizing.");
             mWakeLock.release();
         }
@@ -407,9 +411,9 @@
     }
 
     @Override
-    public CellLocation getCellLocation() {
+    public CellLocation getCellLocation(WorkSource workSource) {
         if (isPhoneTypeGsm()) {
-            return mSST.getCellLocation();
+            return mSST.getCellLocation(workSource);
         } else {
             CdmaCellLocation loc = (CdmaCellLocation)mSST.mCellLoc;
 
@@ -468,9 +472,8 @@
                 // get voice mail count from SIM
                 countVoiceMessages = r.getVoiceMessageCount();
             }
-            int countVoiceMessagesStored = getStoredVoiceMessageCount();
-            if (countVoiceMessages == -1 && countVoiceMessagesStored != 0) {
-                countVoiceMessages = countVoiceMessagesStored;
+            if (countVoiceMessages == IccRecords.DEFAULT_VOICE_MESSAGE_COUNT) {
+                countVoiceMessages = getStoredVoiceMessageCount();
             }
             logd("updateVoiceMail countVoiceMessages = " + countVoiceMessages
                     + " subId " + getSubId());
@@ -496,7 +499,7 @@
 
             ret = PhoneConstants.DataState.DISCONNECTED;
         } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE
-                && (isPhoneTypeCdma() ||
+                && (isPhoneTypeCdma() || isPhoneTypeCdmaLte() ||
                 (isPhoneTypeGsm() && !apnType.equals(PhoneConstants.APN_TYPE_EMERGENCY)))) {
             // If we're out of service, open TCP sockets may still work
             // but no data will flow
@@ -613,46 +616,24 @@
         }
     }
 
-    @Override
-    public boolean isInEcm() {
-        if (isPhoneTypeGsm()) {
-            return false;
-        } else {
-            return mIsPhoneInEcmState;
-        }
-    }
-
     //CDMA
     private void sendEmergencyCallbackModeChange(){
         //Send an Intent
         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
+        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
-        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
         if (DBG) logd("sendEmergencyCallbackModeChange");
     }
 
     @Override
     public void sendEmergencyCallStateChange(boolean callActive) {
         if (mBroadcastEmergencyCallStateChanges) {
-            if (callActive &&
-                    getServiceState().getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
-                // if emergency call is started while on iwlan, do not send the start or end
-                // broadcast
-                mSendEmergencyCallEnd = false;
-                if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange: not sending call start " +
-                        "intent as voice tech is IWLAN");
-            } else if (callActive || mSendEmergencyCallEnd) {
-                Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
-                intent.putExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, callActive);
-                SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
-                ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
-                if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange");
-            } else {
-                if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange: not sending call end " +
-                        "intent as start was not sent");
-                mSendEmergencyCallEnd = true;
-            }
+            Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED);
+            intent.putExtra(PhoneConstants.PHONE_IN_EMERGENCY_CALL, callActive);
+            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
+            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
+            if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallStateChange: callActive " + callActive);
         }
     }
 
@@ -1068,7 +1049,7 @@
             throw new CallStateException("Sending UUS information NOT supported in CDMA!");
         }
 
-        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
+        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString);
         Phone imsPhone = mImsPhone;
 
         CarrierConfigManager configManager =
@@ -1076,7 +1057,7 @@
         boolean alwaysTryImsForEmergencyCarrierConfig = configManager.getConfigForSubId(getSubId())
                 .getBoolean(CarrierConfigManager.KEY_CARRIER_USE_IMS_FIRST_FOR_EMERGENCY_BOOL);
 
-        boolean imsUseEnabled = isImsUseEnabled()
+        boolean useImsForCall = isImsUseEnabled()
                  && imsPhone != null
                  && (imsPhone.isVolteEnabled() || imsPhone.isWifiCallingEnabled() ||
                  (imsPhone.isVideoEnabled() && VideoProfile.isVideo(videoState)))
@@ -1086,7 +1067,7 @@
                 && isEmergency
                 && alwaysTryImsForEmergencyCarrierConfig
                 && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
-                && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
+                && imsPhone.isImsAvailable();
 
         String dialPart = PhoneNumberUtils.extractNetworkPortionAlt(PhoneNumberUtils.
                 stripSeparators(dialString));
@@ -1096,7 +1077,7 @@
         boolean useImsForUt = imsPhone != null && imsPhone.isUtEnabled();
 
         if (DBG) {
-            logd("imsUseEnabled=" + imsUseEnabled
+            logd("useImsForCall=" + useImsForCall
                     + ", useImsForEmergency=" + useImsForEmergency
                     + ", useImsForUt=" + useImsForUt
                     + ", isUt=" + isUt
@@ -1113,14 +1094,19 @@
 
         Phone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
 
-        if ((imsUseEnabled && (!isUt || useImsForUt)) || useImsForEmergency) {
+        if ((useImsForCall && !isUt) || (isUt && useImsForUt) || useImsForEmergency) {
             try {
                 if (DBG) logd("Trying IMS PS call");
                 return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
             } catch (CallStateException e) {
                 if (DBG) logd("IMS PS call exception " + e +
-                        "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
-                if (!Phone.CS_FALLBACK.equals(e.getMessage())) {
+                        "useImsForCall =" + useImsForCall + ", imsPhone =" + imsPhone);
+                // Do not throw a CallStateException and instead fall back to Circuit switch
+                // for emergency calls and MMI codes.
+                if (Phone.CS_FALLBACK.equals(e.getMessage()) || isEmergency) {
+                    logi("IMS call failed with Exception: " + e.getMessage() + ". Falling back "
+                            + "to CS.");
+                } else {
                     CallStateException ce = new CallStateException(e.getMessage());
                     ce.setStackTrace(e.getStackTrace());
                     throw ce;
@@ -1132,6 +1118,26 @@
                 && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
             throw new CallStateException("cannot dial in current state");
         }
+        // Check non-emergency voice CS call - shouldn't dial when POWER_OFF
+        if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_POWER_OFF /* CS POWER_OFF */
+                && !VideoProfile.isVideo(videoState) /* voice call */
+                && !isEmergency /* non-emergency call */) {
+            throw new CallStateException(
+                CallStateException.ERROR_POWER_OFF,
+                "cannot dial voice call in airplane mode");
+        }
+        // Check for service before placing non emergency CS voice call.
+        // Allow dial only if either CS is camped on any RAT (or) PS is in LTE service.
+        if (mSST != null
+                && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE /* CS out of service */
+                && !(mSST.mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
+                    && ServiceState.isLte(mSST.mSS.getRilDataRadioTechnology())) /* PS not in LTE */
+                && !VideoProfile.isVideo(videoState) /* voice call */
+                && !isEmergency /* non-emergency call */) {
+            throw new CallStateException(
+                CallStateException.ERROR_OUT_OF_SERVICE,
+                "cannot dial voice call in out of service");
+        }
         if (DBG) logd("Trying (non-IMS) CS call");
 
         if (isPhoneTypeGsm()) {
@@ -1141,10 +1147,52 @@
         }
     }
 
+    /**
+     * @return {@code true} if the user should be informed of an attempt to dial an international
+     * number while on WFC only, {@code false} otherwise.
+     */
+    public boolean isNotificationOfWfcCallRequired(String dialString) {
+        CarrierConfigManager configManager =
+                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle config = configManager.getConfigForSubId(getSubId());
+
+        // Determine if carrier config indicates that international calls over WFC should trigger a
+        // notification to the user. This is controlled by carrier configuration and is off by
+        // default.
+        boolean shouldNotifyInternationalCallOnWfc = config != null
+                && config.getBoolean(
+                        CarrierConfigManager.KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL);
+
+        if (!shouldNotifyInternationalCallOnWfc) {
+            return false;
+        }
+
+        Phone imsPhone = mImsPhone;
+        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(getSubId(), dialString);
+        boolean shouldConfirmCall =
+                        // Using IMS
+                        isImsUseEnabled()
+                        && imsPhone != null
+                        // VoLTE not available
+                        && !imsPhone.isVolteEnabled()
+                        // WFC is available
+                        && imsPhone.isWifiCallingEnabled()
+                        && !isEmergency
+                        // Dialing international number
+                        && PhoneNumberUtils.isInternationalNumber(dialString, getCountryIso());
+        return shouldConfirmCall;
+    }
+
     @Override
     protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
                                       Bundle intentExtras)
             throws CallStateException {
+        return dialInternal(dialString, uusInfo, videoState, intentExtras, null);
+    }
+
+    protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState,
+                                      Bundle intentExtras, ResultReceiver wrappedCallback)
+            throws CallStateException {
 
         // Need to make sure dialString gets parsed properly
         String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -1157,9 +1205,9 @@
 
             // Only look at the Network portion for mmi
             String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
-            GsmMmiCode mmi =
-                    GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());
-            if (DBG) logd("dialing w/ mmi '" + mmi + "'...");
+            GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this,
+                    mUiccApplication.get(), wrappedCallback);
+            if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
 
             if (mmi == null) {
                 return mCT.dial(newDialString, uusInfo, intentExtras);
@@ -1168,13 +1216,7 @@
             } else {
                 mPendingMMIs.add(mmi);
                 mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
-                try {
-                    mmi.processCode();
-                } catch (CallStateException e) {
-                    //do nothing
-                }
-
-                // FIXME should this return null or something else?
+                mmi.processCode();
                 return null;
             }
         } else {
@@ -1182,7 +1224,7 @@
         }
     }
 
-    @Override
+   @Override
     public boolean handlePinMmi(String dialString) {
         MmiCode mmi;
         if (isPhoneTypeGsm()) {
@@ -1206,6 +1248,52 @@
         return false;
     }
 
+    private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode,
+                                   ResultReceiver wrappedCallback) {
+        UssdResponse response = new UssdResponse(ussdRequest, message);
+        Bundle returnData = new Bundle();
+        returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
+        wrappedCallback.send(returnCode, returnData);
+    }
+
+    @Override
+    public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) {
+        if (!isPhoneTypeGsm() || mPendingMMIs.size() > 0) {
+            //todo: replace the generic failure with specific error code.
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback );
+            return true;
+        }
+
+        // Try over IMS if possible.
+        Phone imsPhone = mImsPhone;
+        if ((imsPhone != null)
+                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                || imsPhone.isUtEnabled())) {
+            try {
+                logd("handleUssdRequest: attempting over IMS");
+                return imsPhone.handleUssdRequest(ussdRequest, wrappedCallback);
+            } catch (CallStateException cse) {
+                if (!CS_FALLBACK.equals(cse.getMessage())) {
+                    return false;
+                }
+                // At this point we've tried over IMS but have been informed we need to handover
+                // back to GSM.
+                logd("handleUssdRequest: fallback to CS required");
+            }
+        }
+
+        // Try USSD over GSM.
+        try {
+            dialInternal(ussdRequest, null, VideoProfile.STATE_AUDIO_ONLY, null,
+                    wrappedCallback);
+        } catch (Exception e) {
+            logd("handleUssdRequest: exception" + e);
+            return false;
+        }
+        return true;
+    }
+
     @Override
     public void sendUssdResponse(String ussdMessge) {
         if (isPhoneTypeGsm()) {
@@ -1405,12 +1493,7 @@
 
     @Override
     public String getMeid() {
-        if (isPhoneTypeGsm()) {
-            loge("[GsmCdmaPhone] getMeid() is a CDMA method");
-            return "0";
-        } else {
-            return mMeid;
-        }
+        return mMeid;
     }
 
     @Override
@@ -1435,6 +1518,16 @@
     }
 
     @Override
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+        return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType);
+    }
+
+    @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+    }
+
+    @Override
     public String getGroupIdLevel1() {
         if (isPhoneTypeGsm()) {
             IccRecords r = mIccRecords.get();
@@ -1723,9 +1816,19 @@
     }
 
     @Override
-    public void getNeighboringCids(Message response) {
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+        mCi.startNetworkScan(nsr, response);
+    }
+
+    @Override
+    public void stopNetworkScan(Message response) {
+        mCi.stopNetworkScan(response);
+    }
+
+    @Override
+    public void getNeighboringCids(Message response, WorkSource workSource) {
         if (isPhoneTypeGsm()) {
-            mCi.getNeighboringCids(response);
+            mCi.getNeighboringCids(response, workSource);
         } else {
             /*
              * This is currently not implemented.  At least as of June
@@ -1745,6 +1848,15 @@
     }
 
     @Override
+    public void setTTYMode(int ttyMode, Message onComplete) {
+        // Send out the TTY Mode change over RIL as well
+        super.setTTYMode(ttyMode, onComplete);
+        if (mImsPhone != null) {
+            mImsPhone.setTTYMode(ttyMode, onComplete);
+        }
+    }
+
+    @Override
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
        if (mImsPhone != null) {
            mImsPhone.setUiTTYMode(uiTtyMode, onComplete);
@@ -1783,12 +1895,12 @@
 
     @Override
     public boolean getDataRoamingEnabled() {
-        return mDcTracker.getDataOnRoamingEnabled();
+        return mDcTracker.getDataRoamingEnabled();
     }
 
     @Override
     public void setDataRoamingEnabled(boolean enable) {
-        mDcTracker.setDataOnRoamingEnabled(enable);
+        mDcTracker.setDataRoamingEnabledByUser(enable);
     }
 
     @Override
@@ -1855,11 +1967,38 @@
          */
         if (mPendingMMIs.remove(mmi) || (isPhoneTypeGsm() && (mmi.isUssdRequest() ||
                 ((GsmMmiCode)mmi).isSsInfo()))) {
-            mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
+
+            ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
+            if (receiverCallback != null) {
+                Rlog.i(LOG_TAG, "onMMIDone: invoking callback: " + mmi);
+                int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
+                    TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
+                sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
+                        receiverCallback );
+            } else {
+                Rlog.i(LOG_TAG, "onMMIDone: notifying registrants: " + mmi);
+                mMmiCompleteRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
+            }
+        } else {
+            Rlog.i(LOG_TAG, "onMMIDone: invalid response or already handled; ignoring: " + mmi);
+        }
+    }
+
+    public boolean supports3gppCallForwardingWhileRoaming() {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle b = configManager.getConfig();
+        if (b != null) {
+            return b.getBoolean(
+                    CarrierConfigManager.KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+        } else {
+            // Default value set in CarrierConfigManager
+            return true;
         }
     }
 
     private void onNetworkInitiatedUssd(MmiCode mmi) {
+        Rlog.v(LOG_TAG, "onNetworkInitiatedUssd: mmi=" + mmi);
         mMmiCompleteRegistrants.notifyRegistrants(
             new AsyncResult(null, mmi, null));
     }
@@ -1904,19 +2043,18 @@
             } else {
                 found.onUssdFinished(ussdMessage, isUssdRequest);
             }
-        } else { // pending USSD not found
+        } else if (!isUssdError && ussdMessage != null) {
+            // pending USSD not found
             // The network may initiate its own USSD request
 
             // ignore everything that isnt a Notify or a Request
             // also, discard if there is no message to present
-            if (!isUssdError && ussdMessage != null) {
-                GsmMmiCode mmi;
-                mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage,
+            GsmMmiCode mmi;
+            mmi = GsmMmiCode.newNetworkInitiatedUssd(ussdMessage,
                                                    isUssdRequest,
                                                    GsmCdmaPhone.this,
                                                    mUiccApplication.get());
-                onNetworkInitiatedUssd(mmi);
-            }
+            onNetworkInitiatedUssd(mmi);
         }
     }
 
@@ -1926,6 +2064,7 @@
     private void syncClirSetting() {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         int clirSetting = sp.getInt(CLIR_KEY + getPhoneId(), -1);
+        Rlog.i(LOG_TAG, "syncClirSetting: " + CLIR_KEY + getPhoneId() + "=" + clirSetting);
         if (clirSetting >= 0) {
             mCi.setCLIR(clirSetting, null);
         }
@@ -1934,12 +2073,7 @@
     private void handleRadioAvailable() {
         mCi.getBasebandVersion(obtainMessage(EVENT_GET_BASEBAND_VERSION_DONE));
 
-        if (isPhoneTypeGsm()) {
-            mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
-            mCi.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
-        } else {
-            mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
-        }
+        mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
         mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
         startLceAfterRadioIsAvailable();
     }
@@ -1970,10 +2104,6 @@
                 }
             }
         }
-        Phone imsPhone = mImsPhone;
-        if (imsPhone != null) {
-            imsPhone.getServiceState().setStateOff();
-        }
         mRadioOffOrNotAvailableRegistrants.notifyRegistrants();
     }
 
@@ -2142,17 +2272,16 @@
                 break;
 
             case EVENT_SIM_RECORDS_LOADED:
-                if (isPhoneTypeGsm()) {
-                    updateCurrentCarrierInProvider();
+                updateCurrentCarrierInProvider();
 
-                    // Check if this is a different SIM than the previous one. If so unset the
-                    // voice mail number.
-                    String imsi = getVmSimImsi();
-                    String imsiFromSIM = getSubscriberId();
-                    if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) {
-                        storeVoiceMailNumber(null);
-                        setVmSimImsi(null);
-                    }
+                // Check if this is a different SIM than the previous one. If so unset the
+                // voice mail number.
+                String imsi = getVmSimImsi();
+                String imsiFromSIM = getSubscriberId();
+                if ((!isPhoneTypeGsm() || imsi != null) && imsiFromSIM != null
+                        && !imsiFromSIM.equals(imsi)) {
+                    storeVoiceMailNumber(null);
+                    setVmSimImsi(null);
                 }
 
                 mSimRecordsLoadedRegistrants.notifyRegistrants();
@@ -2572,7 +2701,7 @@
         if (isPhoneTypeGsm()) {
             return false;
         } else {
-            return mSST.getOtasp() != ServiceStateTracker.OTASP_NOT_NEEDED;
+            return mSST.getOtasp() != TelephonyManager.OTASP_NOT_NEEDED;
         }
     }
 
@@ -2582,26 +2711,35 @@
         return (r != null) ? r.isCspPlmnEnabled() : false;
     }
 
-    public boolean isManualNetSelAllowed() {
+    /**
+     * Whether manual select is now allowed and we should set
+     * to auto network select mode.
+     */
+    public boolean shouldForceAutoNetworkSelect() {
 
         int nwMode = Phone.PREFERRED_NT_MODE;
         int subId = getSubId();
 
+        // If it's invalid subId, we shouldn't force to auto network select mode.
+        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+            return false;
+        }
+
         nwMode = android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                     android.provider.Settings.Global.PREFERRED_NETWORK_MODE + subId, nwMode);
 
-        logd("isManualNetSelAllowed in mode = " + nwMode);
+        logd("shouldForceAutoNetworkSelect in mode = " + nwMode);
         /*
          *  For multimode targets in global mode manual network
-         *  selection is disallowed
+         *  selection is disallowed. So we should force auto select mode.
          */
         if (isManualSelProhibitedInGlobalMode()
                 && ((nwMode == Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
                         || (nwMode == Phone.NT_MODE_GLOBAL)) ){
-            logd("Manual selection not supported in mode = " + nwMode);
-            return false;
+            logd("Should force auto network select mode = " + nwMode);
+            return true;
         } else {
-            logd("Manual selection is supported in mode = " + nwMode);
+            logd("Should not force auto network select mode = " + nwMode);
         }
 
         /*
@@ -2611,7 +2749,7 @@
          *  Note: the actual enabling/disabling manual selection for these
          *  cases will be controlled by csp
          */
-        return true;
+        return false;
     }
 
     private boolean isManualSelProhibitedInGlobalMode() {
@@ -2665,6 +2803,10 @@
 
     @Override
     public void exitEmergencyCallbackMode() {
+        if (DBG) {
+            Rlog.d(LOG_TAG, "exitEmergencyCallbackMode: mImsPhone=" + mImsPhone
+                    + " isPhoneTypeGsm=" + isPhoneTypeGsm());
+        }
         if (isPhoneTypeGsm()) {
             if (mImsPhone != null) {
                 mImsPhone.exitEmergencyCallbackMode();
@@ -2681,13 +2823,13 @@
     //CDMA
     private void handleEnterEmergencyCallbackMode(Message msg) {
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
-                    + mIsPhoneInEcmState);
+            Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode, isInEcm()="
+                    + isInEcm());
         }
         // if phone is not in Ecm mode, and it's changed to Ecm mode
-        if (mIsPhoneInEcmState == false) {
-            setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true");
-            mIsPhoneInEcmState = true;
+        if (!isInEcm()) {
+            setIsInEcm(true);
+
             // notify change
             sendEmergencyCallbackModeChange();
 
@@ -2705,8 +2847,8 @@
     private void handleExitEmergencyCallbackMode(Message msg) {
         AsyncResult ar = (AsyncResult)msg.obj;
         if (DBG) {
-            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , mIsPhoneInEcmState "
-                    + ar.exception + mIsPhoneInEcmState);
+            Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode,ar.exception , isInEcm="
+                    + ar.exception + isInEcm());
         }
         // Remove pending exit Ecm runnable, if any
         removeCallbacks(mExitEcmRunnable);
@@ -2716,9 +2858,8 @@
         }
         // if exiting ecm success
         if (ar.exception == null) {
-            if (mIsPhoneInEcmState) {
-                setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false");
-                mIsPhoneInEcmState = false;
+            if (isInEcm()) {
+                setIsInEcm(false);
             }
 
             // release wakeLock
@@ -3089,7 +3230,7 @@
         Intent intent = new Intent(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
         intent.putExtra(PhoneConstants.PHONE_NAME_KEY, getPhoneName());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
-        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
     }
 
     private void switchVoiceRadioTech(int newVoiceRadioTech) {
@@ -3152,7 +3293,7 @@
         pw.println(" mCdmaSubscriptionSource=" + mCdmaSubscriptionSource);
         pw.println(" mEriManager=" + mEriManager);
         pw.println(" mWakeLock=" + mWakeLock);
-        pw.println(" mIsPhoneInEcmState=" + mIsPhoneInEcmState);
+        pw.println(" isInEcm()=" + isInEcm());
         if (VDBG) pw.println(" mEsn=" + mEsn);
         if (VDBG) pw.println(" mMeid=" + mMeid);
         pw.println(" mCarrierOtaSpNumSchema=" + mCarrierOtaSpNumSchema);
@@ -3173,6 +3314,9 @@
         }
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
+        pw.println("DeviceStateMonitor:");
+        mDeviceStateMonitor.dump(fd, pw, args);
+        pw.println("++++++++++++++++++++++++++++++++");
     }
 
     @Override
@@ -3242,6 +3386,19 @@
         return operatorNumeric;
     }
 
+    /**
+     * @return The country ISO for the subscription associated with this phone.
+     */
+    public String getCountryIso() {
+        int subId = getSubId();
+        SubscriptionInfo subInfo = SubscriptionManager.from(getContext())
+                .getActiveSubscriptionInfo(subId);
+        if (subInfo == null) {
+            return null;
+        }
+        return subInfo.getCountryIso().toUpperCase();
+    }
+
     public void notifyEcbmTimerReset(Boolean flag) {
         mEcmTimerResetRegistrants.notifyResult(flag);
     }
@@ -3288,6 +3445,10 @@
         Rlog.d(LOG_TAG, "[GsmCdmaPhone] " + s);
     }
 
+    private void logi(String s) {
+        Rlog.i(LOG_TAG, "[GsmCdmaPhone] " + s);
+    }
+
     private void loge(String s) {
         Rlog.e(LOG_TAG, "[GsmCdmaPhone] " + s);
     }
diff --git a/src/java/com/android/internal/telephony/HbpcdUtils.java b/src/java/com/android/internal/telephony/HbpcdUtils.java
old mode 100755
new mode 100644
index acd31a6..2f31942
--- a/src/java/com/android/internal/telephony/HbpcdUtils.java
+++ b/src/java/com/android/internal/telephony/HbpcdUtils.java
@@ -16,17 +16,16 @@
 
 package com.android.internal.telephony;
 
-import android.util.Log;
-import android.content.Context;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.database.Cursor;
+import android.telephony.Rlog;
 
-import com.android.internal.telephony.HbpcdLookup;
+import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
 import com.android.internal.telephony.HbpcdLookup.MccLookup;
 import com.android.internal.telephony.HbpcdLookup.MccSidConflicts;
 import com.android.internal.telephony.HbpcdLookup.MccSidRange;
-import com.android.internal.telephony.HbpcdLookup.ArbitraryMccSidMatch;
 
 public final class HbpcdUtils {
     private static final String LOG_TAG = "HbpcdUtils";
@@ -55,16 +54,16 @@
         if (c2 != null) {
             int c2Counter = c2.getCount();
             if (DBG) {
-                Log.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
+                Rlog.d(LOG_TAG, "Query unresolved arbitrary table, entries are " + c2Counter);
             }
             if (c2Counter == 1) {
                 if (DBG) {
-                    Log.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2 );
+                    Rlog.d(LOG_TAG, "Query Unresolved arbitrary returned the cursor " + c2);
                 }
                 c2.moveToFirst();
                 tmpMcc = c2.getInt(0);
                 if (DBG) {
-                    Log.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
+                    Rlog.d(LOG_TAG, "MCC found in arbitrary_mcc_sid_match: " + tmpMcc);
                 }
                 c2.close();
                 return tmpMcc;
@@ -86,22 +85,25 @@
             int c3Counter = c3.getCount();
             if (c3Counter > 0) {
                 if (c3Counter > 1) {
-                    Log.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
+                    Rlog.w(LOG_TAG, "something wrong, get more results for 1 conflict SID: " + c3);
                 }
-                if (DBG) Log.d(LOG_TAG, "Query conflict sid returned the cursor " + c3 );
+                if (DBG) Rlog.d(LOG_TAG, "Query conflict sid returned the cursor " + c3);
                 c3.moveToFirst();
                 tmpMcc = c3.getInt(0);
-                if (DBG) Log.d(LOG_TAG,
-                        "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
-                c3.close();
-                if (isNitzTimeZone) {
-                    return tmpMcc;
-                } else {
-                    // time zone is not accurate, it may get wrong mcc, ignore it.
-                    if (DBG) Log.d(LOG_TAG, "time zone is not accurate, mcc may be "
-                            + tmpMcc);
-                        return 0;
+                if (DBG) {
+                    Rlog.d(LOG_TAG, "MCC found in mcc_lookup_table. Return tmpMcc = " + tmpMcc);
                 }
+                if (!isNitzTimeZone) {
+                    // time zone is not accurate, it may get wrong mcc, ignore it.
+                    if (DBG) {
+                        Rlog.d(LOG_TAG, "time zone is not accurate, mcc may be " + tmpMcc);
+                    }
+                    tmpMcc = 0;
+                }
+                c3.close();
+                return tmpMcc;
+            } else {
+                c3.close();
             }
         }
 
@@ -113,18 +115,18 @@
                 null, null);
         if (c5 != null) {
             if (c5.getCount() > 0) {
-                if (DBG) Log.d(LOG_TAG, "Query Range returned the cursor " + c5 );
+                if (DBG) Rlog.d(LOG_TAG, "Query Range returned the cursor " + c5);
                 c5.moveToFirst();
                 tmpMcc = c5.getInt(0);
-                if (DBG) Log.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
+                if (DBG) Rlog.d(LOG_TAG, "SID found in mcc_sid_range. Return tmpMcc = " + tmpMcc);
                 c5.close();
                 return tmpMcc;
             }
             c5.close();
         }
-        if (DBG) Log.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
+        if (DBG) Rlog.d(LOG_TAG, "SID NOT found in mcc_sid_range.");
 
-        if (DBG) Log.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc =  " + tmpMcc );
+        if (DBG) Rlog.d(LOG_TAG, "Exit getMccByOtherFactors. Return tmpMcc =  " + tmpMcc);
         // If unknown MCC still could not be resolved,
         return tmpMcc;
     }
@@ -133,7 +135,7 @@
      *  Gets country information with given MCC.
     */
     public String getIddByMcc(int mcc) {
-        if (DBG) Log.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
+        if (DBG) Rlog.d(LOG_TAG, "Enter getHbpcdInfoByMCC.");
         String idd = "";
 
         Cursor c = null;
@@ -143,19 +145,19 @@
                 MccIdd.MCC + "=" + mcc, null, null);
         if (cur != null) {
             if (cur.getCount() > 0) {
-                if (DBG) Log.d(LOG_TAG, "Query Idd returned the cursor " + cur );
+                if (DBG) Rlog.d(LOG_TAG, "Query Idd returned the cursor " + cur);
                 // TODO: for those country having more than 1 IDDs, need more information
                 // to decide which IDD would be used. currently just use the first 1.
                 cur.moveToFirst();
                 idd = cur.getString(0);
-                if (DBG) Log.d(LOG_TAG, "IDD = " + idd);
+                if (DBG) Rlog.d(LOG_TAG, "IDD = " + idd);
 
             }
             cur.close();
         }
         if (c != null) c.close();
 
-        if (DBG) Log.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
+        if (DBG) Rlog.d(LOG_TAG, "Exit getHbpcdInfoByMCC.");
         return idd;
     }
 }
diff --git a/src/java/com/android/internal/telephony/IccProvider.java b/src/java/com/android/internal/telephony/IccProvider.java
old mode 100755
new mode 100644
index 222e1d6..8feec94
--- a/src/java/com/android/internal/telephony/IccProvider.java
+++ b/src/java/com/android/internal/telephony/IccProvider.java
@@ -301,7 +301,7 @@
             String param = tokens[n];
             if (DBG) log("parsing '" + param + "'");
 
-            String[] pair = param.split("=");
+            String[] pair = param.split("=", 2);
 
             if (pair.length != 2) {
                 Rlog.e(TAG, "resolve: bad whereClause parameter: " + param);
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 6b32498..0fc08c6 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -25,6 +25,8 @@
 import android.app.PendingIntent;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteException;
 import android.net.Uri;
@@ -32,9 +34,9 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
-import android.os.Process;
 import android.os.UserManager;
 import android.provider.Telephony;
+import android.service.carrier.CarrierMessagingService;
 import android.telephony.Rlog;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
@@ -446,8 +448,7 @@
             return;
         }
         if (!persistMessageForNonDefaultSmsApp) {
-            // Only allow carrier app or phone process to skip auto message persistence.
-            enforceCarrierOrPhonePrivilege();
+            enforcePrivilegedAppPermissions();
         }
         destAddr = filterDestAddress(destAddr);
         mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
@@ -465,7 +466,7 @@
      *  the same time an SMS received from radio is acknowledged back.
      */
     public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
-        enforceCarrierPrivilege();
+        enforcePrivilegedAppPermissions();
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             log("pdu: " + pdu +
                 "\n format=" + format +
@@ -507,8 +508,8 @@
                 Manifest.permission.SEND_SMS,
                 "Sending SMS message");
         if (!persistMessageForNonDefaultSmsApp) {
-            // Only allow carrier app to skip auto message persistence.
-            enforceCarrierPrivilege();
+            // Only allow carrier app or carrier ims to skip auto message persistence.
+            enforcePrivilegedAppPermissions();
         }
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             int i = 0;
@@ -603,9 +604,9 @@
     protected byte[] makeSmsRecordData(int status, byte[] pdu) {
         byte[] data;
         if (PhoneConstants.PHONE_TYPE_GSM == mPhone.getPhoneType()) {
-            data = new byte[IccConstants.SMS_RECORD_LENGTH];
+            data = new byte[SmsManager.SMS_RECORD_LENGTH];
         } else {
-            data = new byte[IccConstants.CDMA_SMS_RECORD_LENGTH];
+            data = new byte[SmsManager.CDMA_SMS_RECORD_LENGTH];
         }
 
         // Status bits for this record.  See TS 51.011 10.5.3
@@ -1115,11 +1116,36 @@
         }
     }
 
-    private void enforceCarrierOrPhonePrivilege() {
-        int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.PHONE_UID) {
-            enforceCarrierPrivilege();
+    /**
+     * Enforces that the caller has {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+     * permission or is one of the following apps:
+     * <ul>
+     *     <li> IMS App
+     *     <li> Carrier App
+     * </ul>
+     */
+    private void enforcePrivilegedAppPermissions() {
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+                == PackageManager.PERMISSION_GRANTED) {
+            return;
         }
+
+        int callingUid = Binder.getCallingUid();
+        String carrierImsPackage = CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+                new Intent(CarrierMessagingService.SERVICE_INTERFACE));
+        try {
+            if (carrierImsPackage != null
+                    && callingUid == mContext.getPackageManager().getPackageUid(
+                            carrierImsPackage, 0)) {
+              return;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            if (Rlog.isLoggable("SMS", Log.DEBUG)) {
+                log("Cannot find configured carrier ims package");
+            }
+        }
+
+        enforceCarrierPrivilege();
     }
 
     private String filterDestAddress(String destAddr) {
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
index 852d4e6..4d8f62c 100644
--- a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
@@ -65,6 +65,7 @@
         mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler);
         SmsBroadcastUndelivered.initialize(phone.getContext(),
             mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
 
         mCi.registerForOn(this, EVENT_RADIO_ON, null);
         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index ac3beb2..59195f8 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -20,7 +20,7 @@
 import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA;
 
 import android.app.Activity;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.app.Notification;
@@ -34,14 +34,12 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.net.Uri;
-import android.os.storage.StorageManager;
 import android.os.AsyncResult;
 import android.os.Binder;
 import android.os.Build;
@@ -53,13 +51,10 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.storage.StorageManager;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
 import android.service.carrier.CarrierMessagingService;
-import android.service.carrier.ICarrierMessagingCallback;
-import android.service.carrier.ICarrierMessagingService;
-import android.service.carrier.MessagePdu;
-import android.telephony.CarrierMessagingServiceManager;
 import android.telephony.Rlog;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
@@ -69,16 +64,16 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.uicc.UiccCard;
-import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
 import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This class broadcasts incoming SMS messages to interested apps after storing them in
@@ -113,9 +108,19 @@
     private static final String[] PDU_SEQUENCE_PORT_PROJECTION = {
             "pdu",
             "sequence",
-            "destination_port"
+            "destination_port",
+            "display_originating_addr"
     };
 
+    /** Mapping from DB COLUMN to PDU_SEQUENCE_PORT PROJECTION index */
+    private static final Map<Integer, Integer> PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING =
+            new HashMap<Integer, Integer>() {{
+                put(PDU_COLUMN, 0);
+                put(SEQUENCE_COLUMN, 1);
+                put(DESTINATION_PORT_COLUMN, 2);
+                put(DISPLAY_ADDRESS_COLUMN, 3);
+    }};
+
     public static final int PDU_COLUMN = 0;
     public static final int SEQUENCE_COLUMN = 1;
     public static final int DESTINATION_PORT_COLUMN = 2;
@@ -125,10 +130,9 @@
     public static final int ADDRESS_COLUMN = 6;
     public static final int ID_COLUMN = 7;
     public static final int MESSAGE_BODY_COLUMN = 8;
+    public static final int DISPLAY_ADDRESS_COLUMN = 9;
 
     public static final String SELECT_BY_ID = "_id=?";
-    public static final String SELECT_BY_REFERENCE = "address=? AND reference_number=? AND " +
-            "count=? AND deleted=0";
 
     /** New SMS received as an AsyncResult. */
     public static final int EVENT_NEW_SMS = 1;
@@ -142,7 +146,7 @@
     /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */
     private static final int EVENT_RETURN_TO_IDLE = 4;
 
-    /** Release wakelock after a short timeout when returning to idle state. */
+    /** Release wakelock after {@link mWakeLockTimeout} when returning to idle state. */
     private static final int EVENT_RELEASE_WAKELOCK = 5;
 
     /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */
@@ -209,6 +213,12 @@
     // Only mark deleted, but keep in db for message de-duping
     private final int MARK_DELETED = 2;
 
+    private static String ACTION_OPEN_SMS_APP =
+        "com.android.internal.telephony.OPEN_DEFAULT_SMS_APP";
+
+    /** Timeout for releasing wakelock */
+    private int mWakeLockTimeout;
+
     /**
      * Create a new SMS broadcast helper.
      * @param name the class name for logging
@@ -318,6 +328,14 @@
      */
     private class StartupState extends State {
         @Override
+        public void enter() {
+            if (DBG) log("entering Startup state");
+            // Set wakelock timeout to 0 during startup, this will ensure that the wakelock is not
+            // held if there are no pending messages to be handled.
+            setWakeLockTimeout(0);
+        }
+
+        @Override
         public boolean processMessage(Message msg) {
             log("StartupState.processMessage:" + msg.what);
             switch (msg.what) {
@@ -349,7 +367,7 @@
         @Override
         public void enter() {
             if (DBG) log("entering Idle state");
-            sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT);
+            sendMessageDelayed(EVENT_RELEASE_WAKELOCK, getWakeLockTimeout());
         }
 
         @Override
@@ -476,6 +494,14 @@
      */
     private class WaitingState extends State {
         @Override
+        public void exit() {
+            if (DBG) log("exiting Waiting state");
+            // Before moving to idle state, set wakelock timeout to WAKE_LOCK_TIMEOUT milliseconds
+            // to give any receivers time to take their own wake locks
+            setWakeLockTimeout(WAKELOCK_TIMEOUT);
+        }
+
+        @Override
         public boolean processMessage(Message msg) {
             log("WaitingState.processMessage:" + msg.what);
             switch (msg.what) {
@@ -635,6 +661,9 @@
             // broadcast SMS_REJECTED_ACTION intent
             Intent intent = new Intent(Intents.SMS_REJECTED_ACTION);
             intent.putExtra("result", result);
+            // Allow registered broadcast receivers to get this intent even
+            // when they are in the background.
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
             mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
         }
         acknowledgeLastIncomingSms(success, result, response);
@@ -670,7 +699,8 @@
 
             tracker = TelephonyComponentFactory.getInstance().makeInboundSmsTracker(sms.getPdu(),
                     sms.getTimestampMillis(), destPort, is3gpp2(), false,
-                    sms.getDisplayOriginatingAddress(), sms.getMessageBody());
+                    sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
+                    sms.getMessageBody());
         } else {
             // Create a tracker for this message segment.
             SmsHeader.ConcatRef concatRef = smsHeader.concatRef;
@@ -678,7 +708,7 @@
             int destPort = (portAddrs != null ? portAddrs.destPort : -1);
 
             tracker = TelephonyComponentFactory.getInstance().makeInboundSmsTracker(sms.getPdu(),
-                    sms.getTimestampMillis(), destPort, is3gpp2(),
+                    sms.getTimestampMillis(), destPort, is3gpp2(), sms.getOriginatingAddress(),
                     sms.getDisplayOriginatingAddress(), concatRef.refNumber, concatRef.seqNumber,
                     concatRef.msgCount, false, sms.getMessageBody());
         }
@@ -724,10 +754,12 @@
         int messageCount = tracker.getMessageCount();
         byte[][] pdus;
         int destPort = tracker.getDestPort();
+        boolean block = false;
 
         if (messageCount == 1) {
             // single-part message
             pdus = new byte[][]{tracker.getPdu()};
+            block = BlockChecker.isBlocked(mContext, tracker.getDisplayAddress());
         } else {
             // multi-part message
             Cursor cursor = null;
@@ -740,7 +772,7 @@
                 // query for all segments and broadcast message if we have all the parts
                 String[] whereArgs = {address, refNumber, count};
                 cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION,
-                        SELECT_BY_REFERENCE, whereArgs, null);
+                        tracker.getQueryForSegments(), whereArgs, null);
 
                 int cursorCount = cursor.getCount();
                 if (cursorCount < messageCount) {
@@ -756,20 +788,36 @@
                 pdus = new byte[messageCount][];
                 while (cursor.moveToNext()) {
                     // subtract offset to convert sequence to 0-based array index
-                    int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset();
+                    int index = cursor.getInt(PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING
+                            .get(SEQUENCE_COLUMN)) - tracker.getIndexOffset();
 
-                    pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN));
+                    pdus[index] = HexDump.hexStringToByteArray(cursor.getString(
+                            PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING.get(PDU_COLUMN)));
 
                     // Read the destination port from the first segment (needed for CDMA WAP PDU).
                     // It's not a bad idea to prefer the port from the first segment in other cases.
-                    if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) {
-                        int port = cursor.getInt(DESTINATION_PORT_COLUMN);
+                    if (index == 0 && !cursor.isNull(PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING
+                            .get(DESTINATION_PORT_COLUMN))) {
+                        int port = cursor.getInt(PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING
+                                .get(DESTINATION_PORT_COLUMN));
                         // strip format flags and convert to real port number, or -1
                         port = InboundSmsTracker.getRealDestPort(port);
                         if (port != -1) {
                             destPort = port;
                         }
                     }
+                    // check if display address should be blocked or not
+                    if (!block) {
+                        // Depending on the nature of the gateway, the display origination address
+                        // is either derived from the content of the SMS TP-OA field, or the TP-OA
+                        // field contains a generic gateway address and the from address is added
+                        // at the beginning in the message body. In that case only the first SMS
+                        // (part of Multi-SMS) comes with the display originating address which
+                        // could be used for block checking purpose.
+                        block = BlockChecker.isBlocked(mContext,
+                                cursor.getString(PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING
+                                        .get(DISPLAY_ADDRESS_COLUMN)));
+                    }
                 }
             } catch (SQLException e) {
                 loge("Can't access multipart SMS database", e);
@@ -823,7 +871,7 @@
             }
         }
 
-        if (BlockChecker.isBlocked(mContext, tracker.getAddress())) {
+        if (block) {
             deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs(),
                     DELETE_PERMANENTLY);
             return false;
@@ -877,8 +925,11 @@
             return;
         }
         log("Show new message notification.");
-        Intent intent = Intent.makeMainSelectorActivity(
-            Intent.ACTION_MAIN, Intent.CATEGORY_APP_MESSAGING);
+        PendingIntent intent = PendingIntent.getBroadcast(
+            mContext,
+            0,
+            new Intent(ACTION_OPEN_SMS_APP),
+            PendingIntent.FLAG_ONE_SHOT);
         Notification.Builder mBuilder = new Notification.Builder(mContext)
                 .setSmallIcon(com.android.internal.R.drawable.sym_action_chat)
                 .setAutoCancel(true)
@@ -886,7 +937,8 @@
                 .setDefaults(Notification.DEFAULT_ALL)
                 .setContentTitle(mContext.getString(R.string.new_sms_notification_title))
                 .setContentText(mContext.getString(R.string.new_sms_notification_content))
-                .setContentIntent(PendingIntent.getActivity(mContext, 1, intent, 0));
+                .setContentIntent(intent)
+                .setChannelId(NotificationChannelController.CHANNEL_ID_SMS);
         NotificationManager mNotificationManager =
             (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mNotificationManager.notify(
@@ -920,43 +972,15 @@
      */
     private boolean filterSms(byte[][] pdus, int destPort,
         InboundSmsTracker tracker, SmsBroadcastReceiver resultReceiver, boolean userUnlocked) {
-        List<String> carrierPackages = null;
-        UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId());
-        if (card != null) {
-            carrierPackages = card.getCarrierPackageNamesForIntent(
-                    mContext.getPackageManager(),
-                    new Intent(CarrierMessagingService.SERVICE_INTERFACE));
-        } else {
-            loge("UiccCard not initialized.");
-        }
-
-        if (carrierPackages != null && carrierPackages.size() == 1) {
-            log("Found carrier package.");
-            CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort,
-                    tracker.getFormat(), resultReceiver);
-            CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter,
-                    userUnlocked);
-            smsFilter.filterSms(carrierPackages.get(0), smsFilterCallback);
+        CarrierServicesSmsFilterCallback filterCallback =
+                new CarrierServicesSmsFilterCallback(
+                        pdus, destPort, tracker.getFormat(), resultReceiver, userUnlocked);
+        CarrierServicesSmsFilter carrierServicesFilter = new CarrierServicesSmsFilter(
+                mContext, mPhone, pdus, destPort, tracker.getFormat(), filterCallback, getName());
+        if (carrierServicesFilter.filter()) {
             return true;
         }
 
-        // It is possible that carrier app is not present as a CarrierPackage, but instead as a
-        // system app
-        List<String> systemPackages =
-                getSystemAppForIntent(new Intent(CarrierMessagingService.SERVICE_INTERFACE));
-
-        if (systemPackages != null && systemPackages.size() == 1) {
-            log("Found system package.");
-            CarrierSmsFilter smsFilter = new CarrierSmsFilter(pdus, destPort,
-                    tracker.getFormat(), resultReceiver);
-            CarrierSmsFilterCallback smsFilterCallback = new CarrierSmsFilterCallback(smsFilter,
-                    userUnlocked);
-            smsFilter.filterSms(systemPackages.get(0), smsFilterCallback);
-            return true;
-        }
-        logv("Unable to find carrier package: " + carrierPackages
-                + ", nor systemPackages: " + systemPackages);
-
         if (VisualVoicemailSmsFilter.filter(
                 mContext, pdus, tracker.getFormat(), destPort, mPhone.getSubId())) {
             log("Visual voicemail SMS dropped");
@@ -967,27 +991,6 @@
         return false;
     }
 
-    private List<String> getSystemAppForIntent(Intent intent) {
-        List<String> packages = new ArrayList<String>();
-        PackageManager packageManager = mContext.getPackageManager();
-        List<ResolveInfo> receivers = packageManager.queryIntentServices(intent, 0);
-        String carrierFilterSmsPerm = "android.permission.CARRIER_FILTER_SMS";
-
-        for (ResolveInfo info : receivers) {
-            if (info.serviceInfo == null) {
-                loge("Can't get service information from " + info);
-                continue;
-            }
-            String packageName = info.serviceInfo.packageName;
-                if (packageManager.checkPermission(carrierFilterSmsPerm, packageName) ==
-                        packageManager.PERMISSION_GRANTED) {
-                    packages.add(packageName);
-                    if (DBG) log("getSystemAppForIntent: added package "+ packageName);
-                }
-        }
-        return packages;
-    }
-
     /**
      * Dispatch the intent with the specified permission, appOp, and result receiver, using
      * this state machine's handler thread to run the result receiver.
@@ -1018,7 +1021,7 @@
             // Get a list of currently started users.
             int[] users = null;
             try {
-                users = ActivityManagerNative.getDefault().getRunningUserIds();
+                users = ActivityManager.getService().getRunningUserIds();
             } catch (RemoteException re) {
             }
             if (users == null) {
@@ -1087,7 +1090,8 @@
     }
 
     /**
-     * Creates and dispatches the intent to the default SMS app or the appropriate port.
+     * Creates and dispatches the intent to the default SMS app, appropriate port or via the {@link
+     * AppSmsManager}.
      *
      * @param pdus message pdus
      * @param format the message format, typically "3gpp" or "3gpp2"
@@ -1095,7 +1099,7 @@
      * @param resultReceiver the receiver handling the delivery result
      */
     private void dispatchSmsDeliveryIntent(byte[][] pdus, String format, int destPort,
-            BroadcastReceiver resultReceiver) {
+            SmsBroadcastReceiver resultReceiver) {
         Intent intent = new Intent();
         intent.putExtra("pdus", pdus);
         intent.putExtra("format", format);
@@ -1123,11 +1127,22 @@
                     intent.putExtra("uri", uri.toString());
                 }
             }
+
+            // Handle app specific sms messages.
+            AppSmsManager appManager = mPhone.getAppSmsManager();
+            if (appManager.handleSmsReceivedIntent(intent)) {
+                // The AppSmsManager handled this intent, we're done.
+                dropSms(resultReceiver);
+                return;
+            }
         } else {
             intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION);
             Uri uri = Uri.parse("sms://localhost:" + destPort);
             intent.setData(uri);
             intent.setComponent(null);
+            // Allow registered broadcast receivers to get this intent even
+            // when they are in the background.
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         }
 
         Bundle options = handleSmsWhitelisting(intent.getComponent());
@@ -1158,9 +1173,8 @@
         } else {
             // for multi-part messages, deduping should also be done against undeleted
             // segments that can cause ambiguity when contacenating the segments, that is,
-            // segments with same address, reference_number, count and sequence
-            where = "address=? AND reference_number=? AND count=? AND sequence=? AND " +
-                    "((date=? AND message_body=?) OR deleted=0)";
+            // segments with same address, reference_number, count, sequence and message type.
+            where = tracker.getQueryForMultiPartDuplicates();
         }
 
         Cursor cursor = null;
@@ -1237,7 +1251,7 @@
             } else {
                 // set the delete selection args for multi-part message
                 String[] deleteWhereArgs = {address, refNumber, count};
-                tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs);
+                tracker.setDeleteWhere(tracker.getQueryForSegments(), deleteWhereArgs);
             }
             return Intents.RESULT_SMS_HANDLED;
         } catch (Exception e) {
@@ -1276,15 +1290,23 @@
             if (action.equals(Intents.SMS_DELIVER_ACTION)) {
                 // Now dispatch the notification only intent
                 intent.setAction(Intents.SMS_RECEIVED_ACTION);
+                // Allow registered broadcast receivers to get this intent even
+                // when they are in the background.
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 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, options, 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);
+                // Allow registered broadcast receivers to get this intent even
+                // when they are in the background.
+                intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 // Only the primary user will receive notification of incoming mms.
                 // That app will do the actual downloading of the mms.
                 Bundle options = null;
@@ -1332,131 +1354,53 @@
     }
 
     /**
-     * Asynchronously binds to the carrier messaging service, and filters out the message if
-     * instructed to do so by the carrier messaging service. A new instance must be used for every
-     * message.
+     * Callback that handles filtering results by carrier services.
      */
-    private final class CarrierSmsFilter extends CarrierMessagingServiceManager {
+    private final class CarrierServicesSmsFilterCallback implements
+            CarrierServicesSmsFilter.CarrierServicesSmsFilterCallbackInterface {
         private final byte[][] mPdus;
         private final int mDestPort;
         private final String mSmsFormat;
         private final SmsBroadcastReceiver mSmsBroadcastReceiver;
-        // Instantiated in filterSms.
-        private volatile CarrierSmsFilterCallback mSmsFilterCallback;
+        private final boolean mUserUnlocked;
 
-        CarrierSmsFilter(byte[][] pdus, int destPort, String smsFormat,
-                SmsBroadcastReceiver smsBroadcastReceiver) {
+        CarrierServicesSmsFilterCallback(byte[][] pdus, int destPort, String smsFormat,
+                         SmsBroadcastReceiver smsBroadcastReceiver,  boolean userUnlocked) {
             mPdus = pdus;
             mDestPort = destPort;
             mSmsFormat = smsFormat;
             mSmsBroadcastReceiver = smsBroadcastReceiver;
-        }
-
-        /**
-         * Attempts to bind to a {@link ICarrierMessagingService}. Filtering is initiated
-         * asynchronously once the service is ready using {@link #onServiceReady}.
-         */
-        void filterSms(String carrierPackageName, CarrierSmsFilterCallback smsFilterCallback) {
-            mSmsFilterCallback = smsFilterCallback;
-            if (!bindToCarrierMessagingService(mContext, carrierPackageName)) {
-                loge("bindService() for carrier messaging service failed");
-                smsFilterCallback.onFilterComplete(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
-            } else {
-                logv("bindService() for carrier messaging service succeeded");
-            }
-        }
-
-        /**
-         * Invokes the {@code carrierMessagingService} to filter messages. The filtering result is
-         * delivered to {@code smsFilterCallback}.
-         */
-        @Override
-        protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
-            try {
-                carrierMessagingService.filterSms(
-                        new MessagePdu(Arrays.asList(mPdus)), mSmsFormat, mDestPort,
-                        mPhone.getSubId(), mSmsFilterCallback);
-            } catch (RemoteException e) {
-                loge("Exception filtering the SMS: " + e);
-                mSmsFilterCallback.onFilterComplete(
-                    CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT);
-            }
-        }
-    }
-
-    /**
-     * A callback used to notify the platform of the carrier messaging app filtering result. Once
-     * the result is ready, the carrier messaging service connection is disposed.
-     */
-    private final class CarrierSmsFilterCallback extends ICarrierMessagingCallback.Stub {
-        private final CarrierSmsFilter mSmsFilter;
-        private final boolean mUserUnlocked;
-
-        CarrierSmsFilterCallback(CarrierSmsFilter smsFilter, boolean userUnlocked) {
-            mSmsFilter = smsFilter;
             mUserUnlocked = userUnlocked;
         }
 
-        /**
-         * This method should be called only once.
-         */
         @Override
         public void onFilterComplete(int result) {
-            mSmsFilter.disposeConnection(mContext);
-            // Calling identity was the CarrierMessagingService in this callback, change it back to
-            // ours. This is required for dropSms() and VisualVoicemailSmsFilter.filter().
-            long token = Binder.clearCallingIdentity();
-            try {
-                logv("onFilterComplete: result is " + result);
-                if ((result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) == 0) {
-                    if (VisualVoicemailSmsFilter.filter(mContext, mSmsFilter.mPdus,
-                            mSmsFilter.mSmsFormat, mSmsFilter.mDestPort, mPhone.getSubId())) {
-                        log("Visual voicemail SMS dropped");
-                        dropSms(mSmsFilter.mSmsBroadcastReceiver);
-                        return;
-                    }
-
-                    if (mUserUnlocked) {
-                        dispatchSmsDeliveryIntent(mSmsFilter.mPdus, mSmsFilter.mSmsFormat,
-                                mSmsFilter.mDestPort, mSmsFilter.mSmsBroadcastReceiver);
-                    } else {
-                        // Don't do anything further, leave the message in the raw table if the
-                        // credential-encrypted storage is still locked and show the new message
-                        // notification if the message is visible to the user.
-                        if (!isSkipNotifyFlagSet(result)) {
-                            showNewMessageNotification();
-                        }
-                        sendMessage(EVENT_BROADCAST_COMPLETE);
-                    }
-                } else {
-                    // Drop this SMS.
-                    dropSms(mSmsFilter.mSmsBroadcastReceiver);
+            logv("onFilterComplete: result is " + result);
+            if ((result & CarrierMessagingService.RECEIVE_OPTIONS_DROP) == 0) {
+                if (VisualVoicemailSmsFilter.filter(mContext, mPdus,
+                        mSmsFormat, mDestPort, mPhone.getSubId())) {
+                    log("Visual voicemail SMS dropped");
+                    dropSms(mSmsBroadcastReceiver);
+                    return;
                 }
-            } finally {
-                // return back to the CarrierMessagingService, restore the calling identity.
-                Binder.restoreCallingIdentity(token);
+
+                if (mUserUnlocked) {
+                    dispatchSmsDeliveryIntent(
+                            mPdus, mSmsFormat, mDestPort, mSmsBroadcastReceiver);
+                } else {
+                    // Don't do anything further, leave the message in the raw table if the
+                    // credential-encrypted storage is still locked and show the new message
+                    // notification if the message is visible to the user.
+                    if (!isSkipNotifyFlagSet(result)) {
+                        showNewMessageNotification();
+                    }
+                    sendMessage(EVENT_BROADCAST_COMPLETE);
+                }
+            } else {
+                // Drop this SMS.
+                dropSms(mSmsBroadcastReceiver);
             }
         }
-
-        @Override
-        public void onSendSmsComplete(int result, int messageRef) {
-            loge("Unexpected onSendSmsComplete call with result: " + result);
-        }
-
-        @Override
-        public void onSendMultipartSmsComplete(int result, int[] messageRefs) {
-            loge("Unexpected onSendMultipartSmsComplete call with result: " + result);
-        }
-
-        @Override
-        public void onSendMmsComplete(int result, byte[] sendConfPdu) {
-            loge("Unexpected onSendMmsComplete call with result: " + result);
-        }
-
-        @Override
-        public void onDownloadMmsComplete(int result) {
-            loge("Unexpected onDownloadMmsComplete call with result: " + result);
-        }
     }
 
     private void dropSms(SmsBroadcastReceiver receiver) {
@@ -1594,6 +1538,37 @@
 
     @VisibleForTesting
     public int getWakeLockTimeout() {
-        return WAKELOCK_TIMEOUT;
+        return mWakeLockTimeout;
+    }
+
+    /**
+    * Sets the wakelock timeout to {@link timeOut} milliseconds
+    */
+    private void setWakeLockTimeout(int timeOut) {
+        mWakeLockTimeout = timeOut;
+    }
+
+    /**
+     * Handler for the broadcast sent when the new message notification is clicked. It launches the
+     * default SMS app.
+     */
+    private static class NewMessageNotificationActionReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_OPEN_SMS_APP.equals(intent.getAction())) {
+                context.startActivity(context.getPackageManager().getLaunchIntentForPackage(
+                    Telephony.Sms.getDefaultSmsPackage(context)));
+            }
+        }
+    }
+
+    /**
+     * Registers the broadcast receiver to launch the default SMS app when the user clicks the
+     * new message notification.
+     */
+    static void registerNewMessageNotificationActionHandler(Context context) {
+        IntentFilter userFilter = new IntentFilter();
+        userFilter.addAction(ACTION_OPEN_SMS_APP);
+        context.registerReceiver(new NewMessageNotificationActionReceiver(), userFilter);
     }
 }
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index 89ea681..c63ccc8 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -19,6 +19,7 @@
 import android.content.ContentValues;
 import android.database.Cursor;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 
 import java.util.Arrays;
@@ -38,10 +39,9 @@
     private final boolean mIs3gpp2;
     private final boolean mIs3gpp2WapPdu;
     private final String mMessageBody;
-    // Copied from SmsMessageBase#getDisplayOriginatingAddress used for blocking messages.
-    private final String mAddress;
 
     // Fields for concatenating multi-part SMS messages
+    private final String mAddress;
     private final int mReferenceNumber;
     private final int mSequenceNumber;
     private final int mMessageCount;
@@ -50,21 +50,57 @@
     private String mDeleteWhere;
     private String[] mDeleteWhereArgs;
 
+    /**
+     * Copied from SmsMessageBase#getDisplayOriginatingAddress used for blocking messages.
+     * DisplayAddress could be email address if this message was from an email gateway, otherwise
+     * same as mAddress. Email gateway might set a generic gateway address as the mAddress which
+     * could not be used for blocking check and append the display email address at the beginning
+     * of the message body. In that case, display email address is only available for the first SMS
+     * in the Multi-part SMS.
+     */
+    private final String mDisplayAddress;
+
+    @VisibleForTesting
     /** Destination port flag bit for no destination port. */
-    private static final int DEST_PORT_FLAG_NO_PORT = (1 << 16);
+    public static final int DEST_PORT_FLAG_NO_PORT = (1 << 16);
 
     /** Destination port flag bit to indicate 3GPP format message. */
     private static final int DEST_PORT_FLAG_3GPP = (1 << 17);
 
+    @VisibleForTesting
     /** Destination port flag bit to indicate 3GPP2 format message. */
-    private static final int DEST_PORT_FLAG_3GPP2 = (1 << 18);
+    public static final int DEST_PORT_FLAG_3GPP2 = (1 << 18);
 
+    @VisibleForTesting
     /** Destination port flag bit to indicate 3GPP2 format WAP message. */
-    private static final int DEST_PORT_FLAG_3GPP2_WAP_PDU = (1 << 19);
+    public static final int DEST_PORT_FLAG_3GPP2_WAP_PDU = (1 << 19);
 
     /** Destination port mask (16-bit unsigned value on GSM and CDMA). */
     private static final int DEST_PORT_MASK = 0xffff;
 
+    @VisibleForTesting
+    public static final String SELECT_BY_REFERENCE = "address=? AND reference_number=? AND "
+            + "count=? AND (destination_port & " + DEST_PORT_FLAG_3GPP2_WAP_PDU
+            + "=0) AND deleted=0";
+
+    @VisibleForTesting
+    public static final String SELECT_BY_REFERENCE_3GPP2WAP = "address=? AND reference_number=? "
+            + "AND count=? AND (destination_port & "
+            + DEST_PORT_FLAG_3GPP2_WAP_PDU + "=" + DEST_PORT_FLAG_3GPP2_WAP_PDU + ") AND deleted=0";
+
+    @VisibleForTesting
+    public static final String SELECT_BY_DUPLICATE_REFERENCE = "address=? AND "
+            + "reference_number=? AND count=? AND sequence=? AND "
+            + "((date=? AND message_body=?) OR deleted=0) AND (destination_port & "
+            + DEST_PORT_FLAG_3GPP2_WAP_PDU + "=0)";
+
+    @VisibleForTesting
+    public static final String SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP = "address=? AND "
+            + "reference_number=? " + "AND count=? AND sequence=? AND "
+            + "((date=? AND message_body=?) OR deleted=0) AND "
+            + "(destination_port & " + DEST_PORT_FLAG_3GPP2_WAP_PDU + "="
+            + DEST_PORT_FLAG_3GPP2_WAP_PDU + ")";
+
     /**
      * Create a tracker for a single-part SMS.
      *
@@ -73,10 +109,12 @@
      * @param destPort the destination port
      * @param is3gpp2 true for 3GPP2 format; false for 3GPP format
      * @param is3gpp2WapPdu true for 3GPP2 format WAP PDU; false otherwise
-     * @param address originating address, or email if this message was from an email gateway
+     * @param address originating address
+     * @param displayAddress email address if this message was from an email gateway, otherwise same
+     *                       as originating address
      */
     public InboundSmsTracker(byte[] pdu, long timestamp, int destPort, boolean is3gpp2,
-            boolean is3gpp2WapPdu, String address, String messageBody) {
+            boolean is3gpp2WapPdu, String address, String displayAddress, String messageBody) {
         mPdu = pdu;
         mTimestamp = timestamp;
         mDestPort = destPort;
@@ -84,6 +122,7 @@
         mIs3gpp2WapPdu = is3gpp2WapPdu;
         mMessageBody = messageBody;
         mAddress = address;
+        mDisplayAddress = displayAddress;
         // fields for multi-part SMS
         mReferenceNumber = -1;
         mSequenceNumber = getIndexOffset();     // 0 or 1, depending on type
@@ -102,22 +141,26 @@
      * @param destPort the destination port
      * @param is3gpp2 true for 3GPP2 format; false for 3GPP format
      * @param address originating address, or email if this message was from an email gateway
+     * @param displayAddress email address if this message was from an email gateway, otherwise same
+     *                       as originating address
      * @param referenceNumber the concatenated reference number
      * @param sequenceNumber the sequence number of this segment (0-based)
      * @param messageCount the total number of segments
      * @param is3gpp2WapPdu true for 3GPP2 format WAP PDU; false otherwise
      */
     public InboundSmsTracker(byte[] pdu, long timestamp, int destPort, boolean is3gpp2,
-            String address, int referenceNumber, int sequenceNumber, int messageCount,
-            boolean is3gpp2WapPdu, String messageBody) {
+            String address, String displayAddress, int referenceNumber, int sequenceNumber,
+            int messageCount, boolean is3gpp2WapPdu, String messageBody) {
         mPdu = pdu;
         mTimestamp = timestamp;
         mDestPort = destPort;
         mIs3gpp2 = is3gpp2;
         mIs3gpp2WapPdu = is3gpp2WapPdu;
         mMessageBody = messageBody;
-        mAddress = address;
+        // fields used for check blocking message
+        mDisplayAddress = displayAddress;
         // fields for multi-part SMS
+        mAddress = address;
         mReferenceNumber = referenceNumber;
         mSequenceNumber = sequenceNumber;
         mMessageCount = messageCount;
@@ -150,6 +193,7 @@
 
         mTimestamp = cursor.getLong(InboundSmsHandler.DATE_COLUMN);
         mAddress = cursor.getString(InboundSmsHandler.ADDRESS_COLUMN);
+        mDisplayAddress = cursor.getString(InboundSmsHandler.DISPLAY_ADDRESS_COLUMN);
 
         if (cursor.isNull(InboundSmsHandler.COUNT_COLUMN)) {
             // single-part message
@@ -173,7 +217,7 @@
                         + " of " + mMessageCount);
             }
 
-            mDeleteWhere = InboundSmsHandler.SELECT_BY_REFERENCE;
+            mDeleteWhere = getQueryForSegments();
             mDeleteWhereArgs = new String[]{mAddress,
                     Integer.toString(mReferenceNumber), Integer.toString(mMessageCount)};
         }
@@ -203,6 +247,7 @@
         values.put("destination_port", destPort);
         if (mAddress != null) {
             values.put("address", mAddress);
+            values.put("display_originating_addr", mDisplayAddress);
             values.put("reference_number", mReferenceNumber);
             values.put("sequence", mSequenceNumber);
             values.put("count", mMessageCount);
@@ -241,6 +286,7 @@
         builder.append(" is3gpp2=").append(mIs3gpp2);
         if (mAddress != null) {
             builder.append(" address=").append(mAddress);
+            builder.append(" display_originating_addr=").append(mDisplayAddress);
             builder.append(" refNumber=").append(mReferenceNumber);
             builder.append(" seqNumber=").append(mSequenceNumber);
             builder.append(" msgCount=").append(mMessageCount);
@@ -274,6 +320,15 @@
         return mIs3gpp2 ? SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
     }
 
+    public String getQueryForSegments() {
+        return mIs3gpp2WapPdu ? SELECT_BY_REFERENCE_3GPP2WAP : SELECT_BY_REFERENCE;
+    }
+
+    public String getQueryForMultiPartDuplicates() {
+        return mIs3gpp2WapPdu ? SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP :
+                SELECT_BY_DUPLICATE_REFERENCE;
+    }
+
     /**
      * Sequence numbers for concatenated messages start at 1. The exception is CDMA WAP PDU
      * messages, which use a 0-based index.
@@ -287,6 +342,10 @@
         return mAddress;
     }
 
+    public String getDisplayAddress() {
+        return mDisplayAddress;
+    }
+
     public String getMessageBody() {
         return mMessageBody;
     }
diff --git a/src/java/com/android/internal/telephony/IntentBroadcaster.java b/src/java/com/android/internal/telephony/IntentBroadcaster.java
new file mode 100644
index 0000000..7528819
--- /dev/null
+++ b/src/java/com/android/internal/telephony/IntentBroadcaster.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This class is used to broadcast intents that need to be rebroadcast after the device is unlocked.
+ * NOTE: Currently this is used only for SIM_STATE_CHANGED so logic is hardcoded for that;
+ * for example broadcasts are always sticky, only the last intent for the slotId is rebroadcast,
+ * etc.
+ */
+public class IntentBroadcaster {
+    private static final String TAG = "IntentBroadcaster";
+
+    private Map<Integer, Intent> mRebroadcastIntents = new HashMap<>();
+    private static IntentBroadcaster sIntentBroadcaster;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
+                synchronized (mRebroadcastIntents) {
+                    // rebroadcast intents
+                    Iterator iterator = mRebroadcastIntents.entrySet().iterator();
+                    while (iterator.hasNext()) {
+                        Map.Entry pair = (Map.Entry) iterator.next();
+                        Intent i = (Intent) pair.getValue();
+                        i.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+                        iterator.remove();
+                        logd("Rebroadcasting intent " + i.getAction() + " "
+                                + i.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
+                                + " for slotId " + pair.getKey());
+                        ActivityManager.broadcastStickyIntent(i, UserHandle.USER_ALL);
+                    }
+                }
+            }
+        }
+    };
+
+    private IntentBroadcaster(Context context) {
+        context.registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED));
+    }
+
+    /**
+     * Method to get an instance of IntentBroadcaster after creating one if needed.
+     * @return IntentBroadcaster instance
+     */
+    public static IntentBroadcaster getInstance(Context context) {
+        if (sIntentBroadcaster == null) {
+            sIntentBroadcaster = new IntentBroadcaster(context);
+        }
+        return sIntentBroadcaster;
+    }
+
+    public static IntentBroadcaster getInstance() {
+        return sIntentBroadcaster;
+    }
+
+    /**
+     * Wrapper for ActivityManager.broadcastStickyIntent() that also stores intent to be rebroadcast
+     * on USER_UNLOCKED
+     */
+    public void broadcastStickyIntent(Intent intent, int slotId) {
+        logd("Broadcasting and adding intent for rebroadcast: " + intent.getAction() + " "
+                + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)
+                + " for slotId " + slotId);
+        synchronized (mRebroadcastIntents) {
+            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
+            mRebroadcastIntents.put(slotId, intent);
+        }
+    }
+
+    private void logd(String s) {
+        Log.d(TAG, s);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 0ee153c..5714b29 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -28,6 +28,12 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.app.LocaleStore;
+import com.android.internal.app.LocaleStore.LocaleInfo;
+
+import libcore.icu.ICU;
+import libcore.icu.TimeZoneNames;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -36,9 +42,6 @@
 import java.util.Locale;
 import java.util.Map;
 
-import libcore.icu.ICU;
-import libcore.icu.TimeZoneNames;
-
 /**
  * Mobile Country Code
  *
@@ -94,7 +97,21 @@
         Locale locale = new Locale("", entry.mIso);
         String[] tz = TimeZoneNames.forLocale(locale);
         if (tz.length == 0) return null;
-        return tz[0];
+
+        String zoneName = tz[0];
+
+        /* Use Australia/Sydney instead of Australia/Lord_Howe for Australia.
+         * http://b/33228250
+         * Todo: remove the code, see b/62418027
+         */
+        if (mcc == 505  /* Australia / Norfolk Island */) {
+            for (String zone : tz) {
+                if (zone.contains("Sydney")) {
+                    zoneName = zone;
+                }
+            }
+        }
+        return zoneName;
     }
 
     /**
@@ -211,7 +228,7 @@
 
                     if (updateConfig) {
                         Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
-                        ActivityManagerNative.getDefault().updateConfiguration(config);
+                        ActivityManager.getService().updateConfiguration(config);
                     } else {
                         Slog.d(LOG_TAG, "updateMccMncConfiguration nothing to update");
                     }
@@ -235,21 +252,21 @@
     static {
         // If we have English (without a country) explicitly prioritize en_US. http://b/28998094
         FALLBACKS.put(Locale.ENGLISH, Locale.US);
-        FALLBACKS.put(Locale.CANADA, Locale.US);
     }
 
     /**
-     * Find the best match we actually have a localization for. This function assumes we
-     * couldn't find an exact match.
+     * Finds a suitable locale among {@code candidates} to use as the fallback locale for
+     * {@code target}. This looks through the list of {@link #FALLBACKS}, and follows the chain
+     * until a locale in {@code candidates} is found.
+     * This function assumes that {@code target} is not in {@code candidates}.
      *
      * 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.
+     *
+     * @return The fallback locale or {@code null} if there is no suitable fallback defined in the
+     *         lookup.
      */
-    private static Locale chooseBestFallback(Locale target, List<Locale> candidates) {
-        if (candidates.isEmpty()) {
-            return null;
-        }
-
+    private static Locale lookupFallback(Locale target, List<Locale> candidates) {
         Locale fallback = target;
         while ((fallback = FALLBACKS.get(fallback)) != null) {
             if (candidates.contains(fallback)) {
@@ -257,11 +274,7 @@
             }
         }
 
-        // 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);
+        return null;
     }
 
     /**
@@ -314,14 +327,40 @@
                 }
             }
 
-            Locale bestMatch = chooseBestFallback(target, languageMatches);
+            if (languageMatches.isEmpty()) {
+                Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " + language);
+                return null;
+            }
+
+            Locale bestMatch = lookupFallback(target, languageMatches);
             if (bestMatch != null) {
-                Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a language-only match: " +
-                       bestMatch.toLanguageTag());
+                Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a fallback match: "
+                        + bestMatch.toLanguageTag());
                 return bestMatch;
             } else {
-                Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " +
-                       language);
+                // Ask {@link LocaleStore} whether this locale is considered "translated".
+                // LocaleStore has a broader definition of translated than just the asset locales
+                // above: a locale is "translated" if it has translation assets, or another locale
+                // with the same language and script has translation assets.
+                // If a locale is "translated", it is selectable in setup wizard, and can therefore
+                // be considerd a valid result for this method.
+                if (!TextUtils.isEmpty(target.getCountry())) {
+                    LocaleStore.fillCache(context);
+                    LocaleInfo targetInfo = LocaleStore.getLocaleInfo(target);
+                    if (targetInfo.isTranslated()) {
+                        Slog.d(LOG_TAG, "getLocaleForLanguageCountry: "
+                                + "target locale is translated: " + target);
+                        return target;
+                    }
+                }
+
+                // 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.
+                Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got language-only match: "
+                        + language);
+                return languageMatches.get(0);
             }
         } catch (Exception e) {
             Slog.d(LOG_TAG, "getLocaleForLanguageCountry: exception", e);
@@ -337,14 +376,21 @@
      */
     private static void setTimezoneFromMccIfNeeded(Context context, int mcc) {
         String timezone = SystemProperties.get(ServiceStateTracker.TIMEZONE_PROPERTY);
-        if (timezone == null || timezone.length() == 0) {
+        // timezone.equals("GMT") will be true and only true if the timezone was
+        // set to a default value by the system server (when starting, system server.
+        // sets the persist.sys.timezone to "GMT" if it's not set)."GMT" is not used by
+        // any code that sets it explicitly (in case where something sets GMT explicitly,
+        // "Etc/GMT" Olsen ID would be used).
+        // TODO(b/64056758): Remove "timezone.equals("GMT")" hack when there's a
+        // better way of telling if the value has been defaulted.
+        if (timezone == null || timezone.length() == 0 || timezone.equals("GMT")) {
             String zoneId = defaultTimeZoneForMcc(mcc);
             if (zoneId != null && zoneId.length() > 0) {
                 // Set time zone based on MCC
                 AlarmManager alarm =
                         (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                 alarm.setTimeZone(zoneId);
-                Slog.d(LOG_TAG, "timezone set to "+zoneId);
+                Slog.d(LOG_TAG, "timezone set to " + zoneId);
             }
         }
     }
@@ -359,7 +405,8 @@
      * @return locale for the mcc or null if none
      */
     public static Locale getLocaleFromMcc(Context context, int mcc, String simLanguage) {
-        String language = (simLanguage == null) ? MccTable.defaultLanguageForMcc(mcc) : simLanguage;
+        boolean hasSimLanguage = !TextUtils.isEmpty(simLanguage);
+        String language = hasSimLanguage ? simLanguage : MccTable.defaultLanguageForMcc(mcc);
         String country = MccTable.countryCodeForMcc(mcc);
 
         Slog.d(LOG_TAG, "getLocaleFromMcc(" + language + ", " + country + ", " + mcc);
@@ -367,10 +414,10 @@
 
         // 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) {
+        if (locale == null && hasSimLanguage) {
             language = MccTable.defaultLanguageForMcc(mcc);
             Slog.d(LOG_TAG, "[retry ] getLocaleFromMcc(" + language + ", " + country + ", " + mcc);
-            return getLocaleForLanguageCountry(context, null, country);
+            return getLocaleForLanguageCountry(context, language, country);
         }
 
         return locale;
@@ -423,7 +470,7 @@
 		sTable.add(new MccEntry(225,"va",2));	//Vatican City State
 		sTable.add(new MccEntry(226,"ro",2));	//Romania
 		sTable.add(new MccEntry(228,"ch",2));	//Switzerland (Confederation of)
-		sTable.add(new MccEntry(230,"cz",2));	//Czech Republic
+		sTable.add(new MccEntry(230,"cz",2));	//Czechia
 		sTable.add(new MccEntry(231,"sk",2));	//Slovak Republic
 		sTable.add(new MccEntry(232,"at",2));	//Austria
 		sTable.add(new MccEntry(234,"gb",2));	//United Kingdom of Great Britain and Northern Ireland
diff --git a/src/java/com/android/internal/telephony/MmiCode.java b/src/java/com/android/internal/telephony/MmiCode.java
index ae55e15..0adf83f 100644
--- a/src/java/com/android/internal/telephony/MmiCode.java
+++ b/src/java/com/android/internal/telephony/MmiCode.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.os.ResultReceiver;
+
 /**
  * {@hide}
  */
@@ -75,4 +77,13 @@
      */
     void processCode() throws CallStateException;
 
+    /**
+     * @return the Receiver for the Ussd Callback.
+     */
+    public ResultReceiver getUssdCallbackReceiver();
+
+    /**
+     * @return the dialString.
+     */
+    public String getDialString();
 }
diff --git a/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
new file mode 100644
index 0000000..14c6810
--- /dev/null
+++ b/src/java/com/android/internal/telephony/NetworkScanRequestTracker.java
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.EUTRAN;
+import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.GERAN;
+import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.UTRAN;
+
+import android.hardware.radio.V1_0.RadioError;
+import android.os.AsyncResult;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.telephony.CellInfo;
+import android.telephony.NetworkScan;
+import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.TelephonyScanManager;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Manages radio access network scan requests.
+ *
+ * Provides methods to start and stop network scan requests, and keeps track of all the live scans.
+ *
+ * {@hide}
+ */
+public final class NetworkScanRequestTracker {
+
+    private static final String TAG = "ScanRequestTracker";
+
+    private static final int CMD_START_NETWORK_SCAN = 1;
+    private static final int EVENT_START_NETWORK_SCAN_DONE = 2;
+    private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3;
+    private static final int CMD_STOP_NETWORK_SCAN = 4;
+    private static final int EVENT_STOP_NETWORK_SCAN_DONE = 5;
+    private static final int CMD_INTERRUPT_NETWORK_SCAN = 6;
+    private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_START_NETWORK_SCAN:
+                    mScheduler.doStartScan((NetworkScanRequestInfo) msg.obj);
+                    break;
+
+                case EVENT_START_NETWORK_SCAN_DONE:
+                    mScheduler.startScanDone((AsyncResult) msg.obj);
+                    break;
+
+                case EVENT_RECEIVE_NETWORK_SCAN_RESULT:
+                    mScheduler.receiveResult((AsyncResult) msg.obj);
+                    break;
+
+                case CMD_STOP_NETWORK_SCAN:
+                    mScheduler.doStopScan(msg.arg1);
+                    break;
+
+                case EVENT_STOP_NETWORK_SCAN_DONE:
+                    mScheduler.stopScanDone((AsyncResult) msg.obj);
+                    break;
+
+                case CMD_INTERRUPT_NETWORK_SCAN:
+                    mScheduler.doInterruptScan(msg.arg1);
+                    break;
+
+                case EVENT_INTERRUPT_NETWORK_SCAN_DONE:
+                    mScheduler.interruptScanDone((AsyncResult) msg.obj);
+                    break;
+            }
+        }
+    };
+
+    // The sequence number of NetworkScanRequests
+    private final AtomicInteger mNextNetworkScanRequestId = new AtomicInteger(1);
+    private final NetworkScanRequestScheduler mScheduler = new NetworkScanRequestScheduler();
+
+    private void logEmptyResultOrException(AsyncResult ar) {
+        if (ar.result == null) {
+            Log.e(TAG, "NetworkScanResult: Empty result");
+        } else {
+            Log.e(TAG, "NetworkScanResult: Exception: " + ar.exception);
+        }
+    }
+
+    private boolean isValidScan(NetworkScanRequestInfo nsri) {
+        if (nsri.mRequest.specifiers == null) {
+            return false;
+        }
+        if (nsri.mRequest.specifiers.length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) {
+            return false;
+        }
+        for (RadioAccessSpecifier ras : nsri.mRequest.specifiers) {
+            if (ras.radioAccessNetwork != GERAN && ras.radioAccessNetwork != UTRAN
+                    && ras.radioAccessNetwork != EUTRAN) {
+                return false;
+            }
+            if (ras.bands != null && ras.bands.length > NetworkScanRequest.MAX_BANDS) {
+                return false;
+            }
+            if (ras.channels != null && ras.channels.length > NetworkScanRequest.MAX_CHANNELS) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Sends a message back to the application via its callback. */
+    private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err,
+            List<CellInfo> result) {
+        Messenger messenger = nsri.mMessenger;
+        Message message = Message.obtain();
+        message.what = what;
+        message.arg1 = err;
+        message.arg2 = nsri.mScanId;
+        if (result != null) {
+            CellInfo[] ci = result.toArray(new CellInfo[result.size()]);
+            Bundle b = new Bundle();
+            b.putParcelableArray(TelephonyScanManager.SCAN_RESULT_KEY, ci);
+            message.setData(b);
+        } else {
+            message.obj = null;
+        }
+        try {
+            messenger.send(message);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in notifyMessenger: " + e);
+        }
+    }
+
+    /**
+    * Tracks info about the radio network scan.
+     *
+    * Also used to notice when the calling process dies so we can self-expire.
+    */
+    class NetworkScanRequestInfo implements IBinder.DeathRecipient {
+        private final NetworkScanRequest mRequest;
+        private final Messenger mMessenger;
+        private final IBinder mBinder;
+        private final Phone mPhone;
+        private final int mScanId;
+        private final int mUid;
+        private final int mPid;
+        private boolean mIsBinderDead;
+
+        NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone) {
+            super();
+            mRequest = r;
+            mMessenger = m;
+            mBinder = b;
+            mScanId = id;
+            mPhone = phone;
+            mUid = Binder.getCallingUid();
+            mPid = Binder.getCallingPid();
+            mIsBinderDead = false;
+
+            try {
+                mBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                binderDied();
+            }
+        }
+
+        synchronized void setIsBinderDead(boolean val) {
+            mIsBinderDead = val;
+        }
+
+        synchronized boolean getIsBinderDead() {
+            return mIsBinderDead;
+        }
+
+        NetworkScanRequest getRequest() {
+            return mRequest;
+        }
+
+        void unlinkDeathRecipient() {
+            if (mBinder != null) {
+                mBinder.unlinkToDeath(this, 0);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied("
+                    + mRequest + ", " + mBinder + ")");
+            setIsBinderDead(true);
+            interruptNetworkScan(mScanId);
+        }
+    }
+
+    /**
+     * Handles multiplexing and scheduling for multiple requests.
+     */
+    private class NetworkScanRequestScheduler {
+
+        private NetworkScanRequestInfo mLiveRequestInfo;
+        private NetworkScanRequestInfo mPendingRequestInfo;
+
+        private int rilErrorToScanError(int rilError) {
+            switch (rilError) {
+                case RadioError.NONE:
+                    return NetworkScan.SUCCESS;
+                case RadioError.RADIO_NOT_AVAILABLE:
+                    Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.REQUEST_NOT_SUPPORTED:
+                    Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED");
+                    return NetworkScan.ERROR_UNSUPPORTED;
+                case RadioError.NO_MEMORY:
+                    Log.e(TAG, "rilErrorToScanError: NO_MEMORY");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.INTERNAL_ERR:
+                    Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.MODEM_ERR:
+                    Log.e(TAG, "rilErrorToScanError: MODEM_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.OPERATION_NOT_ALLOWED:
+                    Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case RadioError.INVALID_ARGUMENTS:
+                    Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS");
+                    return NetworkScan.ERROR_INVALID_SCAN;
+                case RadioError.DEVICE_IN_USE:
+                    Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE");
+                    return NetworkScan.ERROR_MODEM_BUSY;
+                default:
+                    Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " +  rilError);
+                    return NetworkScan.ERROR_RIL_ERROR;
+            }
+        }
+
+        private int commandExceptionErrorToScanError(CommandException.Error error) {
+            switch (error) {
+                case RADIO_NOT_AVAILABLE:
+                    Log.e(TAG, "commandExceptionErrorToScanError: RADIO_NOT_AVAILABLE");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case REQUEST_NOT_SUPPORTED:
+                    Log.e(TAG, "commandExceptionErrorToScanError: REQUEST_NOT_SUPPORTED");
+                    return NetworkScan.ERROR_UNSUPPORTED;
+                case NO_MEMORY:
+                    Log.e(TAG, "commandExceptionErrorToScanError: NO_MEMORY");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case INTERNAL_ERR:
+                    Log.e(TAG, "commandExceptionErrorToScanError: INTERNAL_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case MODEM_ERR:
+                    Log.e(TAG, "commandExceptionErrorToScanError: MODEM_ERR");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case OPERATION_NOT_ALLOWED:
+                    Log.e(TAG, "commandExceptionErrorToScanError: OPERATION_NOT_ALLOWED");
+                    return NetworkScan.ERROR_MODEM_ERROR;
+                case INVALID_ARGUMENTS:
+                    Log.e(TAG, "commandExceptionErrorToScanError: INVALID_ARGUMENTS");
+                    return NetworkScan.ERROR_INVALID_SCAN;
+                case DEVICE_IN_USE:
+                    Log.e(TAG, "commandExceptionErrorToScanError: DEVICE_IN_USE");
+                    return NetworkScan.ERROR_MODEM_BUSY;
+                default:
+                    Log.e(TAG, "commandExceptionErrorToScanError: Unexpected CommandExceptionError "
+                            +  error);
+                    return NetworkScan.ERROR_RIL_ERROR;
+            }
+        }
+
+        private void doStartScan(NetworkScanRequestInfo nsri) {
+            if (nsri == null) {
+                Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null");
+                return;
+            }
+            if (!isValidScan(nsri)) {
+                notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                        NetworkScan.ERROR_INVALID_SCAN, null);
+                return;
+            }
+            if (nsri.getIsBinderDead()) {
+                Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died");
+                return;
+            }
+            if (!startNewScan(nsri)) {
+                if (!interruptLiveScan(nsri)) {
+                    if (!cacheScan(nsri)) {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                                NetworkScan.ERROR_MODEM_BUSY, null);
+                    }
+                }
+            }
+        }
+
+        private synchronized void startScanDone(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null");
+                return;
+            }
+            if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) {
+                Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo");
+                return;
+            }
+            if (ar.exception == null && ar.result != null) {
+                // Register for the scan results if the scan started successfully.
+                nsri.mPhone.mCi.registerForNetworkScanResult(mHandler,
+                        EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri);
+            } else {
+                logEmptyResultOrException(ar);
+                if (ar.exception != null) {
+                    CommandException.Error error =
+                            ((CommandException) (ar.exception)).getCommandError();
+                    deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
+                } else {
+                    Log.wtf(TAG, "EVENT_START_NETWORK_SCAN_DONE: ar.exception can not be null!");
+                }
+            }
+        }
+
+        private void receiveResult(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null");
+                return;
+            }
+            if (ar.exception == null && ar.result != null) {
+                NetworkScanResult nsr = (NetworkScanResult) ar.result;
+                if (nsr.scanError == NetworkScan.SUCCESS) {
+                    notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
+                            rilErrorToScanError(nsr.scanError), nsr.networkInfos);
+                    if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) {
+                        deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
+                        nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                    }
+                } else {
+                    if (nsr.networkInfos != null) {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS,
+                                NetworkScan.SUCCESS, nsr.networkInfos);
+                    }
+                    deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true);
+                    nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+                }
+            } else {
+                logEmptyResultOrException(ar);
+                deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true);
+                nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+            }
+        }
+
+
+        // Stops the scan if the scanId and uid match the mScanId and mUid.
+        // If the scan to be stopped is the live scan, we only send the request to RIL, while the
+        // mLiveRequestInfo will not be cleared and the user will not be notified either.
+        // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and
+        // notify the user.
+        private synchronized void doStopScan(int scanId) {
+            if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
+                mLiveRequestInfo.mPhone.stopNetworkScan(
+                        mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo));
+            } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) {
+                notifyMessenger(mPendingRequestInfo,
+                        TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null);
+                mPendingRequestInfo = null;
+            } else {
+                Log.e(TAG, "stopScan: scan " + scanId + " does not exist!");
+            }
+        }
+
+        private void stopScanDone(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null");
+                return;
+            }
+            if (ar.exception == null && ar.result != null) {
+                deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true);
+            } else {
+                logEmptyResultOrException(ar);
+                if (ar.exception != null) {
+                    CommandException.Error error =
+                            ((CommandException) (ar.exception)).getCommandError();
+                    deleteScanAndMayNotify(nsri, commandExceptionErrorToScanError(error), true);
+                } else {
+                    Log.wtf(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: ar.exception can not be null!");
+                }
+            }
+            nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+        }
+
+        // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo.
+        private synchronized void doInterruptScan(int scanId) {
+            if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) {
+                mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage(
+                        EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo));
+            } else {
+                Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!");
+            }
+        }
+
+        private void interruptScanDone(AsyncResult ar) {
+            NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj;
+            if (nsri == null) {
+                Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null");
+                return;
+            }
+            nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler);
+            deleteScanAndMayNotify(nsri, 0, false);
+        }
+
+        // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is
+        // stopped, a new scan will automatically start with nsri.
+        // The new scan can interrupt the live scan only when all the below requirements are met:
+        //   1. There is 1 live scan and no other pending scan
+        //   2. The new scan is requested by system process
+        //   3. The live scan is not requested by system process
+        private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
+            if (mLiveRequestInfo != null && mPendingRequestInfo == null
+                    && nsri.mUid == Process.SYSTEM_UID
+                            && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
+                doInterruptScan(mLiveRequestInfo.mScanId);
+                mPendingRequestInfo = nsri;
+                notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
+                        NetworkScan.ERROR_INTERRUPTED, null);
+                return true;
+            }
+            return false;
+        }
+
+        private boolean cacheScan(NetworkScanRequestInfo nsri) {
+            // TODO(30954762): Cache periodic scan for OC-MR1.
+            return false;
+        }
+
+        // Starts a new scan with nsri if there is no live scan running.
+        private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) {
+            if (mLiveRequestInfo == null) {
+                mLiveRequestInfo = nsri;
+                nsri.mPhone.startNetworkScan(nsri.getRequest(),
+                        mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
+                return true;
+            }
+            return false;
+        }
+
+
+        // Deletes the mLiveRequestInfo and notify the user if it matches nsri.
+        private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error,
+                boolean notify) {
+            if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) {
+                if (notify) {
+                    if (error == NetworkScan.SUCCESS) {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error,
+                                null);
+                    } else {
+                        notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error,
+                                null);
+                    }
+                }
+                mLiveRequestInfo = null;
+                if (mPendingRequestInfo != null) {
+                    startNewScan(mPendingRequestInfo);
+                    mPendingRequestInfo = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Interrupts an ongoing network scan
+     *
+     * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The
+     * difference is that stopNetworkScan is only used by the callers to stop their own scans, so
+     * sanity check will be done to make sure the request is valid; while this method is only
+     * internally used by NetworkScanRequestTracker so sanity check is not needed.
+     */
+    private void interruptNetworkScan(int scanId) {
+        // scanId will be stored at Message.arg1
+        mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget();
+    }
+
+    /**
+     * Starts a new network scan
+     *
+     * This function only wraps all the incoming information and delegate then to the handler thread
+     * which will actually handles the scan request. So a new scanId will always be generated and
+     * returned to the user, no matter how this scan will be actually handled.
+     */
+    public int startNetworkScan(
+            NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone) {
+        int scanId = mNextNetworkScanRequestId.getAndIncrement();
+        NetworkScanRequestInfo nsri =
+                new NetworkScanRequestInfo(request, messenger, binder, scanId, phone);
+        // nsri will be stored as Message.obj
+        mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget();
+        return scanId;
+    }
+
+    /**
+     * Stops an ongoing network scan
+     *
+     * The ongoing scan will be stopped only when the input scanId and caller's uid matches the
+     * corresponding information associated with it.
+     */
+    public void stopNetworkScan(int scanId) {
+        synchronized (mScheduler) {
+            if ((mScheduler.mLiveRequestInfo != null
+                    && scanId == mScheduler.mLiveRequestInfo.mScanId
+                    && Binder.getCallingUid() == mScheduler.mLiveRequestInfo.mUid)
+                    || (mScheduler.mPendingRequestInfo != null
+                    && scanId == mScheduler.mPendingRequestInfo.mScanId
+                    && Binder.getCallingUid() == mScheduler.mPendingRequestInfo.mUid)) {
+                // scanId will be stored at Message.arg1
+                mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget();
+            } else {
+                throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!");
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/OemHookIndication.java b/src/java/com/android/internal/telephony/OemHookIndication.java
new file mode 100644
index 0000000..122a70e
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OemHookIndication.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.hardware.radio.deprecated.V1_0.IOemHookIndication;
+import android.os.AsyncResult;
+
+import java.util.ArrayList;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_OEM_HOOK_RAW;
+
+/**
+ * Class containing oem hook indication callbacks
+ */
+public class OemHookIndication extends IOemHookIndication.Stub {
+    RIL mRil;
+
+    public OemHookIndication(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * @param indicationType RadioIndicationType
+     * @param data Data sent by oem
+     */
+    public void oemHookRaw(int indicationType, ArrayList<Byte> data) {
+        mRil.processIndication(indicationType);
+
+        byte[] response = RIL.arrayListToPrimitiveArray(data);
+        if (RIL.RILJ_LOGD) {
+            mRil.unsljLogvRet(RIL_UNSOL_OEM_HOOK_RAW,
+                    com.android.internal.telephony.uicc.IccUtils.bytesToHexString(response));
+        }
+
+        if (mRil.mUnsolOemHookRawRegistrant != null) {
+            mRil.mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/OemHookResponse.java b/src/java/com/android/internal/telephony/OemHookResponse.java
new file mode 100644
index 0000000..0afeac8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/OemHookResponse.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.hardware.radio.deprecated.V1_0.IOemHookResponse;
+import android.hardware.radio.V1_0.RadioError;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Class containing oem hook response callbacks
+ */
+public class OemHookResponse extends IOemHookResponse.Stub {
+    RIL mRil;
+
+    public OemHookResponse(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param data Data returned by oem
+     */
+    public void sendRequestRawResponse(RadioResponseInfo responseInfo, ArrayList<Byte> data) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            byte[] ret = null;
+            if (responseInfo.error == RadioError.NONE) {
+                ret = RIL.arrayListToPrimitiveArray(data);
+                RadioResponse.sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param data Data returned by oem
+     */
+    public void sendRequestStringsResponse(RadioResponseInfo responseInfo, ArrayList<String> data) {
+        RadioResponse.responseStringArrayList(mRil, responseInfo, data);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index be09a0c..fad0ddb 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -23,6 +23,7 @@
 import android.content.SharedPreferences;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
@@ -34,6 +35,7 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.service.carrier.CarrierIdentifier;
@@ -41,6 +43,9 @@
 import android.telephony.CellIdentityCdma;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoCdma;
+import android.telephony.CellLocation;
+import android.telephony.ClientRequestStats;
+import android.telephony.ImsiEncryptionInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.RadioAccessFamily;
 import android.telephony.Rlog;
@@ -50,10 +55,14 @@
 import android.telephony.VoLteServiceState;
 import android.text.TextUtils;
 
+import com.android.ims.ImsCall;
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsManager;
 import com.android.internal.R;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DcTracker;
+import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneCall;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccFileHandler;
@@ -90,6 +99,8 @@
 
     protected final static Object lockForRadioTechnologyChange = new Object();
 
+    protected final int USSD_MAX_QUEUE = 10;
+
     private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -132,6 +143,9 @@
     // Key used to read/write "disable data connection on boot" pref (used for testing)
     public static final String DATA_DISABLED_ON_BOOT_KEY = "disabled_on_boot_key";
 
+    // Key used to read/write data_roaming_is_user_setting pref
+    public static final String DATA_ROAMING_IS_USER_SETTING_KEY = "data_roaming_is_user_setting_key";
+
     /* Event Constants */
     protected static final int EVENT_RADIO_AVAILABLE             = 1;
     /** Supplementary Service Notification received. */
@@ -210,6 +224,9 @@
     // Key used to read/write "disable DNS server check" pref (used for testing)
     private static final String DNS_SERVER_CHECK_DISABLED_KEY = "dns_server_check_disabled_key";
 
+    // Integer used to let the calling application know that the we are ignoring auto mode switch.
+    private static final int ALREADY_IN_AUTO_SELECTION = 1;
+
     /**
      * Small container class used to hold information relevant to
      * the carrier selection process. operatorNumeric can be ""
@@ -228,13 +245,16 @@
     protected int mVmCount = 0;
     private boolean mDnsCheckDisabled;
     public DcTracker mDcTracker;
+    /* Used for dispatching signals to configured carrier apps */
+    private CarrierSignalAgent mCarrierSignalAgent;
+    /* Used for dispatching carrier action from carrier apps */
+    private CarrierActionAgent mCarrierActionAgent;
     private boolean mDoesRilSendMultipleCallRing;
     private int mCallRingContinueToken;
     private int mCallRingDelay;
     private boolean mIsVoiceCapable = true;
-    /* Used for communicate between configured CarrierSignalling receivers */
-    private CarrierSignalAgent mCarrierSignalAgent;
-
+    private final AppSmsManager mAppSmsManager;
+    private SimActivationTracker mSimActivationTracker;
     // Keep track of whether or not the phone is in Emergency Callback Mode for Phone and
     // subclasses
     protected boolean mIsPhoneInEcmState = false;
@@ -268,6 +288,11 @@
     protected TelephonyComponentFactory mTelephonyComponentFactory;
 
     //IMS
+    /**
+     * {@link CallStateException} message text used to indicate that an IMS call has failed because
+     * it needs to be retried using GSM or CDMA (e.g. CS fallback).
+     * TODO: Replace this with a proper exception; {@link CallStateException} doesn't make sense.
+     */
     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";
@@ -437,9 +462,9 @@
         mContext = context;
         mLooper = Looper.myLooper();
         mCi = ci;
-        mCarrierSignalAgent = new CarrierSignalAgent(this);
         mActionDetached = this.getClass().getPackage().getName() + ".action_detached";
         mActionAttached = this.getClass().getPackage().getName() + ".action_attached";
+        mAppSmsManager = telephonyComponentFactory.makeAppSmsManager(context);
 
         if (Build.IS_DEBUGGABLE) {
             mTelephonyTester = new TelephonyTester(this);
@@ -505,6 +530,9 @@
         mSmsUsageMonitor = mTelephonyComponentFactory.makeSmsUsageMonitor(context);
         mUiccController = UiccController.getInstance();
         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
+        mCarrierSignalAgent = mTelephonyComponentFactory.makeCarrierSignalAgent(this);
+        mCarrierActionAgent = mTelephonyComponentFactory.makeCarrierActionAgent(this);
+        mSimActivationTracker = mTelephonyComponentFactory.makeSimActivationTracker(this);
         if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
             mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         }
@@ -514,7 +542,8 @@
     }
 
     /**
-     * Start listening for IMS service UP/DOWN events.
+     * Start listening for IMS service UP/DOWN events. If using the new ImsResolver APIs, we should
+     * always be setting up ImsPhones.
      */
     public void startMonitoringImsService() {
         if (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
@@ -523,18 +552,25 @@
 
         synchronized(Phone.lockForRadioTechnologyChange) {
             IntentFilter filter = new IntentFilter();
-            filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
-            filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+            ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
+            // Don't listen to deprecated intents using the new dynamic binding.
+            if (imsManager != null && !imsManager.isDynamicBinding()) {
+                filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+                filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+            }
             filter.addAction(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
             mContext.registerReceiver(mImsIntentReceiver, filter);
 
             // 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();
-                ImsManager.updateImsServiceConfig(mContext, mPhoneId, false);
+            // intent). Also, when using new ImsResolver APIs, the service will be available soon,
+            // so start trying to bind.
+            if (imsManager != null) {
+                // If it is dynamic binding, kick off ImsPhone creation now instead of waiting for
+                // the service to be available.
+                if (imsManager.isDynamicBinding() || imsManager.isServiceAvailable()) {
+                    mImsServiceReady = true;
+                    updateImsPhone();
+                }
             }
         }
     }
@@ -1142,6 +1178,11 @@
             mCi.setNetworkSelectionModeAutomatic(msg);
         } else {
             Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring");
+            // let the calling application know that the we are ignoring automatic mode switch.
+            if (nsm.message != null) {
+                nsm.message.arg1 = ALREADY_IN_AUTO_SELECTION;
+            }
+
             ar.userObj = nsm;
             handleSetSelectNetwork(ar);
         }
@@ -1160,6 +1201,10 @@
         mCi.getNetworkSelectionMode(message);
     }
 
+    public List<ClientRequestStats> getClientRequestStats() {
+        return mCi.getClientRequestStats();
+    }
+
     /**
      * Manually selects a network. <code>response</code> is
      * dispatched when this is complete.  <code>response.obj</code> will be
@@ -1296,6 +1341,8 @@
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
         SharedPreferences.Editor editor = sp.edit();
         editor.putInt(CLIR_KEY + getPhoneId(), commandInterfaceCLIRMode);
+        Rlog.i(LOG_TAG, "saveClirSetting: " + CLIR_KEY + getPhoneId() + "=" +
+                commandInterfaceCLIRMode);
 
         // Commit and log the result.
         if (!editor.commit()) {
@@ -1527,6 +1574,32 @@
     }
 
     /**
+     * Update voice activation state
+     */
+    public void setVoiceActivationState(int state) {
+        mSimActivationTracker.setVoiceActivationState(state);
+    }
+    /**
+     * Update data activation state
+     */
+    public void setDataActivationState(int state) {
+        mSimActivationTracker.setDataActivationState(state);
+    }
+
+    /**
+     * Returns voice activation state
+     */
+    public int getVoiceActivationState() {
+        return mSimActivationTracker.getVoiceActivationState();
+    }
+    /**
+     * Returns data activation state
+     */
+    public int getDataActivationState() {
+        return mSimActivationTracker.getDataActivationState();
+    }
+
+    /**
      * Update voice mail count related fields and notify listeners
      */
     public void updateVoiceMail() {
@@ -1581,13 +1654,18 @@
     }
 
     /**
+     * @param workSource calling WorkSource
      * @return all available cell information or null if none.
      */
-    public List<CellInfo> getAllCellInfo() {
-        List<CellInfo> cellInfoList = getServiceStateTracker().getAllCellInfo();
+    public List<CellInfo> getAllCellInfo(WorkSource workSource) {
+        List<CellInfo> cellInfoList = getServiceStateTracker().getAllCellInfo(workSource);
         return privatizeCellInfoList(cellInfoList);
     }
 
+    public CellLocation getCellLocation() {
+        return getCellLocation(null);
+    }
+
     /**
      * Clear CDMA base station lat/long values if location setting is disabled.
      * @param cellInfoList the original cell info list from the RIL
@@ -1630,9 +1708,10 @@
      * A onCellInfoChanged.
      *
      * @param rateInMillis the rate
+     * @param workSource calling WorkSource
      */
-    public void setCellInfoListRate(int rateInMillis) {
-        mCi.setCellInfoListRate(rateInMillis, null);
+    public void setCellInfoListRate(int rateInMillis, WorkSource workSource) {
+        mCi.setCellInfoListRate(rateInMillis, null, workSource);
     }
 
     /**
@@ -1737,6 +1816,10 @@
         return mCarrierSignalAgent;
     }
 
+    public CarrierActionAgent getCarrierActionAgent() {
+        return mCarrierActionAgent;
+    }
+
     /**
      *  Query the CDMA roaming preference setting
      *
@@ -1766,6 +1849,15 @@
     }
 
     /**
+     * @return true, if the device is in a state where both voice and data
+     * are supported simultaneously. This can change based on location or network condition.
+     */
+    public boolean isConcurrentVoiceAndDataAllowed() {
+        ServiceStateTracker sst = getServiceStateTracker();
+        return sst == null ? false : sst.isConcurrentVoiceAndDataAllowed();
+    }
+
+    /**
      *  Requests to set the CDMA roaming preference
      * @param cdmaRoamingType one of  CDMA_RM_*
      * @param response is callback message
@@ -1937,7 +2029,9 @@
      * com.android.internal.telephony.gsm.CommandException
      *
      * @see #invokeOemRilRequestRaw(byte[], android.os.Message)
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
      */
+    @Deprecated
     public void invokeOemRilRequestRaw(byte[] data, Message response) {
         mCi.invokeOemRilRequestRaw(data, response);
     }
@@ -1955,7 +2049,9 @@
      * com.android.internal.telephony.gsm.CommandException
      *
      * @see #invokeOemRilRequestStrings(java.lang.String[], android.os.Message)
+     * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
      */
+    @Deprecated
     public void invokeOemRilRequestStrings(String[] strings, Message response) {
         mCi.invokeOemRilRequestStrings(strings, response);
     }
@@ -2039,6 +2135,14 @@
         mNotifier.notifyOtaspChanged(this, otaspMode);
     }
 
+    public void notifyVoiceActivationStateChanged(int state) {
+        mNotifier.notifyVoiceActivationStateChanged(this, state);
+    }
+
+    public void notifyDataActivationStateChanged(int state) {
+        mNotifier.notifyDataActivationStateChanged(this, state);
+    }
+
     public void notifySignalStrength() {
         mNotifier.notifySignalStrength(this);
     }
@@ -2068,13 +2172,16 @@
      * @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.
-     *
-     * This method is overridden for GSM phones to return false always
      */
     public boolean isInEcm() {
         return mIsPhoneInEcmState;
     }
 
+    public void setIsInEcm(boolean isInEcm) {
+        setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, String.valueOf(isInEcm));
+        mIsPhoneInEcmState = isInEcm;
+    }
+
     private static int getVideoState(Call call) {
         int videoState = VideoProfile.STATE_AUDIO_ONLY;
         Connection conn = call.getEarliestConnection();
@@ -2084,23 +2191,40 @@
         return videoState;
     }
 
-    private boolean isVideoCall(Call call) {
-        int videoState = getVideoState(call);
-        return (VideoProfile.isVideo(videoState));
+    /**
+     * Determines if the specified call currently is or was at some point a video call, or if it is
+     * a conference call.
+     * @param call The call.
+     * @return {@code true} if the call is or was a video call or is a conference call,
+     *      {@code false} otherwise.
+     */
+    private boolean isVideoCallOrConference(Call call) {
+        if (call.isMultiparty()) {
+            return true;
+        }
+
+        boolean isDowngradedVideoCall = false;
+        if (call instanceof ImsPhoneCall) {
+            ImsPhoneCall imsPhoneCall = (ImsPhoneCall) call;
+            ImsCall imsCall = imsPhoneCall.getImsCall();
+            return imsCall != null && (imsCall.isVideoCall() ||
+                    imsCall.wasVideoCall());
+        }
+        return isDowngradedVideoCall;
     }
 
     /**
-     * @return {@code true} if video call is present, false otherwise.
+     * @return {@code true} if an IMS video call or IMS conference is present, false otherwise.
      */
-    public boolean isVideoCallPresent() {
-        boolean isVideoCallActive = false;
+    public boolean isImsVideoCallOrConferencePresent() {
+        boolean isPresent = false;
         if (mImsPhone != null) {
-            isVideoCallActive = isVideoCall(mImsPhone.getForegroundCall()) ||
-                    isVideoCall(mImsPhone.getBackgroundCall()) ||
-                    isVideoCall(mImsPhone.getRingingCall());
+            isPresent = isVideoCallOrConference(mImsPhone.getForegroundCall()) ||
+                    isVideoCallOrConference(mImsPhone.getBackgroundCall()) ||
+                    isVideoCallOrConference(mImsPhone.getRingingCall());
         }
-        Rlog.d(LOG_TAG, "isVideoCallActive: " + isVideoCallActive);
-        return isVideoCallActive;
+        Rlog.d(LOG_TAG, "isImsVideoCallOrConferencePresent: " + isPresent);
+        return isPresent;
     }
 
     /**
@@ -2181,6 +2305,21 @@
     }
 
     /**
+     * send secret dialer codes to launch arbitrary activities.
+     * an Intent is started with the android_secret_code://<code> URI.
+     *
+     * @param code stripped version of secret code without *#*# prefix and #*#* suffix
+     */
+    public void sendDialerSpecialCode(String code) {
+        if (!TextUtils.isEmpty(code)) {
+            Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,
+                    Uri.parse("android_secret_code://" + code));
+            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+            mContext.sendBroadcast(intent);
+        }
+    }
+
+    /**
      * Returns the CDMA ERI icon index to display
      */
     public int getCdmaEriIconIndex() {
@@ -2630,17 +2769,21 @@
 
     /**
      * Report on whether data connectivity is allowed.
+     *
+     * @return True if data is allowed to be established.
      */
-    public boolean isDataConnectivityPossible() {
-        return isDataConnectivityPossible(PhoneConstants.APN_TYPE_DEFAULT);
+    public boolean isDataAllowed() {
+        return ((mDcTracker != null) && (mDcTracker.isDataAllowed(null)));
     }
 
     /**
-     * Report on whether data connectivity is allowed for an APN.
+     * Report on whether data connectivity is allowed.
+     *
+     * @param reasons The reasons that data can/can't be established. This is an output param.
+     * @return True if data is allowed to be established
      */
-    public boolean isDataConnectivityPossible(String apnType) {
-        return ((mDcTracker != null) &&
-                (mDcTracker.isDataPossible(apnType)));
+    public boolean isDataAllowed(DataConnectionReasons reasons) {
+        return ((mDcTracker != null) && (mDcTracker.isDataAllowed(reasons)));
     }
 
 
@@ -2648,18 +2791,14 @@
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
      */
     public void carrierActionSetMeteredApnsEnabled(boolean enabled) {
-        if(mDcTracker != null) {
-            mDcTracker.setApnsEnabledByCarrier(enabled);
-        }
+        mCarrierActionAgent.carrierActionSetMeteredApnsEnabled(enabled);
     }
 
     /**
      * Action set from carrier signalling broadcast receivers to enable/disable radio
      */
     public void carrierActionSetRadioEnabled(boolean enabled) {
-        if(mDcTracker != null) {
-            mDcTracker.carrierActionSetRadioEnabled(enabled);
-        }
+        mCarrierActionAgent.carrierActionSetRadioEnabled(enabled);
     }
 
     /**
@@ -2837,9 +2976,34 @@
     }
 
     /**
+     * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * @param keyType whether the key is being used for WLAN or ePDG.
+     * @return ImsiEncryptionInfo which includes the Key Type, the Public Key
+     *        {@link java.security.PublicKey} and the Key Identifier.
+     *        The keyIdentifier This is used by the server to help it locate the private key to
+     *        decrypt the permanent identity.
+     */
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+        return null;
+    }
+
+    /**
+     * Sets the carrier information needed to encrypt the IMSI and IMPI.
+     * @param imsiEncryptionInfo Carrier specific information that will be used to encrypt the
+     *        IMSI and IMPI. This includes the Key type, the Public key
+     *        {@link java.security.PublicKey} and the Key identifier.
+     */
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
+        return;
+    }
+
+    /**
      * Return if UT capability of ImsPhone is enabled or not
      */
     public boolean isUtEnabled() {
+        if (mImsPhone != null) {
+            return mImsPhone.isUtEnabled();
+        }
         return false;
     }
 
@@ -3172,14 +3336,25 @@
     }
 
     /**
+     * Determines if the connection to IMS services are available yet.
+     * @return {@code true} if the connection to IMS services are available.
+     */
+    public boolean isImsAvailable() {
+        if (mImsPhone == null) {
+            return false;
+        }
+
+        return mImsPhone.isImsAvailable();
+    }
+
+    /**
      * Determines if video calling is enabled for the phone.
      *
      * @return {@code true} if video calling is enabled, {@code false} otherwise.
      */
     public boolean isVideoEnabled() {
         Phone imsPhone = mImsPhone;
-        if ((imsPhone != null)
-                && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+        if (imsPhone != null) {
             return imsPhone.isVideoEnabled();
         }
         return false;
@@ -3286,7 +3461,7 @@
                             ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
             if (wfcWiFiOnly) {
                 throw new CallStateException(
-                        CallStateException.ERROR_DISCONNECTED,
+                        CallStateException.ERROR_OUT_OF_SERVICE,
                         "WFC Wi-Fi Only Mode: IMS not registered");
             }
         }
@@ -3321,9 +3496,16 @@
         return this;
     }
 
-    public long getVtDataUsage() {
-        if (mImsPhone == null) return 0;
-        return mImsPhone.getVtDataUsage();
+    /**
+     * Get aggregated video call data usage since boot.
+     * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
+     *
+     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
+     * @return Snapshot of video call data usage
+     */
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
+        if (mImsPhone == null) return null;
+        return mImsPhone.getVtDataUsage(perUidStats);
     }
 
     /**
@@ -3343,6 +3525,21 @@
         return null;
     }
 
+    public AppSmsManager getAppSmsManager() {
+        return mAppSmsManager;
+    }
+
+    /**
+     * Set SIM card power state.
+     * @param state State of SIM (power down, power up, pass through)
+     * - {@link android.telephony.TelephonyManager#CARD_POWER_DOWN}
+     * - {@link android.telephony.TelephonyManager#CARD_POWER_UP}
+     * - {@link android.telephony.TelephonyManager#CARD_POWER_UP_PASS_THROUGH}
+     **/
+    public void setSimPowerState(int state) {
+        mCi.setSimCardPower(state, null);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("Phone: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
@@ -3377,7 +3574,6 @@
         pw.println(" getPhoneType()=" + getPhoneType());
         pw.println(" getVoiceMessageCount()=" + getVoiceMessageCount());
         pw.println(" getActiveApnTypes()=" + getActiveApnTypes());
-        pw.println(" isDataConnectivityPossible()=" + isDataConnectivityPossible());
         pw.println(" needsOtaServiceProvisioning=" + needsOtaServiceProvisioning());
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
@@ -3415,6 +3611,28 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
+        if (mCarrierActionAgent != null) {
+            try {
+                mCarrierActionAgent.dump(fd, pw, args);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            pw.flush();
+            pw.println("++++++++++++++++++++++++++++++++");
+        }
+
+        if (mCarrierSignalAgent != null) {
+            try {
+                mCarrierSignalAgent.dump(fd, pw, args);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            pw.flush();
+            pw.println("++++++++++++++++++++++++++++++++");
+        }
+
         if (getCallTracker() != null) {
             try {
                 getCallTracker().dump(fd, pw, args);
@@ -3426,6 +3644,17 @@
             pw.println("++++++++++++++++++++++++++++++++");
         }
 
+        if (mSimActivationTracker != null) {
+            try {
+                mSimActivationTracker.dump(fd, pw, args);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+
+            pw.flush();
+            pw.println("++++++++++++++++++++++++++++++++");
+        }
+
         if (mCi != null && mCi instanceof RIL) {
             try {
                 ((RIL)mCi).dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index aec3d8e..4fcf37e 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -21,12 +21,15 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.net.LocalServerSocket;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.telephony.Rlog;
@@ -36,17 +39,20 @@
 
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory;
+import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneFactory;
 import com.android.internal.telephony.sip.SipPhone;
 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.telephony.util.NotificationChannelController;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.HashMap;
+import java.util.Map;
 
 /**
  * {@hide}
@@ -68,6 +74,7 @@
 
     static private ProxyController sProxyController;
     static private UiccController sUiccController;
+    private static IntentBroadcaster sIntentBroadcaster;
 
     static private CommandsInterface sCommandsInterface = null;
     static private SubscriptionInfoUpdater sSubInfoRecordUpdater = null;
@@ -78,6 +85,8 @@
     static private PhoneSwitcher sPhoneSwitcher;
     static private SubscriptionMonitor sSubscriptionMonitor;
     static private TelephonyNetworkFactory[] sTelephonyNetworkFactories;
+    static private ImsResolver sImsResolver;
+    static private NotificationChannelController sNotificationChannelController;
 
     static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
 
@@ -136,6 +145,13 @@
                    where as in single SIM mode only instance. isMultiSimEnabled() function checks
                    whether it is single SIM or multi SIM mode */
                 int numPhones = TelephonyManager.getDefault().getPhoneCount();
+                // Start ImsResolver and bind to ImsServices.
+                String defaultImsPackage = sContext.getResources().getString(
+                        com.android.internal.R.string.config_ims_package);
+                Rlog.i(LOG_TAG, "ImsResolver: defaultImsPackage: " + defaultImsPackage);
+                sImsResolver = new ImsResolver(sContext, defaultImsPackage, numPhones);
+                sImsResolver.populateCacheAndStartBind();
+
                 int[] networkModes = new int[numPhones];
                 sPhones = new Phone[numPhones];
                 sCommandsInterfaces = new RIL[numPhones];
@@ -203,8 +219,9 @@
                 SubscriptionController.getInstance().updatePhonesAvailability(sPhones);
 
                 // Start monitoring after defaults have been made.
-                // Default phone must be ready before ImsPhone is created
-                // because ImsService might need it when it is being opened.
+                // Default phone must be ready before ImsPhone is created because ImsService might
+                // need it when it is being opened. This should initialize multiple ImsPhones for
+                // ImsResolver implementations of ImsService.
                 for (int i = 0; i < numPhones; i++) {
                     sPhones[i].startMonitoringImsService();
                 }
@@ -222,6 +239,10 @@
                 sProxyController = ProxyController.getInstance(context, sPhones,
                         sUiccController, sCommandsInterfaces, sPhoneSwitcher);
 
+                sIntentBroadcaster = IntentBroadcaster.getInstance(context);
+
+                sNotificationChannelController = new NotificationChannelController(context);
+
                 sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones];
                 for (int i = 0; i < numPhones; i++) {
                     sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory(
@@ -275,6 +296,10 @@
         }
     }
 
+    public static ImsResolver getImsResolver() {
+        return sImsResolver;
+    }
+
     /**
      * Makes a {@link SipPhone} object.
      * @param sipUri the local SIP URI the phone runs on
@@ -459,5 +484,22 @@
             pw.flush();
         }
         pw.decreaseIndent();
+        pw.println("++++++++++++++++++++++++++++++++");
+
+        pw.println("SharedPreferences:");
+        pw.increaseIndent();
+        try {
+            if (sContext != null) {
+                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(sContext);
+                Map spValues = sp.getAll();
+                for (Object key : spValues.keySet()) {
+                    pw.println(key + " : " + spValues.get(key));
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.flush();
+        pw.decreaseIndent();
     }
 }
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 652dac8..6cf6358 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -16,30 +16,20 @@
 
 package com.android.internal.telephony;
 
-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;
-import android.telephony.CellLocation;
+import android.os.ResultReceiver;
+import android.os.WorkSource;
 import android.telephony.CarrierConfigManager;
-import android.telephony.PhoneStateListener;
+import android.telephony.CellLocation;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.ServiceState;
-import android.telephony.SignalStrength;
-
-import com.android.internal.telephony.imsphone.ImsPhone;
-import com.android.internal.telephony.RadioCapability;
-import com.android.internal.telephony.test.SimulatedRadioControl;
-import com.android.internal.telephony.uicc.IsimRecords;
-import com.android.internal.telephony.uicc.UiccCard;
-import com.android.internal.telephony.uicc.UsimServiceTable;
 
 import com.android.internal.telephony.PhoneConstants.*; // ????
 
 import java.util.List;
-import java.util.Locale;
 
 /**
  * Internal interface used to control the phone; SDK developers cannot
@@ -117,6 +107,7 @@
     static final String REASON_CARRIER_CHANGE = "carrierChange";
     static final String REASON_CARRIER_ACTION_DISABLE_METERED_APN =
             "carrierActionDisableMeteredApn";
+    static final String REASON_CSS_INDICATOR_CHANGED = "cssIndicatorChanged";
 
     // Used for band mode selection methods
     static final int BM_UNSPECIFIED = RILConstants.BAND_MODE_UNSPECIFIED; // automatic
@@ -219,8 +210,9 @@
 
     /**
      * Get the current CellLocation.
+     * @param workSource calling WorkSource
      */
-    CellLocation getCellLocation();
+    CellLocation getCellLocation(WorkSource workSource);
 
     /**
      * Get the current DataState. No change notification exists at this
@@ -448,6 +440,15 @@
     boolean handlePinMmi(String dialString);
 
     /**
+     * Handles USSD commands
+     *
+     * @param ussdRequest the USSD command to be executed.
+     * @param wrappedCallback receives the callback result.
+     */
+    boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)
+            throws CallStateException;
+
+    /**
      * Handles in-call MMI commands. While in a call, or while receiving a
      * call, use this to execute MMI commands.
      * see 3GPP 20.030, section 6.5.5.1 for specs on the allowed MMI commands.
@@ -646,6 +647,30 @@
     void getAvailableNetworks(Message response);
 
     /**
+     * Start a network scan. This method is asynchronous; .
+     * On completion, <code>response.obj</code> is set to an AsyncResult with
+     * one of the following members:.<p>
+     * <ul>
+     * <li><code>response.obj.result</code> will be a <code>NetworkScanResult</code> object, or</li>
+     * <li><code>response.obj.exception</code> will be set with an exception
+     * on failure.</li>
+     * </ul>
+     */
+    void startNetworkScan(NetworkScanRequest nsr, Message response);
+
+    /**
+     * Stop ongoing network scan. This method is asynchronous; .
+     * On completion, <code>response.obj</code> is set to an AsyncResult with
+     * one of the following members:.<p>
+     * <ul>
+     * <li><code>response.obj.result</code> will be a <code>NetworkScanResult</code> object, or</li>
+     * <li><code>response.obj.exception</code> will be set with an exception
+     * on failure.</li>
+     * </ul>
+     */
+    void stopNetworkScan(Message response);
+
+    /**
      * Query neighboring cell IDs.  <code>response</code> is dispatched when
      * this is complete.  <code>response.obj</code> will be an AsyncResult,
      * and <code>response.obj.exception</code> will be non-null on failure.
@@ -655,8 +680,9 @@
      *
      * @param response callback message that is dispatched when the query
      * completes.
+     * @param workSource calling WorkSource
      */
-    void getNeighboringCids(Message response);
+    default void getNeighboringCids(Message response, WorkSource workSource){}
 
     /**
      * Mutes or unmutes the microphone for the active call. The microphone
@@ -803,4 +829,22 @@
      *            Callback message is empty on completion
      */
     public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response);
+
+    /*
+    * Sets the carrier information needed to encrypt the IMSI and IMPI.
+    * @param imsiEncryptionInfo Carrier specific information that will be used to encrypt the
+    *        IMSI and IMPI. This includes the Key type, the Public key
+    *        {@link java.security.PublicKey} and the Key identifier.
+    */
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo);
+
+    /**
+     * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
+     * @param keyType whether the key is being used for WLAN or ePDG.
+     * @return ImsiEncryptionInfo which includes the Key Type, the Public Key
+     *        {@link java.security.PublicKey} and the Key Identifier.
+     *        The keyIdentifier This is used by the server to help it locate the private key to
+     *        decrypt the permanent identity.
+     */
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneNotifier.java b/src/java/com/android/internal/telephony/PhoneNotifier.java
index 02f15d9..2caf5e2 100644
--- a/src/java/com/android/internal/telephony/PhoneNotifier.java
+++ b/src/java/com/android/internal/telephony/PhoneNotifier.java
@@ -59,5 +59,9 @@
 
     public void notifyVoLteServiceStateChanged(Phone sender, VoLteServiceState lteState);
 
+    public void notifyVoiceActivationStateChanged(Phone sender, int activationState);
+
+    public void notifyDataActivationStateChanged(Phone sender, int activationState);
+
     public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index a867d12..9cd088f 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -24,6 +24,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.telephony.ImsiEncryptionInfo;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.SubscriptionManager;
 import android.telephony.Rlog;
@@ -33,6 +34,7 @@
 import com.android.internal.telephony.uicc.UiccCardApplication;
 
 import static android.Manifest.permission.CALL_PRIVILEGED;
+import static android.Manifest.permission.READ_PHONE_NUMBERS;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 import static android.Manifest.permission.READ_SMS;
@@ -103,6 +105,35 @@
         }
     }
 
+    public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int subId, int keyType,
+            String callingPackage) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            if (!checkReadPhoneState(callingPackage, "getCarrierInfoForImsiEncryption")) {
+                return null;
+            }
+            return phone.getCarrierInfoForImsiEncryption(keyType);
+        } else {
+            loge("getCarrierInfoForImsiEncryption phone is null for Subscription:" + subId);
+            return null;
+        }
+    }
+
+    public void setCarrierInfoForImsiEncryption(int subId, String callingPackage,
+                                                ImsiEncryptionInfo imsiEncryptionInfo) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            if (!checkReadPhoneState(callingPackage, "setCarrierInfoForImsiEncryption")) {
+                return;
+            }
+            phone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo);
+        } else {
+            loge("setCarrierInfoForImsiEncryption phone is null for Subscription:" + subId);
+            return;
+        }
+    }
+
+
     public String getDeviceSvn(String callingPackage) {
         return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
     }
@@ -301,62 +332,102 @@
     }
 
 
-    public String getIsimImpi() {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimImpi();
+    /**
+    * get the Isim Impi based on subId
+    */
+    public String getIsimImpi(int subId) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimImpi();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimImpi phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String getIsimDomain() {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimDomain();
+    /**
+    * get the Isim Domain based on subId
+    */
+    public String getIsimDomain(int subId) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimDomain();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimDomain phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String[] getIsimImpu() {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimImpu();
+    /**
+    * get the Isim Impu based on subId
+    */
+    public String[] getIsimImpu(int subId) {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimImpu();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimImpu phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String getIsimIst() throws RemoteException {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimIst();
+    /**
+    * get the Isim Ist based on subId
+    */
+    public String getIsimIst(int subId) throws RemoteException {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimIst();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimIst phone is null for Subscription:" + subId);
             return null;
         }
     }
 
-    public String[] getIsimPcscf() throws RemoteException {
-        Phone phone = getPhone(getDefaultSubscription());
-        mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
-                "Requires READ_PRIVILEGED_PHONE_STATE");
-        IsimRecords isim = phone.getIsimRecords();
-        if (isim != null) {
-            return isim.getIsimPcscf();
+    /**
+    * get the Isim Pcscf based on subId
+    */
+    public String[] getIsimPcscf(int subId) throws RemoteException {
+        Phone phone = getPhone(subId);
+        if (phone != null) {
+            mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE,
+                    "Requires READ_PRIVILEGED_PHONE_STATE");
+            IsimRecords isim = phone.getIsimRecords();
+            if (isim != null) {
+                return isim.getIsimPcscf();
+            } else {
+                return null;
+            }
         } else {
+            loge("getIsimPcscf phone is null for Subscription:" + subId);
             return null;
         }
     }
@@ -434,7 +505,8 @@
     }
 
     /**
-     * Besides READ_PHONE_STATE, WRITE_SMS and READ_SMS also allow apps to get phone numbers.
+     * Besides READ_PHONE_STATE, READ_PHONE_NUMBERS, WRITE_SMS and READ_SMS also allow apps to get
+     * phone numbers.
      */
     private boolean checkReadPhoneNumber(String callingPackage, String message) {
         // Default SMS app can always read it.
@@ -445,18 +517,36 @@
         try {
             return checkReadPhoneState(callingPackage, message);
         } catch (SecurityException readPhoneStateSecurityException) {
-            try {
-                // Can be read with READ_SMS too.
-                mContext.enforceCallingOrSelfPermission(READ_SMS, message);
-                return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
-                        Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
-            } catch (SecurityException readSmsSecurityException) {
-                // Throw exception with message including both READ_PHONE_STATE and READ_SMS
-                // permissions
-                throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
-                        " nor current process has " + READ_PHONE_STATE + " or " + READ_SMS + ".");
-            }
         }
+        try {
+            // Can be read with READ_SMS too.
+            mContext.enforceCallingOrSelfPermission(READ_SMS, message);
+            int opCode = mAppOps.permissionToOpCode(READ_SMS);
+            if (opCode != AppOpsManager.OP_NONE) {
+                return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
+                        == AppOpsManager.MODE_ALLOWED;
+            } else {
+                return true;
+            }
+        } catch (SecurityException readSmsSecurityException) {
+        }
+        try {
+            // Can be read with READ_PHONE_NUMBERS too.
+            mContext.enforceCallingOrSelfPermission(READ_PHONE_NUMBERS, message);
+            int opCode = mAppOps.permissionToOpCode(READ_PHONE_NUMBERS);
+            if (opCode != AppOpsManager.OP_NONE) {
+                return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
+                        == AppOpsManager.MODE_ALLOWED;
+            } else {
+                return true;
+            }
+        } catch (SecurityException readPhoneNumberSecurityException) {
+        }
+        // Throw exception with message including READ_PHONE_STATE, READ_SMS, and READ_PHONE_NUMBERS
+        // permissions
+        throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
+                " nor current process has " + READ_PHONE_STATE + ", " +
+                READ_SMS + ", or " + READ_PHONE_STATE + ".");
     }
 
     private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 41cccd4..15dc842 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -23,9 +23,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.MatchAllNetworkSpecifier;
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
+import android.net.StringNetworkSpecifier;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -33,22 +36,16 @@
 import android.os.RegistrantList;
 import android.os.RemoteException;
 import android.telephony.Rlog;
-import android.text.TextUtils;
 import android.util.LocalLog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.IOnSubscriptionsChangedListener;
-import com.android.internal.telephony.ITelephonyRegistry;
 import com.android.internal.telephony.dataconnection.DcRequest;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.IllegalArgumentException;
 import java.util.ArrayList;
 import java.util.Calendar;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -148,7 +145,7 @@
         netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
         netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-        netCap.setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+        netCap.setNetworkSpecifier(new MatchAllNetworkSpecifier());
 
         NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context,
                 netCap, this);
@@ -368,14 +365,23 @@
     }
 
     private int phoneIdForRequest(NetworkRequest netRequest) {
-        String specifier = netRequest.networkCapabilities.getNetworkSpecifier();
+        NetworkSpecifier specifier = netRequest.networkCapabilities.getNetworkSpecifier();
         int subId;
 
-        if (TextUtils.isEmpty(specifier)) {
+        if (specifier == null) {
             subId = mDefaultDataSubscription;
+        } else if (specifier instanceof StringNetworkSpecifier) {
+            try {
+                subId = Integer.parseInt(((StringNetworkSpecifier) specifier).specifier);
+            } catch (NumberFormatException e) {
+                Rlog.e(LOG_TAG, "NumberFormatException on "
+                        + ((StringNetworkSpecifier) specifier).specifier);
+                subId = INVALID_SUBSCRIPTION_ID;
+            }
         } else {
-            subId = Integer.parseInt(specifier);
+            subId = INVALID_SUBSCRIPTION_ID;
         }
+
         int phoneId = INVALID_PHONE_INDEX;
         if (subId == INVALID_SUBSCRIPTION_ID) return phoneId;
 
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index 0eb3310..f6d42af 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -16,9 +16,6 @@
 
 package com.android.internal.telephony;
 
-import java.util.ArrayList;
-import java.util.Random;
-
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
@@ -31,13 +28,12 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
-import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.uicc.UiccController;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
 
 public class ProxyController {
     static final String LOG_TAG = "ProxyController";
@@ -130,7 +126,7 @@
 
         mUiccPhoneBookController = new UiccPhoneBookController(mPhones);
         mPhoneSubInfoController = new PhoneSubInfoController(mContext, mPhones);
-        mUiccSmsController = new UiccSmsController(mPhones);
+        mUiccSmsController = new UiccSmsController();
         mSetRadioAccessFamilyStatus = new int[mPhones.length];
         mNewRadioAccessFamily = new int[mPhones.length];
         mOldRadioAccessFamily = new int[mPhones.length];
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 1bbbe28..3ed591f 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -16,77 +16,96 @@
 
 package com.android.internal.telephony;
 
-import android.content.BroadcastReceiver;
+import static com.android.internal.telephony.RILConstants.*;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.hardware.display.DisplayManager;
+import android.hardware.radio.V1_0.Carrier;
+import android.hardware.radio.V1_0.CarrierRestrictions;
+import android.hardware.radio.V1_0.CdmaBroadcastSmsConfigInfo;
+import android.hardware.radio.V1_0.CdmaSmsAck;
+import android.hardware.radio.V1_0.CdmaSmsMessage;
+import android.hardware.radio.V1_0.CdmaSmsWriteArgs;
+import android.hardware.radio.V1_0.CellInfoCdma;
+import android.hardware.radio.V1_0.CellInfoGsm;
+import android.hardware.radio.V1_0.CellInfoLte;
+import android.hardware.radio.V1_0.CellInfoType;
+import android.hardware.radio.V1_0.CellInfoWcdma;
+import android.hardware.radio.V1_0.DataProfileInfo;
+import android.hardware.radio.V1_0.Dial;
+import android.hardware.radio.V1_0.GsmBroadcastSmsConfigInfo;
+import android.hardware.radio.V1_0.GsmSmsMessage;
+import android.hardware.radio.V1_0.HardwareConfigModem;
+import android.hardware.radio.V1_0.IRadio;
+import android.hardware.radio.V1_0.IccIo;
+import android.hardware.radio.V1_0.ImsSmsMessage;
+import android.hardware.radio.V1_0.LceDataInfo;
+import android.hardware.radio.V1_0.MvnoType;
+import android.hardware.radio.V1_0.NvWriteItem;
+import android.hardware.radio.V1_0.RadioError;
+import android.hardware.radio.V1_0.RadioIndicationType;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+import android.hardware.radio.V1_0.RadioResponseType;
+import android.hardware.radio.V1_0.ResetNvType;
+import android.hardware.radio.V1_0.SelectUiccSub;
+import android.hardware.radio.V1_0.SetupDataCallResult;
+import android.hardware.radio.V1_0.SimApdu;
+import android.hardware.radio.V1_0.SmsWriteArgs;
+import android.hardware.radio.V1_0.UusInfo;
+import android.hardware.radio.deprecated.V1_0.IOemHook;
 import android.net.ConnectivityManager;
-import android.net.LocalSocket;
-import android.net.LocalSocketAddress;
 import android.os.AsyncResult;
-import android.os.BatteryManager;
 import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
+import android.os.HwBinder;
 import android.os.Message;
 import android.os.Parcel;
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.CellInfo;
+import android.telephony.ClientRequestStats;
+import android.telephony.ImsiEncryptionInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.NeighboringCellInfo;
-import android.telephony.PcoData;
+import android.telephony.NetworkScanRequest;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.RadioAccessFamily;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.RadioNetworkConstants.RadioAccessNetworks;
 import android.telephony.Rlog;
 import android.telephony.SignalStrength;
 import android.telephony.SmsManager;
-import android.telephony.SmsMessage;
-import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.Log;
 import android.util.SparseArray;
-import android.view.Display;
 
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
-import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.cdma.CdmaInformationRecords;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.dataconnection.DataProfile;
-import com.android.internal.telephony.dataconnection.DcFailCause;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
-import com.android.internal.telephony.gsm.SsData;
-import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
-import com.android.internal.telephony.uicc.IccCardApplicationStatus;
-import com.android.internal.telephony.uicc.IccCardStatus;
-import com.android.internal.telephony.uicc.IccIoResult;
-import com.android.internal.telephony.uicc.IccRefreshResponse;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
 import com.android.internal.telephony.uicc.IccUtils;
 
 import java.io.ByteArrayInputStream;
 import java.io.DataInputStream;
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-
-import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
-import static com.android.internal.telephony.RILConstants.*;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * {@hide}
@@ -102,15 +121,15 @@
     private static RILRequest sPool = null;
     private static int sPoolSize = 0;
     private static final int MAX_POOL_SIZE = 4;
-    private Context mContext;
 
     //***** Instance Variables
     int mSerial;
     int mRequest;
     Message mResult;
-    Parcel mParcel;
     RILRequest mNext;
     int mWakeLockType;
+    WorkSource mWorkSource;
+    String mClientId;
     // time in ms when RIL request was made
     long mStartTimeMs;
 
@@ -121,7 +140,7 @@
      * @param result sent when operation completes
      * @return a RILRequest instance from the pool.
      */
-    static RILRequest obtain(int request, Message result) {
+    private static RILRequest obtain(int request, Message result) {
         RILRequest rr = null;
 
         synchronized(sPoolSync) {
@@ -141,17 +160,36 @@
 
         rr.mRequest = request;
         rr.mResult = result;
-        rr.mParcel = Parcel.obtain();
 
         rr.mWakeLockType = RIL.INVALID_WAKELOCK;
+        rr.mWorkSource = null;
         rr.mStartTimeMs = SystemClock.elapsedRealtime();
         if (result != null && result.getTarget() == null) {
             throw new NullPointerException("Message target must not be null");
         }
 
-        // first elements in any RIL Parcel
-        rr.mParcel.writeInt(request);
-        rr.mParcel.writeInt(rr.mSerial);
+        return rr;
+    }
+
+
+    /**
+     * Retrieves a new RILRequest instance from the pool and sets the clientId
+     *
+     * @param request RIL_REQUEST_*
+     * @param result sent when operation completes
+     * @param workSource WorkSource to track the client
+     * @return a RILRequest instance from the pool.
+     */
+    static RILRequest obtain(int request, Message result, WorkSource workSource) {
+        RILRequest rr = null;
+
+        rr = obtain(request, result);
+        if(workSource != null) {
+            rr.mWorkSource = workSource;
+            rr.mClientId = String.valueOf(workSource.get(0)) + ":" + workSource.getName(0);
+        } else {
+            Rlog.e(LOG_TAG, "null workSource " + request);
+        }
 
         return rr;
     }
@@ -224,11 +262,6 @@
             AsyncResult.forMessage(mResult, ret, ex);
             mResult.sendToTarget();
         }
-
-        if (mParcel != null) {
-            mParcel.recycle();
-            mParcel = null;
-        }
     }
 }
 
@@ -244,9 +277,6 @@
     static final String RILJ_ACK_WAKELOCK_NAME = "RILJ_ACK_WL";
     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;
     static final int RIL_HISTOGRAM_BUCKET_COUNT = 5;
 
     /**
@@ -264,18 +294,10 @@
     public static final int INVALID_WAKELOCK = -1;
     public static final int FOR_WAKELOCK = 0;
     public static final int FOR_ACK_WAKELOCK = 1;
+    private final ClientWakelockTracker mClientWakelockTracker = new ClientWakelockTracker();
 
     //***** Instance Variables
 
-    LocalSocket mSocket;
-    HandlerThread mSenderThread;
-    RILSender mSender;
-    Thread mReceiverThread;
-    RILReceiver mReceiver;
-    Display mDefaultDisplay;
-    int mDefaultDisplayState = Display.STATE_UNKNOWN;
-    int mRadioScreenState = RADIO_SCREEN_UNSET;
-    boolean mIsDevicePlugged = false;
     final WakeLock mWakeLock;           // Wake lock associated with request/response
     final WakeLock mAckWakeLock;        // Wake lock associated with ack sent
     final int mWakeLockTimeout;         // Timeout associated with request/response
@@ -297,69 +319,39 @@
     // When we are testing emergency calls
     AtomicBoolean mTestingEmergencyCall = new AtomicBoolean(false);
 
-    private Integer mInstanceId;
+    final Integer mPhoneId;
 
+    /* default work source which will blame phone process */
+    private WorkSource mRILDefaultWorkSource;
+
+    /* Worksource containing all applications causing wakelock to be held */
+    private WorkSource mActiveWakelockWorkSource;
+
+    /** Telephony metrics instance for logging metrics event */
     private TelephonyMetrics mMetrics = TelephonyMetrics.getInstance();
 
-    //***** Events
+    boolean mIsMobileNetworkSupported;
+    RadioResponse mRadioResponse;
+    RadioIndication mRadioIndication;
+    volatile IRadio mRadioProxy = null;
+    OemHookResponse mOemHookResponse;
+    OemHookIndication mOemHookIndication;
+    volatile IOemHook mOemHookProxy = null;
+    final AtomicLong mRadioProxyCookie = new AtomicLong(0);
+    final RadioProxyDeathRecipient mRadioProxyDeathRecipient;
+    final RilHandler mRilHandler;
 
-    static final int EVENT_SEND                 = 1;
+    //***** Events
     static final int EVENT_WAKE_LOCK_TIMEOUT    = 2;
-    static final int EVENT_SEND_ACK             = 3;
     static final int EVENT_ACK_WAKE_LOCK_TIMEOUT    = 4;
     static final int EVENT_BLOCKING_RESPONSE_TIMEOUT = 5;
+    static final int EVENT_RADIO_PROXY_DEAD     = 6;
 
     //***** Constants
 
-    // match with constant in ril.cpp
-    static final int RIL_MAX_COMMAND_BYTES = (8 * 1024);
-    static final int RESPONSE_SOLICITED = 0;
-    static final int RESPONSE_UNSOLICITED = 1;
-    static final int RESPONSE_SOLICITED_ACK = 2;
-    static final int RESPONSE_SOLICITED_ACK_EXP = 3;
-    static final int RESPONSE_UNSOLICITED_ACK_EXP = 4;
+    static final String[] HIDL_SERVICE_NAME = {"slot1", "slot2", "slot3"};
 
-    static final String[] SOCKET_NAME_RIL = {"rild", "rild2", "rild3"};
-
-    static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
-
-    // The number of the required config values for broadcast SMS stored in the C struct
-    // RIL_CDMA_BroadcastServiceInfo
-    private static final int CDMA_BSI_NO_OF_INTS_STRUCT = 3;
-
-    private static final int CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES = 31;
-
-    private final DisplayManager.DisplayListener mDisplayListener =
-            new DisplayManager.DisplayListener() {
-        @Override
-        public void onDisplayAdded(int displayId) { }
-
-        @Override
-        public void onDisplayRemoved(int displayId) { }
-
-        @Override
-        public void onDisplayChanged(int displayId) {
-            if (displayId == Display.DEFAULT_DISPLAY) {
-                final int oldState = mDefaultDisplayState;
-                mDefaultDisplayState = mDefaultDisplay.getState();
-                if (mDefaultDisplayState != oldState) {
-                    updateScreenState(false);
-                }
-            }
-        }
-    };
-
-    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(false);
-            }
-        }
-    };
+    static final int IRADIO_GET_SERVICE_DELAY_MILLIS = 4 * 1000;
 
     public static List<TelephonyHistogram> getTelephonyRILTimingHistograms() {
         List<TelephonyHistogram> list;
@@ -373,102 +365,13 @@
         return list;
     }
 
-    class RILSender extends Handler implements Runnable {
-        public RILSender(Looper looper) {
-            super(looper);
-        }
-
-        // Only allocated once
-        byte[] dataLength = new byte[4];
-
-        //***** Runnable implementation
-        @Override
-        public void
-        run() {
-            //setup if needed
-        }
-
-
+    class RilHandler extends Handler {
         //***** Handler implementation
         @Override public void
         handleMessage(Message msg) {
-            RILRequest rr = (RILRequest)(msg.obj);
-            RILRequest req = null;
+            RILRequest rr;
 
             switch (msg.what) {
-                case EVENT_SEND:
-                case EVENT_SEND_ACK:
-                    try {
-                        LocalSocket s;
-
-                        s = mSocket;
-
-                        if (s == null) {
-                            rr.onError(RADIO_NOT_AVAILABLE, null);
-                            decrementWakeLock(rr);
-                            rr.release();
-                            return;
-                        }
-
-                        // Acks should not be stored in list before sending
-                        if (msg.what != EVENT_SEND_ACK) {
-                            synchronized (mRequestList) {
-                                rr.mStartTimeMs = SystemClock.elapsedRealtime();
-                                mRequestList.append(rr.mSerial, rr);
-                            }
-                        }
-
-                        byte[] data;
-
-                        data = rr.mParcel.marshall();
-                        rr.mParcel.recycle();
-                        rr.mParcel = null;
-
-                        if (data.length > RIL_MAX_COMMAND_BYTES) {
-                            throw new RuntimeException(
-                                    "Parcel larger than max bytes allowed! "
-                                                          + data.length);
-                        }
-
-                        // parcel length in big endian
-                        dataLength[0] = dataLength[1] = 0;
-                        dataLength[2] = (byte)((data.length >> 8) & 0xff);
-                        dataLength[3] = (byte)((data.length) & 0xff);
-
-                        //Rlog.v(RILJ_LOG_TAG, "writing packet: " + data.length + " bytes");
-
-                        s.getOutputStream().write(dataLength);
-                        s.getOutputStream().write(data);
-                        if (msg.what == EVENT_SEND_ACK) {
-                            rr.release();
-                            return;
-                        }
-                    } catch (IOException ex) {
-                        Rlog.e(RILJ_LOG_TAG, "IOException", ex);
-                        req = findAndRemoveRequestFromList(rr.mSerial);
-                        // make sure this request has not already been handled,
-                        // eg, if RILReceiver cleared the list.
-                        if (req != null) {
-                            rr.onError(RADIO_NOT_AVAILABLE, null);
-                            decrementWakeLock(rr);
-                            rr.release();
-                            return;
-                        }
-                    } catch (RuntimeException exc) {
-                        Rlog.e(RILJ_LOG_TAG, "Uncaught exception ", exc);
-                        req = findAndRemoveRequestFromList(rr.mSerial);
-                        // make sure this request has not already been handled,
-                        // eg, if RILReceiver cleared the list.
-                        if (req != null) {
-                            rr.onError(GENERIC_FAILURE, null);
-                            decrementWakeLock(rr);
-                            rr.release();
-                            return;
-                        }
-                    }
-
-                    break;
-
                 case EVENT_WAKE_LOCK_TIMEOUT:
                     // Haven't heard back from the last request.  Assume we're
                     // not getting a response and  release the wake lock.
@@ -518,12 +421,25 @@
                         Object timeoutResponse = getResponseForTimedOutRILRequest(rr);
                         AsyncResult.forMessage( rr.mResult, timeoutResponse, null);
                         rr.mResult.sendToTarget();
-                        mMetrics.writeOnRilTimeoutResponse(mInstanceId, rr.mSerial, rr.mRequest);
+                        mMetrics.writeOnRilTimeoutResponse(mPhoneId, rr.mSerial, rr.mRequest);
                     }
 
                     decrementWakeLock(rr);
                     rr.release();
                     break;
+
+                case EVENT_RADIO_PROXY_DEAD:
+                    riljLog("handleMessage: EVENT_RADIO_PROXY_DEAD cookie = " + msg.obj +
+                            " mRadioProxyCookie = " + mRadioProxyCookie.get());
+                    if ((long) msg.obj == mRadioProxyCookie.get()) {
+                        resetProxyAndRequestList();
+
+                        // todo: rild should be back up since message was sent with a delay. this is
+                        // a hack.
+                        getRadioProxy(null);
+                        getOemHookProxy(null);
+                    }
+                    break;
             }
         }
     }
@@ -551,187 +467,129 @@
         return timeoutResponse;
     }
 
-    /**
-     * Reads in a single RIL message off the wire. A RIL message consists
-     * of a 4-byte little-endian length and a subsequent series of bytes.
-     * The final message (length header omitted) is read into
-     * <code>buffer</code> and the length of the final message (less header)
-     * is returned. A return value of -1 indicates end-of-stream.
-     *
-     * @param is non-null; Stream to read from
-     * @param buffer Buffer to fill in. Must be as large as maximum
-     * message size, or an ArrayOutOfBounds exception will be thrown.
-     * @return Length of message less header, or -1 on end of stream.
-     * @throws IOException
-     */
-    private static int readRilMessage(InputStream is, byte[] buffer)
-            throws IOException {
-        int countRead;
-        int offset;
-        int remaining;
-        int messageLength;
-
-        // First, read in the length of the message
-        offset = 0;
-        remaining = 4;
-        do {
-            countRead = is.read(buffer, offset, remaining);
-
-            if (countRead < 0 ) {
-                Rlog.e(RILJ_LOG_TAG, "Hit EOS reading message length");
-                return -1;
-            }
-
-            offset += countRead;
-            remaining -= countRead;
-        } while (remaining > 0);
-
-        messageLength = ((buffer[0] & 0xff) << 24)
-                | ((buffer[1] & 0xff) << 16)
-                | ((buffer[2] & 0xff) << 8)
-                | (buffer[3] & 0xff);
-
-        // Then, re-use the buffer and read in the message itself
-        offset = 0;
-        remaining = messageLength;
-        do {
-            countRead = is.read(buffer, offset, remaining);
-
-            if (countRead < 0 ) {
-                Rlog.e(RILJ_LOG_TAG, "Hit EOS reading message.  messageLength=" + messageLength
-                        + " remaining=" + remaining);
-                return -1;
-            }
-
-            offset += countRead;
-            remaining -= countRead;
-        } while (remaining > 0);
-
-        return messageLength;
-    }
-
-    class RILReceiver implements Runnable {
-        byte[] buffer;
-
-        RILReceiver() {
-            buffer = new byte[RIL_MAX_COMMAND_BYTES];
-        }
-
+    final class RadioProxyDeathRecipient implements HwBinder.DeathRecipient {
         @Override
-        public void
-        run() {
-            int retryCount = 0;
-            String rilSocket = "rild";
-
-            try {for (;;) {
-                LocalSocket s = null;
-                LocalSocketAddress l;
-
-                if (mInstanceId == null || mInstanceId == 0 ) {
-                    rilSocket = SOCKET_NAME_RIL[0];
-                } else {
-                    rilSocket = SOCKET_NAME_RIL[mInstanceId];
-                }
-
-                try {
-                    s = new LocalSocket();
-                    l = new LocalSocketAddress(rilSocket,
-                            LocalSocketAddress.Namespace.RESERVED);
-                    s.connect(l);
-                } catch (IOException ex){
-                    try {
-                        if (s != null) {
-                            s.close();
-                        }
-                    } catch (IOException ex2) {
-                        //ignore failure to close after failure to connect
-                    }
-
-                    // don't print an error message after the the first time
-                    // or after the 8th time
-
-                    if (retryCount == 8) {
-                        Rlog.e (RILJ_LOG_TAG,
-                            "Couldn't find '" + rilSocket
-                            + "' socket after " + retryCount
-                            + " times, continuing to retry silently");
-                    } else if (retryCount >= 0 && retryCount < 8) {
-                        Rlog.i (RILJ_LOG_TAG,
-                            "Couldn't find '" + rilSocket
-                            + "' socket; retrying after timeout");
-                    }
-
-                    try {
-                        Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
-                    } catch (InterruptedException er) {
-                    }
-
-                    retryCount++;
-                    continue;
-                }
-
-                retryCount = 0;
-
-                mSocket = s;
-                Rlog.i(RILJ_LOG_TAG, "(" + mInstanceId + ") Connected to '"
-                        + rilSocket + "' socket");
-
-                int length = 0;
-                try {
-                    InputStream is = mSocket.getInputStream();
-
-                    for (;;) {
-                        Parcel p;
-
-                        length = readRilMessage(is, buffer);
-
-                        if (length < 0) {
-                            // End-of-stream reached
-                            break;
-                        }
-
-                        p = Parcel.obtain();
-                        p.unmarshall(buffer, 0, length);
-                        p.setDataPosition(0);
-
-                        //Rlog.v(RILJ_LOG_TAG, "Read packet: " + length + " bytes");
-
-                        processResponse(p);
-                        p.recycle();
-                    }
-                } catch (java.io.IOException ex) {
-                    Rlog.i(RILJ_LOG_TAG, "'" + rilSocket + "' socket closed",
-                          ex);
-                } catch (Throwable tr) {
-                    Rlog.e(RILJ_LOG_TAG, "Uncaught exception read length=" + length +
-                        "Exception:" + tr.toString());
-                }
-
-                Rlog.i(RILJ_LOG_TAG, "(" + mInstanceId + ") Disconnected from '" + rilSocket
-                      + "' socket");
-
-                setRadioState (RadioState.RADIO_UNAVAILABLE);
-
-                try {
-                    mSocket.close();
-                } catch (IOException ex) {
-                }
-
-                mSocket = null;
-                RILRequest.resetSerial();
-
-                // Clear request list on close
-                clearRequestList(RADIO_NOT_AVAILABLE, false);
-            }} catch (Throwable tr) {
-                Rlog.e(RILJ_LOG_TAG,"Uncaught exception", tr);
-            }
-
-            /* We're disconnected so we don't know the ril version */
-            notifyRegistrantsRilConnectionChanged(-1);
+        public void serviceDied(long cookie) {
+            // Deal with service going away
+            riljLog("serviceDied");
+            // todo: temp hack to send delayed message so that rild is back up by then
+            //mRilHandler.sendMessage(mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie));
+            mRilHandler.sendMessageDelayed(
+                    mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD, cookie),
+                    IRADIO_GET_SERVICE_DELAY_MILLIS);
         }
     }
 
+    private void resetProxyAndRequestList() {
+        mRadioProxy = null;
+        mOemHookProxy = null;
 
+        // increment the cookie so that death notification can be ignored
+        mRadioProxyCookie.incrementAndGet();
+
+        setRadioState(RadioState.RADIO_UNAVAILABLE);
+
+        RILRequest.resetSerial();
+        // Clear request list on close
+        clearRequestList(RADIO_NOT_AVAILABLE, false);
+
+        // todo: need to get service right away so setResponseFunctions() can be called for
+        // unsolicited indications. getService() is not a blocking call, so it doesn't help to call
+        // it here. Current hack is to call getService() on death notification after a delay.
+    }
+
+    private IRadio getRadioProxy(Message result) {
+        if (!mIsMobileNetworkSupported) {
+            if (RILJ_LOGV) riljLog("getRadioProxy: Not calling getService(): wifi-only");
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
+            return null;
+        }
+
+        if (mRadioProxy != null) {
+            return mRadioProxy;
+        }
+
+        try {
+            mRadioProxy = IRadio.getService(HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
+            if (mRadioProxy != null) {
+                mRadioProxy.linkToDeath(mRadioProxyDeathRecipient,
+                        mRadioProxyCookie.incrementAndGet());
+                mRadioProxy.setResponseFunctions(mRadioResponse, mRadioIndication);
+            } else {
+                riljLoge("getRadioProxy: mRadioProxy == null");
+            }
+        } catch (RemoteException | RuntimeException e) {
+            mRadioProxy = null;
+            riljLoge("RadioProxy getService/setResponseFunctions: " + e);
+        }
+
+        if (mRadioProxy == null) {
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
+
+            // if service is not up, treat it like death notification to try to get service again
+            mRilHandler.sendMessageDelayed(
+                    mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+                            mRadioProxyCookie.incrementAndGet()),
+                    IRADIO_GET_SERVICE_DELAY_MILLIS);
+        }
+
+        return mRadioProxy;
+    }
+
+    private IOemHook getOemHookProxy(Message result) {
+        if (!mIsMobileNetworkSupported) {
+            if (RILJ_LOGV) riljLog("getOemHookProxy: Not calling getService(): wifi-only");
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
+            return null;
+        }
+
+        if (mOemHookProxy != null) {
+            return mOemHookProxy;
+        }
+
+        try {
+            mOemHookProxy = IOemHook.getService(
+                    HIDL_SERVICE_NAME[mPhoneId == null ? 0 : mPhoneId]);
+            if (mOemHookProxy != null) {
+                // not calling linkToDeath() as ril service runs in the same process and death
+                // notification for that should be sufficient
+                mOemHookProxy.setResponseFunctions(mOemHookResponse, mOemHookIndication);
+            } else {
+                riljLoge("getOemHookProxy: mOemHookProxy == null");
+            }
+        } catch (RemoteException | RuntimeException e) {
+            mOemHookProxy = null;
+            riljLoge("OemHookProxy getService/setResponseFunctions: " + e);
+        }
+
+        if (mOemHookProxy == null) {
+            if (result != null) {
+                AsyncResult.forMessage(result, null,
+                        CommandException.fromRilErrno(RADIO_NOT_AVAILABLE));
+                result.sendToTarget();
+            }
+
+            // if service is not up, treat it like death notification to try to get service again
+            mRilHandler.sendMessageDelayed(
+                    mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+                            mRadioProxyCookie.incrementAndGet()),
+                    IRADIO_GET_SERVICE_DELAY_MILLIS);
+        }
+
+        return mOemHookProxy;
+    }
 
     //***** Constructors
 
@@ -743,15 +601,26 @@
             int cdmaSubscription, Integer instanceId) {
         super(context);
         if (RILJ_LOGD) {
-            riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
-                    " cdmaSubscription=" + cdmaSubscription + ")");
+            riljLog("RIL: init preferredNetworkType=" + preferredNetworkType
+                    + " cdmaSubscription=" + cdmaSubscription + ")");
         }
 
         mContext = context;
         mCdmaSubscription  = cdmaSubscription;
         mPreferredNetworkType = preferredNetworkType;
         mPhoneType = RILConstants.NO_PHONE;
-        mInstanceId = instanceId;
+        mPhoneId = instanceId;
+
+        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        mIsMobileNetworkSupported = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
+        mRadioResponse = new RadioResponse(this);
+        mRadioIndication = new RadioIndication(this);
+        mOemHookResponse = new OemHookResponse(this);
+        mOemHookIndication = new OemHookIndication(this);
+        mRilHandler = new RilHandler();
+        mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG);
@@ -763,60 +632,16 @@
         mAckWakeLockTimeout = SystemProperties.getInt(
                 TelephonyProperties.PROPERTY_WAKE_LOCK_TIMEOUT, DEFAULT_ACK_WAKE_LOCK_TIMEOUT_MS);
         mWakeLockCount = 0;
-
-        mSenderThread = new HandlerThread("RILSender" + mInstanceId);
-        mSenderThread.start();
-
-        Looper looper = mSenderThread.getLooper();
-        mSender = new RILSender(looper);
-
-        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
-        if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
-            riljLog("Not starting RILReceiver: wifi-only");
-        } else {
-            riljLog("Starting RILReceiver" + mInstanceId);
-            mReceiver = new RILReceiver();
-            mReceiverThread = new Thread(mReceiver, "RILReceiver" + mInstanceId);
-            mReceiverThread.start();
-
-            DisplayManager dm = (DisplayManager)context.getSystemService(
-                    Context.DISPLAY_SERVICE);
-            mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
-            dm.registerDisplayListener(mDisplayListener, null);
-            mDefaultDisplayState = mDefaultDisplay.getState();
-
-            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;
-            }
-        }
+        mRILDefaultWorkSource = new WorkSource(context.getApplicationInfo().uid,
+                context.getPackageName());
 
         TelephonyDevController tdc = TelephonyDevController.getInstance();
         tdc.registerRIL(this);
-    }
 
-    //***** CommandsInterface implementation
-
-    @Override
-    public void getVoiceRadioTechnology(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_VOICE_RADIO_TECH, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-
-    public void getImsRegistrationState(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_REGISTRATION_STATE, result);
-
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-        }
-        send(rr);
+        // set radio callback; needed to set RadioIndication callback (should be done after
+        // wakelock stuff is initialized above as callbacks are received on separate binder threads)
+        getRadioProxy(null);
+        getOemHookProxy(null);
     }
 
     @Override public void
@@ -831,46 +656,52 @@
         }
     }
 
+    private void addRequest(RILRequest rr) {
+        acquireWakeLock(rr, FOR_WAKELOCK);
+        synchronized (mRequestList) {
+            rr.mStartTimeMs = SystemClock.elapsedRealtime();
+            mRequestList.append(rr.mSerial, rr);
+        }
+    }
+
+    private RILRequest obtainRequest(int request, Message result, WorkSource workSource) {
+        RILRequest rr = RILRequest.obtain(request, result, workSource);
+        addRequest(rr);
+        return rr;
+    }
+
+    private void handleRadioProxyExceptionForRR(RILRequest rr, String caller, Exception e) {
+        riljLoge(caller + ": " + e);
+        resetProxyAndRequestList();
+
+        // service most likely died, handle exception like death notification to try to get service
+        // again
+        mRilHandler.sendMessageDelayed(
+                mRilHandler.obtainMessage(EVENT_RADIO_PROXY_DEAD,
+                        mRadioProxyCookie.incrementAndGet()),
+                IRADIO_GET_SERVICE_DELAY_MILLIS);
+    }
+
+    private String convertNullToEmptyString(String string) {
+        return string != null ? string : "";
+    }
+
     @Override
     public void
     getIccCardStatus(Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SIM_STATUS, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        send(rr);
-    }
-
-    public void setUiccSubscription(int slotId, int appIndex, int subId,
-            int subStatus, Message result) {
-        //Note: This RIL request is also valid for SIM and RUIM (ICC card)
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " slot: " + slotId + " appIndex: " + appIndex
-                + " subId: " + subId + " subStatus: " + subStatus);
-
-        rr.mParcel.writeInt(slotId);
-        rr.mParcel.writeInt(appIndex);
-        rr.mParcel.writeInt(subId);
-        rr.mParcel.writeInt(subStatus);
-
-        send(rr);
-    }
-
-    // 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) +
-                    " allowed: " + allowed);
+            try {
+                radioProxy.getIccCardStatus(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getIccCardStatus", e);
+            }
         }
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(allowed ? 1 : 0);
-        send(rr);
     }
 
     @Override public void
@@ -880,38 +711,52 @@
 
     @Override public void
     supplyIccPinForApp(String pin, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN, result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
+            }
 
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeString(pin);
-        rr.mParcel.writeString(aid);
-
-        send(rr);
+            try {
+                radioProxy.supplyIccPinForApp(rr.mSerial,
+                        convertNullToEmptyString(pin),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "supplyIccPinForApp", e);
+            }
+        }
     }
 
-    @Override public void
-    supplyIccPuk(String puk, String newPin, Message result) {
+    @Override
+    public void supplyIccPuk(String puk, String newPin, Message result) {
         supplyIccPukForApp(puk, newPin, null, result);
     }
 
-    @Override public void
-    supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK, result);
+    @Override
+    public void supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
+            }
 
-        rr.mParcel.writeInt(3);
-        rr.mParcel.writeString(puk);
-        rr.mParcel.writeString(newPin);
-        rr.mParcel.writeString(aid);
-
-        send(rr);
+            try {
+                radioProxy.supplyIccPukForApp(rr.mSerial,
+                        convertNullToEmptyString(puk),
+                        convertNullToEmptyString(newPin),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "supplyIccPukForApp", e);
+            }
+        }
     }
 
     @Override public void
@@ -921,17 +766,24 @@
 
     @Override public void
     supplyIccPin2ForApp(String pin, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PIN2, result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PIN2, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
+            }
 
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeString(pin);
-        rr.mParcel.writeString(aid);
-
-        send(rr);
+            try {
+                radioProxy.supplyIccPin2ForApp(rr.mSerial,
+                        convertNullToEmptyString(pin),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "supplyIccPin2ForApp", e);
+            }
+        }
     }
 
     @Override public void
@@ -941,18 +793,25 @@
 
     @Override public void
     supplyIccPuk2ForApp(String puk, String newPin2, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_SIM_PUK2, result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_SIM_PUK2, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " aid = " + aid);
+            }
 
-        rr.mParcel.writeInt(3);
-        rr.mParcel.writeString(puk);
-        rr.mParcel.writeString(newPin2);
-        rr.mParcel.writeString(aid);
-
-        send(rr);
+            try {
+                radioProxy.supplyIccPuk2ForApp(rr.mSerial,
+                        convertNullToEmptyString(puk),
+                        convertNullToEmptyString(newPin2),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "supplyIccPuk2ForApp", e);
+            }
+        }
     }
 
     @Override public void
@@ -962,18 +821,25 @@
 
     @Override public void
     changeIccPinForApp(String oldPin, String newPin, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN, result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " oldPin = "
+                        + oldPin + " newPin = " + newPin + " aid = " + aid);
+            }
 
-        rr.mParcel.writeInt(3);
-        rr.mParcel.writeString(oldPin);
-        rr.mParcel.writeString(newPin);
-        rr.mParcel.writeString(aid);
-
-        send(rr);
+            try {
+                radioProxy.changeIccPinForApp(rr.mSerial,
+                        convertNullToEmptyString(oldPin),
+                        convertNullToEmptyString(newPin),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "changeIccPinForApp", e);
+            }
+        }
     }
 
     @Override public void
@@ -983,292 +849,2988 @@
 
     @Override public void
     changeIccPin2ForApp(String oldPin2, String newPin2, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_SIM_PIN2, result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_SIM_PIN2, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " oldPin = "
+                        + oldPin2 + " newPin = " + newPin2 + " aid = " + aid);
+            }
 
-        rr.mParcel.writeInt(3);
-        rr.mParcel.writeString(oldPin2);
-        rr.mParcel.writeString(newPin2);
-        rr.mParcel.writeString(aid);
-
-        send(rr);
+            try {
+                radioProxy.changeIccPin2ForApp(rr.mSerial,
+                        convertNullToEmptyString(oldPin2),
+                        convertNullToEmptyString(newPin2),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "changeIccPin2ForApp", e);
+            }
+        }
     }
 
     @Override
-    public void
-    changeBarringPassword(String facility, String oldPwd, String newPwd, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result);
+    public void supplyNetworkDepersonalization(String netpin, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " netpin = "
+                        + netpin);
+            }
 
-        rr.mParcel.writeInt(3);
-        rr.mParcel.writeString(facility);
-        rr.mParcel.writeString(oldPwd);
-        rr.mParcel.writeString(newPwd);
-
-        send(rr);
+            try {
+                radioProxy.supplyNetworkDepersonalization(rr.mSerial,
+                        convertNullToEmptyString(netpin));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "supplyNetworkDepersonalization", e);
+            }
+        }
     }
 
     @Override
-    public void
-    supplyNetworkDepersonalization(String netpin, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION, result);
+    public void getCurrentCalls(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CURRENT_CALLS, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
 
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeString(netpin);
-
-        send(rr);
+            try {
+                radioProxy.getCurrentCalls(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getCurrentCalls", e);
+            }
+        }
     }
 
     @Override
-    public void
-    getCurrentCalls (Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CURRENT_CALLS, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    @Deprecated public void
-    getPDPContextList(Message result) {
-        getDataCallList(result);
-    }
-
-    @Override
-    public void
-    getDataCallList(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DATA_CALL_LIST, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    dial (String address, int clirMode, Message result) {
+    public void dial(String address, int clirMode, Message result) {
         dial(address, clirMode, null, result);
     }
 
     @Override
-    public void
-    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
+    public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DIAL, result,
+                    mRILDefaultWorkSource);
 
-        rr.mParcel.writeString(address);
-        rr.mParcel.writeInt(clirMode);
+            Dial dialInfo = new Dial();
+            dialInfo.address = convertNullToEmptyString(address);
+            dialInfo.clir = clirMode;
+            if (uusInfo != null) {
+                UusInfo info = new UusInfo();
+                info.uusType = uusInfo.getType();
+                info.uusDcs = uusInfo.getDcs();
+                info.uusData = new String(uusInfo.getUserData());
+                dialInfo.uusInfo.add(info);
+            }
 
-        if (uusInfo == null) {
-            rr.mParcel.writeInt(0); // UUS information is absent
-        } else {
-            rr.mParcel.writeInt(1); // UUS information is present
-            rr.mParcel.writeInt(uusInfo.getType());
-            rr.mParcel.writeInt(uusInfo.getDcs());
-            rr.mParcel.writeByteArray(uusInfo.getUserData());
+            if (RILJ_LOGD) {
+                // Do not log function arg for privacy
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.dial(rr.mSerial, dialInfo);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "dial", e);
+            }
         }
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        mMetrics.writeRilDial(mInstanceId, rr.mSerial, clirMode, uusInfo);
-
-        send(rr);
     }
 
     @Override
-    public void
-    getIMSI(Message result) {
+    public void getIMSI(Message result) {
         getIMSIForApp(null, result);
     }
 
     @Override
-    public void
-    getIMSIForApp(String aid, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMSI, result);
+    public void getIMSIForApp(String aid, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_IMSI, result,
+                    mRILDefaultWorkSource);
 
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeString(aid);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() +
-                              "> getIMSI: " + requestToString(rr.mRequest)
-                              + " aid: " + aid);
-
-        send(rr);
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString()
+                        + ">  " + requestToString(rr.mRequest) + " aid = " + aid);
+            }
+            try {
+                radioProxy.getImsiForApp(rr.mSerial, convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getIMSIForApp", e);
+            }
+        }
     }
 
     @Override
-    public void
-    getIMEI(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEI, result);
+    public void hangupConnection(int gsmIndex, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " gsmIndex = "
+                        + gsmIndex);
+            }
 
-        send(rr);
+            try {
+                radioProxy.hangup(rr.mSerial, gsmIndex);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "hangupConnection", e);
+            }
+        }
     }
 
     @Override
-    public void
-    getIMEISV(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_IMEISV, result);
+    public void hangupWaitingOrBackground(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        send(rr);
-    }
-
-
-    @Override
-    public void
-    hangupConnection (int gsmIndex, Message result) {
-        if (RILJ_LOGD) riljLog("hangupConnection: gsmIndex=" + gsmIndex);
-
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +
-                gsmIndex);
-
-        mMetrics.writeRilHangup(mInstanceId, rr.mSerial, gsmIndex);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(gsmIndex);
-
-        send(rr);
+            try {
+                radioProxy.hangupWaitingOrBackground(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "hangupWaitingOrBackground", e);
+            }
+        }
     }
 
     @Override
-    public void
-    hangupWaitingOrBackground (Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
-                                        result);
+    public void hangupForegroundResumeBackground(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        mMetrics.writeRilHangup(mInstanceId, rr.mSerial, -1);
-
-        send(rr);
+            try {
+                radioProxy.hangupForegroundResumeBackground(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "hangupForegroundResumeBackground", e);
+            }
+        }
     }
 
     @Override
-    public void
-    hangupForegroundResumeBackground (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(
-                        RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
-                                        result);
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+    public void switchWaitingOrHoldingAndActive(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE, result,
+                    mRILDefaultWorkSource);
 
-        mMetrics.writeRilHangup(mInstanceId, rr.mSerial, -1);
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        send(rr);
+            try {
+                radioProxy.switchWaitingOrHoldingAndActive(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "switchWaitingOrHoldingAndActive", e);
+            }
+        }
     }
 
     @Override
-    public void
-    switchWaitingOrHoldingAndActive (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(
-                        RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE,
-                                        result);
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+    public void conference(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CONFERENCE, result,
+                    mRILDefaultWorkSource);
 
-        send(rr);
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.conference(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "conference", e);
+            }
+        }
     }
 
     @Override
-    public void
-    conference (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_CONFERENCE, result);
+    public void rejectCall(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_UDUB, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        send(rr);
+            try {
+                radioProxy.rejectCall(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "rejectCall", e);
+            }
+        }
     }
 
+    @Override
+    public void getLastCallFailCause(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getLastCallFailCause(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getLastCallFailCause", e);
+            }
+        }
+    }
+
+    @Override
+    public void getSignalStrength(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIGNAL_STRENGTH, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getSignalStrength(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getSignalStrength", e);
+            }
+        }
+    }
+
+    @Override
+    public void getVoiceRegistrationState(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_REGISTRATION_STATE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getVoiceRegistrationState(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getVoiceRegistrationState", e);
+            }
+        }
+    }
+
+    @Override
+    public void getDataRegistrationState(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DATA_REGISTRATION_STATE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getDataRegistrationState(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getDataRegistrationState", e);
+            }
+        }
+    }
+
+    @Override
+    public void getOperator(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_OPERATOR, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getOperator(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getOperator", e);
+            }
+        }
+    }
+
+    @Override
+    public void setRadioPower(boolean on, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_RADIO_POWER, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " on = " + on);
+            }
+
+            try {
+                radioProxy.setRadioPower(rr.mSerial, on);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setRadioPower", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendDtmf(char c, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DTMF, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                // Do not log function arg for privacy
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.sendDtmf(rr.mSerial, c + "");
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendDtmf", e);
+            }
+        }
+    }
+
+    private GsmSmsMessage constructGsmSendSmsRilRequest(String smscPdu, String pdu) {
+        GsmSmsMessage msg = new GsmSmsMessage();
+        msg.smscPdu = smscPdu == null ? "" : smscPdu;
+        msg.pdu = pdu == null ? "" : pdu;
+        return msg;
+    }
+
+    @Override
+    public void sendSMS(String smscPdu, String pdu, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log function args for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            GsmSmsMessage msg = constructGsmSendSmsRilRequest(smscPdu, pdu);
+
+            try {
+                radioProxy.sendSms(rr.mSerial, msg);
+                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendSMS", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendSMSExpectMore(String smscPdu, String pdu, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_SMS_EXPECT_MORE, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log function arg for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            GsmSmsMessage msg = constructGsmSendSmsRilRequest(smscPdu, pdu);
+
+            try {
+                radioProxy.sendSMSExpectMore(rr.mSerial, msg);
+                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendSMSExpectMore", e);
+            }
+        }
+    }
+
+    /**
+     * Convert MVNO type string into MvnoType defined in types.hal.
+     * @param mvnoType MVNO type
+     * @return MVNO type in integer
+     */
+    private static int convertToHalMvnoType(String mvnoType) {
+        switch (mvnoType) {
+            case "imsi" : return MvnoType.IMSI;
+            case "gid" : return MvnoType.GID;
+            case "spn" : return MvnoType.SPN;
+            default: return MvnoType.NONE;
+        }
+    }
+
+    /**
+     * Convert to DataProfileInfo defined in types.hal
+     * @param dp Data profile
+     * @return A converted data profile
+     */
+    private static DataProfileInfo convertToHalDataProfile(DataProfile dp) {
+        DataProfileInfo dpi = new DataProfileInfo();
+
+        dpi.profileId = dp.profileId;
+        dpi.apn = dp.apn;
+        dpi.protocol = dp.protocol;
+        dpi.roamingProtocol = dp.roamingProtocol;
+        dpi.authType = dp.authType;
+        dpi.user = dp.user;
+        dpi.password = dp.password;
+        dpi.type = dp.type;
+        dpi.maxConnsTime = dp.maxConnsTime;
+        dpi.maxConns = dp.maxConns;
+        dpi.waitTime = dp.waitTime;
+        dpi.enabled = dp.enabled;
+        dpi.supportedApnTypesBitmap = dp.supportedApnTypesBitmap;
+        dpi.bearerBitmap = dp.bearerBitmap;
+        dpi.mtu = dp.mtu;
+        dpi.mvnoType = convertToHalMvnoType(dp.mvnoType);
+        dpi.mvnoMatchData = dp.mvnoMatchData;
+
+        return dpi;
+    }
+
+    /**
+     * Convert NV reset type into ResetNvType defined in types.hal.
+     * @param resetType NV reset type.
+     * @return Converted reset type in integer or -1 if param is invalid.
+     */
+    private static int convertToHalResetNvType(int resetType) {
+        /**
+         * resetType values
+         * 1 - reload all NV items
+         * 2 - erase NV reset (SCRTN)
+         * 3 - factory reset (RTN)
+         */
+        switch (resetType) {
+            case 1: return ResetNvType.RELOAD;
+            case 2: return ResetNvType.ERASE;
+            case 3: return ResetNvType.FACTORY_RESET;
+        }
+        return -1;
+    }
+
+    /**
+     * Convert SetupDataCallResult defined in types.hal into DataCallResponse
+     * @param dcResult setup data call result
+     * @return converted DataCallResponse object
+     */
+    static DataCallResponse convertDataCallResult(SetupDataCallResult dcResult) {
+        return new DataCallResponse(dcResult.status,
+                dcResult.suggestedRetryTime,
+                dcResult.cid,
+                dcResult.active,
+                dcResult.type,
+                dcResult.ifname,
+                dcResult.addresses,
+                dcResult.dnses,
+                dcResult.gateways,
+                dcResult.pcscf,
+                dcResult.mtu
+        );
+    }
+
+    @Override
+    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, Message result) {
+
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+
+            RILRequest rr = obtainRequest(RIL_REQUEST_SETUP_DATA_CALL, result,
+                    mRILDefaultWorkSource);
+
+            // Convert to HAL data profile
+            DataProfileInfo dpi = convertToHalDataProfile(dataProfile);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + ",radioTechnology=" + radioTechnology + ",isRoaming="
+                        + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile);
+            }
+
+            try {
+                radioProxy.setupDataCall(rr.mSerial, radioTechnology, dpi,
+                        dataProfile.modemCognitive, allowRoaming, isRoaming);
+                mMetrics.writeRilSetupDataCall(mPhoneId, rr.mSerial, radioTechnology, dpi.profileId,
+                        dpi.apn, dpi.authType, dpi.protocol);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setupDataCall", e);
+            }
+        }
+    }
+
+    @Override
+    public void iccIO(int command, int fileId, String path, int p1, int p2, int p3,
+                      String data, String pin2, Message result) {
+        iccIOForApp(command, fileId, path, p1, p2, p3, data, pin2, null, result);
+    }
+
+    @Override
+    public void iccIOForApp(int command, int fileId, String path, int p1, int p2, int p3,
+                 String data, String pin2, String aid, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_IO, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> iccIO: "
+                        + requestToString(rr.mRequest) + " command = 0x"
+                        + Integer.toHexString(command) + " fileId = 0x"
+                        + Integer.toHexString(fileId) + " path = " + path + " p1 = "
+                        + p1 + " p2 = " + p2 + " p3 = " + " data = " + data
+                        + " aid = " + aid);
+            }
+
+            IccIo iccIo = new IccIo();
+            iccIo.command = command;
+            iccIo.fileId = fileId;
+            iccIo.path = convertNullToEmptyString(path);
+            iccIo.p1 = p1;
+            iccIo.p2 = p2;
+            iccIo.p3 = p3;
+            iccIo.data = convertNullToEmptyString(data);
+            iccIo.pin2 = convertNullToEmptyString(pin2);
+            iccIo.aid = convertNullToEmptyString(aid);
+
+            try {
+                radioProxy.iccIOForApp(rr.mSerial, iccIo);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "iccIOForApp", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendUSSD(String ussd, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_USSD, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                String logUssd = "*******";
+                if (RILJ_LOGV) logUssd = ussd;
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " ussd = " + logUssd);
+            }
+
+            try {
+                radioProxy.sendUssd(rr.mSerial, convertNullToEmptyString(ussd));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendUSSD", e);
+            }
+        }
+    }
+
+    @Override
+    public void cancelPendingUssd(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CANCEL_USSD, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString()
+                        + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.cancelPendingUssd(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "cancelPendingUssd", e);
+            }
+        }
+    }
+
+    @Override
+    public void getCLIR(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CLIR, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getClir(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getCLIR", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCLIR(int clirMode, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CLIR, result, mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " clirMode = " + clirMode);
+            }
+
+            try {
+                radioProxy.setClir(rr.mSerial, clirMode);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCLIR", e);
+            }
+        }
+    }
+
+    @Override
+    public void queryCallForwardStatus(int cfReason, int serviceClass,
+                           String number, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " cfreason = " + cfReason + " serviceClass = " + serviceClass);
+            }
+
+            android.hardware.radio.V1_0.CallForwardInfo cfInfo =
+                    new android.hardware.radio.V1_0.CallForwardInfo();
+            cfInfo.reason = cfReason;
+            cfInfo.serviceClass = serviceClass;
+            cfInfo.toa = PhoneNumberUtils.toaFromString(number);
+            cfInfo.number = convertNullToEmptyString(number);
+            cfInfo.timeSeconds = 0;
+
+            try {
+                radioProxy.getCallForwardStatus(rr.mSerial, cfInfo);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "queryCallForwardStatus", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCallForward(int action, int cfReason, int serviceClass,
+                   String number, int timeSeconds, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_FORWARD, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " action = " + action + " cfReason = " + cfReason + " serviceClass = "
+                        + serviceClass + " timeSeconds = " + timeSeconds);
+            }
+
+            android.hardware.radio.V1_0.CallForwardInfo cfInfo =
+                    new android.hardware.radio.V1_0.CallForwardInfo();
+            cfInfo.status = action;
+            cfInfo.reason = cfReason;
+            cfInfo.serviceClass = serviceClass;
+            cfInfo.toa = PhoneNumberUtils.toaFromString(number);
+            cfInfo.number = convertNullToEmptyString(number);
+            cfInfo.timeSeconds = timeSeconds;
+
+            try {
+                radioProxy.setCallForward(rr.mSerial, cfInfo);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCallForward", e);
+
+            }
+        }
+    }
+
+    @Override
+    public void queryCallWaiting(int serviceClass, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CALL_WAITING, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " serviceClass = " + serviceClass);
+            }
+
+            try {
+                radioProxy.getCallWaiting(rr.mSerial, serviceClass);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "queryCallWaiting", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCallWaiting(boolean enable, int serviceClass, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_CALL_WAITING, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " enable = " + enable + " serviceClass = " + serviceClass);
+            }
+
+            try {
+                radioProxy.setCallWaiting(rr.mSerial, enable, serviceClass);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCallWaiting", e);
+            }
+        }
+    }
+
+    @Override
+    public void acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SMS_ACKNOWLEDGE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " success = " + success + " cause = " + cause);
+            }
+
+            try {
+                radioProxy.acknowledgeLastIncomingGsmSms(rr.mSerial, success, cause);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "acknowledgeLastIncomingGsmSms", e);
+            }
+        }
+    }
+
+    @Override
+    public void acceptCall(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ANSWER, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.acceptCall(rr.mSerial);
+                mMetrics.writeRilAnswer(mPhoneId, rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "acceptCall", e);
+            }
+        }
+    }
+
+    @Override
+    public void deactivateDataCall(int cid, int reason, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DEACTIVATE_DATA_CALL, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest) + " cid = " + cid + " reason = " + reason);
+            }
+
+            try {
+                radioProxy.deactivateDataCall(rr.mSerial, cid, (reason == 0) ? false : true);
+                mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial,
+                        cid, reason);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "deactivateDataCall", e);
+            }
+        }
+    }
+
+    @Override
+    public void queryFacilityLock(String facility, String password, int serviceClass,
+                                  Message result) {
+        queryFacilityLockForApp(facility, password, serviceClass, null, result);
+    }
+
+    @Override
+    public void queryFacilityLockForApp(String facility, String password, int serviceClass,
+                                        String appId, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_FACILITY_LOCK, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " facility = " + facility + " serviceClass = " + serviceClass
+                        + " appId = " + appId);
+            }
+
+            try {
+                radioProxy.getFacilityLockForApp(rr.mSerial,
+                        convertNullToEmptyString(facility),
+                        convertNullToEmptyString(password),
+                        serviceClass,
+                        convertNullToEmptyString(appId));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getFacilityLockForApp", e);
+            }
+        }
+    }
+
+    @Override
+    public void setFacilityLock(String facility, boolean lockState, String password,
+                                int serviceClass, Message result) {
+        setFacilityLockForApp(facility, lockState, password, serviceClass, null, result);
+    }
+
+    @Override
+    public void setFacilityLockForApp(String facility, boolean lockState, String password,
+                                      int serviceClass, String appId, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_FACILITY_LOCK, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " facility = " + facility + " lockstate = " + lockState
+                        + " serviceClass = " + serviceClass + " appId = " + appId);
+            }
+
+            try {
+                radioProxy.setFacilityLockForApp(rr.mSerial,
+                        convertNullToEmptyString(facility),
+                        lockState,
+                        convertNullToEmptyString(password),
+                        serviceClass,
+                        convertNullToEmptyString(appId));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setFacilityLockForApp", e);
+            }
+        }
+    }
+
+    @Override
+    public void changeBarringPassword(String facility, String oldPwd, String newPwd,
+                                      Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CHANGE_BARRING_PASSWORD, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log all function args for privacy
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + "facility = " + facility);
+            }
+
+            try {
+                radioProxy.setBarringPassword(rr.mSerial,
+                        convertNullToEmptyString(facility),
+                        convertNullToEmptyString(oldPwd),
+                        convertNullToEmptyString(newPwd));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "changeBarringPassword", e);
+            }
+        }
+    }
+
+    @Override
+    public void getNetworkSelectionMode(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getNetworkSelectionMode(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getNetworkSelectionMode", e);
+            }
+        }
+    }
+
+    @Override
+    public void setNetworkSelectionModeAutomatic(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.setNetworkSelectionModeAutomatic(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setNetworkSelectionModeAutomatic", e);
+            }
+        }
+    }
+
+    @Override
+    public void setNetworkSelectionModeManual(String operatorNumeric, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " operatorNumeric = " + operatorNumeric);
+            }
+
+            try {
+                radioProxy.setNetworkSelectionModeManual(rr.mSerial,
+                        convertNullToEmptyString(operatorNumeric));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setNetworkSelectionModeManual", e);
+            }
+        }
+    }
+
+    @Override
+    public void getAvailableNetworks(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getAvailableNetworks(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getAvailableNetworks", e);
+            }
+        }
+    }
+
+    @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            android.hardware.radio.V1_1.IRadio radioProxy11 =
+                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+            if (radioProxy11 == null) {
+                if (result != null) {
+                    AsyncResult.forMessage(result, null,
+                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                    result.sendToTarget();
+                }
+            } else {
+                android.hardware.radio.V1_1.NetworkScanRequest request =
+                        new android.hardware.radio.V1_1.NetworkScanRequest();
+                request.type = nsr.scanType;
+                request.interval = 60;
+                for (RadioAccessSpecifier ras : nsr.specifiers) {
+                    android.hardware.radio.V1_1.RadioAccessSpecifier s =
+                            new android.hardware.radio.V1_1.RadioAccessSpecifier();
+                    s.radioAccessNetwork = ras.radioAccessNetwork;
+                    List<Integer> bands = null;
+                    switch (ras.radioAccessNetwork) {
+                        case RadioAccessNetworks.GERAN:
+                            bands = s.geranBands;
+                            break;
+                        case RadioAccessNetworks.UTRAN:
+                            bands = s.utranBands;
+                            break;
+                        case RadioAccessNetworks.EUTRAN:
+                            bands = s.eutranBands;
+                            break;
+                        default:
+                            Log.wtf(RILJ_LOG_TAG, "radioAccessNetwork " + ras.radioAccessNetwork
+                                    + " not supported!");
+                            return;
+                    }
+                    if (ras.bands != null) {
+                        for (int band : ras.bands) {
+                            bands.add(band);
+                        }
+                    }
+                    if (ras.channels != null) {
+                        for (int channel : ras.channels) {
+                            s.channels.add(channel);
+                        }
+                    }
+                    request.specifiers.add(s);
+                }
+
+                RILRequest rr = obtainRequest(RIL_REQUEST_START_NETWORK_SCAN, result,
+                        mRILDefaultWorkSource);
+
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                }
+
+                try {
+                    radioProxy11.startNetworkScan(rr.mSerial, request);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "startNetworkScan", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void stopNetworkScan(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            android.hardware.radio.V1_1.IRadio radioProxy11 =
+                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+            if (radioProxy11 == null) {
+                if (result != null) {
+                    AsyncResult.forMessage(result, null,
+                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                    result.sendToTarget();
+                }
+            } else {
+                RILRequest rr = obtainRequest(RIL_REQUEST_STOP_NETWORK_SCAN, result,
+                        mRILDefaultWorkSource);
+
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+                }
+
+                try {
+                    radioProxy11.stopNetworkScan(rr.mSerial);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "stopNetworkScan", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void startDtmf(char c, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_START, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log function arg for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.startDtmf(rr.mSerial, c + "");
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "startDtmf", e);
+            }
+        }
+    }
+
+    @Override
+    public void stopDtmf(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DTMF_STOP, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.stopDtmf(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "stopDtmf", e);
+            }
+        }
+    }
+
+    @Override
+    public void separateConnection(int gsmIndex, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SEPARATE_CONNECTION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " gsmIndex = " + gsmIndex);
+            }
+
+            try {
+                radioProxy.separateConnection(rr.mSerial, gsmIndex);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "separateConnection", e);
+            }
+        }
+    }
+
+    @Override
+    public void getBasebandVersion(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_BASEBAND_VERSION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getBasebandVersion(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getBasebandVersion", e);
+            }
+        }
+    }
+
+    @Override
+    public void setMute(boolean enableMute, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_MUTE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " enableMute = " + enableMute);
+            }
+
+            try {
+                radioProxy.setMute(rr.mSerial, enableMute);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setMute", e);
+            }
+        }
+    }
+
+    @Override
+    public void getMute(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_MUTE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getMute(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getMute", e);
+            }
+        }
+    }
+
+    @Override
+    public void queryCLIP(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_CLIP, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getClip(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "queryCLIP", e);
+            }
+        }
+    }
+
+    /**
+     * @deprecated
+     */
+    @Override
+    @Deprecated
+    public void getPDPContextList(Message result) {
+        getDataCallList(result);
+    }
+
+    @Override
+    public void getDataCallList(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DATA_CALL_LIST, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getDataCallList(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getDataCallList", e);
+            }
+        }
+    }
+
+    @Override
+    public void invokeOemRilRequestRaw(byte[] data, Message response) {
+        IOemHook oemHookProxy = getOemHookProxy(response);
+        if (oemHookProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_RAW, response,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + "[" + IccUtils.bytesToHexString(data) + "]");
+            }
+
+            try {
+                oemHookProxy.sendRequestRaw(rr.mSerial, primitiveArrayToArrayList(data));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
+            }
+        }
+    }
+
+    @Override
+    public void invokeOemRilRequestStrings(String[] strings, Message result) {
+        IOemHook oemHookProxy = getOemHookProxy(result);
+        if (oemHookProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_OEM_HOOK_STRINGS, result,
+                    mRILDefaultWorkSource);
+
+            String logStr = "";
+            for (int i = 0; i < strings.length; i++) {
+                logStr = logStr + strings[i] + " ";
+            }
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " strings = "
+                        + logStr);
+            }
+
+            try {
+                oemHookProxy.sendRequestStrings(rr.mSerial,
+                        new ArrayList<String>(Arrays.asList(strings)));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "invokeOemRilRequestStrings", e);
+            }
+        }
+    }
+
+    @Override
+    public void setSuppServiceNotifications(boolean enable, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " enable = "
+                        + enable);
+            }
+
+            try {
+                radioProxy.setSuppServiceNotifications(rr.mSerial, enable);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setSuppServiceNotifications", e);
+            }
+        }
+    }
+
+    @Override
+    public void writeSmsToSim(int status, String smsc, String pdu, Message result) {
+        status = translateStatus(status);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_WRITE_SMS_TO_SIM, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGV) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest)
+                        + " " + status);
+            }
+
+            SmsWriteArgs args = new SmsWriteArgs();
+            args.status = status;
+            args.smsc = convertNullToEmptyString(smsc);
+            args.pdu = convertNullToEmptyString(pdu);
+
+            try {
+                radioProxy.writeSmsToSim(rr.mSerial, args);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "writeSmsToSim", e);
+            }
+        }
+    }
+
+    @Override
+    public void deleteSmsOnSim(int index, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DELETE_SMS_ON_SIM, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGV) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest) + " index = " + index);
+            }
+
+            try {
+                radioProxy.deleteSmsOnSim(rr.mSerial, index);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "deleteSmsOnSim", e);
+            }
+        }
+    }
+
+    @Override
+    public void setBandMode(int bandMode, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_BAND_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " bandMode = " + bandMode);
+            }
+
+            try {
+                radioProxy.setBandMode(rr.mSerial, bandMode);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setBandMode", e);
+            }
+        }
+    }
+
+    @Override
+    public void queryAvailableBandMode(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getAvailableBandModes(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "queryAvailableBandMode", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendEnvelope(String contents, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
+                        + contents);
+            }
+
+            try {
+                radioProxy.sendEnvelope(rr.mSerial, convertNullToEmptyString(contents));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendEnvelope", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendTerminalResponse(String contents, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
+                        + contents);
+            }
+
+            try {
+                radioProxy.sendTerminalResponseToSim(rr.mSerial,
+                        convertNullToEmptyString(contents));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendTerminalResponse", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendEnvelopeWithStatus(String contents, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " contents = "
+                        + contents);
+            }
+
+            try {
+                radioProxy.sendEnvelopeWithStatus(rr.mSerial, convertNullToEmptyString(contents));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendEnvelopeWithStatus", e);
+            }
+        }
+    }
+
+    @Override
+    public void explicitCallTransfer(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.explicitCallTransfer(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "explicitCallTransfer", e);
+            }
+        }
+    }
+
+    @Override
+    public void setPreferredNetworkType(int networkType , Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " networkType = " + networkType);
+            }
+            mPreferredNetworkType = networkType;
+            mMetrics.writeSetPreferredNetworkType(mPhoneId, networkType);
+
+            try {
+                radioProxy.setPreferredNetworkType(rr.mSerial, networkType);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setPreferredNetworkType", e);
+            }
+        }
+    }
+
+    @Override
+    public void getPreferredNetworkType(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getPreferredNetworkType(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getPreferredNetworkType", e);
+            }
+        }
+    }
+
+    @Override
+    public void getNeighboringCids(Message result, WorkSource workSource) {
+        workSource = getDeafultWorkSourceIfInvalid(workSource);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, result,
+                    workSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getNeighboringCids(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getNeighboringCids", e);
+            }
+        }
+    }
+
+    @Override
+    public void setLocationUpdates(boolean enable, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_LOCATION_UPDATES, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest) + " enable = " + enable);
+            }
+
+            try {
+                radioProxy.setLocationUpdates(rr.mSerial, enable);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setLocationUpdates", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCdmaSubscriptionSource(int cdmaSubscription , Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " cdmaSubscription = " + cdmaSubscription);
+            }
+
+            try {
+                radioProxy.setCdmaSubscriptionSource(rr.mSerial, cdmaSubscription);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCdmaSubscriptionSource", e);
+            }
+        }
+    }
+
+    @Override
+    public void queryCdmaRoamingPreference(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getCdmaRoamingPreference(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "queryCdmaRoamingPreference", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCdmaRoamingPreference(int cdmaRoamingType, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " cdmaRoamingType = " + cdmaRoamingType);
+            }
+
+            try {
+                radioProxy.setCdmaRoamingPreference(rr.mSerial, cdmaRoamingType);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCdmaRoamingPreference", e);
+            }
+        }
+    }
+
+    @Override
+    public void queryTTYMode(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_QUERY_TTY_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getTTYMode(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "queryTTYMode", e);
+            }
+        }
+    }
+
+    @Override
+    public void setTTYMode(int ttyMode, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_TTY_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " ttyMode = " + ttyMode);
+            }
+
+            try {
+                radioProxy.setTTYMode(rr.mSerial, ttyMode);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setTTYMode", e);
+            }
+        }
+    }
 
     @Override
     public void setPreferredVoicePrivacy(boolean enable, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE,
-                result);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE, result,
+                    mRILDefaultWorkSource);
 
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(enable ? 1:0);
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " enable = " + enable);
+            }
 
-        send(rr);
+            try {
+                radioProxy.setPreferredVoicePrivacy(rr.mSerial, enable);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setPreferredVoicePrivacy", e);
+            }
+        }
     }
 
     @Override
     public void getPreferredVoicePrivacy(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
-                result);
-        send(rr);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE,
+                    result, mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getPreferredVoicePrivacy(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getPreferredVoicePrivacy", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendCDMAFeatureCode(String featureCode, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_FLASH, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " featureCode = " + featureCode);
+            }
+
+            try {
+                radioProxy.sendCDMAFeatureCode(rr.mSerial, convertNullToEmptyString(featureCode));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendCDMAFeatureCode", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BURST_DTMF, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " dtmfString = " + dtmfString + " on = " + on + " off = " + off);
+            }
+
+            try {
+                radioProxy.sendBurstDtmf(rr.mSerial, convertNullToEmptyString(dtmfString), on, off);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendBurstDtmf", e);
+            }
+        }
+    }
+
+    private void constructCdmaSendSmsRilRequest(CdmaSmsMessage msg, byte[] pdu) {
+        int addrNbrOfDigits;
+        int subaddrNbrOfDigits;
+        int bearerDataLength;
+        ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
+        DataInputStream dis = new DataInputStream(bais);
+
+        try {
+            msg.teleserviceId = dis.readInt(); // teleServiceId
+            msg.isServicePresent = (byte) dis.readInt() == 1 ? true : false; // servicePresent
+            msg.serviceCategory = dis.readInt(); // serviceCategory
+            msg.address.digitMode = dis.read();  // address digit mode
+            msg.address.numberMode = dis.read(); // address number mode
+            msg.address.numberType = dis.read(); // address number type
+            msg.address.numberPlan = dis.read(); // address number plan
+            addrNbrOfDigits = (byte) dis.read();
+            for (int i = 0; i < addrNbrOfDigits; i++) {
+                msg.address.digits.add(dis.readByte()); // address_orig_bytes[i]
+            }
+            msg.subAddress.subaddressType = dis.read(); //subaddressType
+            msg.subAddress.odd = (byte) dis.read() == 1 ? true : false; //subaddr odd
+            subaddrNbrOfDigits = (byte) dis.read();
+            for (int i = 0; i < subaddrNbrOfDigits; i++) {
+                msg.subAddress.digits.add(dis.readByte()); //subaddr_orig_bytes[i]
+            }
+
+            bearerDataLength = dis.read();
+            for (int i = 0; i < bearerDataLength; i++) {
+                msg.bearerData.add(dis.readByte()); //bearerData[i]
+            }
+        } catch (IOException ex) {
+            if (RILJ_LOGD) {
+                riljLog("sendSmsCdma: conversion from input stream to object failed: "
+                        + ex);
+            }
+        }
+    }
+
+    @Override
+    public void sendCdmaSms(byte[] pdu, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SEND_SMS, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log function arg for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            CdmaSmsMessage msg = new CdmaSmsMessage();
+            constructCdmaSendSmsRilRequest(msg, pdu);
+
+            try {
+                radioProxy.sendCdmaSms(rr.mSerial, msg);
+                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP2);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendCdmaSms", e);
+            }
+        }
+    }
+
+    @Override
+    public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " success = " + success + " cause = " + cause);
+            }
+
+            CdmaSmsAck msg = new CdmaSmsAck();
+            msg.errorClass = success ? 0 : 1;
+            msg.smsCauseCode = cause;
+
+            try {
+                radioProxy.acknowledgeLastIncomingCdmaSms(rr.mSerial, msg);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "acknowledgeLastIncomingCdmaSms", e);
+            }
+        }
+    }
+
+    @Override
+    public void getGsmBroadcastConfig(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getGsmBroadcastConfig(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getGsmBroadcastConfig", e);
+            }
+        }
+    }
+
+    @Override
+    public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " with " + config.length + " configs : ");
+                for (int i = 0; i < config.length; i++) {
+                    riljLog(config[i].toString());
+                }
+            }
+
+            ArrayList<GsmBroadcastSmsConfigInfo> configs = new ArrayList<>();
+
+            int numOfConfig = config.length;
+            GsmBroadcastSmsConfigInfo info;
+
+            for (int i = 0; i < numOfConfig; i++) {
+                info = new GsmBroadcastSmsConfigInfo();
+                info.fromServiceId = config[i].getFromServiceId();
+                info.toServiceId = config[i].getToServiceId();
+                info.fromCodeScheme = config[i].getFromCodeScheme();
+                info.toCodeScheme = config[i].getToCodeScheme();
+                info.selected = config[i].isSelected();
+                configs.add(info);
+            }
+
+            try {
+                radioProxy.setGsmBroadcastConfig(rr.mSerial, configs);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setGsmBroadcastConfig", e);
+            }
+        }
+    }
+
+    @Override
+    public void setGsmBroadcastActivation(boolean activate, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " activate = " + activate);
+            }
+
+            try {
+                radioProxy.setGsmBroadcastActivation(rr.mSerial, activate);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setGsmBroadcastActivation", e);
+            }
+        }
+    }
+
+    @Override
+    public void getCdmaBroadcastConfig(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getCdmaBroadcastConfig(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getCdmaBroadcastConfig", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, result,
+                    mRILDefaultWorkSource);
+
+            ArrayList<CdmaBroadcastSmsConfigInfo> halConfigs = new ArrayList<>();
+
+            for (CdmaSmsBroadcastConfigInfo config: configs) {
+                for (int i = config.getFromServiceCategory();
+                        i <= config.getToServiceCategory();
+                        i++) {
+                    CdmaBroadcastSmsConfigInfo info = new CdmaBroadcastSmsConfigInfo();
+                    info.serviceCategory = i;
+                    info.language = config.getLanguage();
+                    info.selected = config.isSelected();
+                    halConfigs.add(info);
+                }
+            }
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " with " + halConfigs.size() + " configs : ");
+                for (CdmaBroadcastSmsConfigInfo config : halConfigs) {
+                    riljLog(config.toString());
+                }
+            }
+
+            try {
+                radioProxy.setCdmaBroadcastConfig(rr.mSerial, halConfigs);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCdmaBroadcastConfig", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCdmaBroadcastActivation(boolean activate, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " activate = " + activate);
+            }
+
+            try {
+                radioProxy.setCdmaBroadcastActivation(rr.mSerial, activate);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCdmaBroadcastActivation", e);
+            }
+        }
+    }
+
+    @Override
+    public void getCDMASubscription(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_SUBSCRIPTION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getCDMASubscription(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getCDMASubscription", e);
+            }
+        }
+    }
+
+    @Override
+    public void writeSmsToRuim(int status, String pdu, Message result) {
+        status = translateStatus(status);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGV) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest)
+                        + " status = " + status);
+            }
+
+            CdmaSmsWriteArgs args = new CdmaSmsWriteArgs();
+            args.status = status;
+            constructCdmaSendSmsRilRequest(args.message, pdu.getBytes());
+
+            try {
+                radioProxy.writeSmsToRuim(rr.mSerial, args);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "writeSmsToRuim", e);
+            }
+        }
+    }
+
+    @Override
+    public void deleteSmsOnRuim(int index, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGV) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest)
+                        + " index = " + index);
+            }
+
+            try {
+                radioProxy.deleteSmsOnRuim(rr.mSerial, index);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "deleteSmsOnRuim", e);
+            }
+        }
+    }
+
+    @Override
+    public void getDeviceIdentity(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_DEVICE_IDENTITY, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getDeviceIdentity(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getDeviceIdentity", e);
+            }
+        }
+    }
+
+    @Override
+    public void exitEmergencyCallbackMode(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.exitEmergencyCallbackMode(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "exitEmergencyCallbackMode", e);
+            }
+        }
+    }
+
+    @Override
+    public void getSmscAddress(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_SMSC_ADDRESS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getSmscAddress(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getSmscAddress", e);
+            }
+        }
+    }
+
+    @Override
+    public void setSmscAddress(String address, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SMSC_ADDRESS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " address = " + address);
+            }
+
+            try {
+                radioProxy.setSmscAddress(rr.mSerial, convertNullToEmptyString(address));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setSmscAddress", e);
+            }
+        }
+    }
+
+    @Override
+    public void reportSmsMemoryStatus(boolean available, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> "
+                        + requestToString(rr.mRequest) + " available = " + available);
+            }
+
+            try {
+                radioProxy.reportSmsMemoryStatus(rr.mSerial, available);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "reportSmsMemoryStatus", e);
+            }
+        }
+    }
+
+    @Override
+    public void reportStkServiceIsRunning(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.reportStkServiceIsRunning(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "reportStkServiceIsRunning", e);
+            }
+        }
+    }
+
+    @Override
+    public void getCdmaSubscriptionSource(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getCdmaSubscriptionSource(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getCdmaSubscriptionSource", e);
+            }
+        }
+    }
+
+    @Override
+    public void requestIsimAuthentication(String nonce, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ISIM_AUTHENTICATION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " nonce = " + nonce);
+            }
+
+            try {
+                radioProxy.requestIsimAuthentication(rr.mSerial, convertNullToEmptyString(nonce));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "requestIsimAuthentication", e);
+            }
+        }
+    }
+
+    @Override
+    public void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " success = " + success);
+            }
+
+            try {
+                radioProxy.acknowledgeIncomingGsmSmsWithPdu(rr.mSerial, success,
+                        convertNullToEmptyString(ackPdu));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "acknowledgeIncomingGsmSmsWithPdu", e);
+            }
+        }
+    }
+
+    @Override
+    public void getVoiceRadioTechnology(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_VOICE_RADIO_TECH, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            try {
+                radioProxy.getVoiceRadioTechnology(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getVoiceRadioTechnology", e);
+            }
+        }
+    }
+
+    @Override
+    public void getCellInfoList(Message result, WorkSource workSource) {
+        workSource = getDeafultWorkSourceIfInvalid(workSource);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_CELL_INFO_LIST, result,
+                    workSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.getCellInfoList(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getCellInfoList", e);
+            }
+        }
+    }
+
+    @Override
+    public void setCellInfoListRate(int rateInMillis, Message result, WorkSource workSource) {
+        workSource = getDeafultWorkSourceIfInvalid(workSource);
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, result,
+                    workSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " rateInMillis = " + rateInMillis);
+            }
+
+            try {
+                radioProxy.setCellInfoListRate(rr.mSerial, rateInMillis);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setCellInfoListRate", e);
+            }
+        }
+    }
+
+    void setCellInfoListRate() {
+        setCellInfoListRate(Integer.MAX_VALUE, null, mRILDefaultWorkSource);
+    }
+
+    @Override
+    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
+
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_INITIAL_ATTACH_APN, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + dataProfile);
+            }
+
+            try {
+                radioProxy.setInitialAttachApn(rr.mSerial, convertToHalDataProfile(dataProfile),
+                        dataProfile.modemCognitive, isRoaming);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setInitialAttachApn", e);
+            }
+        }
+    }
+
+    @Override
+    public void getImsRegistrationState(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_IMS_REGISTRATION_STATE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.getImsRegistrationState(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getImsRegistrationState", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendImsGsmSms(String smscPdu, String pdu, int retry, int messageRef,
+                   Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log function args for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            ImsSmsMessage msg = new ImsSmsMessage();
+            msg.tech = RILConstants.GSM_PHONE;
+            msg.retry = (byte) retry == 1 ? true : false;
+            msg.messageRef = messageRef;
+
+            GsmSmsMessage gsmMsg = constructGsmSendSmsRilRequest(smscPdu, pdu);
+            msg.gsmMessage.add(gsmMsg);
+            try {
+                radioProxy.sendImsSms(rr.mSerial, msg);
+                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendImsGsmSms", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_IMS_SEND_SMS, result,
+                    mRILDefaultWorkSource);
+
+            // Do not log function args for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+            ImsSmsMessage msg = new ImsSmsMessage();
+            msg.tech = RILConstants.CDMA_PHONE;
+            msg.retry = (byte) retry == 1 ? true : false;
+            msg.messageRef = messageRef;
+
+            CdmaSmsMessage cdmaMsg = new CdmaSmsMessage();
+            constructCdmaSendSmsRilRequest(cdmaMsg, pdu);
+            msg.cdmaMessage.add(cdmaMsg);
+
+            try {
+                radioProxy.sendImsSms(rr.mSerial, msg);
+                mMetrics.writeRilSendSms(mPhoneId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
+                        SmsSession.Event.Format.SMS_FORMAT_3GPP);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendImsCdmaSms", e);
+            }
+        }
+    }
+
+    private SimApdu createSimApdu(int channel, int cla, int instruction, int p1, int p2, int p3,
+                                  String data) {
+        SimApdu msg = new SimApdu();
+        msg.sessionId = channel;
+        msg.cla = cla;
+        msg.instruction = instruction;
+        msg.p1 = p1;
+        msg.p2 = p2;
+        msg.p3 = p3;
+        msg.data = convertNullToEmptyString(data);
+        return msg;
+    }
+
+    @Override
+    public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
+                                            int p3, String data, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " cla = " + cla + " instruction = " + instruction
+                        + " p1 = " + p1 + " p2 = " + " p3 = " + p3 + " data = " + data);
+            }
+
+            SimApdu msg = createSimApdu(0, cla, instruction, p1, p2, p3, data);
+            try {
+                radioProxy.iccTransmitApduBasicChannel(rr.mSerial, msg);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "iccTransmitApduBasicChannel", e);
+            }
+        }
+    }
+
+    @Override
+    public void iccOpenLogicalChannel(String aid, int p2, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_OPEN_CHANNEL, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " aid = " + aid
+                        + " p2 = " + p2);
+            }
+
+            try {
+                radioProxy.iccOpenLogicalChannel(rr.mSerial, convertNullToEmptyString(aid), p2);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "iccOpenLogicalChannel", e);
+            }
+        }
+    }
+
+    @Override
+    public void iccCloseLogicalChannel(int channel, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_CLOSE_CHANNEL, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " channel = "
+                        + channel);
+            }
+
+            try {
+                radioProxy.iccCloseLogicalChannel(rr.mSerial, channel);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "iccCloseLogicalChannel", e);
+            }
+        }
+    }
+
+    @Override
+    public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
+                                              int p1, int p2, int p3, String data,
+                                              Message result) {
+        if (channel <= 0) {
+            throw new RuntimeException(
+                    "Invalid channel in iccTransmitApduLogicalChannel: " + channel);
+        }
+
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " channel = "
+                        + channel + " cla = " + cla + " instruction = " + instruction
+                        + " p1 = " + p1 + " p2 = " + " p3 = " + p3 + " data = " + data);
+            }
+
+            SimApdu msg = createSimApdu(channel, cla, instruction, p1, p2, p3, data);
+
+            try {
+                radioProxy.iccTransmitApduLogicalChannel(rr.mSerial, msg);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "iccTransmitApduLogicalChannel", e);
+            }
+        }
+    }
+
+    @Override
+    public void nvReadItem(int itemID, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_NV_READ_ITEM, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " itemId = " + itemID);
+            }
+
+            try {
+                radioProxy.nvReadItem(rr.mSerial, itemID);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "nvReadItem", e);
+            }
+        }
+    }
+
+    @Override
+    public void nvWriteItem(int itemId, String itemValue, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_ITEM, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " itemId = " + itemId + " itemValue = " + itemValue);
+            }
+
+            NvWriteItem item = new NvWriteItem();
+            item.itemId = itemId;
+            item.value = convertNullToEmptyString(itemValue);
+
+            try {
+                radioProxy.nvWriteItem(rr.mSerial, item);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "nvWriteItem", e);
+            }
+        }
+    }
+
+    @Override
+    public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_NV_WRITE_CDMA_PRL, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " PreferredRoamingList = 0x"
+                        + IccUtils.bytesToHexString(preferredRoamingList));
+            }
+
+            ArrayList<Byte> arrList = new ArrayList<>();
+            for (int i = 0; i < preferredRoamingList.length; i++) {
+                arrList.add(preferredRoamingList[i]);
+            }
+
+            try {
+                radioProxy.nvWriteCdmaPrl(rr.mSerial, arrList);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "nvWriteCdmaPrl", e);
+            }
+        }
+    }
+
+    @Override
+    public void nvResetConfig(int resetType, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_NV_RESET_CONFIG, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " resetType = " + resetType);
+            }
+
+            try {
+                radioProxy.nvResetConfig(rr.mSerial, convertToHalResetNvType(resetType));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "nvResetConfig", e);
+            }
+        }
+    }
+
+    @Override
+    public void setUiccSubscription(int slotId, int appIndex, int subId,
+                                    int subStatus, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " slot = " + slotId + " appIndex = " + appIndex
+                        + " subId = " + subId + " subStatus = " + subStatus);
+            }
+
+            SelectUiccSub info = new SelectUiccSub();
+            info.slot = slotId;
+            info.appIndex = appIndex;
+            info.subType = subId;
+            info.actStatus = subStatus;
+
+            try {
+                radioProxy.setUiccSubscription(rr.mSerial, info);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setUiccSubscription", e);
+            }
+        }
+    }
+
+    @Override
+    public void setDataAllowed(boolean allowed, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_ALLOW_DATA, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " allowed = " + allowed);
+            }
+
+            try {
+                radioProxy.setDataAllowed(rr.mSerial, allowed);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setDataAllowed", e);
+            }
+        }
     }
 
     @Override
     public void
-    separateConnection (int gsmIndex, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SEPARATE_CONNECTION, result);
+    getHardwareConfig (Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_HARDWARE_CONFIG, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                            + " " + gsmIndex);
+            // Do not log function args for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(gsmIndex);
-
-        send(rr);
+            try {
+                radioProxy.getHardwareConfig(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getHardwareConfig", e);
+            }
+        }
     }
 
     @Override
-    public void
-    acceptCall (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_ANSWER, result);
+    public void requestIccSimAuthentication(int authContext, String data, String aid,
+                                            Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SIM_AUTHENTICATION, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            // Do not log function args for privacy
+            if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        mMetrics.writeRilAnswer(mInstanceId, rr.mSerial);
-
-        send(rr);
+            try {
+                radioProxy.requestIccSimAuthentication(rr.mSerial,
+                        authContext,
+                        convertNullToEmptyString(data),
+                        convertNullToEmptyString(aid));
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "requestIccSimAuthentication", e);
+            }
+        }
     }
 
     @Override
-    public void
-    rejectCall (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_UDUB, result);
+    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_DATA_PROFILE, result,
+                    mRILDefaultWorkSource);
 
-        send(rr);
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " with data profiles : ");
+                for (DataProfile profile : dps) {
+                    riljLog(profile.toString());
+                }
+            }
+
+            ArrayList<DataProfileInfo> dpis = new ArrayList<>();
+            for (DataProfile dp : dps) {
+                dpis.add(convertToHalDataProfile(dp));
+            }
+
+            try {
+                radioProxy.setDataProfile(rr.mSerial, dpis, isRoaming);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setDataProfile", e);
+            }
+        }
     }
 
     @Override
-    public void
-    explicitCallTransfer (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_EXPLICIT_CALL_TRANSFER, result);
+    public void requestShutdown(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SHUTDOWN, result,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
 
-        send(rr);
+            try {
+                radioProxy.requestShutdown(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "requestShutdown", e);
+            }
+        }
     }
 
     @Override
-    public void
-    getLastCallFailCause (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_LAST_CALL_FAIL_CAUSE, result);
+    public void getRadioCapability(Message response) {
+        IRadio radioProxy = getRadioProxy(response);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_RADIO_CAPABILITY, response,
+                    mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
 
-        send(rr);
+            try {
+                radioProxy.getRadioCapability(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getRadioCapability", e);
+            }
+        }
+    }
+
+    @Override
+    public void setRadioCapability(RadioCapability rc, Message response) {
+        IRadio radioProxy = getRadioProxy(response);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_RADIO_CAPABILITY, response,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " RadioCapability = " + rc.toString());
+            }
+
+            android.hardware.radio.V1_0.RadioCapability halRc =
+                    new android.hardware.radio.V1_0.RadioCapability();
+
+            halRc.session = rc.getSession();
+            halRc.phase = rc.getPhase();
+            halRc.raf = rc.getRadioAccessFamily();
+            halRc.logicalModemUuid = convertNullToEmptyString(rc.getLogicalModemUuid());
+            halRc.status = rc.getStatus();
+
+            try {
+                radioProxy.setRadioCapability(rr.mSerial, halRc);
+            } catch (Exception e) {
+                handleRadioProxyExceptionForRR(rr, "setRadioCapability", e);
+            }
+        }
+    }
+
+    @Override
+    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_START_LCE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                        + " reportIntervalMs = " + reportIntervalMs + " pullMode = " + pullMode);
+            }
+
+            try {
+                radioProxy.startLceService(rr.mSerial, reportIntervalMs, pullMode);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "startLceService", e);
+            }
+        }
+    }
+
+    @Override
+    public void stopLceService(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_STOP_LCE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.stopLceService(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "stopLceService", e);
+            }
+        }
+    }
+
+    @Override
+    public void pullLceData(Message response) {
+        IRadio radioProxy = getRadioProxy(response);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_PULL_LCEDATA, response,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.pullLceData(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "pullLceData", e);
+            }
+        }
+    }
+
+    @Override
+    public void getModemActivityInfo(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_ACTIVITY_INFO, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.getModemActivityInfo(rr.mSerial);
+
+                Message msg = mRilHandler.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT);
+                msg.obj = null;
+                msg.arg1 = rr.mSerial;
+                mRilHandler.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getModemActivityInfo", e);
+            }
+        }
+
+
+    }
+
+    @Override
+    public void setAllowedCarriers(List<CarrierIdentifier> carriers, Message result) {
+        checkNotNull(carriers, "Allowed carriers list cannot be null.");
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_ALLOWED_CARRIERS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                String logStr = "";
+                for (int i = 0; i < carriers.size(); i++) {
+                    logStr = logStr + carriers.get(i) + " ";
+                }
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " carriers = "
+                        + logStr);
+            }
+
+            boolean allAllowed;
+            if (carriers.size() == 0) {
+                allAllowed = true;
+            } else {
+                allAllowed = false;
+            }
+            CarrierRestrictions carrierList = new CarrierRestrictions();
+
+            for (CarrierIdentifier ci : carriers) { /* allowed carriers */
+                Carrier c = new Carrier();
+                c.mcc = convertNullToEmptyString(ci.getMcc());
+                c.mnc = convertNullToEmptyString(ci.getMnc());
+                int matchType = CarrierIdentifier.MatchType.ALL;
+                String matchData = null;
+                if (!TextUtils.isEmpty(ci.getSpn())) {
+                    matchType = CarrierIdentifier.MatchType.SPN;
+                    matchData = ci.getSpn();
+                } else if (!TextUtils.isEmpty(ci.getImsi())) {
+                    matchType = CarrierIdentifier.MatchType.IMSI_PREFIX;
+                    matchData = ci.getImsi();
+                } else if (!TextUtils.isEmpty(ci.getGid1())) {
+                    matchType = CarrierIdentifier.MatchType.GID1;
+                    matchData = ci.getGid1();
+                } else if (!TextUtils.isEmpty(ci.getGid2())) {
+                    matchType = CarrierIdentifier.MatchType.GID2;
+                    matchData = ci.getGid2();
+                }
+                c.matchType = matchType;
+                c.matchData = convertNullToEmptyString(matchData);
+                carrierList.allowedCarriers.add(c);
+            }
+
+            /* TODO: add excluded carriers */
+
+            try {
+                radioProxy.setAllowedCarriers(rr.mSerial, allAllowed, carrierList);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setAllowedCarriers", e);
+            }
+        }
+    }
+
+    @Override
+    public void getAllowedCarriers(Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_GET_ALLOWED_CARRIERS, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
+
+            try {
+                radioProxy.getAllowedCarriers(rr.mSerial);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getAllowedCarriers", e);
+            }
+        }
+    }
+
+    @Override
+    public void sendDeviceState(int stateType, boolean state,
+                                Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SEND_DEVICE_STATE, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " "
+                        + stateType + ":" + state);
+            }
+
+            try {
+                radioProxy.sendDeviceState(rr.mSerial, stateType, state);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendDeviceState", e);
+            }
+        }
+    }
+
+    @Override
+    public void setUnsolResponseFilter(int filter, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + filter);
+            }
+
+            try {
+                radioProxy.setIndicationFilter(rr.mSerial, filter);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "setIndicationFilter", e);
+            }
+        }
+    }
+
+    @Override
+    public void setSimCardPower(int state, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_SET_SIM_CARD_POWER, result,
+                    mRILDefaultWorkSource);
+
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + state);
+            }
+            android.hardware.radio.V1_1.IRadio radioProxy11 =
+                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+            if (radioProxy11 == null) {
+                try {
+                    switch (state) {
+                        case TelephonyManager.CARD_POWER_DOWN: {
+                            radioProxy.setSimCardPower(rr.mSerial, false);
+                            break;
+                        }
+                        case TelephonyManager.CARD_POWER_UP: {
+                            radioProxy.setSimCardPower(rr.mSerial, true);
+                            break;
+                        }
+                        default: {
+                            if (result != null) {
+                                AsyncResult.forMessage(result, null,
+                                        CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                                result.sendToTarget();
+                            }
+                        }
+                    }
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
+                }
+            } else {
+                try {
+                    radioProxy11.setSimCardPower_1_1(rr.mSerial, state);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "setSimCardPower", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+                                                Message result) {
+        checkNotNull(imsiEncryptionInfo, "ImsiEncryptionInfo cannot be null.");
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            android.hardware.radio.V1_1.IRadio radioProxy11 =
+                    android.hardware.radio.V1_1.IRadio.castFrom(radioProxy);
+            if (radioProxy11 == null) {
+                if (result != null) {
+                    AsyncResult.forMessage(result, null,
+                            CommandException.fromRilErrno(REQUEST_NOT_SUPPORTED));
+                    result.sendToTarget();
+                }
+            } else {
+                RILRequest rr = obtainRequest(RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION, result,
+                        mRILDefaultWorkSource);
+                if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+                try {
+                    android.hardware.radio.V1_1.ImsiEncryptionInfo halImsiInfo =
+                            new android.hardware.radio.V1_1.ImsiEncryptionInfo();
+                    halImsiInfo.mnc = imsiEncryptionInfo.getMnc();
+                    halImsiInfo.mcc = imsiEncryptionInfo.getMcc();
+                    halImsiInfo.keyIdentifier = imsiEncryptionInfo.getKeyIdentifier();
+                    if (imsiEncryptionInfo.getExpirationTime() != null) {
+                        halImsiInfo.expirationTime =
+                                imsiEncryptionInfo.getExpirationTime().getTime();
+                    }
+                    for (byte b : imsiEncryptionInfo.getPublicKey().getEncoded()) {
+                        halImsiInfo.carrierKey.add(new Byte(b));
+                    }
+
+                    radioProxy11.setCarrierInfoForImsiEncryption(
+                            rr.mSerial, halImsiInfo);
+                } catch (RemoteException | RuntimeException e) {
+                    handleRadioProxyExceptionForRR(rr, "setCarrierInfoForImsiEncryption", e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void getIMEI(Message result) {
+        throw new RuntimeException("getIMEI not expected to be called");
+    }
+
+    @Override
+    public void getIMEISV(Message result) {
+        throw new RuntimeException("getIMEISV not expected to be called");
     }
 
     /**
@@ -1276,351 +3838,16 @@
      */
     @Deprecated
     @Override
-    public void
-    getLastPdpFailCause (Message result) {
-        getLastDataCallFailCause (result);
+    public void getLastPdpFailCause(Message result) {
+        throw new RuntimeException("getLastPdpFailCause not expected to be called");
     }
 
     /**
      * The preferred new alternative to getLastPdpFailCause
      */
     @Override
-    public void
-    getLastDataCallFailCause (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setMute (boolean enableMute, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_MUTE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                            + " " + enableMute);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(enableMute ? 1 : 0);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getMute (Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_GET_MUTE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getSignalStrength (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SIGNAL_STRENGTH, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getVoiceRegistrationState (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_VOICE_REGISTRATION_STATE, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getDataRegistrationState (Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_DATA_REGISTRATION_STATE, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getOperator(Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_OPERATOR, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getHardwareConfig (Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_HARDWARE_CONFIG, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    sendDtmf(char c, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_DTMF, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        rr.mParcel.writeString(Character.toString(c));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    startDtmf(char c, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_DTMF_START, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        rr.mParcel.writeString(Character.toString(c));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    stopDtmf(Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_DTMF_STOP, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    sendBurstDtmf(String dtmfString, int on, int off, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BURST_DTMF, result);
-
-        rr.mParcel.writeInt(3);
-        rr.mParcel.writeString(dtmfString);
-        rr.mParcel.writeString(Integer.toString(on));
-        rr.mParcel.writeString(Integer.toString(off));
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + dtmfString);
-
-        send(rr);
-    }
-
-    private void
-    constructGsmSendSmsRilRequest (RILRequest rr, String smscPDU, String pdu) {
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeString(smscPDU);
-        rr.mParcel.writeString(pdu);
-    }
-
-    public void
-    sendSMS (String smscPDU, String pdu, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
-
-        constructGsmSendSmsRilRequest(rr, smscPDU, pdu);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        mMetrics.writeRilSendSms(mInstanceId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
-                SmsSession.Event.Format.SMS_FORMAT_3GPP);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    sendSMSExpectMore (String smscPDU, String pdu, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SEND_SMS_EXPECT_MORE, result);
-
-        constructGsmSendSmsRilRequest(rr, smscPDU, pdu);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        mMetrics.writeRilSendSms(mInstanceId, rr.mSerial, SmsSession.Event.Tech.SMS_GSM,
-                SmsSession.Event.Format.SMS_FORMAT_3GPP);
-
-        send(rr);
-    }
-
-    private void
-    constructCdmaSendSmsRilRequest(RILRequest rr, byte[] pdu) {
-        int address_nbr_of_digits;
-        int subaddr_nbr_of_digits;
-        int bearerDataLength;
-        ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
-        DataInputStream dis = new DataInputStream(bais);
-
-        try {
-            rr.mParcel.writeInt(dis.readInt()); //teleServiceId
-            rr.mParcel.writeByte((byte) dis.readInt()); //servicePresent
-            rr.mParcel.writeInt(dis.readInt()); //serviceCategory
-            rr.mParcel.writeInt(dis.read()); //address_digit_mode
-            rr.mParcel.writeInt(dis.read()); //address_nbr_mode
-            rr.mParcel.writeInt(dis.read()); //address_ton
-            rr.mParcel.writeInt(dis.read()); //address_nbr_plan
-            address_nbr_of_digits = (byte) dis.read();
-            rr.mParcel.writeByte((byte) address_nbr_of_digits);
-            for(int i=0; i < address_nbr_of_digits; i++){
-                rr.mParcel.writeByte(dis.readByte()); // address_orig_bytes[i]
-            }
-            rr.mParcel.writeInt(dis.read()); //subaddressType
-            rr.mParcel.writeByte((byte) dis.read()); //subaddr_odd
-            subaddr_nbr_of_digits = (byte) dis.read();
-            rr.mParcel.writeByte((byte) subaddr_nbr_of_digits);
-            for(int i=0; i < subaddr_nbr_of_digits; i++){
-                rr.mParcel.writeByte(dis.readByte()); //subaddr_orig_bytes[i]
-            }
-
-            bearerDataLength = dis.read();
-            rr.mParcel.writeInt(bearerDataLength);
-            for(int i=0; i < bearerDataLength; i++){
-                rr.mParcel.writeByte(dis.readByte()); //bearerData[i]
-            }
-        }catch (IOException ex){
-            if (RILJ_LOGD) riljLog("sendSmsCdma: conversion from input stream to object failed: "
-                    + ex);
-        }
-    }
-
-    public void
-    sendCdmaSms(byte[] pdu, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_CDMA_SEND_SMS, result);
-
-        constructCdmaSendSmsRilRequest(rr, pdu);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        mMetrics.writeRilSendSms(mInstanceId, rr.mSerial, SmsSession.Event.Tech.SMS_CDMA,
-                SmsSession.Event.Format.SMS_FORMAT_3GPP2);
-
-        send(rr);
-    }
-
-    public void
-    sendImsGsmSms (String smscPDU, String pdu, int retry, int messageRef,
-            Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_SEND_SMS, result);
-
-        rr.mParcel.writeInt(RILConstants.GSM_PHONE);
-        rr.mParcel.writeByte((byte)retry);
-        rr.mParcel.writeInt(messageRef);
-
-        constructGsmSendSmsRilRequest(rr, smscPDU, pdu);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        mMetrics.writeRilSendSms(mInstanceId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
-                SmsSession.Event.Format.SMS_FORMAT_3GPP);
-
-        send(rr);
-    }
-
-    public void
-    sendImsCdmaSms(byte[] pdu, int retry, int messageRef, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_SEND_SMS, result);
-
-        rr.mParcel.writeInt(RILConstants.CDMA_PHONE);
-        rr.mParcel.writeByte((byte)retry);
-        rr.mParcel.writeInt(messageRef);
-
-        constructCdmaSendSmsRilRequest(rr, pdu);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        mMetrics.writeRilSendSms(mInstanceId, rr.mSerial, SmsSession.Event.Tech.SMS_IMS,
-                SmsSession.Event.Format.SMS_FORMAT_3GPP2);
-
-        send(rr);
-    }
-
-    @Override
-    public void deleteSmsOnSim(int index, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DELETE_SMS_ON_SIM,
-                response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(index);
-
-        if (RILJ_LOGV) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest)
-                + " " + index);
-
-        send(rr);
-    }
-
-    @Override
-    public void deleteSmsOnRuim(int index, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM,
-                response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(index);
-
-        if (RILJ_LOGV) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest)
-                + " " + index);
-
-        send(rr);
-    }
-
-    @Override
-    public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
-        status = translateStatus(status);
-
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_WRITE_SMS_TO_SIM,
-                response);
-
-        rr.mParcel.writeInt(status);
-        rr.mParcel.writeString(pdu);
-        rr.mParcel.writeString(smsc);
-
-        if (RILJ_LOGV) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest)
-                + " " + status);
-
-        send(rr);
-    }
-
-    @Override
-    public void writeSmsToRuim(int status, String pdu, Message response) {
-        status = translateStatus(status);
-
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM,
-                response);
-
-        rr.mParcel.writeInt(status);
-        rr.mParcel.writeString(pdu);
-
-        if (RILJ_LOGV) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest)
-                + " " + status);
-
-        send(rr);
+    public void getLastDataCallFailCause(Message result) {
+        throw new RuntimeException("getLastDataCallFailCause not expected to be called");
     }
 
     /**
@@ -1644,817 +3871,230 @@
     }
 
     @Override
-    public void
-    setupDataCall(int radioTechnology, int profile, String apn,
-            String user, String password, int authType, String protocol,
-            Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);
-
-        rr.mParcel.writeInt(7);
-
-        rr.mParcel.writeString(Integer.toString(radioTechnology + 2));
-        rr.mParcel.writeString(Integer.toString(profile));
-        rr.mParcel.writeString(apn);
-        rr.mParcel.writeString(user);
-        rr.mParcel.writeString(password);
-        rr.mParcel.writeString(Integer.toString(authType));
-        rr.mParcel.writeString(protocol);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest) + " " + radioTechnology + " "
-                + profile + " " + apn + " " + user + " "
-                + password + " " + authType + " " + protocol);
-
-        mMetrics.writeRilSetupDataCall(mInstanceId, rr.mSerial,
-                radioTechnology, profile, apn, authType, protocol);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    deactivateDataCall(int cid, int reason, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_DEACTIVATE_DATA_CALL, result);
-
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeString(Integer.toString(cid));
-        rr.mParcel.writeString(Integer.toString(reason));
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " +
-                requestToString(rr.mRequest) + " " + cid + " " + reason);
-
-        mMetrics.writeRilDeactivateDataCall(mInstanceId, rr.mSerial,
-                cid, reason);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setRadioPower(boolean on, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_RADIO_POWER, result);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(on ? 1 : 0);
-
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + (on ? " on" : " off"));
-        }
-
-        send(rr);
-    }
-
-    @Override
-    public void requestShutdown(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SHUTDOWN, result);
-
-        if (RILJ_LOGD)
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setSuppServiceNotifications(boolean enable, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION, result);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(enable ? 1 : 0);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    acknowledgeLastIncomingGsmSms(boolean success, int cause, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SMS_ACKNOWLEDGE, result);
-
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeInt(success ? 1 : 0);
-        rr.mParcel.writeInt(cause);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " " + success + " " + cause);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE, result);
-
-        rr.mParcel.writeInt(success ? 0 : 1); //RIL_CDMA_SMS_ErrorClass
-        // cause code according to X.S004-550E
-        rr.mParcel.writeInt(cause);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " " + success + " " + cause);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, result);
-
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeString(success ? "1" : "0");
-        rr.mParcel.writeString(ackPdu);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + ' ' + success + " [" + ackPdu + ']');
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    iccIO (int command, int fileid, String path, int p1, int p2, int p3,
-            String data, String pin2, Message result) {
-        iccIOForApp(command, fileid, path, p1, p2, p3, data, pin2, null, result);
-    }
-    @Override
-    public void
-    iccIOForApp (int command, int fileid, String path, int p1, int p2, int p3,
-            String data, String pin2, String aid, Message result) {
-        //Note: This RIL request has not been renamed to ICC,
-        //       but this request is also valid for SIM and RUIM
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SIM_IO, result);
-
-        rr.mParcel.writeInt(command);
-        rr.mParcel.writeInt(fileid);
-        rr.mParcel.writeString(path);
-        rr.mParcel.writeInt(p1);
-        rr.mParcel.writeInt(p2);
-        rr.mParcel.writeInt(p3);
-        rr.mParcel.writeString(data);
-        rr.mParcel.writeString(pin2);
-        rr.mParcel.writeString(aid);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> iccIO: "
-                + requestToString(rr.mRequest)
-                + " 0x" + Integer.toHexString(command)
-                + " 0x" + Integer.toHexString(fileid) + " "
-                + " path: " + path + ","
-                + p1 + "," + p2 + "," + p3
-                + " aid: " + aid);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getCLIR(Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_GET_CLIR, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setCLIR(int clirMode, Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_CLIR, result);
-
-        // count ints
-        rr.mParcel.writeInt(1);
-
-        rr.mParcel.writeInt(clirMode);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " " + clirMode);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    queryCallWaiting(int serviceClass, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_WAITING, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(serviceClass);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " " + serviceClass);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setCallWaiting(boolean enable, int serviceClass, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_CALL_WAITING, response);
-
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeInt(enable ? 1 : 0);
-        rr.mParcel.writeInt(serviceClass);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " " + enable + ", " + serviceClass);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setNetworkSelectionModeAutomatic(Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC,
-                                    response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setNetworkSelectionModeManual(String operatorNumeric, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL,
-                                    response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " " + operatorNumeric);
-
-        rr.mParcel.writeString(operatorNumeric);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getNetworkSelectionMode(Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE,
-                                    response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getAvailableNetworks(Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_NETWORKS,
-                                    response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setCallForward(int action, int cfReason, int serviceClass,
-                String number, int timeSeconds, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_CALL_FORWARD, response);
-
-        rr.mParcel.writeInt(action);
-        rr.mParcel.writeInt(cfReason);
-        rr.mParcel.writeInt(serviceClass);
-        rr.mParcel.writeInt(PhoneNumberUtils.toaFromString(number));
-        rr.mParcel.writeString(number);
-        rr.mParcel.writeInt (timeSeconds);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " " + action + " " + cfReason + " " + serviceClass
-                    + timeSeconds);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    queryCallForwardStatus(int cfReason, int serviceClass,
-                String number, Message response) {
-        RILRequest rr
-            = RILRequest.obtain(RIL_REQUEST_QUERY_CALL_FORWARD_STATUS, response);
-
-        rr.mParcel.writeInt(2); // 2 is for query action, not in used anyway
-        rr.mParcel.writeInt(cfReason);
-        rr.mParcel.writeInt(serviceClass);
-        rr.mParcel.writeInt(PhoneNumberUtils.toaFromString(number));
-        rr.mParcel.writeString(number);
-        rr.mParcel.writeInt (0);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " " + cfReason + " " + serviceClass);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    queryCLIP(Message response) {
-        RILRequest rr
-            = RILRequest.obtain(RIL_REQUEST_QUERY_CLIP, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-
-    @Override
-    public void
-    getBasebandVersion (Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_BASEBAND_VERSION, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    queryFacilityLock(String facility, String password, int serviceClass,
-                            Message response) {
-        queryFacilityLockForApp(facility, password, serviceClass, null, response);
-    }
-
-    @Override
-    public void
-    queryFacilityLockForApp(String facility, String password, int serviceClass, String appId,
-                            Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_QUERY_FACILITY_LOCK, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                                                 + " [" + facility + " " + serviceClass
-                                                 + " " + appId + "]");
-
-        // count strings
-        rr.mParcel.writeInt(4);
-
-        rr.mParcel.writeString(facility);
-        rr.mParcel.writeString(password);
-
-        rr.mParcel.writeString(Integer.toString(serviceClass));
-        rr.mParcel.writeString(appId);
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    setFacilityLock (String facility, boolean lockState, String password,
-                        int serviceClass, Message response) {
-        setFacilityLockForApp(facility, lockState, password, serviceClass, null, response);
-    }
-
-    @Override
-    public void
-    setFacilityLockForApp(String facility, boolean lockState, String password,
-                        int serviceClass, String appId, Message response) {
-        String lockString;
-         RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_FACILITY_LOCK, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                                                        + " [" + facility + " " + lockState
-                                                        + " " + serviceClass + " " + appId + "]");
-
-        // count strings
-        rr.mParcel.writeInt(5);
-
-        rr.mParcel.writeString(facility);
-        lockString = (lockState)?"1":"0";
-        rr.mParcel.writeString(lockString);
-        rr.mParcel.writeString(password);
-        rr.mParcel.writeString(Integer.toString(serviceClass));
-        rr.mParcel.writeString(appId);
-
-        send(rr);
-
-    }
-
-    @Override
-    public void
-    sendUSSD (String ussdString, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SEND_USSD, response);
-
-        if (RILJ_LOGD) {
-            String logUssdString = "*******";
-            if (RILJ_LOGV) logUssdString = ussdString;
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                                   + " " + logUssdString);
-        }
-
-        rr.mParcel.writeString(ussdString);
-
-        send(rr);
-    }
-
-    // inherited javadoc suffices
-    @Override
-    public void cancelPendingUssd (Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_CANCEL_USSD, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString()
-                + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-
-    @Override
     public void resetRadio(Message result) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_RESET_RADIO, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void invokeOemRilRequestRaw(byte[] data, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_RAW, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-               + "[" + IccUtils.bytesToHexString(data) + "]");
-
-        rr.mParcel.writeByteArray(data);
-
-        send(rr);
-
-    }
-
-    @Override
-    public void invokeOemRilRequestStrings(String[] strings, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_OEM_HOOK_STRINGS, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        rr.mParcel.writeStringArray(strings);
-
-        send(rr);
-    }
-
-     /**
-     * Assign a specified band for RF configuration.
-     *
-     * @param bandMode one of BM_*_BAND
-     * @param response is callback message
-     */
-    @Override
-    public void setBandMode (int bandMode, Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_SET_BAND_MODE, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(bandMode);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                 + " " + bandMode);
-
-        send(rr);
-     }
-
-    /**
-     * Query the list of band mode supported by RF.
-     *
-     * @param response is callback message
-     *        ((AsyncResult)response.obj).result  is an int[] where int[0] is
-     *        the size of the array and the rest of each element representing
-     *        one available BM_*_BAND
-     */
-    @Override
-    public void queryAvailableBandMode (Message response) {
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE,
-                response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
+        throw new RuntimeException("resetRadio not expected to be called");
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void sendTerminalResponse(String contents, Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE, response);
+    public void handleCallSetupRequestFromSim(boolean accept, Message result) {
+        IRadio radioProxy = getRadioProxy(result);
+        if (radioProxy != null) {
+            RILRequest rr = obtainRequest(RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
+                    result, mRILDefaultWorkSource);
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            }
 
-        rr.mParcel.writeString(contents);
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void sendEnvelope(String contents, Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        rr.mParcel.writeString(contents);
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void sendEnvelopeWithStatus(String contents, Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + '[' + contents + ']');
-
-        rr.mParcel.writeString(contents);
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void handleCallSetupRequestFromSim(
-            boolean accept, Message response) {
-
-        RILRequest rr = RILRequest.obtain(
-            RILConstants.RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM,
-            response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        int[] param = new int[1];
-        param[0] = accept ? 1 : 0;
-        rr.mParcel.writeIntArray(param);
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setPreferredNetworkType(int networkType , Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(networkType);
-
-        mPreferredNetworkType = networkType;
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + networkType);
-
-        mMetrics.writeSetPreferredNetworkType(mInstanceId, networkType);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getPreferredNetworkType(Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getNeighboringCids(Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_GET_NEIGHBORING_CELL_IDS, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setLocationUpdates(boolean enable, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_LOCATION_UPDATES, response);
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(enable ? 1 : 0);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest) + ": " + enable);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getSmscAddress(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SMSC_ADDRESS, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setSmscAddress(String address, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_SMSC_ADDRESS, result);
-
-        rr.mParcel.writeString(address);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + address);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void reportSmsMemoryStatus(boolean available, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_SMS_MEMORY_STATUS, result);
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(available ? 1 : 0);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> "
-                + requestToString(rr.mRequest) + ": " + available);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void reportStkServiceIsRunning(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getGsmBroadcastConfig(Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_GET_BROADCAST_CONFIG, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_SET_BROADCAST_CONFIG, response);
-
-        int numOfConfig = config.length;
-        rr.mParcel.writeInt(numOfConfig);
-
-        for(int i = 0; i < numOfConfig; i++) {
-            rr.mParcel.writeInt(config[i].getFromServiceId());
-            rr.mParcel.writeInt(config[i].getToServiceId());
-            rr.mParcel.writeInt(config[i].getFromCodeScheme());
-            rr.mParcel.writeInt(config[i].getToCodeScheme());
-            rr.mParcel.writeInt(config[i].isSelected() ? 1 : 0);
-        }
-
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " with " + numOfConfig + " configs : ");
-            for (int i = 0; i < numOfConfig; i++) {
-                riljLog(config[i].toString());
+            try {
+                radioProxy.handleStkCallSetupRequestFromSim(rr.mSerial, accept);
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "getAllowedCarriers", e);
             }
         }
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setGsmBroadcastActivation(boolean activate, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GSM_BROADCAST_ACTIVATION, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(activate ? 0 : 1);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
     }
 
     //***** Private Methods
 
-    // TODO(jeffbrown): Delete me.
-    // The RIL should *not* be listening for screen state changes since they are
-    // becoming increasingly ambiguous on our devices.  The RIL_REQUEST_SCREEN_STATE
-    // 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.
-     * @param forceUpdate. If it is true, update screen state without compare to oldState.
+     * This is a helper function to be called when a RadioIndication callback is called.
+     * It takes care of acquiring wakelock and sending ack if needed.
+     * @param indicationType RadioIndicationType received
      */
-    private void updateScreenState(boolean forceUpdate) {
-        final int oldState = mRadioScreenState;
-        mRadioScreenState = (mDefaultDisplayState == Display.STATE_ON || mIsDevicePlugged)
-                ? RADIO_SCREEN_ON : RADIO_SCREEN_OFF;
-        if (mRadioScreenState != oldState || forceUpdate) {
-            if (RILJ_LOGV) {
-                riljLog("defaultDisplayState: " + mDefaultDisplayState
-                        + ", isDevicePlugged: " + mIsDevicePlugged);
+    void processIndication(int indicationType) {
+        if (indicationType == RadioIndicationType.UNSOLICITED_ACK_EXP) {
+            sendAck();
+            if (RILJ_LOGD) riljLog("Unsol response received; Sending ack to ril.cpp");
+        } else {
+            // ack is not expected to be sent back. Nothing is required to be done here.
+        }
+    }
+
+    void processRequestAck(int serial) {
+        RILRequest rr;
+        synchronized (mRequestList) {
+            rr = mRequestList.get(serial);
+        }
+        if (rr == null) {
+            Rlog.w(RIL.RILJ_LOG_TAG, "processRequestAck: Unexpected solicited ack response! "
+                    + "serial: " + serial);
+        } else {
+            decrementWakeLock(rr);
+            if (RIL.RILJ_LOGD) {
+                riljLog(rr.serialString() + " Ack < " + RIL.requestToString(rr.mRequest));
             }
-            sendScreenState(mRadioScreenState == RADIO_SCREEN_ON);
         }
     }
 
-    private void sendScreenState(boolean on) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SCREEN_STATE, null);
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(on ? 1 : 0);
+    /**
+     * This is a helper function to be called when a RadioResponse callback is called.
+     * It takes care of acks, wakelocks, and finds and returns RILRequest corresponding to the
+     * response if one is found.
+     * @param responseInfo RadioResponseInfo received in response callback
+     * @return RILRequest corresponding to the response
+     */
+    RILRequest processResponse(RadioResponseInfo responseInfo) {
+        int serial = responseInfo.serial;
+        int error = responseInfo.error;
+        int type = responseInfo.type;
 
-        if (RILJ_LOGD) riljLog(rr.serialString()
-                + "> " + requestToString(rr.mRequest) + ": " + on);
+        RILRequest rr = null;
 
-        send(rr);
-    }
-
-    @Override
-    protected void
-    onRadioAvailable() {
-        // In case screen state was lost (due to process crash),
-        // this ensures that the RIL knows the correct screen state.
-        updateScreenState(false);
-   }
-
-    private RadioState getRadioStateFromInt(int stateInt) {
-        RadioState state;
-
-        /* RIL_RadioState ril.h */
-        switch(stateInt) {
-            case 0: state = RadioState.RADIO_OFF; break;
-            case 1: state = RadioState.RADIO_UNAVAILABLE; break;
-            case 10: state = RadioState.RADIO_ON; break;
-
-            default:
-                throw new RuntimeException(
-                            "Unrecognized RIL_RadioState: " + stateInt);
+        if (type == RadioResponseType.SOLICITED_ACK) {
+            synchronized (mRequestList) {
+                rr = mRequestList.get(serial);
+            }
+            if (rr == null) {
+                Rlog.w(RILJ_LOG_TAG, "Unexpected solicited ack response! sn: " + serial);
+            } else {
+                decrementWakeLock(rr);
+                if (RILJ_LOGD) {
+                    riljLog(rr.serialString() + " Ack < " + requestToString(rr.mRequest));
+                }
+            }
+            return rr;
         }
-        return state;
+
+        rr = findAndRemoveRequestFromList(serial);
+        if (rr == null) {
+            Rlog.e(RIL.RILJ_LOG_TAG, "processResponse: Unexpected response! serial: " + serial
+                    + " error: " + error);
+            return null;
+        }
+
+        // Time logging for RIL command and storing it in TelephonyHistogram.
+        addToRilHistogram(rr);
+
+        if (type == RadioResponseType.SOLICITED_ACK_EXP) {
+            sendAck();
+            if (RIL.RILJ_LOGD) {
+                riljLog("Response received for " + rr.serialString() + " "
+                        + RIL.requestToString(rr.mRequest) + " Sending ack to ril.cpp");
+            }
+        } else {
+            // ack sent for SOLICITED_ACK_EXP above; nothing to do for SOLICITED response
+        }
+
+        // Here and below fake RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, see b/7255789.
+        // This is needed otherwise we don't automatically transition to the main lock
+        // screen when the pin or puk is entered incorrectly.
+        switch (rr.mRequest) {
+            case RIL_REQUEST_ENTER_SIM_PUK:
+            case RIL_REQUEST_ENTER_SIM_PUK2:
+                if (mIccStatusChangedRegistrants != null) {
+                    if (RILJ_LOGD) {
+                        riljLog("ON enter sim puk fakeSimStatusChanged: reg count="
+                                + mIccStatusChangedRegistrants.size());
+                    }
+                    mIccStatusChangedRegistrants.notifyRegistrants();
+                }
+                break;
+            case RIL_REQUEST_SHUTDOWN:
+                setRadioState(RadioState.RADIO_UNAVAILABLE);
+                break;
+        }
+
+        if (error != RadioError.NONE) {
+            switch (rr.mRequest) {
+                case RIL_REQUEST_ENTER_SIM_PIN:
+                case RIL_REQUEST_ENTER_SIM_PIN2:
+                case RIL_REQUEST_CHANGE_SIM_PIN:
+                case RIL_REQUEST_CHANGE_SIM_PIN2:
+                case RIL_REQUEST_SET_FACILITY_LOCK:
+                    if (mIccStatusChangedRegistrants != null) {
+                        if (RILJ_LOGD) {
+                            riljLog("ON some errors fakeSimStatusChanged: reg count="
+                                    + mIccStatusChangedRegistrants.size());
+                        }
+                        mIccStatusChangedRegistrants.notifyRegistrants();
+                    }
+                    break;
+
+            }
+        } else {
+            switch (rr.mRequest) {
+                case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
+                if (mTestingEmergencyCall.getAndSet(false)) {
+                    if (mEmergencyCallbackModeRegistrant != null) {
+                        riljLog("testing emergency call, notify ECM Registrants");
+                        mEmergencyCallbackModeRegistrant.notifyRegistrant();
+                    }
+                }
+            }
+        }
+        return rr;
     }
 
-    private void switchToRadioState(RadioState newState) {
-        setRadioState(newState);
+    /**
+     * This is a helper function to be called at the end of all RadioResponse callbacks.
+     * It takes care of sending error response, logging, decrementing wakelock if needed, and
+     * releases the request from memory pool.
+     * @param rr RILRequest for which response callback was called
+     * @param responseInfo RadioResponseInfo received in the callback
+     * @param ret object to be returned to request sender
+     */
+    void processResponseDone(RILRequest rr, RadioResponseInfo responseInfo, Object ret) {
+        if (responseInfo.error == 0) {
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+                        + " " + retToString(rr.mRequest, ret));
+            }
+        } else {
+            if (RILJ_LOGD) {
+                riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
+                        + " error " + responseInfo.error);
+            }
+            rr.onError(responseInfo.error, ret);
+        }
+        mMetrics.writeOnRilSolicitedResponse(mPhoneId, rr.mSerial, responseInfo.error,
+                rr.mRequest, ret);
+        if (rr != null) {
+            if (responseInfo.type == RadioResponseType.SOLICITED) {
+                decrementWakeLock(rr);
+            }
+            rr.release();
+        }
+    }
+
+    /**
+     * Function to send ack and acquire related wakelock
+     */
+    private void sendAck() {
+        // TODO: Remove rr and clean up acquireWakelock for response and ack
+        RILRequest rr = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null,
+                mRILDefaultWorkSource);
+        acquireWakeLock(rr, RIL.FOR_ACK_WAKELOCK);
+        IRadio radioProxy = getRadioProxy(null);
+        if (radioProxy != null) {
+            try {
+                radioProxy.responseAcknowledgement();
+            } catch (RemoteException | RuntimeException e) {
+                handleRadioProxyExceptionForRR(rr, "sendAck", e);
+                riljLoge("sendAck: " + e);
+            }
+        } else {
+            Rlog.e(RILJ_LOG_TAG, "Error trying to send ack, radioProxy = null");
+        }
+        rr.release();
+    }
+
+    private WorkSource getDeafultWorkSourceIfInvalid(WorkSource workSource) {
+        if (workSource == null) {
+            workSource = mRILDefaultWorkSource;
+        }
+
+        return workSource;
+    }
+
+    private String getWorkSourceClientId(WorkSource workSource) {
+        if (workSource != null) {
+            return String.valueOf(workSource.get(0)) + ":" + workSource.getName(0);
+        }
+
+        return null;
     }
 
     /**
@@ -2466,10 +4106,9 @@
      * happen often.
      */
 
-    private void
-    acquireWakeLock(RILRequest rr, int wakeLockType) {
-        synchronized(rr) {
-            if(rr.mWakeLockType != INVALID_WAKELOCK) {
+    private void acquireWakeLock(RILRequest rr, int wakeLockType) {
+        synchronized (rr) {
+            if (rr.mWakeLockType != INVALID_WAKELOCK) {
                 Rlog.d(RILJ_LOG_TAG, "Failed to aquire wakelock for " + rr.serialString());
                 return;
             }
@@ -2481,9 +4120,22 @@
                         mWakeLockCount++;
                         mWlSequenceNum++;
 
-                        Message msg = mSender.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
+                        String clientId = getWorkSourceClientId(rr.mWorkSource);
+                        if (!mClientWakelockTracker.isClientActive(clientId)) {
+                            if (mActiveWakelockWorkSource != null) {
+                                mActiveWakelockWorkSource.add(rr.mWorkSource);
+                            } else {
+                                mActiveWakelockWorkSource = rr.mWorkSource;
+                            }
+                            mWakeLock.setWorkSource(mActiveWakelockWorkSource);
+                        }
+
+                        mClientWakelockTracker.startTracking(rr.mClientId,
+                                rr.mRequest, rr.mSerial, mWakeLockCount);
+
+                        Message msg = mRilHandler.obtainMessage(EVENT_WAKE_LOCK_TIMEOUT);
                         msg.arg1 = mWlSequenceNum;
-                        mSender.sendMessageDelayed(msg, mWakeLockTimeout);
+                        mRilHandler.sendMessageDelayed(msg, mWakeLockTimeout);
                     }
                     break;
                 case FOR_ACK_WAKELOCK:
@@ -2491,9 +4143,9 @@
                         mAckWakeLock.acquire();
                         mAckWlSequenceNum++;
 
-                        Message msg = mSender.obtainMessage(EVENT_ACK_WAKE_LOCK_TIMEOUT);
+                        Message msg = mRilHandler.obtainMessage(EVENT_ACK_WAKE_LOCK_TIMEOUT);
                         msg.arg1 = mAckWlSequenceNum;
-                        mSender.sendMessageDelayed(msg, mAckWakeLockTimeout);
+                        mRilHandler.sendMessageDelayed(msg, mAckWakeLockTimeout);
                     }
                     break;
                 default: //WTF
@@ -2504,12 +4156,24 @@
         }
     }
 
-    private void
-    decrementWakeLock(RILRequest rr) {
-        synchronized(rr) {
+    private void decrementWakeLock(RILRequest rr) {
+        synchronized (rr) {
             switch(rr.mWakeLockType) {
                 case FOR_WAKELOCK:
                     synchronized (mWakeLock) {
+                        mClientWakelockTracker.stopTracking(rr.mClientId,
+                                rr.mRequest, rr.mSerial,
+                                (mWakeLockCount > 1) ? mWakeLockCount - 1 : 0);
+                        String clientId = getWorkSourceClientId(rr.mWorkSource);;
+                        if (!mClientWakelockTracker.isClientActive(clientId)
+                                && (mActiveWakelockWorkSource != null)) {
+                            mActiveWakelockWorkSource.remove(rr.mWorkSource);
+                            if (mActiveWakelockWorkSource.size() == 0) {
+                                mActiveWakelockWorkSource = null;
+                            }
+                            mWakeLock.setWorkSource(mActiveWakelockWorkSource);
+                        }
+
                         if (mWakeLockCount > 1) {
                             mWakeLockCount--;
                         } else {
@@ -2530,77 +4194,27 @@
         }
     }
 
-    private boolean
-    clearWakeLock(int wakeLockType) {
+    private boolean clearWakeLock(int wakeLockType) {
         if (wakeLockType == FOR_WAKELOCK) {
             synchronized (mWakeLock) {
-                if (mWakeLockCount == 0 && mWakeLock.isHeld() == false) return false;
+                if (mWakeLockCount == 0 && !mWakeLock.isHeld()) return false;
                 Rlog.d(RILJ_LOG_TAG, "NOTE: mWakeLockCount is " + mWakeLockCount
                         + "at time of clearing");
                 mWakeLockCount = 0;
                 mWakeLock.release();
+                mClientWakelockTracker.stopTrackingAll();
+                mActiveWakelockWorkSource = null;
                 return true;
             }
         } else {
             synchronized (mAckWakeLock) {
-                if (mAckWakeLock.isHeld() == false) return false;
+                if (!mAckWakeLock.isHeld()) return false;
                 mAckWakeLock.release();
                 return true;
             }
         }
     }
 
-    private void
-    send(RILRequest rr) {
-        Message msg;
-
-        if (mSocket == null) {
-            rr.onError(RADIO_NOT_AVAILABLE, null);
-            rr.release();
-            return;
-        }
-
-        msg = mSender.obtainMessage(EVENT_SEND, rr);
-        acquireWakeLock(rr, FOR_WAKELOCK);
-        msg.sendToTarget();
-    }
-
-    private void
-    processResponse (Parcel p) {
-        int type;
-
-        type = p.readInt();
-
-        if (type == RESPONSE_UNSOLICITED || type == RESPONSE_UNSOLICITED_ACK_EXP) {
-            processUnsolicited (p, type);
-        } else if (type == RESPONSE_SOLICITED || type == RESPONSE_SOLICITED_ACK_EXP) {
-            RILRequest rr = processSolicited (p, type);
-            if (rr != null) {
-                if (type == RESPONSE_SOLICITED) {
-                    decrementWakeLock(rr);
-                }
-                rr.release();
-                return;
-            }
-        } else if (type == RESPONSE_SOLICITED_ACK) {
-            int serial;
-            serial = p.readInt();
-
-            RILRequest rr;
-            synchronized (mRequestList) {
-                rr = mRequestList.get(serial);
-            }
-            if (rr == null) {
-                Rlog.w(RILJ_LOG_TAG, "Unexpected solicited ack response! sn: " + serial);
-            } else {
-                decrementWakeLock(rr);
-                if (RILJ_LOGD) {
-                    riljLog(rr.serialString() + " Ack < " + requestToString(rr.mRequest));
-                }
-            }
-        }
-    }
-
     /**
      * Release each request in mRequestList then clear the list
      * @param error is the RIL_Errno sent back
@@ -2611,16 +4225,15 @@
         synchronized (mRequestList) {
             int count = mRequestList.size();
             if (RILJ_LOGD && loggable) {
-                Rlog.d(RILJ_LOG_TAG, "clearRequestList " +
-                        " mWakeLockCount=" + mWakeLockCount +
-                        " mRequestList=" + count);
+                Rlog.d(RILJ_LOG_TAG, "clearRequestList " + " mWakeLockCount="
+                        + mWakeLockCount + " mRequestList=" + count);
             }
 
-            for (int i = 0; i < count ; i++) {
+            for (int i = 0; i < count; i++) {
                 rr = mRequestList.valueAt(i);
                 if (RILJ_LOGD && loggable) {
-                    Rlog.d(RILJ_LOG_TAG, i + ": [" + rr.mSerial + "] " +
-                            requestToString(rr.mRequest));
+                    Rlog.d(RILJ_LOG_TAG, i + ": [" + rr.mSerial + "] "
+                            + requestToString(rr.mRequest));
                 }
                 rr.onError(error, null);
                 decrementWakeLock(rr);
@@ -2644,9 +4257,9 @@
 
     private void addToRilHistogram(RILRequest rr) {
         long endTime = SystemClock.elapsedRealtime();
-        int totalTime = (int)(endTime - rr.mStartTimeMs);
+        int totalTime = (int) (endTime - rr.mStartTimeMs);
 
-        synchronized(mRilTimeHistograms) {
+        synchronized (mRilTimeHistograms) {
             TelephonyHistogram entry = mRilTimeHistograms.get(rr.mRequest);
             if (entry == null) {
                 // We would have total #RIL_HISTOGRAM_BUCKET_COUNT range buckets for RIL commands
@@ -2658,307 +4271,22 @@
         }
     }
 
-    private RILRequest
-    processSolicited (Parcel p, int type) {
-        int serial, error;
-        boolean found = false;
-
-        serial = p.readInt();
-        error = p.readInt();
-
-        RILRequest rr;
-
-        rr = findAndRemoveRequestFromList(serial);
-
-        if (rr == null) {
-            Rlog.w(RILJ_LOG_TAG, "Unexpected solicited response! sn: "
-                            + serial + " error: " + error);
-            return null;
-        }
-
-        // Time logging for RIL command and storing it in TelephonyHistogram.
-        addToRilHistogram(rr);
-
-        if (getRilVersion() >= 13 && type == RESPONSE_SOLICITED_ACK_EXP) {
-            Message msg;
-            RILRequest response = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null);
-            msg = mSender.obtainMessage(EVENT_SEND_ACK, response);
-            acquireWakeLock(rr, FOR_ACK_WAKELOCK);
-            msg.sendToTarget();
-            if (RILJ_LOGD) {
-                riljLog("Response received for " + rr.serialString() + " " +
-                        requestToString(rr.mRequest) + " Sending ack to ril.cpp");
-            }
-        }
-
-
-        Object ret = null;
-
-        if (error == 0 || p.dataAvail() > 0) {
-            // either command succeeds or command fails but with data payload
-            try {switch (rr.mRequest) {
-            /*
- cat libs/telephony/ril_commands.h \
- | egrep "^ *{RIL_" \
- | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/'
-             */
-            case RIL_REQUEST_GET_SIM_STATUS: ret =  responseIccCardStatus(p); break;
-            case RIL_REQUEST_ENTER_SIM_PIN: ret =  responseInts(p); break;
-            case RIL_REQUEST_ENTER_SIM_PUK: ret =  responseInts(p); break;
-            case RIL_REQUEST_ENTER_SIM_PIN2: ret =  responseInts(p); break;
-            case RIL_REQUEST_ENTER_SIM_PUK2: ret =  responseInts(p); break;
-            case RIL_REQUEST_CHANGE_SIM_PIN: ret =  responseInts(p); break;
-            case RIL_REQUEST_CHANGE_SIM_PIN2: ret =  responseInts(p); break;
-            case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: ret =  responseInts(p); break;
-            case RIL_REQUEST_GET_CURRENT_CALLS: ret =  responseCallList(p); break;
-            case RIL_REQUEST_DIAL: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_IMSI: ret =  responseString(p); break;
-            case RIL_REQUEST_HANGUP: ret =  responseVoid(p); break;
-            case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: ret =  responseVoid(p); break;
-            case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: {
-                if (mTestingEmergencyCall.getAndSet(false)) {
-                    if (mEmergencyCallbackModeRegistrant != null) {
-                        riljLog("testing emergency call, notify ECM Registrants");
-                        mEmergencyCallbackModeRegistrant.notifyRegistrant();
-                    }
-                }
-                ret =  responseVoid(p);
-                break;
-            }
-            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 =  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;
-            case RIL_REQUEST_OPERATOR: ret =  responseStrings(p); break;
-            case RIL_REQUEST_RADIO_POWER: ret =  responseVoid(p); break;
-            case RIL_REQUEST_DTMF: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SEND_SMS: ret =  responseSMS(p); break;
-            case RIL_REQUEST_SEND_SMS_EXPECT_MORE: ret =  responseSMS(p); break;
-            case RIL_REQUEST_SETUP_DATA_CALL: ret =  responseSetupDataCall(p); break;
-            case RIL_REQUEST_SIM_IO: ret =  responseICC_IO(p); break;
-            case RIL_REQUEST_SEND_USSD: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CANCEL_USSD: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_CLIR: ret =  responseInts(p); break;
-            case RIL_REQUEST_SET_CLIR: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: ret =  responseCallForward(p); break;
-            case RIL_REQUEST_SET_CALL_FORWARD: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_CALL_WAITING: ret =  responseInts(p); break;
-            case RIL_REQUEST_SET_CALL_WAITING: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SMS_ACKNOWLEDGE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_IMEI: ret =  responseString(p); break;
-            case RIL_REQUEST_GET_IMEISV: ret =  responseString(p); break;
-            case RIL_REQUEST_ANSWER: ret =  responseVoid(p); break;
-            case RIL_REQUEST_DEACTIVATE_DATA_CALL: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_FACILITY_LOCK: ret =  responseInts(p); break;
-            case RIL_REQUEST_SET_FACILITY_LOCK: ret =  responseInts(p); break;
-            case RIL_REQUEST_CHANGE_BARRING_PASSWORD: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: ret =  responseInts(p); break;
-            case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : ret =  responseOperatorInfos(p); break;
-            case RIL_REQUEST_DTMF_START: ret =  responseVoid(p); break;
-            case RIL_REQUEST_DTMF_STOP: ret =  responseVoid(p); break;
-            case RIL_REQUEST_BASEBAND_VERSION: ret =  responseString(p); break;
-            case RIL_REQUEST_SEPARATE_CONNECTION: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SET_MUTE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_MUTE: ret =  responseInts(p); break;
-            case RIL_REQUEST_QUERY_CLIP: ret =  responseInts(p); break;
-            case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: ret =  responseInts(p); break;
-            case RIL_REQUEST_DATA_CALL_LIST: ret =  responseDataCallList(p); break;
-            case RIL_REQUEST_RESET_RADIO: ret =  responseVoid(p); break;
-            case RIL_REQUEST_OEM_HOOK_RAW: ret =  responseRaw(p); break;
-            case RIL_REQUEST_OEM_HOOK_STRINGS: ret =  responseStrings(p); break;
-            case RIL_REQUEST_SCREEN_STATE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: ret =  responseVoid(p); break;
-            case RIL_REQUEST_WRITE_SMS_TO_SIM: ret =  responseInts(p); break;
-            case RIL_REQUEST_DELETE_SMS_ON_SIM: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SET_BAND_MODE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: ret =  responseInts(p); break;
-            case RIL_REQUEST_STK_GET_PROFILE: ret =  responseString(p); break;
-            case RIL_REQUEST_STK_SET_PROFILE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: ret =  responseString(p); break;
-            case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret =  responseInts(p); break;
-            case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret =  responseVoid(p); break;
-            case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret =  responseGetPreferredNetworkType(p); break;
-            case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
-            case RIL_REQUEST_SET_LOCATION_UPDATES: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: ret =  responseInts(p); break;
-            case RIL_REQUEST_SET_TTY_MODE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_QUERY_TTY_MODE: ret =  responseInts(p); break;
-            case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: ret =  responseInts(p); break;
-            case RIL_REQUEST_CDMA_FLASH: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_BURST_DTMF: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_SEND_SMS: ret =  responseSMS(p); break;
-            case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: ret =  responseGmsBroadcastConfig(p); break;
-            case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: ret =  responseCdmaBroadcastConfig(p); break;
-            case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: ret =  responseVoid(p); break;
-            case RIL_REQUEST_CDMA_SUBSCRIPTION: ret =  responseStrings(p); break;
-            case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: ret =  responseInts(p); break;
-            case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: ret =  responseVoid(p); break;
-            case RIL_REQUEST_DEVICE_IDENTITY: ret =  responseStrings(p); break;
-            case RIL_REQUEST_GET_SMSC_ADDRESS: ret = responseString(p); break;
-            case RIL_REQUEST_SET_SMSC_ADDRESS: ret = responseVoid(p); break;
-            case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
-            case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: ret = responseVoid(p); break;
-            case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: ret = responseVoid(p); break;
-            case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: ret =  responseInts(p); break;
-            case RIL_REQUEST_ISIM_AUTHENTICATION: ret =  responseString(p); break;
-            case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: ret = responseVoid(p); break;
-            case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: ret = responseICC_IO(p); break;
-            case RIL_REQUEST_VOICE_RADIO_TECH: ret = responseInts(p); break;
-            case RIL_REQUEST_GET_CELL_INFO_LIST: ret = responseCellInfoList(p); break;
-            case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: ret = responseVoid(p); break;
-            case RIL_REQUEST_SET_INITIAL_ATTACH_APN: ret = responseVoid(p); break;
-            case RIL_REQUEST_SET_DATA_PROFILE: ret = responseVoid(p); break;
-            case RIL_REQUEST_IMS_REGISTRATION_STATE: ret = responseInts(p); break;
-            case RIL_REQUEST_IMS_SEND_SMS: ret =  responseSMS(p); break;
-            case RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC: ret =  responseICC_IO(p); break;
-            case RIL_REQUEST_SIM_OPEN_CHANNEL: ret  = responseInts(p); break;
-            case RIL_REQUEST_SIM_CLOSE_CHANNEL: ret  = responseVoid(p); break;
-            case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL: ret = responseICC_IO(p); break;
-            case RIL_REQUEST_NV_READ_ITEM: ret = responseString(p); break;
-            case RIL_REQUEST_NV_WRITE_ITEM: ret = responseVoid(p); break;
-            case RIL_REQUEST_NV_WRITE_CDMA_PRL: ret = responseVoid(p); break;
-            case RIL_REQUEST_NV_RESET_CONFIG: ret = responseVoid(p); break;
-            case RIL_REQUEST_SET_UICC_SUBSCRIPTION: ret = responseVoid(p); break;
-            case RIL_REQUEST_ALLOW_DATA: ret = responseVoid(p); break;
-            case RIL_REQUEST_GET_HARDWARE_CONFIG: ret = responseHardwareConfig(p); break;
-            case RIL_REQUEST_SIM_AUTHENTICATION: ret =  responseICC_IOBase64(p); break;
-            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;
-            case RIL_REQUEST_SET_ALLOWED_CARRIERS: ret = responseInts(p); break;
-            case RIL_REQUEST_GET_ALLOWED_CARRIERS: ret = responseCarrierIdentifiers(p); break;
-            default:
-                throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
-            //break;
-            }} catch (Throwable tr) {
-                // Exceptions here usually mean invalid RIL responses
-
-                Rlog.w(RILJ_LOG_TAG, rr.serialString() + "< "
-                        + requestToString(rr.mRequest)
-                        + " exception, possible invalid RIL response", tr);
-
-                if (rr.mResult != null) {
-                    AsyncResult.forMessage(rr.mResult, null, tr);
-                    rr.mResult.sendToTarget();
-                }
-                return rr;
-            }
-        }
-
-        if (rr.mRequest == RIL_REQUEST_SHUTDOWN) {
-            // Set RADIO_STATE to RADIO_UNAVAILABLE to continue shutdown process
-            // regardless of error code to continue shutdown procedure.
-            riljLog("Response to RIL_REQUEST_SHUTDOWN received. Error is " +
-                    error + " Setting Radio State to Unavailable regardless of error.");
-            setRadioState(RadioState.RADIO_UNAVAILABLE);
-        }
-
-        // Here and below fake RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, see b/7255789.
-        // This is needed otherwise we don't automatically transition to the main lock
-        // screen when the pin or puk is entered incorrectly.
-        switch (rr.mRequest) {
-            case RIL_REQUEST_ENTER_SIM_PUK:
-            case RIL_REQUEST_ENTER_SIM_PUK2:
-                if (mIccStatusChangedRegistrants != null) {
-                    if (RILJ_LOGD) {
-                        riljLog("ON enter sim puk fakeSimStatusChanged: reg count="
-                                + mIccStatusChangedRegistrants.size());
-                    }
-                    mIccStatusChangedRegistrants.notifyRegistrants();
-                }
-                break;
-        }
-
-        if (error != 0) {
-            switch (rr.mRequest) {
-                case RIL_REQUEST_ENTER_SIM_PIN:
-                case RIL_REQUEST_ENTER_SIM_PIN2:
-                case RIL_REQUEST_CHANGE_SIM_PIN:
-                case RIL_REQUEST_CHANGE_SIM_PIN2:
-                case RIL_REQUEST_SET_FACILITY_LOCK:
-                    if (mIccStatusChangedRegistrants != null) {
-                        if (RILJ_LOGD) {
-                            riljLog("ON some errors fakeSimStatusChanged: reg count="
-                                    + mIccStatusChangedRegistrants.size());
-                        }
-                        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;
-            }
-
-            if (error != 0) rr.onError(error, ret);
-        }
-        if (error == 0) {
-
-            if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
-                    + " " + retToString(rr.mRequest, ret));
-
-            if (rr.mResult != null) {
-                AsyncResult.forMessage(rr.mResult, ret, null);
-                rr.mResult.sendToTarget();
-            }
-        }
-
-        mMetrics.writeOnRilSolicitedResponse(mInstanceId, rr.mSerial, error,
-                rr.mRequest, ret);
-
-        return rr;
-    }
-
-    private RadioCapability makeStaticRadioCapability() {
+    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) {
+        if (!TextUtils.isEmpty(rafString)) {
             raf = RadioAccessFamily.rafTypeFromString(rafString);
         }
-        RadioCapability rc = new RadioCapability(mInstanceId.intValue(), 0, 0, raf,
+        RadioCapability rc = new RadioCapability(mPhoneId.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) {
+    static String retToString(int req, Object ret) {
         if (ret == null) return "";
         switch (req) {
             // Don't log these return values, for privacy's sake.
@@ -2977,14 +4305,14 @@
         StringBuilder sb;
         String s;
         int length;
-        if (ret instanceof int[]){
+        if (ret instanceof int[]) {
             int[] intArray = (int[]) ret;
             length = intArray.length;
             sb = new StringBuilder("{");
             if (length > 0) {
                 int i = 0;
                 sb.append(intArray[i++]);
-                while ( i < length) {
+                while (i < length) {
                     sb.append(", ").append(intArray[i++]);
                 }
             }
@@ -2997,13 +4325,13 @@
             if (length > 0) {
                 int i = 0;
                 sb.append(strings[i++]);
-                while ( i < length) {
+                while (i < length) {
                     sb.append(", ").append(strings[i++]);
                 }
             }
             sb.append("}");
             s = sb.toString();
-        }else if (req == RIL_REQUEST_GET_CURRENT_CALLS) {
+        } else if (req == RIL_REQUEST_GET_CURRENT_CALLS) {
             ArrayList<DriverCall> calls = (ArrayList<DriverCall>) ret;
             sb = new StringBuilder("{");
             for (DriverCall dc : calls) {
@@ -3023,7 +4351,7 @@
             CallForwardInfo[] cinfo = (CallForwardInfo[]) ret;
             length = cinfo.length;
             sb = new StringBuilder("{");
-            for(int i = 0; i < length; i++) {
+            for (int i = 0; i < length; i++) {
                 sb.append("[").append(cinfo[i]).append("] ");
             }
             sb.append("}");
@@ -3041,1894 +4369,521 @@
         return s;
     }
 
-    private void
-    processUnsolicited (Parcel p, int type) {
-        int response;
-        Object ret;
+    void writeMetricsNewSms(int tech, int format) {
+        mMetrics.writeRilNewSms(mPhoneId, tech, format);
+    }
 
-        response = p.readInt();
+    void writeMetricsCallRing(char[] response) {
+        mMetrics.writeRilCallRing(mPhoneId, response);
+    }
 
-        // Follow new symantics of sending an Ack starting from RIL version 13
-        if (getRilVersion() >= 13 && type == RESPONSE_UNSOLICITED_ACK_EXP) {
-            Message msg;
-            RILRequest rr = RILRequest.obtain(RIL_RESPONSE_ACKNOWLEDGEMENT, null);
-            msg = mSender.obtainMessage(EVENT_SEND_ACK, rr);
-            acquireWakeLock(rr, FOR_ACK_WAKELOCK);
-            msg.sendToTarget();
-            if (RILJ_LOGD) {
-                riljLog("Unsol response received for " + responseToString(response) +
-                        " Sending ack to ril.cpp");
-            }
-        }
+    void writeMetricsSrvcc(int state) {
+        mMetrics.writeRilSrvcc(mPhoneId, state);
+    }
 
-        try {switch(response) {
-/*
- cat libs/telephony/ril_unsol_commands.h \
- | egrep "^ *{RIL_" \
- | sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: \2(rr, p); break;/'
-*/
-
-            case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: ret =  responseVoid(p); break;
-            case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: ret =  responseVoid(p); break;
-            case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: ret =  responseVoid(p); break;
-            case RIL_UNSOL_RESPONSE_NEW_SMS: ret =  responseString(p); break;
-            case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: ret =  responseString(p); break;
-            case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: ret =  responseInts(p); break;
-            case RIL_UNSOL_ON_USSD: ret =  responseStrings(p); break;
-            case RIL_UNSOL_NITZ_TIME_RECEIVED: ret =  responseString(p); break;
-            case RIL_UNSOL_SIGNAL_STRENGTH: ret = responseSignalStrength(p); break;
-            case RIL_UNSOL_DATA_CALL_LIST_CHANGED: ret = responseDataCallList(p);break;
-            case RIL_UNSOL_SUPP_SVC_NOTIFICATION: ret = responseSuppServiceNotification(p); break;
-            case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break;
-            case RIL_UNSOL_STK_PROACTIVE_COMMAND: ret = responseString(p); break;
-            case RIL_UNSOL_STK_EVENT_NOTIFY: ret = responseString(p); break;
-            case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break;
-            case RIL_UNSOL_SIM_SMS_STORAGE_FULL: ret =  responseVoid(p); break;
-            case RIL_UNSOL_SIM_REFRESH: ret =  responseSimRefresh(p); break;
-            case RIL_UNSOL_CALL_RING: ret =  responseCallRing(p); break;
-            case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break;
-            case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:  ret =  responseVoid(p); break;
-            case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS:  ret =  responseCdmaSms(p); break;
-            case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:  ret =  responseRaw(p); break;
-            case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL:  ret =  responseVoid(p); break;
-            case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
-            case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break;
-            case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: ret = responseInts(p); break;
-            case RIL_UNSOL_CDMA_INFO_REC: ret = responseCdmaInformationRecord(p); break;
-            case RIL_UNSOL_OEM_HOOK_RAW: ret = responseRaw(p); break;
-            case RIL_UNSOL_RINGBACK_TONE: ret = responseInts(p); break;
-            case RIL_UNSOL_RESEND_INCALL_MUTE: ret = responseVoid(p); break;
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: ret = responseInts(p); break;
-            case RIL_UNSOl_CDMA_PRL_CHANGED: ret = responseInts(p); break;
-            case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break;
-            case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break;
-            case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: ret =  responseInts(p); break;
-            case RIL_UNSOL_CELL_INFO_LIST: ret = responseCellInfoList(p); break;
-            case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED: ret =  responseVoid(p); break;
-            case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED: ret =  responseInts(p); break;
-            case RIL_UNSOL_SRVCC_STATE_NOTIFY: ret = responseInts(p); break;
-            case RIL_UNSOL_HARDWARE_CONFIG_CHANGED: ret = responseHardwareConfig(p); break;
-            case RIL_UNSOL_RADIO_CAPABILITY:
-                    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;
-            case RIL_UNSOL_PCO_DATA: ret = responsePcoData(p); break;
-
-            default:
-                throw new RuntimeException("Unrecognized unsol response: " + response);
-            //break; (implied)
-        }} catch (Throwable tr) {
-            Rlog.e(RILJ_LOG_TAG, "Exception processing unsol response: " + response +
-                "Exception:" + tr.toString());
-            return;
-        }
-
-        switch(response) {
-            case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
-                /* has bonus radio state int */
-                RadioState newState = getRadioStateFromInt(p.readInt());
-                if (RILJ_LOGD) unsljLogMore(response, newState.toString());
-
-                switchToRadioState(newState);
-            break;
-            case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
-                if (RILJ_LOGD) unsljLog(response);
-
-                mImsNetworkStateChangedRegistrants
-                    .notifyRegistrants(new AsyncResult(null, null, null));
-            break;
-            case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
-                if (RILJ_LOGD) unsljLog(response);
-
-                mCallStateRegistrants
-                    .notifyRegistrants(new AsyncResult(null, null, null));
-            break;
-            case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED:
-                if (RILJ_LOGD) unsljLog(response);
-
-                mVoiceNetworkStateRegistrants
-                    .notifyRegistrants(new AsyncResult(null, null, null));
-            break;
-            case RIL_UNSOL_RESPONSE_NEW_SMS: {
-                if (RILJ_LOGD) unsljLog(response);
-
-                mMetrics.writeRilNewSms(mInstanceId, SmsSession.Event.Tech.SMS_GSM,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP);
-
-                // FIXME this should move up a layer
-                String a[] = new String[2];
-
-                a[1] = (String)ret;
-
-                SmsMessage sms;
-
-                sms = SmsMessage.newFromCMT(a);
-                if (mGsmSmsRegistrant != null) {
-                    mGsmSmsRegistrant
-                        .notifyRegistrant(new AsyncResult(null, sms, null));
-                }
-            break;
-            }
-            case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mSmsStatusRegistrant != null) {
-                    mSmsStatusRegistrant.notifyRegistrant(
-                            new AsyncResult(null, ret, null));
-                }
-            break;
-            case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                int[] smsIndex = (int[])ret;
-
-                if(smsIndex.length == 1) {
-                    if (mSmsOnSimRegistrant != null) {
-                        mSmsOnSimRegistrant.
-                                notifyRegistrant(new AsyncResult(null, smsIndex, null));
-                    }
-                } else {
-                    if (RILJ_LOGD) riljLog(" NEW_SMS_ON_SIM ERROR with wrong length "
-                            + smsIndex.length);
-                }
-            break;
-            case RIL_UNSOL_ON_USSD:
-                String[] resp = (String[])ret;
-
-                if (resp.length < 2) {
-                    resp = new String[2];
-                    resp[0] = ((String[])ret)[0];
-                    resp[1] = null;
-                }
-                if (RILJ_LOGD) unsljLogMore(response, resp[0]);
-                if (mUSSDRegistrant != null) {
-                    mUSSDRegistrant.notifyRegistrant(
-                        new AsyncResult (null, resp, null));
-                }
-            break;
-            case RIL_UNSOL_NITZ_TIME_RECEIVED:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                // has bonus long containing milliseconds since boot that the NITZ
-                // time was received
-                long nitzReceiveTime = p.readLong();
-
-                Object[] result = new Object[2];
-
-                result[0] = ret;
-                result[1] = Long.valueOf(nitzReceiveTime);
-
-                boolean ignoreNitz = SystemProperties.getBoolean(
-                        TelephonyProperties.PROPERTY_IGNORE_NITZ, false);
-
-                if (ignoreNitz) {
-                    if (RILJ_LOGD) riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
-                } else {
-                    if (mNITZTimeRegistrant != null) {
-
-                        mNITZTimeRegistrant
-                            .notifyRegistrant(new AsyncResult (null, result, null));
-                    }
-                    // in case NITZ time registrant isn't registered yet, or a new registrant
-                    // registers later
-                    mLastNITZTimeInfo = result;
-                }
-            break;
-
-            case RIL_UNSOL_SIGNAL_STRENGTH:
-                // Note this is set to "verbose" because it happens
-                // frequently
-                if (RILJ_LOGV) unsljLogvRet(response, ret);
-
-                if (mSignalStrengthRegistrant != null) {
-                    mSignalStrengthRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-            break;
-            case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                mDataNetworkStateRegistrants.notifyRegistrants(new AsyncResult(null, ret, null));
-            break;
-
-            case RIL_UNSOL_SUPP_SVC_NOTIFICATION:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mSsnRegistrant != null) {
-                    mSsnRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_STK_SESSION_END:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mCatSessionEndRegistrant != null) {
-                    mCatSessionEndRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_STK_PROACTIVE_COMMAND:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mCatProCmdRegistrant != null) {
-                    mCatProCmdRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_STK_EVENT_NOTIFY:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mCatEventRegistrant != null) {
-                    mCatEventRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_STK_CALL_SETUP:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mCatCallSetUpRegistrant != null) {
-                    mCatCallSetUpRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_SIM_SMS_STORAGE_FULL:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mIccSmsFullRegistrant != null) {
-                    mIccSmsFullRegistrant.notifyRegistrant();
-                }
-                break;
-
-            case RIL_UNSOL_SIM_REFRESH:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mIccRefreshRegistrants != null) {
-                    mIccRefreshRegistrants.notifyRegistrants(
-                            new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_CALL_RING:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mRingRegistrant != null) {
-                    mRingRegistrant.notifyRegistrant(
-                            new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_RESTRICTED_STATE_CHANGED:
-                if (RILJ_LOGD) unsljLogvRet(response, ret);
-                if (mRestrictedStateRegistrant != null) {
-                    mRestrictedStateRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mIccStatusChangedRegistrants != null) {
-                    mIccStatusChangedRegistrants.notifyRegistrants();
-                }
-                break;
-
-            case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS:
-                if (RILJ_LOGD) unsljLog(response);
-
-                mMetrics.writeRilNewSms(mInstanceId, SmsSession.Event.Tech.SMS_CDMA,
-                        SmsSession.Event.Format.SMS_FORMAT_3GPP2);
-
-                SmsMessage sms = (SmsMessage) ret;
-
-                if (mCdmaSmsRegistrant != null) {
-                    mCdmaSmsRegistrant
-                        .notifyRegistrant(new AsyncResult(null, sms, null));
-                }
-                break;
-
-            case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:
-                if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret));
-
-                if (mGsmBroadcastSmsRegistrant != null) {
-                    mGsmBroadcastSmsRegistrant
-                        .notifyRegistrant(new AsyncResult(null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mIccSmsFullRegistrant != null) {
-                    mIccSmsFullRegistrant.notifyRegistrant();
-                }
-                break;
-
-            case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE:
-                if (RILJ_LOGD) unsljLog(response);
-
-                if (mEmergencyCallbackModeRegistrant != null) {
-                    mEmergencyCallbackModeRegistrant.notifyRegistrant();
-                }
-                break;
-
-            case RIL_UNSOL_CDMA_CALL_WAITING:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mCallWaitingInfoRegistrants != null) {
-                    mCallWaitingInfoRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mOtaProvisionRegistrants != null) {
-                    mOtaProvisionRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_CDMA_INFO_REC:
-                ArrayList<CdmaInformationRecords> listInfoRecs;
-
-                try {
-                    listInfoRecs = (ArrayList<CdmaInformationRecords>)ret;
-                } catch (ClassCastException e) {
-                    Rlog.e(RILJ_LOG_TAG, "Unexpected exception casting to listInfoRecs", e);
-                    break;
-                }
-
-                for (CdmaInformationRecords rec : listInfoRecs) {
-                    if (RILJ_LOGD) unsljLogRet(response, rec);
-                    notifyRegistrantsCdmaInfoRec(rec);
-                }
-                break;
-
-            case RIL_UNSOL_OEM_HOOK_RAW:
-                if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[]) ret));
-                if (mUnsolOemHookRawRegistrant != null) {
-                    mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_RINGBACK_TONE:
-                if (RILJ_LOGD) unsljLogvRet(response, ret);
-                if (mRingbackToneRegistrants != null) {
-                    boolean playtone = (((int[])ret)[0] == 1);
-                    mRingbackToneRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, playtone, null));
-                }
-                break;
-
-            case RIL_UNSOL_RESEND_INCALL_MUTE:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mResendIncallMuteRegistrants != null) {
-                    mResendIncallMuteRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mVoiceRadioTechChangedRegistrants != null) {
-                    mVoiceRadioTechChangedRegistrants.notifyRegistrants(
-                            new AsyncResult(null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mCdmaSubscriptionChangedRegistrants != null) {
-                    mCdmaSubscriptionChangedRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOl_CDMA_PRL_CHANGED:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mCdmaPrlChangedRegistrants != null) {
-                    mCdmaPrlChangedRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-
-            case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mExitEmergencyCallbackModeRegistrants != null) {
-                    mExitEmergencyCallbackModeRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, null, null));
-                }
-                break;
-
-            case RIL_UNSOL_RIL_CONNECTED: {
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                // Initial conditions
-                setRadioPower(false, null);
-                setCdmaSubscriptionSource(mCdmaSubscription, null);
-                setCellInfoListRate(Integer.MAX_VALUE, null);
-                notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
-                // When modem crashes, if user turns the screen off before RIL reconnects, screen
-                // state cannot be sent to modem. Resend the display state here so that modem
-                // has the correct state (to stop signal strength reporting, etc).
-                updateScreenState(true);
-                break;
-            }
-            case RIL_UNSOL_CELL_INFO_LIST: {
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mRilCellInfoListRegistrants != null) {
-                    mRilCellInfoListRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-            }
-            case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED: {
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mSubscriptionStatusRegistrants != null) {
-                    mSubscriptionStatusRegistrants.notifyRegistrants(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-            }
-            case RIL_UNSOL_SRVCC_STATE_NOTIFY: {
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                mMetrics.writeRilSrvcc(mInstanceId, ((int[])ret)[0]);
-
-                if (mSrvccStateRegistrants != null) {
-                    mSrvccStateRegistrants
-                            .notifyRegistrants(new AsyncResult(null, ret, null));
-                }
-                break;
-            }
-            case RIL_UNSOL_HARDWARE_CONFIG_CHANGED:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mHardwareConfigChangeRegistrants != null) {
-                    mHardwareConfigChangeRegistrants.notifyRegistrants(
-                                             new AsyncResult (null, ret, null));
-                }
-                break;
-            case RIL_UNSOL_RADIO_CAPABILITY:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mPhoneRadioCapabilityChangedRegistrants != null) {
-                    mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants(
-                            new AsyncResult(null, ret, null));
-                 }
-                 break;
-            case RIL_UNSOL_ON_SS:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mSsRegistrant != null) {
-                    mSsRegistrant.notifyRegistrant(
-                                        new AsyncResult (null, ret, null));
-                }
-                break;
-            case RIL_UNSOL_STK_CC_ALPHA_NOTIFY:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                if (mCatCcAlphaRegistrant != null) {
-                    mCatCcAlphaRegistrant.notifyRegistrant(
-                                        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;
-            case RIL_UNSOL_PCO_DATA:
-                if (RILJ_LOGD) unsljLogRet(response, ret);
-
-                mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, ret, null));
-                break;
-        }
+    void writeMetricsModemRestartEvent(String reason) {
+        mMetrics.writeModemRestartEvent(mPhoneId, reason);
     }
 
     /**
-     * Notifiy all registrants that the ril has connected or disconnected.
+     * Notify all registrants that the ril has connected or disconnected.
      *
      * @param rilVer is the version of the ril or -1 if disconnected.
      */
-    private void notifyRegistrantsRilConnectionChanged(int rilVer) {
+    void notifyRegistrantsRilConnectionChanged(int rilVer) {
         mRilVersion = rilVer;
         if (mRilConnectedRegistrants != null) {
             mRilConnectedRegistrants.notifyRegistrants(
-                                new AsyncResult (null, new Integer(rilVer), null));
+                    new AsyncResult(null, new Integer(rilVer), null));
         }
     }
 
-    private Object
-    responseInts(Parcel p) {
-        int numInts;
-        int response[];
-
-        numInts = p.readInt();
-
-        response = new int[numInts];
-
-        for (int i = 0 ; i < numInts ; i++) {
-            response[i] = p.readInt();
-        }
-
-        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) {
-        return null;
-    }
-
-    private Object
-    responseCallForward(Parcel p) {
-        int numInfos;
-        CallForwardInfo infos[];
-
-        numInfos = p.readInt();
-
-        infos = new CallForwardInfo[numInfos];
-
-        for (int i = 0 ; i < numInfos ; i++) {
-            infos[i] = new CallForwardInfo();
-
-            infos[i].status = p.readInt();
-            infos[i].reason = p.readInt();
-            infos[i].serviceClass = p.readInt();
-            infos[i].toa = p.readInt();
-            infos[i].number = p.readString();
-            infos[i].timeSeconds = p.readInt();
-        }
-
-        return infos;
-    }
-
-    private Object
-    responseSuppServiceNotification(Parcel p) {
-        SuppServiceNotification notification = new SuppServiceNotification();
-
-        notification.notificationType = p.readInt();
-        notification.code = p.readInt();
-        notification.index = p.readInt();
-        notification.type = p.readInt();
-        notification.number = p.readString();
-
-        return notification;
-    }
-
-    private Object
-    responseCdmaSms(Parcel p) {
-        SmsMessage sms;
-        sms = SmsMessage.newFromParcel(p);
-
-        return sms;
-    }
-
-    private Object
-    responseString(Parcel p) {
-        String response;
-
-        response = p.readString();
-
-        return response;
-    }
-
-    private Object
-    responseStrings(Parcel p) {
-        int num;
-        String response[];
-
-        response = p.readStringArray();
-
-        return response;
-    }
-
-    private Object
-    responseRaw(Parcel p) {
-        int num;
-        byte response[];
-
-        response = p.createByteArray();
-
-        return response;
-    }
-
-    private Object
-    responseSMS(Parcel p) {
-        int messageRef, errorCode;
-        String ackPDU;
-
-        messageRef = p.readInt();
-        ackPDU = p.readString();
-        errorCode = p.readInt();
-
-        SmsResponse response = new SmsResponse(messageRef, ackPDU, errorCode);
-
-        return response;
-    }
-
-
-    private Object
-    responseICC_IO(Parcel p) {
-        int sw1, sw2;
-        Message ret;
-
-        sw1 = p.readInt();
-        sw2 = p.readInt();
-
-        String s = p.readString();
-
-        if (RILJ_LOGV) riljLog("< iccIO: "
-                + " 0x" + Integer.toHexString(sw1)
-                + " 0x" + Integer.toHexString(sw2) + " "
-                + s);
-
-        return new IccIoResult(sw1, sw2, s);
-    }
-
-    private Object
-    responseICC_IOBase64(Parcel p) {
-        int sw1, sw2;
-        Message ret;
-
-        sw1 = p.readInt();
-        sw2 = p.readInt();
-
-        String s = p.readString();
-
-        if (RILJ_LOGV) riljLog("< iccIO: "
-                + " 0x" + Integer.toHexString(sw1)
-                + " 0x" + Integer.toHexString(sw2) + " "
-                + s);
-
-        return new IccIoResult(sw1, sw2, (s != null)
-                ? android.util.Base64.decode(s, android.util.Base64.DEFAULT) : (byte[]) null);
-    }
-
-    private Object
-    responseIccCardStatus(Parcel p) {
-        IccCardApplicationStatus appStatus;
-
-        IccCardStatus cardStatus = new IccCardStatus();
-        cardStatus.setCardState(p.readInt());
-        cardStatus.setUniversalPinState(p.readInt());
-        cardStatus.mGsmUmtsSubscriptionAppIndex = p.readInt();
-        cardStatus.mCdmaSubscriptionAppIndex = p.readInt();
-        cardStatus.mImsSubscriptionAppIndex = p.readInt();
-        int numApplications = p.readInt();
-
-        // limit to maximum allowed applications
-        if (numApplications > IccCardStatus.CARD_MAX_APPS) {
-            numApplications = IccCardStatus.CARD_MAX_APPS;
-        }
-        cardStatus.mApplications = new IccCardApplicationStatus[numApplications];
-        for (int i = 0 ; i < numApplications ; i++) {
-            appStatus = new IccCardApplicationStatus();
-            appStatus.app_type       = appStatus.AppTypeFromRILInt(p.readInt());
-            appStatus.app_state      = appStatus.AppStateFromRILInt(p.readInt());
-            appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(p.readInt());
-            appStatus.aid            = p.readString();
-            appStatus.app_label      = p.readString();
-            appStatus.pin1_replaced  = p.readInt();
-            appStatus.pin1           = appStatus.PinStateFromRILInt(p.readInt());
-            appStatus.pin2           = appStatus.PinStateFromRILInt(p.readInt());
-            cardStatus.mApplications[i] = appStatus;
-        }
-        return cardStatus;
-    }
-
-    private Object
-    responseSimRefresh(Parcel p) {
-        IccRefreshResponse response = new IccRefreshResponse();
-
-        response.refreshResult = p.readInt();
-        response.efId   = p.readInt();
-        response.aid = p.readString();
-        return response;
-    }
-
-    private Object
-    responseCallList(Parcel p) {
-        int num;
-        int voiceSettings;
-        ArrayList<DriverCall> response;
-        DriverCall dc;
-
-        num = p.readInt();
-        response = new ArrayList<DriverCall>(num);
-
-        if (RILJ_LOGV) {
-            riljLog("responseCallList: num=" + num +
-                    " mEmergencyCallbackModeRegistrant=" + mEmergencyCallbackModeRegistrant +
-                    " mTestingEmergencyCall=" + mTestingEmergencyCall.get());
-        }
-        for (int i = 0 ; i < num ; i++) {
-            dc = new DriverCall();
-
-            dc.state = DriverCall.stateFromCLCC(p.readInt());
-            dc.index = p.readInt();
-            dc.TOA = p.readInt();
-            dc.isMpty = (0 != p.readInt());
-            dc.isMT = (0 != p.readInt());
-            dc.als = p.readInt();
-            voiceSettings = p.readInt();
-            dc.isVoice = (0 == voiceSettings) ? false : true;
-            dc.isVoicePrivacy = (0 != p.readInt());
-            dc.number = p.readString();
-            int np = p.readInt();
-            dc.numberPresentation = DriverCall.presentationFromCLIP(np);
-            dc.name = p.readString();
-            // according to ril.h, namePresentation should be handled as numberPresentation;
-            dc.namePresentation = DriverCall.presentationFromCLIP(p.readInt());
-            int uusInfoPresent = p.readInt();
-            if (uusInfoPresent == 1) {
-                dc.uusInfo = new UUSInfo();
-                dc.uusInfo.setType(p.readInt());
-                dc.uusInfo.setDcs(p.readInt());
-                byte[] userData = p.createByteArray();
-                dc.uusInfo.setUserData(userData);
-                riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
-                                dc.uusInfo.getType(), dc.uusInfo.getDcs(),
-                                dc.uusInfo.getUserData().length));
-                riljLogv("Incoming UUS : data (string)="
-                        + new String(dc.uusInfo.getUserData()));
-                riljLogv("Incoming UUS : data (hex): "
-                        + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
-            } else {
-                riljLogv("Incoming UUS : NOT present!");
-            }
-
-            // Make sure there's a leading + on addresses with a TOA of 145
-            dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
-
-            response.add(dc);
-
-            if (dc.isVoicePrivacy) {
-                mVoicePrivacyOnRegistrants.notifyRegistrants();
-                riljLog("InCall VoicePrivacy is enabled");
-            } else {
-                mVoicePrivacyOffRegistrants.notifyRegistrants();
-                riljLog("InCall VoicePrivacy is disabled");
-            }
-        }
-
-        Collections.sort(response);
-
-        if ((num == 0) && mTestingEmergencyCall.getAndSet(false)) {
-            if (mEmergencyCallbackModeRegistrant != null) {
-                riljLog("responseCallList: call ended, testing emergency call," +
-                            " notify ECM Registrants");
-                mEmergencyCallbackModeRegistrant.notifyRegistrant();
-            }
-        }
-
-        return response;
-    }
-
-    private DataCallResponse getDataCallResponse(Parcel p, int version) {
-        DataCallResponse dataCall = new DataCallResponse();
-
-        dataCall.version = version;
-        if (version < 5) {
-            dataCall.cid = p.readInt();
-            dataCall.active = p.readInt();
-            dataCall.type = p.readString();
-            String addresses = p.readString();
-            if (!TextUtils.isEmpty(addresses)) {
-                dataCall.addresses = addresses.split(" ");
-            }
-        } else {
-            dataCall.status = p.readInt();
-            dataCall.suggestedRetryTime = p.readInt();
-            dataCall.cid = p.readInt();
-            dataCall.active = p.readInt();
-            dataCall.type = p.readString();
-            dataCall.ifname = p.readString();
-            if ((dataCall.status == DcFailCause.NONE.getErrorCode()) &&
-                    TextUtils.isEmpty(dataCall.ifname)) {
-              throw new RuntimeException("getDataCallResponse, no ifname");
-            }
-            String addresses = p.readString();
-            if (!TextUtils.isEmpty(addresses)) {
-                dataCall.addresses = addresses.split(" ");
-            }
-            String dnses = p.readString();
-            if (!TextUtils.isEmpty(dnses)) {
-                dataCall.dnses = dnses.split(" ");
-            }
-            String gateways = p.readString();
-            if (!TextUtils.isEmpty(gateways)) {
-                dataCall.gateways = gateways.split(" ");
-            }
-            if (version >= 10) {
-                String pcscf = p.readString();
-                if (!TextUtils.isEmpty(pcscf)) {
-                    dataCall.pcscf = pcscf.split(" ");
-                }
-            }
-            if (version >= 11) {
-                dataCall.mtu = p.readInt();
-            }
-        }
-        return dataCall;
-    }
-
-    private Object
-    responseDataCallList(Parcel p) {
-        ArrayList<DataCallResponse> response;
-
-        int ver = p.readInt();
-        int num = p.readInt();
-        riljLog("responseDataCallList ver=" + ver + " num=" + num);
-
-        response = new ArrayList<DataCallResponse>(num);
-        for (int i = 0; i < num; i++) {
-            response.add(getDataCallResponse(p, ver));
-        }
-
-
-        return response;
-    }
-
-    private Object
-    responseSetupDataCall(Parcel p) {
-        int ver = p.readInt();
-        int num = p.readInt();
-        if (RILJ_LOGV) riljLog("responseSetupDataCall ver=" + ver + " num=" + num);
-
-        DataCallResponse dataCall;
-
-        if (ver < 5) {
-            dataCall = new DataCallResponse();
-            dataCall.version = ver;
-            dataCall.cid = Integer.parseInt(p.readString());
-            dataCall.ifname = p.readString();
-            if (TextUtils.isEmpty(dataCall.ifname)) {
-                throw new RuntimeException(
-                        "RIL_REQUEST_SETUP_DATA_CALL response, no ifname");
-            }
-            String addresses = p.readString();
-            if (!TextUtils.isEmpty(addresses)) {
-              dataCall.addresses = addresses.split(" ");
-            }
-            if (num >= 4) {
-                String dnses = p.readString();
-                if (RILJ_LOGD) riljLog("responseSetupDataCall got dnses=" + dnses);
-                if (!TextUtils.isEmpty(dnses)) {
-                    dataCall.dnses = dnses.split(" ");
-                }
-            }
-            if (num >= 5) {
-                String gateways = p.readString();
-                if (RILJ_LOGD) riljLog("responseSetupDataCall got gateways=" + gateways);
-                if (!TextUtils.isEmpty(gateways)) {
-                    dataCall.gateways = gateways.split(" ");
-                }
-            }
-            if (num >= 6) {
-                String pcscf = p.readString();
-                if (RILJ_LOGD) riljLog("responseSetupDataCall got pcscf=" + pcscf);
-                if (!TextUtils.isEmpty(pcscf)) {
-                    dataCall.pcscf = pcscf.split(" ");
-                }
-            }
-        } else {
-            if (num != 1) {
-                throw new RuntimeException(
-                        "RIL_REQUEST_SETUP_DATA_CALL response expecting 1 RIL_Data_Call_response_v5"
-                        + " got " + num);
-            }
-            dataCall = getDataCallResponse(p, ver);
-        }
-
-        return dataCall;
-    }
-
-    private Object
-    responseOperatorInfos(Parcel p) {
-        String strings[] = (String [])responseStrings(p);
-        ArrayList<OperatorInfo> ret;
-
-        if (strings.length % 4 != 0) {
-            throw new RuntimeException(
-                "RIL_REQUEST_QUERY_AVAILABLE_NETWORKS: invalid response. Got "
-                + strings.length + " strings, expected multible of 4");
-        }
-
-        ret = new ArrayList<OperatorInfo>(strings.length / 4);
-
-        for (int i = 0 ; i < strings.length ; i += 4) {
-            ret.add (
-                new OperatorInfo(
-                    strings[i+0],
-                    strings[i+1],
-                    strings[i+2],
-                    strings[i+3]));
-        }
-
-        return ret;
-    }
-
-    private Object
-    responseCellList(Parcel p) {
-       int num, rssi;
-       String location;
-       ArrayList<NeighboringCellInfo> response;
-       NeighboringCellInfo cell;
-
-       num = p.readInt();
-       response = new ArrayList<NeighboringCellInfo>();
-
-       // Determine the radio access type
-       int[] subId = SubscriptionManager.getSubId(mInstanceId);
-       int radioType =
-               ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)).
-               getDataNetworkType(subId[0]);
-
-       // Interpret the location based on radio access type
-       if (radioType != NETWORK_TYPE_UNKNOWN) {
-           for (int i = 0 ; i < num ; i++) {
-               rssi = p.readInt();
-               location = p.readString();
-               cell = new NeighboringCellInfo(rssi, location, radioType);
-               response.add(cell);
-           }
-       }
-       return response;
-    }
-
-    private Object responseGetPreferredNetworkType(Parcel p) {
-       int [] response = (int[]) responseInts(p);
-
-       if (response.length >= 1) {
-           // Since this is the response for getPreferredNetworkType
-           // we'll assume that it should be the value we want the
-           // vendor ril to take if we reestablish a connection to it.
-           mPreferredNetworkType = response[0];
-       }
-       return response;
-    }
-
-    private Object responseGmsBroadcastConfig(Parcel p) {
-        int num;
-        ArrayList<SmsBroadcastConfigInfo> response;
-        SmsBroadcastConfigInfo info;
-
-        num = p.readInt();
-        response = new ArrayList<SmsBroadcastConfigInfo>(num);
-
-        for (int i = 0; i < num; i++) {
-            int fromId = p.readInt();
-            int toId = p.readInt();
-            int fromScheme = p.readInt();
-            int toScheme = p.readInt();
-            boolean selected = (p.readInt() == 1);
-
-            info = new SmsBroadcastConfigInfo(fromId, toId, fromScheme,
-                    toScheme, selected);
-            response.add(info);
-        }
-        return response;
-    }
-
-    private Object
-    responseCdmaBroadcastConfig(Parcel p) {
-        int numServiceCategories;
-        int response[];
-
-        numServiceCategories = p.readInt();
-
-        if (numServiceCategories == 0) {
-            // TODO: The logic of providing default values should
-            // not be done by this transport layer. And needs to
-            // be done by the vendor ril or application logic.
-            int numInts;
-            numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES * CDMA_BSI_NO_OF_INTS_STRUCT + 1;
-            response = new int[numInts];
-
-            // Faking a default record for all possible records.
-            response[0] = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES;
-
-            // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as
-            // default language and selection status to false for all.
-            for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT ) {
-                response[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT;
-                response[i + 1] = 1;
-                response[i + 2] = 0;
-            }
-        } else {
-            int numInts;
-            numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1;
-            response = new int[numInts];
-
-            response[0] = numServiceCategories;
-            for (int i = 1 ; i < numInts; i++) {
-                 response[i] = p.readInt();
-             }
-        }
-
-        return response;
-    }
-
-    private Object
-    responseSignalStrength(Parcel p) {
-        // Assume this is gsm, but doesn't matter as ServiceStateTracker
-        // sets the proper value.
-        SignalStrength signalStrength = SignalStrength.makeSignalStrengthFromRilParcel(p);
-        return signalStrength;
-    }
-
-    private ArrayList<CdmaInformationRecords>
-    responseCdmaInformationRecord(Parcel p) {
-        int numberOfInfoRecs;
-        ArrayList<CdmaInformationRecords> response;
-
-        /**
-         * Loop through all of the information records unmarshalling them
-         * and converting them to Java Objects.
-         */
-        numberOfInfoRecs = p.readInt();
-        response = new ArrayList<CdmaInformationRecords>(numberOfInfoRecs);
-
-        for (int i = 0; i < numberOfInfoRecs; i++) {
-            CdmaInformationRecords InfoRec = new CdmaInformationRecords(p);
-            response.add(InfoRec);
-        }
-
-        return response;
-    }
-
-    private Object
-    responseCdmaCallWaiting(Parcel p) {
-        CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification();
-
-        notification.number = p.readString();
-        notification.numberPresentation =
-                CdmaCallWaitingNotification.presentationFromCLIP(p.readInt());
-        notification.name = p.readString();
-        notification.namePresentation = notification.numberPresentation;
-        notification.isPresent = p.readInt();
-        notification.signalType = p.readInt();
-        notification.alertPitch = p.readInt();
-        notification.signal = p.readInt();
-        notification.numberType = p.readInt();
-        notification.numberPlan = p.readInt();
-
-        return notification;
-    }
-
-    private Object
-    responseCallRing(Parcel p){
-        char response[] = new char[4];
-
-        response[0] = (char) p.readInt();    // isPresent
-        response[1] = (char) p.readInt();    // signalType
-        response[2] = (char) p.readInt();    // alertPitch
-        response[3] = (char) p.readInt();    // signal
-
-        mMetrics.writeRilCallRing(mInstanceId, response);
-
-        return response;
-    }
-
-    private void
+    void
     notifyRegistrantsCdmaInfoRec(CdmaInformationRecords infoRec) {
         int response = RIL_UNSOL_CDMA_INFO_REC;
         if (infoRec.record instanceof CdmaInformationRecords.CdmaDisplayInfoRec) {
             if (mDisplayInfoRegistrants != null) {
                 if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                 mDisplayInfoRegistrants.notifyRegistrants(
-                        new AsyncResult (null, infoRec.record, null));
+                        new AsyncResult(null, infoRec.record, null));
             }
         } else if (infoRec.record instanceof CdmaInformationRecords.CdmaSignalInfoRec) {
             if (mSignalInfoRegistrants != null) {
                 if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                 mSignalInfoRegistrants.notifyRegistrants(
-                        new AsyncResult (null, infoRec.record, null));
+                        new AsyncResult(null, infoRec.record, null));
             }
         } else if (infoRec.record instanceof CdmaInformationRecords.CdmaNumberInfoRec) {
             if (mNumberInfoRegistrants != null) {
                 if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                 mNumberInfoRegistrants.notifyRegistrants(
-                        new AsyncResult (null, infoRec.record, null));
+                        new AsyncResult(null, infoRec.record, null));
             }
         } else if (infoRec.record instanceof CdmaInformationRecords.CdmaRedirectingNumberInfoRec) {
             if (mRedirNumInfoRegistrants != null) {
                 if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                 mRedirNumInfoRegistrants.notifyRegistrants(
-                        new AsyncResult (null, infoRec.record, null));
+                        new AsyncResult(null, infoRec.record, null));
             }
         } else if (infoRec.record instanceof CdmaInformationRecords.CdmaLineControlInfoRec) {
             if (mLineControlInfoRegistrants != null) {
                 if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                 mLineControlInfoRegistrants.notifyRegistrants(
-                        new AsyncResult (null, infoRec.record, null));
+                        new AsyncResult(null, infoRec.record, null));
             }
         } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53ClirInfoRec) {
             if (mT53ClirInfoRegistrants != null) {
                 if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
                 mT53ClirInfoRegistrants.notifyRegistrants(
-                        new AsyncResult (null, infoRec.record, null));
+                        new AsyncResult(null, infoRec.record, null));
             }
         } else if (infoRec.record instanceof CdmaInformationRecords.CdmaT53AudioControlInfoRec) {
             if (mT53AudCntrlInfoRegistrants != null) {
-               if (RILJ_LOGD) unsljLogRet(response, infoRec.record);
-               mT53AudCntrlInfoRegistrants.notifyRegistrants(
-                       new AsyncResult (null, infoRec.record, null));
+                if (RILJ_LOGD) {
+                    unsljLogRet(response, infoRec.record);
+                }
+                mT53AudCntrlInfoRegistrants.notifyRegistrants(
+                        new AsyncResult(null, infoRec.record, null));
             }
         }
     }
 
-    private ArrayList<CellInfo> responseCellInfoList(Parcel p) {
-        int numberOfInfoRecs;
-        ArrayList<CellInfo> response;
-
-        /**
-         * Loop through all of the information records unmarshalling them
-         * and converting them to Java Objects.
-         */
-        numberOfInfoRecs = p.readInt();
-        response = new ArrayList<CellInfo>(numberOfInfoRecs);
-
-        for (int i = 0; i < numberOfInfoRecs; i++) {
-            CellInfo InfoRec = CellInfo.CREATOR.createFromParcel(p);
-            response.add(InfoRec);
-        }
-
-        return response;
-    }
-
-   private Object
-   responseHardwareConfig(Parcel p) {
-      int num;
-      ArrayList<HardwareConfig> response;
-      HardwareConfig hw;
-
-      num = p.readInt();
-      response = new ArrayList<HardwareConfig>(num);
-
-      if (RILJ_LOGV) {
-         riljLog("responseHardwareConfig: num=" + num);
-      }
-      for (int i = 0 ; i < num ; i++) {
-         int type = p.readInt();
-         switch(type) {
-            case HardwareConfig.DEV_HARDWARE_TYPE_MODEM: {
-               hw = new HardwareConfig(type);
-               hw.assignModem(p.readString(), p.readInt(), p.readInt(),
-                  p.readInt(), p.readInt(), p.readInt(), p.readInt());
-               break;
-            }
-            case HardwareConfig.DEV_HARDWARE_TYPE_SIM: {
-               hw = new HardwareConfig(type);
-               hw.assignSim(p.readString(), p.readInt(), p.readString());
-               break;
-            }
-            default: {
-               throw new RuntimeException(
-                  "RIL_REQUEST_GET_HARDWARE_CONFIG invalid hardward type:" + type);
-            }
-         }
-
-         response.add(hw);
-      }
-
-      return response;
-   }
-
-    private Object
-    responseRadioCapability(Parcel p) {
-        int version = p.readInt();
-        int session = p.readInt();
-        int phase = p.readInt();
-        int rat = p.readInt();
-        String logicModemUuid = p.readString();
-        int status = p.readInt();
-
-        riljLog("responseRadioCapability: version= " + version +
-                ", session=" + session +
-                ", phase=" + phase +
-                ", rat=" + rat +
-                ", logicModemUuid=" + logicModemUuid +
-                ", status=" + status);
-        RadioCapability rc = new RadioCapability(
-                mInstanceId.intValue(), session, phase, rat, logicModemUuid, status);
-        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);
-    }
-
-    private Object responseCarrierIdentifiers(Parcel p) {
-        List<CarrierIdentifier> retVal = new ArrayList<CarrierIdentifier>();
-        int len_allowed_carriers = p.readInt();
-        int len_excluded_carriers = p.readInt();
-        for (int i = 0; i < len_allowed_carriers; i++) {
-            String mcc = p.readString();
-            String mnc = p.readString();
-            String spn = null, imsi = null, gid1 = null, gid2 = null;
-            int matchType = p.readInt();
-            String matchData = p.readString();
-            if (matchType == CarrierIdentifier.MatchType.SPN) {
-                spn = matchData;
-            } else if (matchType == CarrierIdentifier.MatchType.IMSI_PREFIX) {
-                imsi = matchData;
-            } else if (matchType == CarrierIdentifier.MatchType.GID1) {
-                gid1 = matchData;
-            } else if (matchType == CarrierIdentifier.MatchType.GID2) {
-                gid2 = matchData;
-            }
-            retVal.add(new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2));
-        }
-        /* TODO: Handle excluded carriers */
-        return retVal;
-    }
-
-    private Object responsePcoData(Parcel p) {
-        return new PcoData(p);
-    }
-
-
-    static String
-    requestToString(int request) {
-/*
- cat libs/telephony/ril_commands.h \
- | egrep "^ *{RIL_" \
- | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/'
-*/
+    static String requestToString(int request) {
         switch(request) {
-            case RIL_REQUEST_GET_SIM_STATUS: return "GET_SIM_STATUS";
-            case RIL_REQUEST_ENTER_SIM_PIN: return "ENTER_SIM_PIN";
-            case RIL_REQUEST_ENTER_SIM_PUK: return "ENTER_SIM_PUK";
-            case RIL_REQUEST_ENTER_SIM_PIN2: return "ENTER_SIM_PIN2";
-            case RIL_REQUEST_ENTER_SIM_PUK2: return "ENTER_SIM_PUK2";
-            case RIL_REQUEST_CHANGE_SIM_PIN: return "CHANGE_SIM_PIN";
-            case RIL_REQUEST_CHANGE_SIM_PIN2: return "CHANGE_SIM_PIN2";
-            case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION: return "ENTER_NETWORK_DEPERSONALIZATION";
-            case RIL_REQUEST_GET_CURRENT_CALLS: return "GET_CURRENT_CALLS";
-            case RIL_REQUEST_DIAL: return "DIAL";
-            case RIL_REQUEST_GET_IMSI: return "GET_IMSI";
-            case RIL_REQUEST_HANGUP: return "HANGUP";
-            case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "HANGUP_WAITING_OR_BACKGROUND";
-            case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "HANGUP_FOREGROUND_RESUME_BACKGROUND";
-            case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE";
-            case RIL_REQUEST_CONFERENCE: return "CONFERENCE";
-            case RIL_REQUEST_UDUB: return "UDUB";
-            case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: return "LAST_CALL_FAIL_CAUSE";
-            case RIL_REQUEST_SIGNAL_STRENGTH: return "SIGNAL_STRENGTH";
-            case RIL_REQUEST_VOICE_REGISTRATION_STATE: return "VOICE_REGISTRATION_STATE";
-            case RIL_REQUEST_DATA_REGISTRATION_STATE: return "DATA_REGISTRATION_STATE";
-            case RIL_REQUEST_OPERATOR: return "OPERATOR";
-            case RIL_REQUEST_RADIO_POWER: return "RADIO_POWER";
-            case RIL_REQUEST_DTMF: return "DTMF";
-            case RIL_REQUEST_SEND_SMS: return "SEND_SMS";
-            case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "SEND_SMS_EXPECT_MORE";
-            case RIL_REQUEST_SETUP_DATA_CALL: return "SETUP_DATA_CALL";
-            case RIL_REQUEST_SIM_IO: return "SIM_IO";
-            case RIL_REQUEST_SEND_USSD: return "SEND_USSD";
-            case RIL_REQUEST_CANCEL_USSD: return "CANCEL_USSD";
-            case RIL_REQUEST_GET_CLIR: return "GET_CLIR";
-            case RIL_REQUEST_SET_CLIR: return "SET_CLIR";
-            case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS: return "QUERY_CALL_FORWARD_STATUS";
-            case RIL_REQUEST_SET_CALL_FORWARD: return "SET_CALL_FORWARD";
-            case RIL_REQUEST_QUERY_CALL_WAITING: return "QUERY_CALL_WAITING";
-            case RIL_REQUEST_SET_CALL_WAITING: return "SET_CALL_WAITING";
-            case RIL_REQUEST_SMS_ACKNOWLEDGE: return "SMS_ACKNOWLEDGE";
-            case RIL_REQUEST_GET_IMEI: return "GET_IMEI";
-            case RIL_REQUEST_GET_IMEISV: return "GET_IMEISV";
-            case RIL_REQUEST_ANSWER: return "ANSWER";
-            case RIL_REQUEST_DEACTIVATE_DATA_CALL: return "DEACTIVATE_DATA_CALL";
-            case RIL_REQUEST_QUERY_FACILITY_LOCK: return "QUERY_FACILITY_LOCK";
-            case RIL_REQUEST_SET_FACILITY_LOCK: return "SET_FACILITY_LOCK";
-            case RIL_REQUEST_CHANGE_BARRING_PASSWORD: return "CHANGE_BARRING_PASSWORD";
-            case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: return "QUERY_NETWORK_SELECTION_MODE";
-            case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: return "SET_NETWORK_SELECTION_AUTOMATIC";
-            case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL: return "SET_NETWORK_SELECTION_MANUAL";
-            case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS : return "QUERY_AVAILABLE_NETWORKS ";
-            case RIL_REQUEST_DTMF_START: return "DTMF_START";
-            case RIL_REQUEST_DTMF_STOP: return "DTMF_STOP";
-            case RIL_REQUEST_BASEBAND_VERSION: return "BASEBAND_VERSION";
-            case RIL_REQUEST_SEPARATE_CONNECTION: return "SEPARATE_CONNECTION";
-            case RIL_REQUEST_SET_MUTE: return "SET_MUTE";
-            case RIL_REQUEST_GET_MUTE: return "GET_MUTE";
-            case RIL_REQUEST_QUERY_CLIP: return "QUERY_CLIP";
-            case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE: return "LAST_DATA_CALL_FAIL_CAUSE";
-            case RIL_REQUEST_DATA_CALL_LIST: return "DATA_CALL_LIST";
-            case RIL_REQUEST_RESET_RADIO: return "RESET_RADIO";
-            case RIL_REQUEST_OEM_HOOK_RAW: return "OEM_HOOK_RAW";
-            case RIL_REQUEST_OEM_HOOK_STRINGS: return "OEM_HOOK_STRINGS";
-            case RIL_REQUEST_SCREEN_STATE: return "SCREEN_STATE";
-            case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION: return "SET_SUPP_SVC_NOTIFICATION";
-            case RIL_REQUEST_WRITE_SMS_TO_SIM: return "WRITE_SMS_TO_SIM";
-            case RIL_REQUEST_DELETE_SMS_ON_SIM: return "DELETE_SMS_ON_SIM";
-            case RIL_REQUEST_SET_BAND_MODE: return "SET_BAND_MODE";
-            case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE: return "QUERY_AVAILABLE_BAND_MODE";
-            case RIL_REQUEST_STK_GET_PROFILE: return "REQUEST_STK_GET_PROFILE";
-            case RIL_REQUEST_STK_SET_PROFILE: return "REQUEST_STK_SET_PROFILE";
-            case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND: return "REQUEST_STK_SEND_ENVELOPE_COMMAND";
-            case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE: return "REQUEST_STK_SEND_TERMINAL_RESPONSE";
-            case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM";
+            case RIL_REQUEST_GET_SIM_STATUS:
+                return "GET_SIM_STATUS";
+            case RIL_REQUEST_ENTER_SIM_PIN:
+                return "ENTER_SIM_PIN";
+            case RIL_REQUEST_ENTER_SIM_PUK:
+                return "ENTER_SIM_PUK";
+            case RIL_REQUEST_ENTER_SIM_PIN2:
+                return "ENTER_SIM_PIN2";
+            case RIL_REQUEST_ENTER_SIM_PUK2:
+                return "ENTER_SIM_PUK2";
+            case RIL_REQUEST_CHANGE_SIM_PIN:
+                return "CHANGE_SIM_PIN";
+            case RIL_REQUEST_CHANGE_SIM_PIN2:
+                return "CHANGE_SIM_PIN2";
+            case RIL_REQUEST_ENTER_NETWORK_DEPERSONALIZATION:
+                return "ENTER_NETWORK_DEPERSONALIZATION";
+            case RIL_REQUEST_GET_CURRENT_CALLS:
+                return "GET_CURRENT_CALLS";
+            case RIL_REQUEST_DIAL:
+                return "DIAL";
+            case RIL_REQUEST_GET_IMSI:
+                return "GET_IMSI";
+            case RIL_REQUEST_HANGUP:
+                return "HANGUP";
+            case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
+                return "HANGUP_WAITING_OR_BACKGROUND";
+            case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
+                return "HANGUP_FOREGROUND_RESUME_BACKGROUND";
+            case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE:
+                return "REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE";
+            case RIL_REQUEST_CONFERENCE:
+                return "CONFERENCE";
+            case RIL_REQUEST_UDUB:
+                return "UDUB";
+            case RIL_REQUEST_LAST_CALL_FAIL_CAUSE:
+                return "LAST_CALL_FAIL_CAUSE";
+            case RIL_REQUEST_SIGNAL_STRENGTH:
+                return "SIGNAL_STRENGTH";
+            case RIL_REQUEST_VOICE_REGISTRATION_STATE:
+                return "VOICE_REGISTRATION_STATE";
+            case RIL_REQUEST_DATA_REGISTRATION_STATE:
+                return "DATA_REGISTRATION_STATE";
+            case RIL_REQUEST_OPERATOR:
+                return "OPERATOR";
+            case RIL_REQUEST_RADIO_POWER:
+                return "RADIO_POWER";
+            case RIL_REQUEST_DTMF:
+                return "DTMF";
+            case RIL_REQUEST_SEND_SMS:
+                return "SEND_SMS";
+            case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
+                return "SEND_SMS_EXPECT_MORE";
+            case RIL_REQUEST_SETUP_DATA_CALL:
+                return "SETUP_DATA_CALL";
+            case RIL_REQUEST_SIM_IO:
+                return "SIM_IO";
+            case RIL_REQUEST_SEND_USSD:
+                return "SEND_USSD";
+            case RIL_REQUEST_CANCEL_USSD:
+                return "CANCEL_USSD";
+            case RIL_REQUEST_GET_CLIR:
+                return "GET_CLIR";
+            case RIL_REQUEST_SET_CLIR:
+                return "SET_CLIR";
+            case RIL_REQUEST_QUERY_CALL_FORWARD_STATUS:
+                return "QUERY_CALL_FORWARD_STATUS";
+            case RIL_REQUEST_SET_CALL_FORWARD:
+                return "SET_CALL_FORWARD";
+            case RIL_REQUEST_QUERY_CALL_WAITING:
+                return "QUERY_CALL_WAITING";
+            case RIL_REQUEST_SET_CALL_WAITING:
+                return "SET_CALL_WAITING";
+            case RIL_REQUEST_SMS_ACKNOWLEDGE:
+                return "SMS_ACKNOWLEDGE";
+            case RIL_REQUEST_GET_IMEI:
+                return "GET_IMEI";
+            case RIL_REQUEST_GET_IMEISV:
+                return "GET_IMEISV";
+            case RIL_REQUEST_ANSWER:
+                return "ANSWER";
+            case RIL_REQUEST_DEACTIVATE_DATA_CALL:
+                return "DEACTIVATE_DATA_CALL";
+            case RIL_REQUEST_QUERY_FACILITY_LOCK:
+                return "QUERY_FACILITY_LOCK";
+            case RIL_REQUEST_SET_FACILITY_LOCK:
+                return "SET_FACILITY_LOCK";
+            case RIL_REQUEST_CHANGE_BARRING_PASSWORD:
+                return "CHANGE_BARRING_PASSWORD";
+            case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE:
+                return "QUERY_NETWORK_SELECTION_MODE";
+            case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC:
+                return "SET_NETWORK_SELECTION_AUTOMATIC";
+            case RIL_REQUEST_SET_NETWORK_SELECTION_MANUAL:
+                return "SET_NETWORK_SELECTION_MANUAL";
+            case RIL_REQUEST_QUERY_AVAILABLE_NETWORKS :
+                return "QUERY_AVAILABLE_NETWORKS ";
+            case RIL_REQUEST_DTMF_START:
+                return "DTMF_START";
+            case RIL_REQUEST_DTMF_STOP:
+                return "DTMF_STOP";
+            case RIL_REQUEST_BASEBAND_VERSION:
+                return "BASEBAND_VERSION";
+            case RIL_REQUEST_SEPARATE_CONNECTION:
+                return "SEPARATE_CONNECTION";
+            case RIL_REQUEST_SET_MUTE:
+                return "SET_MUTE";
+            case RIL_REQUEST_GET_MUTE:
+                return "GET_MUTE";
+            case RIL_REQUEST_QUERY_CLIP:
+                return "QUERY_CLIP";
+            case RIL_REQUEST_LAST_DATA_CALL_FAIL_CAUSE:
+                return "LAST_DATA_CALL_FAIL_CAUSE";
+            case RIL_REQUEST_DATA_CALL_LIST:
+                return "DATA_CALL_LIST";
+            case RIL_REQUEST_RESET_RADIO:
+                return "RESET_RADIO";
+            case RIL_REQUEST_OEM_HOOK_RAW:
+                return "OEM_HOOK_RAW";
+            case RIL_REQUEST_OEM_HOOK_STRINGS:
+                return "OEM_HOOK_STRINGS";
+            case RIL_REQUEST_SCREEN_STATE:
+                return "SCREEN_STATE";
+            case RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION:
+                return "SET_SUPP_SVC_NOTIFICATION";
+            case RIL_REQUEST_WRITE_SMS_TO_SIM:
+                return "WRITE_SMS_TO_SIM";
+            case RIL_REQUEST_DELETE_SMS_ON_SIM:
+                return "DELETE_SMS_ON_SIM";
+            case RIL_REQUEST_SET_BAND_MODE:
+                return "SET_BAND_MODE";
+            case RIL_REQUEST_QUERY_AVAILABLE_BAND_MODE:
+                return "QUERY_AVAILABLE_BAND_MODE";
+            case RIL_REQUEST_STK_GET_PROFILE:
+                return "REQUEST_STK_GET_PROFILE";
+            case RIL_REQUEST_STK_SET_PROFILE:
+                return "REQUEST_STK_SET_PROFILE";
+            case RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND:
+                return "REQUEST_STK_SEND_ENVELOPE_COMMAND";
+            case RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE:
+                return "REQUEST_STK_SEND_TERMINAL_RESPONSE";
+            case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM:
+                return "REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM";
             case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: return "REQUEST_EXPLICIT_CALL_TRANSFER";
-            case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: return "REQUEST_SET_PREFERRED_NETWORK_TYPE";
-            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: return "REQUEST_GET_PREFERRED_NETWORK_TYPE";
-            case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: return "REQUEST_GET_NEIGHBORING_CELL_IDS";
-            case RIL_REQUEST_SET_LOCATION_UPDATES: return "REQUEST_SET_LOCATION_UPDATES";
-            case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE";
-            case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE";
-            case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE";
-            case RIL_REQUEST_SET_TTY_MODE: return "RIL_REQUEST_SET_TTY_MODE";
-            case RIL_REQUEST_QUERY_TTY_MODE: return "RIL_REQUEST_QUERY_TTY_MODE";
-            case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE";
-            case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE: return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE";
-            case RIL_REQUEST_CDMA_FLASH: return "RIL_REQUEST_CDMA_FLASH";
-            case RIL_REQUEST_CDMA_BURST_DTMF: return "RIL_REQUEST_CDMA_BURST_DTMF";
-            case RIL_REQUEST_CDMA_SEND_SMS: return "RIL_REQUEST_CDMA_SEND_SMS";
-            case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE: return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE";
-            case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG: return "RIL_REQUEST_GSM_GET_BROADCAST_CONFIG";
-            case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG: return "RIL_REQUEST_GSM_SET_BROADCAST_CONFIG";
-            case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG";
-            case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG: return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG";
-            case RIL_REQUEST_GSM_BROADCAST_ACTIVATION: return "RIL_REQUEST_GSM_BROADCAST_ACTIVATION";
-            case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY: return "RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY";
-            case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION: return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION";
-            case RIL_REQUEST_CDMA_SUBSCRIPTION: return "RIL_REQUEST_CDMA_SUBSCRIPTION";
-            case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM: return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM";
-            case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM: return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM";
-            case RIL_REQUEST_DEVICE_IDENTITY: return "RIL_REQUEST_DEVICE_IDENTITY";
-            case RIL_REQUEST_GET_SMSC_ADDRESS: return "RIL_REQUEST_GET_SMSC_ADDRESS";
-            case RIL_REQUEST_SET_SMSC_ADDRESS: return "RIL_REQUEST_SET_SMSC_ADDRESS";
-            case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE";
-            case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS: return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS";
-            case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING: return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING";
-            case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE";
-            case RIL_REQUEST_ISIM_AUTHENTICATION: return "RIL_REQUEST_ISIM_AUTHENTICATION";
-            case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
-            case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS";
-            case RIL_REQUEST_VOICE_RADIO_TECH: return "RIL_REQUEST_VOICE_RADIO_TECH";
-            case RIL_REQUEST_GET_CELL_INFO_LIST: return "RIL_REQUEST_GET_CELL_INFO_LIST";
-            case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: return "RIL_REQUEST_SET_CELL_INFO_LIST_RATE";
-            case RIL_REQUEST_SET_INITIAL_ATTACH_APN: return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
-            case RIL_REQUEST_SET_DATA_PROFILE: return "RIL_REQUEST_SET_DATA_PROFILE";
-            case RIL_REQUEST_IMS_REGISTRATION_STATE: return "RIL_REQUEST_IMS_REGISTRATION_STATE";
-            case RIL_REQUEST_IMS_SEND_SMS: return "RIL_REQUEST_IMS_SEND_SMS";
-            case RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC: return "RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC";
-            case RIL_REQUEST_SIM_OPEN_CHANNEL: return "RIL_REQUEST_SIM_OPEN_CHANNEL";
-            case RIL_REQUEST_SIM_CLOSE_CHANNEL: return "RIL_REQUEST_SIM_CLOSE_CHANNEL";
-            case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL: return "RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL";
-            case RIL_REQUEST_NV_READ_ITEM: return "RIL_REQUEST_NV_READ_ITEM";
-            case RIL_REQUEST_NV_WRITE_ITEM: return "RIL_REQUEST_NV_WRITE_ITEM";
-            case RIL_REQUEST_NV_WRITE_CDMA_PRL: return "RIL_REQUEST_NV_WRITE_CDMA_PRL";
-            case RIL_REQUEST_NV_RESET_CONFIG: return "RIL_REQUEST_NV_RESET_CONFIG";
-            case RIL_REQUEST_SET_UICC_SUBSCRIPTION: return "RIL_REQUEST_SET_UICC_SUBSCRIPTION";
-            case RIL_REQUEST_ALLOW_DATA: return "RIL_REQUEST_ALLOW_DATA";
-            case RIL_REQUEST_GET_HARDWARE_CONFIG: return "GET_HARDWARE_CONFIG";
-            case RIL_REQUEST_SIM_AUTHENTICATION: return "RIL_REQUEST_SIM_AUTHENTICATION";
-            case RIL_REQUEST_SHUTDOWN: return "RIL_REQUEST_SHUTDOWN";
+            case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
+                return "REQUEST_SET_PREFERRED_NETWORK_TYPE";
+            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
+                return "REQUEST_GET_PREFERRED_NETWORK_TYPE";
+            case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS:
+                return "REQUEST_GET_NEIGHBORING_CELL_IDS";
+            case RIL_REQUEST_SET_LOCATION_UPDATES:
+                return "REQUEST_SET_LOCATION_UPDATES";
+            case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE:
+                return "RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE";
+            case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:
+                return "RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE";
+            case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:
+                return "RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE";
+            case RIL_REQUEST_SET_TTY_MODE:
+                return "RIL_REQUEST_SET_TTY_MODE";
+            case RIL_REQUEST_QUERY_TTY_MODE:
+                return "RIL_REQUEST_QUERY_TTY_MODE";
+            case RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE:
+                return "RIL_REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE";
+            case RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE:
+                return "RIL_REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE";
+            case RIL_REQUEST_CDMA_FLASH:
+                return "RIL_REQUEST_CDMA_FLASH";
+            case RIL_REQUEST_CDMA_BURST_DTMF:
+                return "RIL_REQUEST_CDMA_BURST_DTMF";
+            case RIL_REQUEST_CDMA_SEND_SMS:
+                return "RIL_REQUEST_CDMA_SEND_SMS";
+            case RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE:
+                return "RIL_REQUEST_CDMA_SMS_ACKNOWLEDGE";
+            case RIL_REQUEST_GSM_GET_BROADCAST_CONFIG:
+                return "RIL_REQUEST_GSM_GET_BROADCAST_CONFIG";
+            case RIL_REQUEST_GSM_SET_BROADCAST_CONFIG:
+                return "RIL_REQUEST_GSM_SET_BROADCAST_CONFIG";
+            case RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG:
+                return "RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG";
+            case RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG:
+                return "RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG";
+            case RIL_REQUEST_GSM_BROADCAST_ACTIVATION:
+                return "RIL_REQUEST_GSM_BROADCAST_ACTIVATION";
+            case RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY:
+                return "RIL_REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY";
+            case RIL_REQUEST_CDMA_BROADCAST_ACTIVATION:
+                return "RIL_REQUEST_CDMA_BROADCAST_ACTIVATION";
+            case RIL_REQUEST_CDMA_SUBSCRIPTION:
+                return "RIL_REQUEST_CDMA_SUBSCRIPTION";
+            case RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM:
+                return "RIL_REQUEST_CDMA_WRITE_SMS_TO_RUIM";
+            case RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM:
+                return "RIL_REQUEST_CDMA_DELETE_SMS_ON_RUIM";
+            case RIL_REQUEST_DEVICE_IDENTITY:
+                return "RIL_REQUEST_DEVICE_IDENTITY";
+            case RIL_REQUEST_GET_SMSC_ADDRESS:
+                return "RIL_REQUEST_GET_SMSC_ADDRESS";
+            case RIL_REQUEST_SET_SMSC_ADDRESS:
+                return "RIL_REQUEST_SET_SMSC_ADDRESS";
+            case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE:
+                return "REQUEST_EXIT_EMERGENCY_CALLBACK_MODE";
+            case RIL_REQUEST_REPORT_SMS_MEMORY_STATUS:
+                return "RIL_REQUEST_REPORT_SMS_MEMORY_STATUS";
+            case RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING:
+                return "RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING";
+            case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE:
+                return "RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE";
+            case RIL_REQUEST_ISIM_AUTHENTICATION:
+                return "RIL_REQUEST_ISIM_AUTHENTICATION";
+            case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU:
+                return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
+            case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS:
+                return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS";
+            case RIL_REQUEST_VOICE_RADIO_TECH:
+                return "RIL_REQUEST_VOICE_RADIO_TECH";
+            case RIL_REQUEST_GET_CELL_INFO_LIST:
+                return "RIL_REQUEST_GET_CELL_INFO_LIST";
+            case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
+                return "RIL_REQUEST_SET_CELL_INFO_LIST_RATE";
+            case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
+                return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
+            case RIL_REQUEST_SET_DATA_PROFILE:
+                return "RIL_REQUEST_SET_DATA_PROFILE";
+            case RIL_REQUEST_IMS_REGISTRATION_STATE:
+                return "RIL_REQUEST_IMS_REGISTRATION_STATE";
+            case RIL_REQUEST_IMS_SEND_SMS:
+                return "RIL_REQUEST_IMS_SEND_SMS";
+            case RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC:
+                return "RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC";
+            case RIL_REQUEST_SIM_OPEN_CHANNEL:
+                return "RIL_REQUEST_SIM_OPEN_CHANNEL";
+            case RIL_REQUEST_SIM_CLOSE_CHANNEL:
+                return "RIL_REQUEST_SIM_CLOSE_CHANNEL";
+            case RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL:
+                return "RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL";
+            case RIL_REQUEST_NV_READ_ITEM:
+                return "RIL_REQUEST_NV_READ_ITEM";
+            case RIL_REQUEST_NV_WRITE_ITEM:
+                return "RIL_REQUEST_NV_WRITE_ITEM";
+            case RIL_REQUEST_NV_WRITE_CDMA_PRL:
+                return "RIL_REQUEST_NV_WRITE_CDMA_PRL";
+            case RIL_REQUEST_NV_RESET_CONFIG:
+                return "RIL_REQUEST_NV_RESET_CONFIG";
+            case RIL_REQUEST_SET_UICC_SUBSCRIPTION:
+                return "RIL_REQUEST_SET_UICC_SUBSCRIPTION";
+            case RIL_REQUEST_ALLOW_DATA:
+                return "RIL_REQUEST_ALLOW_DATA";
+            case RIL_REQUEST_GET_HARDWARE_CONFIG:
+                return "GET_HARDWARE_CONFIG";
+            case RIL_REQUEST_SIM_AUTHENTICATION:
+                return "RIL_REQUEST_SIM_AUTHENTICATION";
+            case RIL_REQUEST_SHUTDOWN:
+                return "RIL_REQUEST_SHUTDOWN";
             case RIL_REQUEST_SET_RADIO_CAPABILITY:
-                    return "RIL_REQUEST_SET_RADIO_CAPABILITY";
+                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";
-            case RIL_REQUEST_SET_ALLOWED_CARRIERS: return "RIL_REQUEST_SET_ALLOWED_CARRIERS";
-            case RIL_REQUEST_GET_ALLOWED_CARRIERS: return "RIL_REQUEST_GET_ALLOWED_CARRIERS";
-            case RIL_RESPONSE_ACKNOWLEDGEMENT: return "RIL_RESPONSE_ACKNOWLEDGEMENT";
+                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";
+            case RIL_REQUEST_SET_ALLOWED_CARRIERS:
+                return "RIL_REQUEST_SET_ALLOWED_CARRIERS";
+            case RIL_REQUEST_GET_ALLOWED_CARRIERS:
+                return "RIL_REQUEST_GET_ALLOWED_CARRIERS";
+            case RIL_REQUEST_SET_SIM_CARD_POWER:
+                return "RIL_REQUEST_SET_SIM_CARD_POWER";
+            case RIL_REQUEST_SEND_DEVICE_STATE:
+                return "RIL_REQUEST_SEND_DEVICE_STATE";
+            case RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER:
+                return "RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER";
+            case RIL_RESPONSE_ACKNOWLEDGEMENT:
+                return "RIL_RESPONSE_ACKNOWLEDGEMENT";
+            case RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION:
+                return "RIL_REQUEST_SET_CARRIER_INFO_IMSI_ENCRYPTION";
+            case RIL_REQUEST_START_NETWORK_SCAN:
+                return "RIL_REQUEST_START_NETWORK_SCAN";
+            case RIL_REQUEST_STOP_NETWORK_SCAN:
+                return "RIL_REQUEST_STOP_NETWORK_SCAN";
             default: return "<unknown request>";
         }
     }
 
-    static String
-    responseToString(int request)
-    {
-/*
- cat libs/telephony/ril_unsol_commands.h \
- | egrep "^ *{RIL_" \
- | sed -re 's/\{RIL_([^,]+),[^,]+,([^}]+).+/case RIL_\1: return "\1";/'
-*/
+    static String responseToString(int request) {
         switch(request) {
-            case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
-            case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
-            case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED";
-            case RIL_UNSOL_RESPONSE_NEW_SMS: return "UNSOL_RESPONSE_NEW_SMS";
-            case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT: return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT";
-            case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM: return "UNSOL_RESPONSE_NEW_SMS_ON_SIM";
-            case RIL_UNSOL_ON_USSD: return "UNSOL_ON_USSD";
-            case RIL_UNSOL_ON_USSD_REQUEST: return "UNSOL_ON_USSD_REQUEST";
-            case RIL_UNSOL_NITZ_TIME_RECEIVED: return "UNSOL_NITZ_TIME_RECEIVED";
-            case RIL_UNSOL_SIGNAL_STRENGTH: return "UNSOL_SIGNAL_STRENGTH";
-            case RIL_UNSOL_DATA_CALL_LIST_CHANGED: return "UNSOL_DATA_CALL_LIST_CHANGED";
-            case RIL_UNSOL_SUPP_SVC_NOTIFICATION: return "UNSOL_SUPP_SVC_NOTIFICATION";
-            case RIL_UNSOL_STK_SESSION_END: return "UNSOL_STK_SESSION_END";
-            case RIL_UNSOL_STK_PROACTIVE_COMMAND: return "UNSOL_STK_PROACTIVE_COMMAND";
-            case RIL_UNSOL_STK_EVENT_NOTIFY: return "UNSOL_STK_EVENT_NOTIFY";
-            case RIL_UNSOL_STK_CALL_SETUP: return "UNSOL_STK_CALL_SETUP";
-            case RIL_UNSOL_SIM_SMS_STORAGE_FULL: return "UNSOL_SIM_SMS_STORAGE_FULL";
-            case RIL_UNSOL_SIM_REFRESH: return "UNSOL_SIM_REFRESH";
-            case RIL_UNSOL_CALL_RING: return "UNSOL_CALL_RING";
-            case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: return "UNSOL_RESPONSE_SIM_STATUS_CHANGED";
-            case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: return "UNSOL_RESPONSE_CDMA_NEW_SMS";
-            case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: return "UNSOL_RESPONSE_NEW_BROADCAST_SMS";
-            case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL";
-            case RIL_UNSOL_RESTRICTED_STATE_CHANGED: return "UNSOL_RESTRICTED_STATE_CHANGED";
-            case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE";
-            case RIL_UNSOL_CDMA_CALL_WAITING: return "UNSOL_CDMA_CALL_WAITING";
-            case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS: return "UNSOL_CDMA_OTA_PROVISION_STATUS";
-            case RIL_UNSOL_CDMA_INFO_REC: return "UNSOL_CDMA_INFO_REC";
-            case RIL_UNSOL_OEM_HOOK_RAW: return "UNSOL_OEM_HOOK_RAW";
-            case RIL_UNSOL_RINGBACK_TONE: return "UNSOL_RINGBACK_TONE";
-            case RIL_UNSOL_RESEND_INCALL_MUTE: return "UNSOL_RESEND_INCALL_MUTE";
-            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED: return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
-            case RIL_UNSOl_CDMA_PRL_CHANGED: return "UNSOL_CDMA_PRL_CHANGED";
-            case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
-            case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED";
-            case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: return "UNSOL_VOICE_RADIO_TECH_CHANGED";
-            case RIL_UNSOL_CELL_INFO_LIST: return "UNSOL_CELL_INFO_LIST";
+            case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED:
+                return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
+            case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
+                return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
+            case RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED:
+                return "UNSOL_RESPONSE_NETWORK_STATE_CHANGED";
+            case RIL_UNSOL_RESPONSE_NEW_SMS:
+                return "UNSOL_RESPONSE_NEW_SMS";
+            case RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT:
+                return "UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT";
+            case RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM:
+                return "UNSOL_RESPONSE_NEW_SMS_ON_SIM";
+            case RIL_UNSOL_ON_USSD:
+                return "UNSOL_ON_USSD";
+            case RIL_UNSOL_ON_USSD_REQUEST:
+                return "UNSOL_ON_USSD_REQUEST";
+            case RIL_UNSOL_NITZ_TIME_RECEIVED:
+                return "UNSOL_NITZ_TIME_RECEIVED";
+            case RIL_UNSOL_SIGNAL_STRENGTH:
+                return "UNSOL_SIGNAL_STRENGTH";
+            case RIL_UNSOL_DATA_CALL_LIST_CHANGED:
+                return "UNSOL_DATA_CALL_LIST_CHANGED";
+            case RIL_UNSOL_SUPP_SVC_NOTIFICATION:
+                return "UNSOL_SUPP_SVC_NOTIFICATION";
+            case RIL_UNSOL_STK_SESSION_END:
+                return "UNSOL_STK_SESSION_END";
+            case RIL_UNSOL_STK_PROACTIVE_COMMAND:
+                return "UNSOL_STK_PROACTIVE_COMMAND";
+            case RIL_UNSOL_STK_EVENT_NOTIFY:
+                return "UNSOL_STK_EVENT_NOTIFY";
+            case RIL_UNSOL_STK_CALL_SETUP:
+                return "UNSOL_STK_CALL_SETUP";
+            case RIL_UNSOL_SIM_SMS_STORAGE_FULL:
+                return "UNSOL_SIM_SMS_STORAGE_FULL";
+            case RIL_UNSOL_SIM_REFRESH:
+                return "UNSOL_SIM_REFRESH";
+            case RIL_UNSOL_CALL_RING:
+                return "UNSOL_CALL_RING";
+            case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED:
+                return "UNSOL_RESPONSE_SIM_STATUS_CHANGED";
+            case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS:
+                return "UNSOL_RESPONSE_CDMA_NEW_SMS";
+            case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS:
+                return "UNSOL_RESPONSE_NEW_BROADCAST_SMS";
+            case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL:
+                return "UNSOL_CDMA_RUIM_SMS_STORAGE_FULL";
+            case RIL_UNSOL_RESTRICTED_STATE_CHANGED:
+                return "UNSOL_RESTRICTED_STATE_CHANGED";
+            case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE:
+                return "UNSOL_ENTER_EMERGENCY_CALLBACK_MODE";
+            case RIL_UNSOL_CDMA_CALL_WAITING:
+                return "UNSOL_CDMA_CALL_WAITING";
+            case RIL_UNSOL_CDMA_OTA_PROVISION_STATUS:
+                return "UNSOL_CDMA_OTA_PROVISION_STATUS";
+            case RIL_UNSOL_CDMA_INFO_REC:
+                return "UNSOL_CDMA_INFO_REC";
+            case RIL_UNSOL_OEM_HOOK_RAW:
+                return "UNSOL_OEM_HOOK_RAW";
+            case RIL_UNSOL_RINGBACK_TONE:
+                return "UNSOL_RINGBACK_TONE";
+            case RIL_UNSOL_RESEND_INCALL_MUTE:
+                return "UNSOL_RESEND_INCALL_MUTE";
+            case RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED:
+                return "CDMA_SUBSCRIPTION_SOURCE_CHANGED";
+            case RIL_UNSOl_CDMA_PRL_CHANGED:
+                return "UNSOL_CDMA_PRL_CHANGED";
+            case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE:
+                return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
+            case RIL_UNSOL_RIL_CONNECTED:
+                return "UNSOL_RIL_CONNECTED";
+            case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED:
+                return "UNSOL_VOICE_RADIO_TECH_CHANGED";
+            case RIL_UNSOL_CELL_INFO_LIST:
+                return "UNSOL_CELL_INFO_LIST";
             case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
                 return "UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED";
             case RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED:
-                    return "RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED";
+                return "RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED";
             case RIL_UNSOL_SRVCC_STATE_NOTIFY:
-                    return "UNSOL_SRVCC_STATE_NOTIFY";
-            case RIL_UNSOL_HARDWARE_CONFIG_CHANGED: return "RIL_UNSOL_HARDWARE_CONFIG_CHANGED";
+                return "UNSOL_SRVCC_STATE_NOTIFY";
+            case RIL_UNSOL_HARDWARE_CONFIG_CHANGED:
+                return "RIL_UNSOL_HARDWARE_CONFIG_CHANGED";
             case RIL_UNSOL_RADIO_CAPABILITY:
-                    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";
-            case RIL_UNSOL_PCO_DATA: return "UNSOL_PCO_DATA";
-            default: return "<unknown response>";
+                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";
+            case RIL_UNSOL_PCO_DATA:
+                return "UNSOL_PCO_DATA";
+            case RIL_UNSOL_MODEM_RESTART:
+                return "UNSOL_MODEM_RESTART";
+            case RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION:
+                return "RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION";
+            case RIL_UNSOL_NETWORK_SCAN_RESULT:
+                return "RIL_UNSOL_NETWORK_SCAN_RESULT";
+            default:
+                return "<unknown response>";
         }
     }
 
-    private void riljLog(String msg) {
+    void riljLog(String msg) {
         Rlog.d(RILJ_LOG_TAG, msg
-                + (mInstanceId != null ? (" [SUB" + mInstanceId + "]") : ""));
+                + (mPhoneId != null ? (" [SUB" + mPhoneId + "]") : ""));
     }
 
-    private void riljLogv(String msg) {
+    void riljLoge(String msg) {
+        Rlog.e(RILJ_LOG_TAG, msg
+                + (mPhoneId != null ? (" [SUB" + mPhoneId + "]") : ""));
+    }
+
+    void riljLoge(String msg, Exception e) {
+        Rlog.e(RILJ_LOG_TAG, msg
+                + (mPhoneId != null ? (" [SUB" + mPhoneId + "]") : ""), e);
+    }
+
+    void riljLogv(String msg) {
         Rlog.v(RILJ_LOG_TAG, msg
-                + (mInstanceId != null ? (" [SUB" + mInstanceId + "]") : ""));
+                + (mPhoneId != null ? (" [SUB" + mPhoneId + "]") : ""));
     }
 
-    private void unsljLog(int response) {
+    void unsljLog(int response) {
         riljLog("[UNSL]< " + responseToString(response));
     }
 
-    private void unsljLogMore(int response, String more) {
+    void unsljLogMore(int response, String more) {
         riljLog("[UNSL]< " + responseToString(response) + " " + more);
     }
 
-    private void unsljLogRet(int response, Object ret) {
+    void unsljLogRet(int response, Object ret) {
         riljLog("[UNSL]< " + responseToString(response) + " " + retToString(response, ret));
     }
 
-    private void unsljLogvRet(int response, Object ret) {
+    void unsljLogvRet(int response, Object ret) {
         riljLogv("[UNSL]< " + responseToString(response) + " " + retToString(response, ret));
     }
 
-    private Object
-    responseSsData(Parcel p) {
-        int num;
-        SsData ssData = new SsData();
-
-        ssData.serviceType = ssData.ServiceTypeFromRILInt(p.readInt());
-        ssData.requestType = ssData.RequestTypeFromRILInt(p.readInt());
-        ssData.teleserviceType = ssData.TeleserviceTypeFromRILInt(p.readInt());
-        ssData.serviceClass = p.readInt(); // This is service class sent in the SS request.
-        ssData.result = p.readInt(); // This is the result of the SS request.
-        num = p.readInt();
-
-        if (ssData.serviceType.isTypeCF() &&
-            ssData.requestType.isTypeInterrogation()) {
-            ssData.cfInfo = new CallForwardInfo[num];
-
-            for (int i = 0; i < num; i++) {
-                ssData.cfInfo[i] = new CallForwardInfo();
-
-                ssData.cfInfo[i].status = p.readInt();
-                ssData.cfInfo[i].reason = p.readInt();
-                ssData.cfInfo[i].serviceClass = p.readInt();
-                ssData.cfInfo[i].toa = p.readInt();
-                ssData.cfInfo[i].number = p.readString();
-                ssData.cfInfo[i].timeSeconds = p.readInt();
-
-                riljLog("[SS Data] CF Info " + i + " : " +  ssData.cfInfo[i]);
-            }
-        } else {
-            ssData.ssInfo = new int[num];
-            for (int i = 0; i < num; i++) {
-                ssData.ssInfo[i] = p.readInt();
-                riljLog("[SS Data] SS Info " + i + " : " +  ssData.ssInfo[i]);
-            }
-        }
-
-        return ssData;
-    }
-
-
-    // ***** Methods for CDMA support
-    @Override
-    public void
-    getDeviceIdentity(Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DEVICE_IDENTITY, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void
-    getCDMASubscription(Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SUBSCRIPTION, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
     @Override
     public void setPhoneType(int phoneType) { // Called by GsmCdmaPhone
         if (RILJ_LOGD) riljLog("setPhoneType=" + phoneType + " old value=" + mPhoneType);
         mPhoneType = phoneType;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void queryCdmaRoamingPreference(Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(cdmaRoamingType);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + cdmaRoamingType);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setCdmaSubscriptionSource(int cdmaSubscription , Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(cdmaSubscription);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + cdmaSubscription);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getCdmaSubscriptionSource(Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void queryTTYMode(Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setTTYMode(int ttyMode, Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RILConstants.RIL_REQUEST_SET_TTY_MODE, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(ttyMode);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + ttyMode);
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void
-    sendCDMAFeatureCode(String FeatureCode, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_FLASH, response);
-
-        rr.mParcel.writeString(FeatureCode);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " : " + FeatureCode);
-
-        send(rr);
-    }
-
-    @Override
-    public void getCdmaBroadcastConfig(Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_GET_BROADCAST_CONFIG, response);
-
-        send(rr);
-    }
-
-    @Override
-    public void setCdmaBroadcastConfig(CdmaSmsBroadcastConfigInfo[] configs, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_SET_BROADCAST_CONFIG, response);
-
-        // Convert to 1 service category per config (the way RIL takes is)
-        ArrayList<CdmaSmsBroadcastConfigInfo> processedConfigs =
-            new ArrayList<CdmaSmsBroadcastConfigInfo>();
-        for (CdmaSmsBroadcastConfigInfo config : configs) {
-            for (int i = config.getFromServiceCategory(); i <= config.getToServiceCategory(); i++) {
-                processedConfigs.add(new CdmaSmsBroadcastConfigInfo(i,
-                        i,
-                        config.getLanguage(),
-                        config.isSelected()));
-            }
-        }
-
-        CdmaSmsBroadcastConfigInfo[] rilConfigs = processedConfigs.toArray(configs);
-        rr.mParcel.writeInt(rilConfigs.length);
-        for(int i = 0; i < rilConfigs.length; i++) {
-            rr.mParcel.writeInt(rilConfigs[i].getFromServiceCategory());
-            rr.mParcel.writeInt(rilConfigs[i].getLanguage());
-            rr.mParcel.writeInt(rilConfigs[i].isSelected() ? 1 : 0);
-        }
-
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " with " + rilConfigs.length + " configs : ");
-            for (int i = 0; i < rilConfigs.length; i++) {
-                riljLog(rilConfigs[i].toString());
-            }
-        }
-
-        send(rr);
-    }
-
-    @Override
-    public void setCdmaBroadcastActivation(boolean activate, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BROADCAST_ACTIVATION, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(activate ? 0 :1);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void exitEmergencyCallbackMode(Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE, response);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void requestIsimAuthentication(String nonce, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_ISIM_AUTHENTICATION, response);
-
-        rr.mParcel.writeString(nonce);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void requestIccSimAuthentication(int authContext, String data, String aid,
-                                            Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_AUTHENTICATION, response);
-
-        rr.mParcel.writeInt(authContext);
-        rr.mParcel.writeString(data);
-        rr.mParcel.writeString(aid);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void getCellInfoList(Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_CELL_INFO_LIST, result);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setCellInfoListRate(int rateInMillis, Message response) {
-        if (RILJ_LOGD) riljLog("setCellInfoListRate: " + rateInMillis);
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(rateInMillis);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-            String password, Message result) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_INITIAL_ATTACH_APN, result);
-
-        if (RILJ_LOGD) riljLog("Set RIL_REQUEST_SET_INITIAL_ATTACH_APN");
-
-        rr.mParcel.writeString(apn);
-        rr.mParcel.writeString(protocol);
-        rr.mParcel.writeInt(authType);
-        rr.mParcel.writeString(username);
-        rr.mParcel.writeString(password);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + ", apn:" + apn + ", protocol:" + protocol + ", authType:" + authType
-                + ", username:" + username + ", password:" + password);
-
-        send(rr);
-    }
-
-    public void setDataProfile(DataProfile[] dps, Message result) {
-        if (RILJ_LOGD) riljLog("Set RIL_REQUEST_SET_DATA_PROFILE");
-
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_DATA_PROFILE, null);
-        DataProfile.toParcel(rr.mParcel, dps);
-
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " with " + dps + " Data Profiles : ");
-            for (int i = 0; i < dps.length; i++) {
-                riljLog(dps[i].toString());
-            }
-        }
-
-        send(rr);
-    }
-
     /* (non-Javadoc)
      * @see com.android.internal.telephony.BaseCommands#testingEmergencyCall()
      */
@@ -4940,11 +4895,6 @@
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("RIL: " + this);
-        pw.println(" mSocket=" + mSocket);
-        pw.println(" mSenderThread=" + mSenderThread);
-        pw.println(" mSender=" + mSender);
-        pw.println(" mReceiverThread=" + mReceiverThread);
-        pw.println(" mReceiver=" + mReceiver);
         pw.println(" mWakeLock=" + mWakeLock);
         pw.println(" mWakeLockTimeout=" + mWakeLockTimeout);
         synchronized (mRequestList) {
@@ -4960,251 +4910,203 @@
         }
         pw.println(" mLastNITZTimeInfo=" + Arrays.toString(mLastNITZTimeInfo));
         pw.println(" mTestingEmergencyCall=" + mTestingEmergencyCall.get());
+        mClientWakelockTracker.dumpClientRequestTracker();
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void iccOpenLogicalChannel(String AID, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_OPEN_CHANNEL, response);
-        rr.mParcel.writeString(AID);
-
-        if (RILJ_LOGD)
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
+    public List<ClientRequestStats> getClientRequestStats() {
+        return mClientWakelockTracker.getClientRequestStats();
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void iccCloseLogicalChannel(int channel, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SIM_CLOSE_CHANNEL, response);
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(channel);
-
-        if (RILJ_LOGD)
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void iccTransmitApduLogicalChannel(int channel, int cla, int instruction,
-            int p1, int p2, int p3, String data, Message response) {
-        if (channel <= 0) {
-            throw new RuntimeException(
-                "Invalid channel in iccTransmitApduLogicalChannel: " + channel);
+    public static ArrayList<Byte> primitiveArrayToArrayList(byte[] arr) {
+        ArrayList<Byte> arrayList = new ArrayList<>(arr.length);
+        for (byte b : arr) {
+            arrayList.add(b);
         }
-
-        iccTransmitApduHelper(RIL_REQUEST_SIM_TRANSMIT_APDU_CHANNEL, channel, cla,
-                instruction, p1, p2, p3, data, response);
+        return arrayList;
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void iccTransmitApduBasicChannel(int cla, int instruction, int p1, int p2,
-            int p3, String data, Message response) {
-        iccTransmitApduHelper(RIL_REQUEST_SIM_TRANSMIT_APDU_BASIC, 0, cla, instruction,
-                p1, p2, p3, data, response);
-    }
-
-    /*
-     * Helper function for the iccTransmitApdu* commands above.
-     */
-    private void iccTransmitApduHelper(int rilCommand, int channel, int cla,
-            int instruction, int p1, int p2, int p3, String data, Message response) {
-        RILRequest rr = RILRequest.obtain(rilCommand, response);
-        rr.mParcel.writeInt(channel);
-        rr.mParcel.writeInt(cla);
-        rr.mParcel.writeInt(instruction);
-        rr.mParcel.writeInt(p1);
-        rr.mParcel.writeInt(p2);
-        rr.mParcel.writeInt(p3);
-        rr.mParcel.writeString(data);
-
-        if (RILJ_LOGD)
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
-        send(rr);
-    }
-
-    @Override
-    public void nvReadItem(int itemID, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_NV_READ_ITEM, response);
-
-        rr.mParcel.writeInt(itemID);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + ' ' + itemID);
-
-        send(rr);
-    }
-
-    @Override
-    public void nvWriteItem(int itemID, String itemValue, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_NV_WRITE_ITEM, response);
-
-        rr.mParcel.writeInt(itemID);
-        rr.mParcel.writeString(itemValue);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + ' ' + itemID + ": " + itemValue);
-
-        send(rr);
-    }
-
-    @Override
-    public void nvWriteCdmaPrl(byte[] preferredRoamingList, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_NV_WRITE_CDMA_PRL, response);
-
-        rr.mParcel.writeByteArray(preferredRoamingList);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + " (" + preferredRoamingList.length + " bytes)");
-
-        send(rr);
-    }
-
-    @Override
-    public void nvResetConfig(int resetType, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_NV_RESET_CONFIG, response);
-
-        rr.mParcel.writeInt(1);
-        rr.mParcel.writeInt(resetType);
-
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                + ' ' + resetType);
-
-        send(rr);
-    }
-
-    @Override
-    public void setRadioCapability(RadioCapability rc, Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RIL_REQUEST_SET_RADIO_CAPABILITY, response);
-
-        rr.mParcel.writeInt(rc.getVersion());
-        rr.mParcel.writeInt(rc.getSession());
-        rr.mParcel.writeInt(rc.getPhase());
-        rr.mParcel.writeInt(rc.getRadioAccessFamily());
-        rr.mParcel.writeString(rc.getLogicalModemUuid());
-        rr.mParcel.writeInt(rc.getStatus());
-
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                    + " " + rc.toString());
+    public static byte[] arrayListToPrimitiveArray(ArrayList<Byte> bytes) {
+        byte[] ret = new byte[bytes.size()];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = bytes.get(i);
         }
-
-        send(rr);
+        return ret;
     }
 
-    @Override
-    public void getRadioCapability(Message response) {
-        RILRequest rr = RILRequest.obtain(
-                RIL_REQUEST_GET_RADIO_CAPABILITY, response);
+    static ArrayList<HardwareConfig> convertHalHwConfigList(
+            ArrayList<android.hardware.radio.V1_0.HardwareConfig> hwListRil,
+            RIL ril) {
+        int num;
+        ArrayList<HardwareConfig> response;
+        HardwareConfig hw;
 
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        num = hwListRil.size();
+        response = new ArrayList<HardwareConfig>(num);
 
-        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));
+        if (RILJ_LOGV) {
+            ril.riljLog("convertHalHwConfigList: num=" + num);
         }
-
-        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);
-
-        Message msg = mSender.obtainMessage(EVENT_BLOCKING_RESPONSE_TIMEOUT);
-        msg.obj = null;
-        msg.arg1 = rr.mSerial;
-        mSender.sendMessageDelayed(msg, DEFAULT_BLOCKING_MESSAGE_RESPONSE_TIMEOUT_MS);
-    }
-
-    @Override
-    public void setAllowedCarriers(List<CarrierIdentifier> carriers, Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_SET_ALLOWED_CARRIERS, response);
-        rr.mParcel.writeInt(carriers.size()); /* len_allowed_carriers */
-        rr.mParcel.writeInt(0); /* len_excluded_carriers */ /* TODO: add excluded carriers */
-        for (CarrierIdentifier ci : carriers) { /* allowed carriers */
-            rr.mParcel.writeString(ci.getMcc());
-            rr.mParcel.writeString(ci.getMnc());
-            int matchType = CarrierIdentifier.MatchType.ALL;
-            String matchData = null;
-            if (!TextUtils.isEmpty(ci.getSpn())) {
-                matchType = CarrierIdentifier.MatchType.SPN;
-                matchData = ci.getSpn();
-            } else if (!TextUtils.isEmpty(ci.getImsi())) {
-                matchType = CarrierIdentifier.MatchType.IMSI_PREFIX;
-                matchData = ci.getImsi();
-            } else if (!TextUtils.isEmpty(ci.getGid1())) {
-                matchType = CarrierIdentifier.MatchType.GID1;
-                matchData = ci.getGid1();
-            } else if (!TextUtils.isEmpty(ci.getGid2())) {
-                matchType = CarrierIdentifier.MatchType.GID2;
-                matchData = ci.getGid2();
+        for (android.hardware.radio.V1_0.HardwareConfig hwRil : hwListRil) {
+            int type = hwRil.type;
+            switch(type) {
+                case HardwareConfig.DEV_HARDWARE_TYPE_MODEM: {
+                    hw = new HardwareConfig(type);
+                    HardwareConfigModem hwModem = hwRil.modem.get(0);
+                    hw.assignModem(hwRil.uuid, hwRil.state, hwModem.rilModel, hwModem.rat,
+                            hwModem.maxVoice, hwModem.maxData, hwModem.maxStandby);
+                    break;
+                }
+                case HardwareConfig.DEV_HARDWARE_TYPE_SIM: {
+                    hw = new HardwareConfig(type);
+                    hw.assignSim(hwRil.uuid, hwRil.state, hwRil.sim.get(0).modemUuid);
+                    break;
+                }
+                default: {
+                    throw new RuntimeException(
+                            "RIL_REQUEST_GET_HARDWARE_CONFIG invalid hardward type:" + type);
+                }
             }
-            rr.mParcel.writeInt(matchType);
-            rr.mParcel.writeString(matchData);
-        }
-        /* TODO: add excluded carriers */
 
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+            response.add(hw);
         }
-        send(rr);
+
+        return response;
     }
 
-    @Override
-    public void getAllowedCarriers(Message response) {
-        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_ALLOWED_CARRIERS, response);
-        if (RILJ_LOGD) {
-            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+    static RadioCapability convertHalRadioCapability(
+            android.hardware.radio.V1_0.RadioCapability rcRil, RIL ril) {
+        int session = rcRil.session;
+        int phase = rcRil.phase;
+        int rat = rcRil.raf;
+        String logicModemUuid = rcRil.logicalModemUuid;
+        int status = rcRil.status;
+
+        ril.riljLog("convertHalRadioCapability: session=" + session +
+                ", phase=" + phase +
+                ", rat=" + rat +
+                ", logicModemUuid=" + logicModemUuid +
+                ", status=" + status);
+        RadioCapability rc = new RadioCapability(
+                ril.mPhoneId, session, phase, rat, logicModemUuid, status);
+        return rc;
+    }
+
+    static ArrayList<Integer> convertHalLceData(LceDataInfo lce, RIL ril) {
+        final ArrayList<Integer> capacityResponse = new ArrayList<Integer>();
+        final int capacityDownKbps = lce.lastHopCapacityKbps;
+        final int confidenceLevel = Byte.toUnsignedInt(lce.confidenceLevel);
+        final int lceSuspended = lce.lceSuspended ? 1 : 0;
+
+        ril.riljLog("LCE capacity information received:" +
+                " capacity=" + capacityDownKbps +
+                " confidence=" + confidenceLevel +
+                " lceSuspended=" + lceSuspended);
+
+        capacityResponse.add(capacityDownKbps);
+        capacityResponse.add(confidenceLevel);
+        capacityResponse.add(lceSuspended);
+        return capacityResponse;
+    }
+
+    static ArrayList<CellInfo> convertHalCellInfoList(
+            ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
+        ArrayList<CellInfo> response = new ArrayList<CellInfo>(records.size());
+
+        for (android.hardware.radio.V1_0.CellInfo record : records) {
+            // first convert RIL CellInfo to Parcel
+            Parcel p = Parcel.obtain();
+            p.writeInt(record.cellInfoType);
+            p.writeInt(record.registered ? 1 : 0);
+            p.writeInt(record.timeStampType);
+            p.writeLong(record.timeStamp);
+            switch (record.cellInfoType) {
+                case CellInfoType.GSM: {
+                    CellInfoGsm cellInfoGsm = record.gsm.get(0);
+                    p.writeInt(Integer.parseInt(cellInfoGsm.cellIdentityGsm.mcc));
+                    p.writeInt(Integer.parseInt(cellInfoGsm.cellIdentityGsm.mnc));
+                    p.writeInt(cellInfoGsm.cellIdentityGsm.lac);
+                    p.writeInt(cellInfoGsm.cellIdentityGsm.cid);
+                    p.writeInt(cellInfoGsm.cellIdentityGsm.arfcn);
+                    p.writeInt(Byte.toUnsignedInt(cellInfoGsm.cellIdentityGsm.bsic));
+                    p.writeInt(cellInfoGsm.signalStrengthGsm.signalStrength);
+                    p.writeInt(cellInfoGsm.signalStrengthGsm.bitErrorRate);
+                    p.writeInt(cellInfoGsm.signalStrengthGsm.timingAdvance);
+                    break;
+                }
+
+                case CellInfoType.CDMA: {
+                    CellInfoCdma cellInfoCdma = record.cdma.get(0);
+                    p.writeInt(cellInfoCdma.cellIdentityCdma.networkId);
+                    p.writeInt(cellInfoCdma.cellIdentityCdma.systemId);
+                    p.writeInt(cellInfoCdma.cellIdentityCdma.baseStationId);
+                    p.writeInt(cellInfoCdma.cellIdentityCdma.longitude);
+                    p.writeInt(cellInfoCdma.cellIdentityCdma.latitude);
+                    p.writeInt(cellInfoCdma.signalStrengthCdma.dbm);
+                    p.writeInt(cellInfoCdma.signalStrengthCdma.ecio);
+                    p.writeInt(cellInfoCdma.signalStrengthEvdo.dbm);
+                    p.writeInt(cellInfoCdma.signalStrengthEvdo.ecio);
+                    p.writeInt(cellInfoCdma.signalStrengthEvdo.signalNoiseRatio);
+                    break;
+                }
+
+                case CellInfoType.LTE: {
+                    CellInfoLte cellInfoLte = record.lte.get(0);
+                    p.writeInt(Integer.parseInt(cellInfoLte.cellIdentityLte.mcc));
+                    p.writeInt(Integer.parseInt(cellInfoLte.cellIdentityLte.mnc));
+                    p.writeInt(cellInfoLte.cellIdentityLte.ci);
+                    p.writeInt(cellInfoLte.cellIdentityLte.pci);
+                    p.writeInt(cellInfoLte.cellIdentityLte.tac);
+                    p.writeInt(cellInfoLte.cellIdentityLte.earfcn);
+                    p.writeInt(cellInfoLte.signalStrengthLte.signalStrength);
+                    p.writeInt(cellInfoLte.signalStrengthLte.rsrp);
+                    p.writeInt(cellInfoLte.signalStrengthLte.rsrq);
+                    p.writeInt(cellInfoLte.signalStrengthLte.rssnr);
+                    p.writeInt(cellInfoLte.signalStrengthLte.cqi);
+                    p.writeInt(cellInfoLte.signalStrengthLte.timingAdvance);
+                    break;
+                }
+
+                case CellInfoType.WCDMA: {
+                    CellInfoWcdma cellInfoWcdma = record.wcdma.get(0);
+                    p.writeInt(Integer.parseInt(cellInfoWcdma.cellIdentityWcdma.mcc));
+                    p.writeInt(Integer.parseInt(cellInfoWcdma.cellIdentityWcdma.mnc));
+                    p.writeInt(cellInfoWcdma.cellIdentityWcdma.lac);
+                    p.writeInt(cellInfoWcdma.cellIdentityWcdma.cid);
+                    p.writeInt(cellInfoWcdma.cellIdentityWcdma.psc);
+                    p.writeInt(cellInfoWcdma.cellIdentityWcdma.uarfcn);
+                    p.writeInt(cellInfoWcdma.signalStrengthWcdma.signalStrength);
+                    p.writeInt(cellInfoWcdma.signalStrengthWcdma.bitErrorRate);
+                    break;
+                }
+
+                default:
+                    throw new RuntimeException("unexpected cellinfotype: " + record.cellInfoType);
+            }
+
+            p.setDataPosition(0);
+            CellInfo InfoRec = CellInfo.CREATOR.createFromParcel(p);
+            p.recycle();
+            response.add(InfoRec);
         }
-        send(rr);
+
+        return response;
+    }
+
+    static SignalStrength convertHalSignalStrength(
+            android.hardware.radio.V1_0.SignalStrength signalStrength) {
+        return new SignalStrength(signalStrength.gw.signalStrength,
+                signalStrength.gw.bitErrorRate,
+                signalStrength.cdma.dbm,
+                signalStrength.cdma.ecio,
+                signalStrength.evdo.dbm,
+                signalStrength.evdo.ecio,
+                signalStrength.evdo.signalNoiseRatio,
+                signalStrength.lte.signalStrength,
+                signalStrength.lte.rsrp,
+                signalStrength.lte.rsrq,
+                signalStrength.lte.rssnr,
+                signalStrength.lte.cqi,
+                signalStrength.tdScdma.rscp,
+                false /* gsmFlag - don't care; will be changed by SST */);
     }
 }
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
new file mode 100644
index 0000000..663f8a4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CALL_RING;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_CALL_WAITING;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_INFO_REC;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_OTA_PROVISION_STATUS;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_CELL_INFO_LIST;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_HARDWARE_CONFIG_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_LCEDATA_RECV;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_MODEM_RESTART;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NETWORK_SCAN_RESULT;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_NITZ_TIME_RECEIVED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_SS;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_ON_USSD;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RADIO_CAPABILITY;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESEND_INCALL_MUTE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_CDMA_NEW_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RESTRICTED_STATE_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RIL_CONNECTED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_RINGBACK_TONE;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIGNAL_STRENGTH;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_REFRESH;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SIM_SMS_STORAGE_FULL;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SRVCC_STATE_NOTIFY;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CALL_SETUP;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_CC_ALPHA_NOTIFY;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_EVENT_NOTIFY;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_PROACTIVE_COMMAND;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_STK_SESSION_END;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SUPP_SVC_NOTIFICATION;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_VOICE_RADIO_TECH_CHANGED;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOl_CDMA_PRL_CHANGED;
+
+import android.hardware.radio.V1_0.CdmaCallWaiting;
+import android.hardware.radio.V1_0.CdmaInformationRecord;
+import android.hardware.radio.V1_0.CdmaLineControlInfoRecord;
+import android.hardware.radio.V1_0.CdmaNumberInfoRecord;
+import android.hardware.radio.V1_0.CdmaRedirectingNumberInfoRecord;
+import android.hardware.radio.V1_0.CdmaSignalInfoRecord;
+import android.hardware.radio.V1_0.CdmaSmsMessage;
+import android.hardware.radio.V1_0.CdmaT53AudioControlInfoRecord;
+import android.hardware.radio.V1_0.CfData;
+import android.hardware.radio.V1_0.LceDataInfo;
+import android.hardware.radio.V1_0.PcoDataInfo;
+import android.hardware.radio.V1_0.SetupDataCallResult;
+import android.hardware.radio.V1_0.SimRefreshResult;
+import android.hardware.radio.V1_0.SsInfoData;
+import android.hardware.radio.V1_0.StkCcUnsolSsResult;
+import android.hardware.radio.V1_0.SuppSvcNotification;
+import android.hardware.radio.V1_1.IRadioIndication;
+import android.hardware.radio.V1_1.KeepaliveStatus;
+import android.os.AsyncResult;
+import android.os.SystemProperties;
+import android.telephony.CellInfo;
+import android.telephony.PcoData;
+import android.telephony.SignalStrength;
+import android.telephony.SmsMessage;
+
+import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
+import com.android.internal.telephony.cdma.CdmaInformationRecords;
+import com.android.internal.telephony.cdma.SmsMessageConverter;
+import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.gsm.SsData;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.uicc.IccRefreshResponse;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.ArrayList;
+
+public class RadioIndication extends IRadioIndication.Stub {
+    RIL mRil;
+
+    RadioIndication(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * Indicates when radio state changes.
+     * @param indicationType RadioIndicationType
+     * @param radioState android.hardware.radio.V1_0.RadioState
+     */
+    public void radioStateChanged(int indicationType, int radioState) {
+        mRil.processIndication(indicationType);
+
+        CommandsInterface.RadioState newState = getRadioStateFromInt(radioState);
+        if (RIL.RILJ_LOGD) {
+            mRil.unsljLogMore(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, "radioStateChanged: " +
+                    newState);
+        }
+
+        mRil.setRadioState(newState);
+    }
+
+    public void callStateChanged(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED);
+
+        mRil.mCallStateRegistrants.notifyRegistrants();
+    }
+
+    /**
+     * Indicates when either voice or data network state changed
+     * @param indicationType RadioIndicationType
+     */
+    public void networkStateChanged(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED);
+
+        mRil.mNetworkStateRegistrants.notifyRegistrants();
+    }
+
+    public void newSms(int indicationType, ArrayList<Byte> pdu) {
+        mRil.processIndication(indicationType);
+
+        byte[] pduArray = RIL.arrayListToPrimitiveArray(pdu);
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS);
+
+        mRil.writeMetricsNewSms(SmsSession.Event.Tech.SMS_GSM,
+                SmsSession.Event.Format.SMS_FORMAT_3GPP);
+
+        SmsMessage sms = SmsMessage.newFromCMT(pduArray);
+        if (mRil.mGsmSmsRegistrant != null) {
+            mRil.mGsmSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
+        }
+    }
+
+    public void newSmsStatusReport(int indicationType, ArrayList<Byte> pdu) {
+        mRil.processIndication(indicationType);
+
+        byte[] pduArray = RIL.arrayListToPrimitiveArray(pdu);
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT);
+
+        if (mRil.mSmsStatusRegistrant != null) {
+            mRil.mSmsStatusRegistrant.notifyRegistrant(new AsyncResult(null, pduArray, null));
+        }
+    }
+
+    public void newSmsOnSim(int indicationType, int recordNumber) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM);
+
+        if (mRil.mSmsOnSimRegistrant != null) {
+            mRil.mSmsOnSimRegistrant.notifyRegistrant(new AsyncResult(null, recordNumber, null));
+        }
+    }
+
+    public void onUssd(int indicationType, int ussdModeType, String msg) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogMore(RIL_UNSOL_ON_USSD, "" + ussdModeType);
+
+        // todo: Clean this up with a parcelable class for better self-documentation
+        String[] resp = new String[2];
+        resp[0] = "" + ussdModeType;
+        resp[1] = msg;
+        if (mRil.mUSSDRegistrant != null) {
+            mRil.mUSSDRegistrant.notifyRegistrant(new AsyncResult (null, resp, null));
+        }
+    }
+
+    public void nitzTimeReceived(int indicationType, String nitzTime, long receivedTime) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NITZ_TIME_RECEIVED, nitzTime);
+
+        // todo: Clean this up with a parcelable class for better self-documentation
+        Object[] result = new Object[2];
+        result[0] = nitzTime;
+        result[1] = receivedTime;
+
+        boolean ignoreNitz = SystemProperties.getBoolean(
+                TelephonyProperties.PROPERTY_IGNORE_NITZ, false);
+
+        if (ignoreNitz) {
+            if (RIL.RILJ_LOGD) mRil.riljLog("ignoring UNSOL_NITZ_TIME_RECEIVED");
+        } else {
+            if (mRil.mNITZTimeRegistrant != null) {
+                mRil.mNITZTimeRegistrant.notifyRegistrant(new AsyncResult (null, result, null));
+            }
+            // in case NITZ time registrant isn't registered yet, or a new registrant
+            // registers later
+            mRil.mLastNITZTimeInfo = result;
+        }
+    }
+
+    public void currentSignalStrength(int indicationType,
+                                      android.hardware.radio.V1_0.SignalStrength signalStrength) {
+        mRil.processIndication(indicationType);
+
+        SignalStrength ss = RIL.convertHalSignalStrength(signalStrength);
+        // Note this is set to "verbose" because it happens frequently
+        if (RIL.RILJ_LOGV) mRil.unsljLogvRet(RIL_UNSOL_SIGNAL_STRENGTH, ss);
+
+        if (mRil.mSignalStrengthRegistrant != null) {
+            mRil.mSignalStrengthRegistrant.notifyRegistrant(new AsyncResult (null, ss, null));
+        }
+    }
+
+    public void dataCallListChanged(int indicationType, ArrayList<SetupDataCallResult> dcList) {
+        mRil.processIndication(indicationType);
+
+        ArrayList<DataCallResponse> response = new ArrayList<>();
+
+        for (SetupDataCallResult dcResult : dcList) {
+            response.add(RIL.convertDataCallResult(dcResult));
+        }
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_DATA_CALL_LIST_CHANGED, response);
+
+        mRil.mDataCallListChangedRegistrants.notifyRegistrants(
+                new AsyncResult(null, response, null));
+    }
+
+    public void suppSvcNotify(int indicationType, SuppSvcNotification suppSvcNotification) {
+        mRil.processIndication(indicationType);
+
+        SuppServiceNotification notification = new SuppServiceNotification();
+        notification.notificationType = suppSvcNotification.isMT ? 1 : 0;
+        notification.code = suppSvcNotification.code;
+        notification.index = suppSvcNotification.index;
+        notification.type = suppSvcNotification.type;
+        notification.number = suppSvcNotification.number;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SUPP_SVC_NOTIFICATION, notification);
+
+        if (mRil.mSsnRegistrant != null) {
+            mRil.mSsnRegistrant.notifyRegistrant(new AsyncResult (null, notification, null));
+        }
+    }
+
+    public void stkSessionEnd(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_SESSION_END);
+
+        if (mRil.mCatSessionEndRegistrant != null) {
+            mRil.mCatSessionEndRegistrant.notifyRegistrant(new AsyncResult (null, null, null));
+        }
+    }
+
+    public void stkProactiveCommand(int indicationType, String cmd) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_PROACTIVE_COMMAND);
+
+        if (mRil.mCatProCmdRegistrant != null) {
+            mRil.mCatProCmdRegistrant.notifyRegistrant(new AsyncResult (null, cmd, null));
+        }
+    }
+
+    public void stkEventNotify(int indicationType, String cmd) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_STK_EVENT_NOTIFY);
+
+        if (mRil.mCatEventRegistrant != null) {
+            mRil.mCatEventRegistrant.notifyRegistrant(new AsyncResult (null, cmd, null));
+        }
+    }
+
+    public void stkCallSetup(int indicationType, long timeout) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CALL_SETUP, timeout);
+
+        if (mRil.mCatCallSetUpRegistrant != null) {
+            mRil.mCatCallSetUpRegistrant.notifyRegistrant(new AsyncResult (null, timeout, null));
+        }
+    }
+
+    public void simSmsStorageFull(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_SIM_SMS_STORAGE_FULL);
+
+        if (mRil.mIccSmsFullRegistrant != null) {
+            mRil.mIccSmsFullRegistrant.notifyRegistrant();
+        }
+    }
+
+    public void simRefresh(int indicationType, SimRefreshResult refreshResult) {
+        mRil.processIndication(indicationType);
+
+        IccRefreshResponse response = new IccRefreshResponse();
+        response.refreshResult = refreshResult.type;
+        response.efId = refreshResult.efId;
+        response.aid = refreshResult.aid;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SIM_REFRESH, response);
+
+        mRil.mIccRefreshRegistrants.notifyRegistrants(new AsyncResult (null, response, null));
+    }
+
+    public void callRing(int indicationType, boolean isGsm, CdmaSignalInfoRecord record) {
+        mRil.processIndication(indicationType);
+
+        char response[] = null;
+
+        // Ignore record for gsm
+        if (!isGsm) {
+            // todo: Clean this up with a parcelable class for better self-documentation
+            response = new char[4];
+            response[0] = (char) (record.isPresent ? 1 : 0);
+            response[1] = (char) record.signalType;
+            response[2] = (char) record.alertPitch;
+            response[3] = (char) record.signal;
+            mRil.writeMetricsCallRing(response);
+        }
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CALL_RING, response);
+
+        if (mRil.mRingRegistrant != null) {
+            mRil.mRingRegistrant.notifyRegistrant(new AsyncResult (null, response, null));
+        }
+    }
+
+    public void simStatusChanged(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED);
+
+        mRil.mIccStatusChangedRegistrants.notifyRegistrants();
+    }
+
+    public void cdmaNewSms(int indicationType, CdmaSmsMessage msg) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
+
+        mRil.writeMetricsNewSms(SmsSession.Event.Tech.SMS_CDMA,
+                SmsSession.Event.Format.SMS_FORMAT_3GPP2);
+
+        // todo: conversion from CdmaSmsMessage to SmsMessage should be contained in this class so
+        // that usage of auto-generated HAL classes is limited to this file
+        SmsMessage sms = SmsMessageConverter.newSmsMessageFromCdmaSmsMessage(msg);
+        if (mRil.mCdmaSmsRegistrant != null) {
+            mRil.mCdmaSmsRegistrant.notifyRegistrant(new AsyncResult(null, sms, null));
+        }
+    }
+
+    public void newBroadcastSms(int indicationType, ArrayList<Byte> data) {
+        mRil.processIndication(indicationType);
+
+        byte response[] = RIL.arrayListToPrimitiveArray(data);
+        if (RIL.RILJ_LOGD) {
+            mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
+                    IccUtils.bytesToHexString(response));
+        }
+
+        if (mRil.mGsmBroadcastSmsRegistrant != null) {
+            mRil.mGsmBroadcastSmsRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
+        }
+    }
+
+    public void cdmaRuimSmsStorageFull(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL);
+
+        if (mRil.mIccSmsFullRegistrant != null) {
+            mRil.mIccSmsFullRegistrant.notifyRegistrant();
+        }
+    }
+
+    public void restrictedStateChanged(int indicationType, int state) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RESTRICTED_STATE_CHANGED, state);
+
+        if (mRil.mRestrictedStateRegistrant != null) {
+            mRil.mRestrictedStateRegistrant.notifyRegistrant(new AsyncResult (null, state, null));
+        }
+    }
+
+    public void enterEmergencyCallbackMode(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE);
+
+        if (mRil.mEmergencyCallbackModeRegistrant != null) {
+            mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
+        }
+    }
+
+    public void cdmaCallWaiting(int indicationType, CdmaCallWaiting callWaitingRecord) {
+        mRil.processIndication(indicationType);
+
+        // todo: create a CdmaCallWaitingNotification constructor that takes in these fields to make
+        // sure no fields are missing
+        CdmaCallWaitingNotification notification = new CdmaCallWaitingNotification();
+        notification.number = callWaitingRecord.number;
+        notification.numberPresentation = CdmaCallWaitingNotification.presentationFromCLIP(
+                callWaitingRecord.numberPresentation);
+        notification.name = callWaitingRecord.name;
+        notification.namePresentation = notification.numberPresentation;
+        notification.isPresent = callWaitingRecord.signalInfoRecord.isPresent ? 1 : 0;
+        notification.signalType = callWaitingRecord.signalInfoRecord.signalType;
+        notification.alertPitch = callWaitingRecord.signalInfoRecord.alertPitch;
+        notification.signal = callWaitingRecord.signalInfoRecord.signal;
+        notification.numberType = callWaitingRecord.numberType;
+        notification.numberPlan = callWaitingRecord.numberPlan;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_CALL_WAITING, notification);
+
+        mRil.mCallWaitingInfoRegistrants.notifyRegistrants(
+                new AsyncResult (null, notification, null));
+    }
+
+    public void cdmaOtaProvisionStatus(int indicationType, int status) {
+        mRil.processIndication(indicationType);
+
+        int response[] = new int[1];
+        response[0] = status;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_OTA_PROVISION_STATUS, response);
+
+        mRil.mOtaProvisionRegistrants.notifyRegistrants(new AsyncResult (null, response, null));
+    }
+
+    public void cdmaInfoRec(int indicationType,
+                            android.hardware.radio.V1_0.CdmaInformationRecords records) {
+        mRil.processIndication(indicationType);
+
+        int numberOfInfoRecs = records.infoRec.size();
+        for (int i = 0; i < numberOfInfoRecs; i++) {
+            CdmaInformationRecord record = records.infoRec.get(i);
+            int id = record.name;
+            CdmaInformationRecords cdmaInformationRecords;
+            switch (id) {
+                case CdmaInformationRecords.RIL_CDMA_DISPLAY_INFO_REC:
+                case CdmaInformationRecords.RIL_CDMA_EXTENDED_DISPLAY_INFO_REC:
+                    CdmaInformationRecords.CdmaDisplayInfoRec cdmaDisplayInfoRec =
+                            new CdmaInformationRecords.CdmaDisplayInfoRec(id,
+                            record.display.get(0).alphaBuf);
+                    cdmaInformationRecords = new CdmaInformationRecords(cdmaDisplayInfoRec);
+                    break;
+
+                case CdmaInformationRecords.RIL_CDMA_CALLED_PARTY_NUMBER_INFO_REC:
+                case CdmaInformationRecords.RIL_CDMA_CALLING_PARTY_NUMBER_INFO_REC:
+                case CdmaInformationRecords.RIL_CDMA_CONNECTED_NUMBER_INFO_REC:
+                    CdmaNumberInfoRecord numInfoRecord = record.number.get(0);
+                    CdmaInformationRecords.CdmaNumberInfoRec cdmaNumberInfoRec =
+                            new CdmaInformationRecords.CdmaNumberInfoRec(id,
+                            numInfoRecord.number,
+                            numInfoRecord.numberType,
+                            numInfoRecord.numberPlan,
+                            numInfoRecord.pi,
+                            numInfoRecord.si);
+                    cdmaInformationRecords = new CdmaInformationRecords(cdmaNumberInfoRec);
+                    break;
+
+                case CdmaInformationRecords.RIL_CDMA_SIGNAL_INFO_REC:
+                    CdmaSignalInfoRecord signalInfoRecord = record.signal.get(0);
+                    CdmaInformationRecords.CdmaSignalInfoRec cdmaSignalInfoRec =
+                            new CdmaInformationRecords.CdmaSignalInfoRec(
+                            signalInfoRecord.isPresent ? 1 : 0,
+                            signalInfoRecord.signalType,
+                            signalInfoRecord.alertPitch,
+                            signalInfoRecord.signal);
+                    cdmaInformationRecords = new CdmaInformationRecords(cdmaSignalInfoRec);
+                    break;
+
+                case CdmaInformationRecords.RIL_CDMA_REDIRECTING_NUMBER_INFO_REC:
+                    CdmaRedirectingNumberInfoRecord redirectingNumberInfoRecord =
+                            record.redir.get(0);
+                    CdmaInformationRecords.CdmaRedirectingNumberInfoRec
+                            cdmaRedirectingNumberInfoRec =
+                            new CdmaInformationRecords.CdmaRedirectingNumberInfoRec(
+                            redirectingNumberInfoRecord.redirectingNumber.number,
+                            redirectingNumberInfoRecord.redirectingNumber.numberType,
+                            redirectingNumberInfoRecord.redirectingNumber.numberPlan,
+                            redirectingNumberInfoRecord.redirectingNumber.pi,
+                            redirectingNumberInfoRecord.redirectingNumber.si,
+                            redirectingNumberInfoRecord.redirectingReason);
+                    cdmaInformationRecords = new CdmaInformationRecords(
+                            cdmaRedirectingNumberInfoRec);
+                    break;
+
+                case CdmaInformationRecords.RIL_CDMA_LINE_CONTROL_INFO_REC:
+                    CdmaLineControlInfoRecord lineControlInfoRecord = record.lineCtrl.get(0);
+                    CdmaInformationRecords.CdmaLineControlInfoRec cdmaLineControlInfoRec =
+                            new CdmaInformationRecords.CdmaLineControlInfoRec(
+                            lineControlInfoRecord.lineCtrlPolarityIncluded,
+                            lineControlInfoRecord.lineCtrlToggle,
+                            lineControlInfoRecord.lineCtrlReverse,
+                            lineControlInfoRecord.lineCtrlPowerDenial);
+                    cdmaInformationRecords = new CdmaInformationRecords(cdmaLineControlInfoRec);
+                    break;
+
+                case CdmaInformationRecords.RIL_CDMA_T53_CLIR_INFO_REC:
+                    CdmaInformationRecords.CdmaT53ClirInfoRec cdmaT53ClirInfoRec =
+                            new CdmaInformationRecords.CdmaT53ClirInfoRec(record.clir.get(0).cause);
+                    cdmaInformationRecords = new CdmaInformationRecords(cdmaT53ClirInfoRec);
+                    break;
+
+                case CdmaInformationRecords.RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC:
+                    CdmaT53AudioControlInfoRecord audioControlInfoRecord = record.audioCtrl.get(0);
+                    CdmaInformationRecords.CdmaT53AudioControlInfoRec cdmaT53AudioControlInfoRec =
+                            new CdmaInformationRecords.CdmaT53AudioControlInfoRec(
+                            audioControlInfoRecord.upLink,
+                            audioControlInfoRecord.downLink);
+                    cdmaInformationRecords = new CdmaInformationRecords(cdmaT53AudioControlInfoRec);
+                    break;
+
+                default:
+                    throw new RuntimeException("RIL_UNSOL_CDMA_INFO_REC: unsupported record. Got "
+                            + CdmaInformationRecords.idToString(id) + " ");
+            }
+
+            if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_INFO_REC, cdmaInformationRecords);
+            mRil.notifyRegistrantsCdmaInfoRec(cdmaInformationRecords);
+        }
+    }
+
+    public void indicateRingbackTone(int indicationType, boolean start) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogvRet(RIL_UNSOL_RINGBACK_TONE, start);
+
+        mRil.mRingbackToneRegistrants.notifyRegistrants(new AsyncResult(null, start, null));
+    }
+
+    public void resendIncallMute(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESEND_INCALL_MUTE);
+
+        mRil.mResendIncallMuteRegistrants.notifyRegistrants();
+    }
+
+    public void cdmaSubscriptionSourceChanged(int indicationType, int cdmaSource) {
+        mRil.processIndication(indicationType);
+
+        int response[] = new int[1];
+        response[0] = cdmaSource;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, response);
+
+        mRil.mCdmaSubscriptionChangedRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void cdmaPrlChanged(int indicationType, int version) {
+        mRil.processIndication(indicationType);
+
+        int response[] = new int[1];
+        response[0] = version;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOl_CDMA_PRL_CHANGED, response);
+
+        mRil.mCdmaPrlChangedRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void exitEmergencyCallbackMode(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE);
+
+        mRil.mExitEmergencyCallbackModeRegistrants.notifyRegistrants();
+    }
+
+    public void rilConnected(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RIL_CONNECTED);
+
+        // Initial conditions
+        mRil.setRadioPower(false, null);
+        mRil.setCdmaSubscriptionSource(mRil.mCdmaSubscription, null);
+        mRil.setCellInfoListRate();
+        // todo: this should not require a version number now. Setting it to latest RIL version for
+        // now.
+        mRil.notifyRegistrantsRilConnectionChanged(15);
+    }
+
+    public void voiceRadioTechChanged(int indicationType, int rat) {
+        mRil.processIndication(indicationType);
+
+        int response[] = new int[1];
+        response[0] = rat;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, response);
+
+        mRil.mVoiceRadioTechChangedRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void cellInfoList(int indicationType,
+                             ArrayList<android.hardware.radio.V1_0.CellInfo> records) {
+        mRil.processIndication(indicationType);
+
+        ArrayList<CellInfo> response = RIL.convertHalCellInfoList(records);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CELL_INFO_LIST, response);
+
+        mRil.mRilCellInfoListRegistrants.notifyRegistrants(new AsyncResult (null, response, null));
+    }
+
+    /** Incremental network scan results */
+    public void networkScanResult(int indicationType,
+                                  android.hardware.radio.V1_1.NetworkScanResult result) {
+        responseCellInfos(indicationType, result);
+    }
+
+    public void imsNetworkStateChanged(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLog(RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED);
+
+        mRil.mImsNetworkStateChangedRegistrants.notifyRegistrants();
+    }
+
+    public void subscriptionStatusChanged(int indicationType, boolean activate) {
+        mRil.processIndication(indicationType);
+
+        int response[] = new int[1];
+        response[0] = activate ? 1 : 0;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_UICC_SUBSCRIPTION_STATUS_CHANGED, response);
+
+        mRil.mSubscriptionStatusRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void srvccStateNotify(int indicationType, int state) {
+        mRil.processIndication(indicationType);
+
+        int response[] = new int[1];
+        response[0] = state;
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SRVCC_STATE_NOTIFY, response);
+
+        mRil.writeMetricsSrvcc(state);
+
+        mRil.mSrvccStateRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void hardwareConfigChanged(
+            int indicationType,
+            ArrayList<android.hardware.radio.V1_0.HardwareConfig> configs) {
+        mRil.processIndication(indicationType);
+
+        ArrayList<HardwareConfig> response = RIL.convertHalHwConfigList(configs, mRil);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_HARDWARE_CONFIG_CHANGED, response);
+
+        mRil.mHardwareConfigChangeRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void radioCapabilityIndication(int indicationType,
+                                          android.hardware.radio.V1_0.RadioCapability rc) {
+        mRil.processIndication(indicationType);
+
+        RadioCapability response = RIL.convertHalRadioCapability(rc, mRil);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_RADIO_CAPABILITY, response);
+
+        mRil.mPhoneRadioCapabilityChangedRegistrants.notifyRegistrants(
+                new AsyncResult (null, response, null));
+    }
+
+    public void onSupplementaryServiceIndication(int indicationType, StkCcUnsolSsResult ss) {
+        mRil.processIndication(indicationType);
+
+        int num;
+        SsData ssData = new SsData();
+
+        ssData.serviceType = ssData.ServiceTypeFromRILInt(ss.serviceType);
+        ssData.requestType = ssData.RequestTypeFromRILInt(ss.requestType);
+        ssData.teleserviceType = ssData.TeleserviceTypeFromRILInt(ss.teleserviceType);
+        ssData.serviceClass = ss.serviceClass; // This is service class sent in the SS request.
+        ssData.result = ss.result; // This is the result of the SS request.
+
+        if (ssData.serviceType.isTypeCF() &&
+                ssData.requestType.isTypeInterrogation()) {
+            CfData cfData = ss.cfData.get(0);
+            num = cfData.cfInfo.size();
+            ssData.cfInfo = new CallForwardInfo[num];
+
+            for (int i = 0; i < num; i++) {
+                android.hardware.radio.V1_0.CallForwardInfo cfInfo = cfData.cfInfo.get(i);
+                ssData.cfInfo[i] = new CallForwardInfo();
+
+                ssData.cfInfo[i].status = cfInfo.status;
+                ssData.cfInfo[i].reason = cfInfo.reason;
+                ssData.cfInfo[i].serviceClass = cfInfo.serviceClass;
+                ssData.cfInfo[i].toa = cfInfo.toa;
+                ssData.cfInfo[i].number = cfInfo.number;
+                ssData.cfInfo[i].timeSeconds = cfInfo.timeSeconds;
+
+                mRil.riljLog("[SS Data] CF Info " + i + " : " +  ssData.cfInfo[i]);
+            }
+        } else {
+            SsInfoData ssInfo = ss.ssInfo.get(0);
+            num = ssInfo.ssInfo.size();
+            ssData.ssInfo = new int[num];
+            for (int i = 0; i < num; i++) {
+                ssData.ssInfo[i] = ssInfo.ssInfo.get(i);
+                mRil.riljLog("[SS Data] SS Info " + i + " : " +  ssData.ssInfo[i]);
+            }
+        }
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_ON_SS, ssData);
+
+        if (mRil.mSsRegistrant != null) {
+            mRil.mSsRegistrant.notifyRegistrant(new AsyncResult(null, ssData, null));
+        }
+    }
+
+    public void stkCallControlAlphaNotify(int indicationType, String alpha) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_STK_CC_ALPHA_NOTIFY, alpha);
+
+        if (mRil.mCatCcAlphaRegistrant != null) {
+            mRil.mCatCcAlphaRegistrant.notifyRegistrant(new AsyncResult (null, alpha, null));
+        }
+    }
+
+    public void lceData(int indicationType, LceDataInfo lce) {
+        mRil.processIndication(indicationType);
+
+        ArrayList<Integer> response = RIL.convertHalLceData(lce, mRil);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_LCEDATA_RECV, response);
+
+        if (mRil.mLceInfoRegistrant != null) {
+            mRil.mLceInfoRegistrant.notifyRegistrant(new AsyncResult(null, response, null));
+        }
+    }
+
+    public void pcoData(int indicationType, PcoDataInfo pco) {
+        mRil.processIndication(indicationType);
+
+        PcoData response = new PcoData(pco.cid,
+                pco.bearerProto,
+                pco.pcoId,
+                RIL.arrayListToPrimitiveArray(pco.contents));
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_PCO_DATA, response);
+
+        mRil.mPcoDataRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
+    }
+
+    public void modemReset(int indicationType, String reason) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_MODEM_RESTART, reason);
+
+        mRil.writeMetricsModemRestartEvent(reason);
+    }
+
+    /**
+     * Indicates when the carrier info to encrypt IMSI is being requested
+     * @param indicationType RadioIndicationType
+     */
+    public void carrierInfoForImsiEncryption(int indicationType) {
+        mRil.processIndication(indicationType);
+
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_CARRIER_INFO_IMSI_ENCRYPTION, null);
+
+        mRil.mCarrierInfoForImsiEncryptionRegistrants.notifyRegistrants(
+                new AsyncResult(null, null, null));
+    }
+
+    /**
+     * Indicates a change in the status of an ongoing Keepalive session
+     * @param indicationType RadioIndicationType
+     * @param keepaliveStatus Status of the ongoing Keepalive session
+     */
+    public void keepaliveStatus(int indicationType, KeepaliveStatus keepaliveStatus) {
+        throw new UnsupportedOperationException("keepaliveStatus Indications are not implemented");
+    }
+
+    private CommandsInterface.RadioState getRadioStateFromInt(int stateInt) {
+        CommandsInterface.RadioState state;
+
+        switch(stateInt) {
+            case android.hardware.radio.V1_0.RadioState.OFF:
+                state = CommandsInterface.RadioState.RADIO_OFF;
+                break;
+            case android.hardware.radio.V1_0.RadioState.UNAVAILABLE:
+                state = CommandsInterface.RadioState.RADIO_UNAVAILABLE;
+                break;
+            case android.hardware.radio.V1_0.RadioState.ON:
+                state = CommandsInterface.RadioState.RADIO_ON;
+                break;
+            default:
+                throw new RuntimeException("Unrecognized RadioState: " + stateInt);
+        }
+        return state;
+    }
+
+    private void responseCellInfos(int indicationType,
+                                   android.hardware.radio.V1_1.NetworkScanResult result) {
+        mRil.processIndication(indicationType);
+
+        NetworkScanResult nsr = null;
+        ArrayList<CellInfo> infos = RIL.convertHalCellInfoList(result.networkInfos);
+        nsr = new NetworkScanResult(result.status, result.error, infos);
+        if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_NETWORK_SCAN_RESULT, nsr);
+        mRil.mRilNetworkScanResultRegistrants.notifyRegistrants(new AsyncResult(null, nsr, null));
+    }
+}
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
new file mode 100644
index 0000000..caf4477
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -0,0 +1,1814 @@
+/*
+ * Copyright (C) 2016 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.Context;
+import android.hardware.radio.V1_0.ActivityStatsInfo;
+import android.hardware.radio.V1_0.AppStatus;
+import android.hardware.radio.V1_0.CardStatus;
+import android.hardware.radio.V1_0.CarrierRestrictions;
+import android.hardware.radio.V1_0.CdmaBroadcastSmsConfigInfo;
+import android.hardware.radio.V1_0.DataRegStateResult;
+import android.hardware.radio.V1_0.GsmBroadcastSmsConfigInfo;
+import android.hardware.radio.V1_0.LastCallFailCauseInfo;
+import android.hardware.radio.V1_0.LceDataInfo;
+import android.hardware.radio.V1_0.LceStatusInfo;
+import android.hardware.radio.V1_0.NeighboringCell;
+import android.hardware.radio.V1_0.RadioError;
+import android.hardware.radio.V1_0.RadioResponseInfo;
+import android.hardware.radio.V1_0.SendSmsResult;
+import android.hardware.radio.V1_0.SetupDataCallResult;
+import android.hardware.radio.V1_0.VoiceRegStateResult;
+import android.hardware.radio.V1_1.IRadioResponse;
+import android.hardware.radio.V1_1.KeepaliveStatus;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.os.SystemClock;
+import android.service.carrier.CarrierIdentifier;
+import android.telephony.CellInfo;
+import android.telephony.ModemActivityInfo;
+import android.telephony.NeighboringCellInfo;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SignalStrength;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus;
+import com.android.internal.telephony.uicc.IccCardStatus;
+import com.android.internal.telephony.uicc.IccIoResult;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class RadioResponse extends IRadioResponse.Stub {
+    // The number of the required config values for broadcast SMS stored in the C struct
+    // RIL_CDMA_BroadcastServiceInfo
+    private static final int CDMA_BSI_NO_OF_INTS_STRUCT = 3;
+
+    private static final int CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES = 31;
+
+    RIL mRil;
+
+    public RadioResponse(RIL ril) {
+        mRil = ril;
+    }
+
+    /**
+     * Helper function to send response msg
+     * @param msg Response message to be sent
+     * @param ret Return object to be included in the response message
+     */
+    static void sendMessageResponse(Message msg, Object ret) {
+        if (msg != null) {
+            AsyncResult.forMessage(msg, ret, null);
+            msg.sendToTarget();
+        }
+    }
+
+    /**
+     * Acknowledge the receipt of radio request sent to the vendor. This must be sent only for
+     * radio request which take long time to respond.
+     * For more details, refer https://source.android.com/devices/tech/connect/ril.html
+     *
+     * @param serial Serial no. of the request whose acknowledgement is sent.
+     */
+    public void acknowledgeRequest(int serial) {
+        mRil.processRequestAck(serial);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param cardStatus ICC card status as defined by CardStatus in types.hal
+     */
+    public void getIccCardStatusResponse(RadioResponseInfo responseInfo, CardStatus cardStatus) {
+        responseIccCardStatus(responseInfo, cardStatus);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void supplyIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
+        responseInts(responseInfo, remainingAttempts);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void supplyIccPukForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
+        responseInts(responseInfo, remainingAttempts);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void supplyIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
+        responseInts(responseInfo, remainingAttempts);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void supplyIccPuk2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
+        responseInts(responseInfo, remainingAttempts);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void changeIccPinForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
+        responseInts(responseInfo, remainingAttempts);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param remainingAttempts Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void changeIccPin2ForAppResponse(RadioResponseInfo responseInfo, int remainingAttempts) {
+        responseInts(responseInfo, remainingAttempts);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param retriesRemaining Number of retries remaining, must be equal to -1 if unknown.
+     */
+    public void supplyNetworkDepersonalizationResponse(RadioResponseInfo responseInfo,
+                                                       int retriesRemaining) {
+        responseInts(responseInfo, retriesRemaining);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param calls Current call list
+     */
+    public void getCurrentCallsResponse(RadioResponseInfo responseInfo,
+                                        ArrayList<android.hardware.radio.V1_0.Call> calls) {
+        responseCurrentCalls(responseInfo, calls);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void dialResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param imsi String containing the IMSI
+     */
+    public void getIMSIForAppResponse(RadioResponseInfo responseInfo, String imsi) {
+        responseString(responseInfo, imsi);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void hangupConnectionResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void hangupWaitingOrBackgroundResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void hangupForegroundResumeBackgroundResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void switchWaitingOrHoldingAndActiveResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void conferenceResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void rejectCallResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param fcInfo Contains LastCallFailCause and vendor cause code. GSM failure reasons
+     *        are mapped to cause codes defined in TS 24.008 Annex H where possible. CDMA
+     *        failure reasons are derived from the possible call failure scenarios
+     *        described in the "CDMA IS-2000 Release A (C.S0005-A v6.0)" standard.
+     */
+    public void getLastCallFailCauseResponse(RadioResponseInfo responseInfo,
+                                             LastCallFailCauseInfo fcInfo) {
+        responseLastCallFailCauseInfo(responseInfo, fcInfo);
+    }
+
+    public void getSignalStrengthResponse(RadioResponseInfo responseInfo,
+                                          android.hardware.radio.V1_0.SignalStrength sigStrength) {
+        responseSignalStrength(responseInfo, sigStrength);
+    }
+
+    /*
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param voiceRegResponse Current Voice registration response as defined by VoiceRegStateResult
+     *        in types.hal
+     */
+    public void getVoiceRegistrationStateResponse(RadioResponseInfo responseInfo,
+                                                  VoiceRegStateResult voiceRegResponse) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, voiceRegResponse);
+            }
+            mRil.processResponseDone(rr, responseInfo, voiceRegResponse);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param dataRegResponse Current Data registration response as defined by DataRegStateResult in
+     *        types.hal
+     */
+    public void getDataRegistrationStateResponse(RadioResponseInfo responseInfo,
+                                                 DataRegStateResult dataRegResponse) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, dataRegResponse);
+            }
+            mRil.processResponseDone(rr, responseInfo, dataRegResponse);
+        }
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param longName is long alpha ONS or EONS or empty string if unregistered
+     * @param shortName is short alpha ONS or EONS or empty string if unregistered
+     * @param numeric is 5 or 6 digit numeric code (MCC + MNC) or empty string if unregistered
+     */
+    public void getOperatorResponse(RadioResponseInfo responseInfo,
+                                    String longName,
+                                    String shortName,
+                                    String numeric) {
+        responseStrings(responseInfo, longName, shortName, numeric);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setRadioPowerResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void sendDtmfResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     */
+    public void sendSmsResponse(RadioResponseInfo responseInfo,
+                                SendSmsResult sms) {
+        responseSms(responseInfo, sms);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     */
+    public void sendSMSExpectMoreResponse(RadioResponseInfo responseInfo,
+                                          SendSmsResult sms) {
+        responseSms(responseInfo, sms);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param setupDataCallResult Response to data call setup as defined by setupDataCallResult in
+     *                            types.hal
+     */
+    public void setupDataCallResponse(RadioResponseInfo responseInfo,
+                                      SetupDataCallResult setupDataCallResult) {
+        responseSetupDataCall(responseInfo, setupDataCallResult);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param iccIo ICC io operation response as defined by IccIoResult in types.hal
+     */
+    public void iccIOForAppResponse(RadioResponseInfo responseInfo,
+                            android.hardware.radio.V1_0.IccIoResult iccIo) {
+        responseIccIo(responseInfo, iccIo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void sendUssdResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void cancelPendingUssdResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param n is "n" parameter from TS 27.007 7.7
+     * @param m is "m" parameter from TS 27.007 7.7
+     */
+    public void getClirResponse(RadioResponseInfo responseInfo, int n, int m) {
+        responseInts(responseInfo, n, m);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setClirResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param callForwardInfos points to a vector of CallForwardInfo, one for
+     *        each distinct registered phone number.
+     */
+    public void getCallForwardStatusResponse(RadioResponseInfo responseInfo,
+                                             ArrayList<android.hardware.radio.V1_0.CallForwardInfo>
+                                                     callForwardInfos) {
+        responseCallForwardInfo(responseInfo, callForwardInfos);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCallForwardResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param enable If current call waiting state is disabled, enable = false else true
+     * @param serviceClass If enable, then callWaitingResp[1]
+     *        must follow, with the TS 27.007 service class bit vector of services
+     *        for which call waiting is enabled.
+     *        For example, if callWaitingResp[0] is 1 and
+     *        callWaitingResp[1] is 3, then call waiting is enabled for data
+     *        and voice and disabled for everything else.
+     */
+    public void getCallWaitingResponse(RadioResponseInfo responseInfo,
+                                   boolean enable,
+                                   int serviceClass) {
+        responseInts(responseInfo, enable ? 1 : 0, serviceClass);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCallWaitingResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void acknowledgeLastIncomingGsmSmsResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void acceptCallResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void deactivateDataCallResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param response 0 is the TS 27.007 service class bit vector of
+     *        services for which the specified barring facility
+     *        is active. "0" means "disabled for all"
+     */
+    public void getFacilityLockForAppResponse(RadioResponseInfo responseInfo, int response) {
+        responseInts(responseInfo, response);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param retry 0 is the number of retries remaining, or -1 if unknown
+     */
+    public void setFacilityLockForAppResponse(RadioResponseInfo responseInfo, int retry) {
+        responseInts(responseInfo, retry);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setBarringPasswordResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param selection false for automatic selection, true for manual selection
+     */
+    public void getNetworkSelectionModeResponse(RadioResponseInfo responseInfo, boolean selection) {
+        responseInts(responseInfo, selection ? 1 : 0);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setNetworkSelectionModeAutomaticResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setNetworkSelectionModeManualResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param networkInfos List of network operator information as OperatorInfos defined in
+     *                     types.hal
+     */
+    public void getAvailableNetworksResponse(RadioResponseInfo responseInfo,
+                                             ArrayList<android.hardware.radio.V1_0.OperatorInfo>
+                                                     networkInfos) {
+        responseOperatorInfos(responseInfo, networkInfos);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void startNetworkScanResponse(RadioResponseInfo responseInfo) {
+        responseScanStatus(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void stopNetworkScanResponse(RadioResponseInfo responseInfo) {
+        responseScanStatus(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void startDtmfResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void stopDtmfResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param version string containing version string for log reporting
+     */
+    public void getBasebandVersionResponse(RadioResponseInfo responseInfo, String version) {
+        responseString(responseInfo, version);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void separateConnectionResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setMuteResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param enable true for "mute enabled" and false for "mute disabled"
+     */
+    public void getMuteResponse(RadioResponseInfo responseInfo, boolean enable) {
+        responseInts(responseInfo, enable ? 1 : 0);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param status indicates CLIP status
+     */
+    public void getClipResponse(RadioResponseInfo responseInfo, int status) {
+        responseInts(responseInfo, status);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param dataCallResultList Response to get data call list as defined by setupDataCallResult in
+     *                           types.hal
+     */
+    public void getDataCallListResponse(RadioResponseInfo responseInfo,
+                                        ArrayList<SetupDataCallResult> dataCallResultList) {
+        responseDataCallList(responseInfo, dataCallResultList);
+    }
+
+    public void sendOemRilRequestRawResponse(RadioResponseInfo responseInfo,
+                                             ArrayList<Byte> var2) {}
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSuppServiceNotificationsResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param index record index where the message is stored
+     */
+    public void writeSmsToSimResponse(RadioResponseInfo responseInfo, int index) {
+        responseInts(responseInfo, index);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void deleteSmsOnSimResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setBandModeResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param bandModes List of RadioBandMode listing supported modes
+     */
+    public void getAvailableBandModesResponse(RadioResponseInfo responseInfo,
+                                              ArrayList<Integer> bandModes) {
+        responseIntArrayList(responseInfo, bandModes);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param commandResponse SAT/USAT response in hexadecimal format
+     *        string starting with first byte of response
+     */
+    public void sendEnvelopeResponse(RadioResponseInfo responseInfo, String commandResponse) {
+        responseString(responseInfo, commandResponse);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void sendTerminalResponseToSimResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void handleStkCallSetupRequestFromSimResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void explicitCallTransferResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setPreferredNetworkTypeResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param nwType RadioPreferredNetworkType defined in types.hal
+     */
+    public void getPreferredNetworkTypeResponse(RadioResponseInfo responseInfo, int nwType) {
+        mRil.mPreferredNetworkType = nwType;
+        responseInts(responseInfo, nwType);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param cells Vector of neighboring radio cell information
+     */
+    public void getNeighboringCidsResponse(RadioResponseInfo responseInfo,
+                                           ArrayList<NeighboringCell> cells) {
+        responseCellList(responseInfo, cells);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setLocationUpdatesResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param type CdmaRoamingType defined in types.hal
+     */
+    public void getCdmaRoamingPreferenceResponse(RadioResponseInfo responseInfo, int type) {
+        responseInts(responseInfo, type);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setTTYModeResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param mode TTY mode
+     */
+    public void getTTYModeResponse(RadioResponseInfo responseInfo, int mode) {
+        responseInts(responseInfo, mode);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param enable false for Standard Privacy Mode (Public Long Code Mask)
+     *        true for Enhanced Privacy Mode (Private Long Code Mask)
+     */
+    public void getPreferredVoicePrivacyResponse(RadioResponseInfo responseInfo,
+                                                 boolean enable) {
+        responseInts(responseInfo, enable ? 1 : 0);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void sendCDMAFeatureCodeResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void sendBurstDtmfResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param sms Sms result struct as defined by SendSmsResult in types.hal
+     */
+    public void sendCdmaSmsResponse(RadioResponseInfo responseInfo, SendSmsResult sms) {
+        responseSms(responseInfo, sms);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void acknowledgeLastIncomingCdmaSmsResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param configs Vector of GSM/WCDMA Cell broadcast configs
+     */
+    public void getGsmBroadcastConfigResponse(RadioResponseInfo responseInfo,
+                                              ArrayList<GsmBroadcastSmsConfigInfo> configs) {
+        responseGmsBroadcastConfig(responseInfo, configs);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setGsmBroadcastConfigResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setGsmBroadcastActivationResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param configs Vector of CDMA Broadcast SMS configs.
+     */
+    public void getCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo,
+                                               ArrayList<CdmaBroadcastSmsConfigInfo> configs) {
+        responseCdmaBroadcastConfig(responseInfo, configs);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCdmaBroadcastConfigResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCdmaBroadcastActivationResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param mdn MDN if CDMA subscription is available
+     * @param hSid is a comma separated list of H_SID (Home SID) if
+     *        CDMA subscription is available, in decimal format
+     * @param hNid is a comma separated list of H_NID (Home NID) if
+     *        CDMA subscription is available, in decimal format
+     * @param min MIN (10 digits, MIN2+MIN1) if CDMA subscription is available
+     * @param prl PRL version if CDMA subscription is available
+     */
+    public void getCDMASubscriptionResponse(RadioResponseInfo responseInfo, String mdn,
+                                            String hSid, String hNid, String min, String prl) {
+        responseStrings(responseInfo, mdn, hSid, hNid, min, prl);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param index record index where the cmda sms message is stored
+     */
+    public void writeSmsToRuimResponse(RadioResponseInfo responseInfo, int index) {
+        responseInts(responseInfo, index);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void deleteSmsOnRuimResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param imei IMEI if GSM subscription is available
+     * @param imeisv IMEISV if GSM subscription is available
+     * @param esn ESN if CDMA subscription is available
+     * @param meid MEID if CDMA subscription is available
+     */
+    public void getDeviceIdentityResponse(RadioResponseInfo responseInfo, String imei,
+                                          String imeisv, String esn, String meid) {
+        responseStrings(responseInfo, imei, imeisv, esn, meid);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void exitEmergencyCallbackModeResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param smsc Short Message Service Center address on the device
+     */
+    public void getSmscAddressResponse(RadioResponseInfo responseInfo, String smsc) {
+        responseString(responseInfo, smsc);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSmscAddressResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void reportSmsMemoryStatusResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void reportStkServiceIsRunningResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param source CDMA subscription source
+     */
+    public void getCdmaSubscriptionSourceResponse(RadioResponseInfo responseInfo, int source) {
+        responseInts(responseInfo, source);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param response response string of the challenge/response algo for ISIM auth in base64 format
+     */
+    public void requestIsimAuthenticationResponse(RadioResponseInfo responseInfo, String response) {
+        responseString(responseInfo, response);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void acknowledgeIncomingGsmSmsWithPduResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param iccIo IccIoResult as defined in types.hal corresponding to ICC IO response
+     */
+    public void sendEnvelopeWithStatusResponse(RadioResponseInfo responseInfo,
+                                               android.hardware.radio.V1_0.IccIoResult iccIo) {
+        responseIccIo(responseInfo, iccIo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param rat Current voice RAT
+     */
+    public void getVoiceRadioTechnologyResponse(RadioResponseInfo responseInfo, int rat) {
+        responseInts(responseInfo, rat);
+    }
+
+    public void getCellInfoListResponse(RadioResponseInfo responseInfo,
+                                        ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
+        responseCellInfoList(responseInfo, cellInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCellInfoListRateResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setInitialAttachApnResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param isRegistered false = not registered, true = registered
+     * @param ratFamily RadioTechnologyFamily as defined in types.hal. This value is valid only if
+     *        isRegistered is true.
+     */
+    public void getImsRegistrationStateResponse(RadioResponseInfo responseInfo,
+                                                boolean isRegistered, int ratFamily) {
+        responseInts(responseInfo, isRegistered ? 1 : 0, ratFamily);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     */
+    public void sendImsSmsResponse(RadioResponseInfo responseInfo, SendSmsResult sms) {
+        responseSms(responseInfo, sms);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param result IccIoResult as defined in types.hal
+     */
+    public void iccTransmitApduBasicChannelResponse(RadioResponseInfo responseInfo,
+                                                    android.hardware.radio.V1_0.IccIoResult
+                                                            result) {
+        responseIccIo(responseInfo, result);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param channelId session id of the logical channel.
+     * @param selectResponse Contains the select response for the open channel command with one
+     *        byte per integer
+     */
+    public void iccOpenLogicalChannelResponse(RadioResponseInfo responseInfo, int channelId,
+                                              ArrayList<Byte> selectResponse) {
+        ArrayList<Integer> arr = new ArrayList<>();
+        arr.add(channelId);
+        for (int i = 0; i < selectResponse.size(); i++) {
+            arr.add((int) selectResponse.get(i));
+        }
+        responseIntArrayList(responseInfo, arr);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void iccCloseLogicalChannelResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param result IccIoResult as defined in types.hal
+     */
+    public void iccTransmitApduLogicalChannelResponse(
+            RadioResponseInfo responseInfo,
+            android.hardware.radio.V1_0.IccIoResult result) {
+        responseIccIo(responseInfo, result);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param result string containing the contents of the NV item
+     */
+    public void nvReadItemResponse(RadioResponseInfo responseInfo, String result) {
+        responseString(responseInfo, result);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void nvWriteItemResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void nvWriteCdmaPrlResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void nvResetConfigResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setUiccSubscriptionResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setDataAllowedResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    public void getHardwareConfigResponse(
+            RadioResponseInfo responseInfo,
+            ArrayList<android.hardware.radio.V1_0.HardwareConfig> config) {
+        responseHardwareConfig(responseInfo, config);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param result IccIoResult as defined in types.hal
+     */
+    public void requestIccSimAuthenticationResponse(RadioResponseInfo responseInfo,
+                                                    android.hardware.radio.V1_0.IccIoResult
+                                                            result) {
+        responseICC_IOBase64(responseInfo, result);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setDataProfileResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void requestShutdownResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    public void getRadioCapabilityResponse(RadioResponseInfo responseInfo,
+                                           android.hardware.radio.V1_0.RadioCapability rc) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            RadioCapability ret = RIL.convertHalRadioCapability(rc, mRil);
+            if (responseInfo.error == RadioError.REQUEST_NOT_SUPPORTED
+                    || responseInfo.error == RadioError.GENERIC_FAILURE) {
+                // we should construct the RAF bitmask the radio
+                // supports based on preferred network bitmasks
+                ret = mRil.makeStaticRadioCapability();
+                responseInfo.error = RadioError.NONE;
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    public void setRadioCapabilityResponse(RadioResponseInfo responseInfo,
+                                           android.hardware.radio.V1_0.RadioCapability rc) {
+        responseRadioCapability(responseInfo, rc);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param statusInfo LceStatusInfo indicating LCE status
+     */
+    public void startLceServiceResponse(RadioResponseInfo responseInfo, LceStatusInfo statusInfo) {
+        responseLceStatus(responseInfo, statusInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param statusInfo LceStatusInfo indicating LCE status
+     */
+    public void stopLceServiceResponse(RadioResponseInfo responseInfo, LceStatusInfo statusInfo) {
+        responseLceStatus(responseInfo, statusInfo);
+    }
+
+    public void pullLceDataResponse(RadioResponseInfo responseInfo, LceDataInfo lceInfo) {
+        responseLceData(responseInfo, lceInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param activityInfo modem activity information
+     */
+    public void getModemActivityInfoResponse(RadioResponseInfo responseInfo,
+                                             ActivityStatsInfo activityInfo) {
+        responseActivityData(responseInfo, activityInfo);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param numAllowed number of allowed carriers which have been set correctly.
+     *        On success, it must match the length of list Carriers->allowedCarriers.
+     *        if Length of allowed carriers list is 0, numAllowed = 0.
+     */
+    public void setAllowedCarriersResponse(RadioResponseInfo responseInfo, int numAllowed) {
+        responseInts(responseInfo, numAllowed);
+    }
+
+    /**
+     *
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param allAllowed true only when all carriers are allowed. Ignore "carriers" struct.
+     *                   If false, consider "carriers" struct
+     * @param carriers Carrier restriction information.
+     */
+    public void getAllowedCarriersResponse(RadioResponseInfo responseInfo, boolean allAllowed,
+                                           CarrierRestrictions carriers) {
+        responseCarrierIdentifiers(responseInfo, allAllowed, carriers);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void sendDeviceStateResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setCarrierInfoForImsiEncryptionResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setIndicationFilterResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSimCardPowerResponse(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void setSimCardPowerResponse_1_1(RadioResponseInfo responseInfo) {
+        responseVoid(responseInfo);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param keepaliveStatus status of the keepalive with a handle for the session
+     */
+    public void startKeepaliveResponse(RadioResponseInfo responseInfo,
+            KeepaliveStatus keepaliveStatus) {
+        throw new UnsupportedOperationException("startKeepaliveResponse not implemented");
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
+     */
+    public void stopKeepaliveResponse(RadioResponseInfo responseInfo) {
+        throw new UnsupportedOperationException("stopKeepaliveResponse not implemented");
+    }
+
+    private void responseIccCardStatus(RadioResponseInfo responseInfo, CardStatus cardStatus) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            IccCardStatus iccCardStatus = new IccCardStatus();
+            iccCardStatus.setCardState(cardStatus.cardState);
+            iccCardStatus.setUniversalPinState(cardStatus.universalPinState);
+            iccCardStatus.mGsmUmtsSubscriptionAppIndex = cardStatus.gsmUmtsSubscriptionAppIndex;
+            iccCardStatus.mCdmaSubscriptionAppIndex = cardStatus.cdmaSubscriptionAppIndex;
+            iccCardStatus.mImsSubscriptionAppIndex = cardStatus.imsSubscriptionAppIndex;
+            int numApplications = cardStatus.applications.size();
+
+            // limit to maximum allowed applications
+            if (numApplications
+                    > com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS) {
+                numApplications =
+                        com.android.internal.telephony.uicc.IccCardStatus.CARD_MAX_APPS;
+            }
+            iccCardStatus.mApplications = new IccCardApplicationStatus[numApplications];
+            for (int i = 0; i < numApplications; i++) {
+                AppStatus rilAppStatus = cardStatus.applications.get(i);
+                IccCardApplicationStatus appStatus = new IccCardApplicationStatus();
+                appStatus.app_type       = appStatus.AppTypeFromRILInt(rilAppStatus.appType);
+                appStatus.app_state      = appStatus.AppStateFromRILInt(rilAppStatus.appState);
+                appStatus.perso_substate = appStatus.PersoSubstateFromRILInt(
+                        rilAppStatus.persoSubstate);
+                appStatus.aid            = rilAppStatus.aidPtr;
+                appStatus.app_label      = rilAppStatus.appLabelPtr;
+                appStatus.pin1_replaced  = rilAppStatus.pin1Replaced;
+                appStatus.pin1           = appStatus.PinStateFromRILInt(rilAppStatus.pin1);
+                appStatus.pin2           = appStatus.PinStateFromRILInt(rilAppStatus.pin2);
+                iccCardStatus.mApplications[i] = appStatus;
+            }
+            mRil.riljLog("responseIccCardStatus: from HIDL: " + iccCardStatus);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, iccCardStatus);
+            }
+            mRil.processResponseDone(rr, responseInfo, iccCardStatus);
+        }
+    }
+
+    private void responseInts(RadioResponseInfo responseInfo, int ...var) {
+        final ArrayList<Integer> ints = new ArrayList<>();
+        for (int i = 0; i < var.length; i++) {
+            ints.add(var[i]);
+        }
+        responseIntArrayList(responseInfo, ints);
+    }
+
+    private void responseIntArrayList(RadioResponseInfo responseInfo, ArrayList<Integer> var) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            int[] ret = new int[var.size()];
+            for (int i = 0; i < var.size(); i++) {
+                ret[i] = var.get(i);
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseCurrentCalls(RadioResponseInfo responseInfo,
+                                      ArrayList<android.hardware.radio.V1_0.Call> calls) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            int num = calls.size();
+            ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
+            DriverCall dc;
+
+            for (int i = 0; i < num; i++) {
+                dc = new DriverCall();
+                // TODO: change name of function stateFromCLCC() in DriverCall.java to name
+                // clarifying what is CLCC
+                dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).state));
+                dc.index = calls.get(i).index;
+                dc.TOA = calls.get(i).toa;
+                dc.isMpty = calls.get(i).isMpty;
+                dc.isMT = calls.get(i).isMT;
+                dc.als = calls.get(i).als;
+                dc.isVoice = calls.get(i).isVoice;
+                dc.isVoicePrivacy = calls.get(i).isVoicePrivacy;
+                dc.number = calls.get(i).number;
+                dc.numberPresentation =
+                        DriverCall.presentationFromCLIP(
+                                (int) (calls.get(i).numberPresentation));
+                dc.name = calls.get(i).name;
+                dc.namePresentation =
+                        DriverCall.presentationFromCLIP((int) (calls.get(i).namePresentation));
+                if (calls.get(i).uusInfo.size() == 1) {
+                    dc.uusInfo = new UUSInfo();
+                    dc.uusInfo.setType(calls.get(i).uusInfo.get(0).uusType);
+                    dc.uusInfo.setDcs(calls.get(i).uusInfo.get(0).uusDcs);
+                    if (!TextUtils.isEmpty(calls.get(i).uusInfo.get(0).uusData)) {
+                        byte[] userData = calls.get(i).uusInfo.get(0).uusData.getBytes();
+                        dc.uusInfo.setUserData(userData);
+                    } else {
+                        mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
+                    }
+
+                    mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+                            dc.uusInfo.getType(), dc.uusInfo.getDcs(),
+                            dc.uusInfo.getUserData().length));
+                    mRil.riljLogv("Incoming UUS : data (hex): "
+                            + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+                } else {
+                    mRil.riljLogv("Incoming UUS : NOT present!");
+                }
+
+                // Make sure there's a leading + on addresses with a TOA of 145
+                dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+
+                dcCalls.add(dc);
+
+                if (dc.isVoicePrivacy) {
+                    mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
+                    mRil.riljLog("InCall VoicePrivacy is enabled");
+                } else {
+                    mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
+                    mRil.riljLog("InCall VoicePrivacy is disabled");
+                }
+            }
+
+            Collections.sort(dcCalls);
+
+            if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
+                if (mRil.mEmergencyCallbackModeRegistrant != null) {
+                    mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
+                            + " notify ECM Registrants");
+                    mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
+                }
+            }
+
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, dcCalls);
+            }
+            mRil.processResponseDone(rr, responseInfo, dcCalls);
+        }
+    }
+
+    private void responseVoid(RadioResponseInfo responseInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            Object ret = null;
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseString(RadioResponseInfo responseInfo, String str) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, str);
+            }
+            mRil.processResponseDone(rr, responseInfo, str);
+        }
+    }
+
+    private void responseStrings(RadioResponseInfo responseInfo, String ...str) {
+        ArrayList<String> strings = new ArrayList<>();
+        for (int i = 0; i < str.length; i++) {
+            strings.add(str[i]);
+        }
+        responseStringArrayList(mRil, responseInfo, strings);
+    }
+
+    static void responseStringArrayList(RIL ril, RadioResponseInfo responseInfo,
+                                        ArrayList<String> strings) {
+        RILRequest rr = ril.processResponse(responseInfo);
+
+        if (rr != null) {
+            String[] ret = new String[strings.size()];
+            for (int i = 0; i < strings.size(); i++) {
+                ret[i] = strings.get(i);
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            ril.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseLastCallFailCauseInfo(RadioResponseInfo responseInfo,
+                                               LastCallFailCauseInfo fcInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            LastCallFailCause ret = new LastCallFailCause();
+            ret.causeCode = fcInfo.causeCode;
+            ret.vendorCause = fcInfo.vendorCause;
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseSignalStrength(RadioResponseInfo responseInfo,
+                                        android.hardware.radio.V1_0.SignalStrength sigStrength) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            SignalStrength ret = RIL.convertHalSignalStrength(sigStrength);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseSms(RadioResponseInfo responseInfo, SendSmsResult sms) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            SmsResponse ret = new SmsResponse(sms.messageRef, sms.ackPDU, sms.errorCode);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseSetupDataCall(RadioResponseInfo responseInfo,
+                                       SetupDataCallResult setupDataCallResult) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            DataCallResponse ret = RIL.convertDataCallResult(setupDataCallResult);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseIccIo(RadioResponseInfo responseInfo,
+                               android.hardware.radio.V1_0.IccIoResult result) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            IccIoResult ret = new IccIoResult(result.sw1, result.sw2, result.simResponse);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseCallForwardInfo(RadioResponseInfo responseInfo,
+                                         ArrayList<android.hardware.radio.V1_0.CallForwardInfo>
+                                                 callForwardInfos) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+        if (rr != null) {
+            CallForwardInfo[] ret = new CallForwardInfo[callForwardInfos.size()];
+            for (int i = 0; i < callForwardInfos.size(); i++) {
+                ret[i] = new CallForwardInfo();
+                ret[i].status = callForwardInfos.get(i).status;
+                ret[i].reason = callForwardInfos.get(i).reason;
+                ret[i].serviceClass = callForwardInfos.get(i).serviceClass;
+                ret[i].toa = callForwardInfos.get(i).toa;
+                ret[i].number = callForwardInfos.get(i).number;
+                ret[i].timeSeconds = callForwardInfos.get(i).timeSeconds;
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private static String convertOpertatorInfoToString(int status) {
+        if (status == android.hardware.radio.V1_0.OperatorStatus.UNKNOWN) {
+            return "unknown";
+        } else if (status == android.hardware.radio.V1_0.OperatorStatus.AVAILABLE) {
+            return "available";
+        } else if (status == android.hardware.radio.V1_0.OperatorStatus.CURRENT) {
+            return "current";
+        } else if (status == android.hardware.radio.V1_0.OperatorStatus.FORBIDDEN) {
+            return "forbidden";
+        } else {
+            return "";
+        }
+    }
+
+    private void responseOperatorInfos(RadioResponseInfo responseInfo,
+                                       ArrayList<android.hardware.radio.V1_0.OperatorInfo>
+                                               networkInfos) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<OperatorInfo> ret = new ArrayList<OperatorInfo>();
+            for (int i = 0; i < networkInfos.size(); i++) {
+                ret.add(new OperatorInfo(networkInfos.get(i).alphaLong,
+                        networkInfos.get(i).alphaShort, networkInfos.get(i).operatorNumeric,
+                        convertOpertatorInfoToString(networkInfos.get(i).status)));
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseScanStatus(RadioResponseInfo responseInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            NetworkScanResult nsr = null;
+            if (responseInfo.error == RadioError.NONE) {
+                nsr = new NetworkScanResult(
+                        NetworkScanResult.SCAN_STATUS_PARTIAL, RadioError.NONE, null);
+                sendMessageResponse(rr.mResult, nsr);
+            }
+            mRil.processResponseDone(rr, responseInfo, nsr);
+        }
+    }
+
+    private void responseDataCallList(RadioResponseInfo responseInfo,
+                                      ArrayList<SetupDataCallResult> dataCallResultList) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<DataCallResponse> dcResponseList = new ArrayList<>();
+            for (SetupDataCallResult dcResult : dataCallResultList) {
+                dcResponseList.add(RIL.convertDataCallResult(dcResult));
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, dcResponseList);
+            }
+            mRil.processResponseDone(rr, responseInfo, dcResponseList);
+        }
+    }
+
+    private void responseCellList(RadioResponseInfo responseInfo,
+                                  ArrayList<NeighboringCell> cells) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            int rssi;
+            String location;
+            ArrayList<NeighboringCellInfo> ret = new ArrayList<NeighboringCellInfo>();
+            NeighboringCellInfo cell;
+
+            int[] subId = SubscriptionManager.getSubId(mRil.mPhoneId);
+            int radioType =
+                    ((TelephonyManager) mRil.mContext.getSystemService(
+                            Context.TELEPHONY_SERVICE)).getDataNetworkType(subId[0]);
+
+            if (radioType != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
+                for (int i = 0; i < cells.size(); i++) {
+                    rssi = cells.get(i).rssi;
+                    location = cells.get(i).cid;
+                    cell = new NeighboringCellInfo(rssi, location, radioType);
+                    ret.add(cell);
+                }
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseGmsBroadcastConfig(RadioResponseInfo responseInfo,
+                                            ArrayList<GsmBroadcastSmsConfigInfo> configs) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<SmsBroadcastConfigInfo> ret = new ArrayList<SmsBroadcastConfigInfo>();
+            for (int i = 0; i < configs.size(); i++) {
+                ret.add(new SmsBroadcastConfigInfo(configs.get(i).fromServiceId,
+                        configs.get(i).toServiceId, configs.get(i).fromCodeScheme,
+                        configs.get(i).toCodeScheme, configs.get(i).selected));
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseCdmaBroadcastConfig(RadioResponseInfo responseInfo,
+                                            ArrayList<CdmaBroadcastSmsConfigInfo> configs) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            int[] ret = null;
+
+            int numServiceCategories = configs.size();
+
+            if (numServiceCategories == 0) {
+                // TODO: The logic of providing default values should
+                // not be done by this transport layer. And needs to
+                // be done by the vendor ril or application logic.
+                int numInts;
+                numInts = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES
+                        * CDMA_BSI_NO_OF_INTS_STRUCT + 1;
+                ret = new int[numInts];
+
+                // Faking a default record for all possible records.
+                ret[0] = CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES;
+
+                // Loop over CDMA_BROADCAST_SMS_NO_OF_SERVICE_CATEGORIES set 'english' as
+                // default language and selection status to false for all.
+                for (int i = 1; i < numInts; i += CDMA_BSI_NO_OF_INTS_STRUCT) {
+                    ret[i + 0] = i / CDMA_BSI_NO_OF_INTS_STRUCT;
+                    ret[i + 1] = 1;
+                    ret[i + 2] = 0;
+                }
+            } else {
+                int numInts;
+                numInts = (numServiceCategories * CDMA_BSI_NO_OF_INTS_STRUCT) + 1;
+                ret = new int[numInts];
+
+                ret[0] = numServiceCategories;
+                for (int i = 1, j = 0; j < configs.size();
+                        j++, i = i + CDMA_BSI_NO_OF_INTS_STRUCT) {
+                    ret[i] = configs.get(j).serviceCategory;
+                    ret[i + 1] = configs.get(j).language;
+                    ret[i + 2] = configs.get(j).selected ? 1 : 0;
+                }
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseCellInfoList(RadioResponseInfo responseInfo,
+                                      ArrayList<android.hardware.radio.V1_0.CellInfo> cellInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<CellInfo> ret = RIL.convertHalCellInfoList(cellInfo);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseActivityData(RadioResponseInfo responseInfo,
+                                      ActivityStatsInfo activityInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ModemActivityInfo ret = null;
+            if (responseInfo.error == RadioError.NONE) {
+                final int sleepModeTimeMs = activityInfo.sleepModeTimeMs;
+                final int idleModeTimeMs = activityInfo.idleModeTimeMs;
+                int [] txModeTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+                for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+                    txModeTimeMs[i] = activityInfo.txmModetimeMs[i];
+                }
+                final int rxModeTimeMs = activityInfo.rxModeTimeMs;
+                ret = new ModemActivityInfo(SystemClock.elapsedRealtime(), sleepModeTimeMs,
+                        idleModeTimeMs, txModeTimeMs, rxModeTimeMs, 0);
+            } else {
+                ret = new ModemActivityInfo(0, 0, 0, new int [ModemActivityInfo.TX_POWER_LEVELS],
+                        0, 0);
+                responseInfo.error = RadioError.NONE;
+            }
+            sendMessageResponse(rr.mResult, ret);
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseHardwareConfig(
+            RadioResponseInfo responseInfo,
+            ArrayList<android.hardware.radio.V1_0.HardwareConfig> config) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<HardwareConfig> ret = RIL.convertHalHwConfigList(config, mRil);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseICC_IOBase64(RadioResponseInfo responseInfo,
+                                      android.hardware.radio.V1_0.IccIoResult result) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            IccIoResult ret = new IccIoResult(
+                    result.sw1,
+                    result.sw2,
+                    (!(result.simResponse).equals(""))
+                            ? android.util.Base64.decode(result.simResponse,
+                            android.util.Base64.DEFAULT) : (byte[]) null);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseRadioCapability(RadioResponseInfo responseInfo,
+                                         android.hardware.radio.V1_0.RadioCapability rc) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            RadioCapability ret = RIL.convertHalRadioCapability(rc, mRil);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseLceStatus(RadioResponseInfo responseInfo, LceStatusInfo statusInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<Integer> ret = new ArrayList<Integer>();
+            ret.add(statusInfo.lceStatus);
+            ret.add(Byte.toUnsignedInt(statusInfo.actualIntervalMs));
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseLceData(RadioResponseInfo responseInfo, LceDataInfo lceInfo) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            ArrayList<Integer> ret = RIL.convertHalLceData(lceInfo, mRil);
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+
+    private void responseCarrierIdentifiers(RadioResponseInfo responseInfo, boolean allAllowed,
+                                            CarrierRestrictions carriers) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            List<CarrierIdentifier> ret = new ArrayList<CarrierIdentifier>();
+            for (int i = 0; i < carriers.allowedCarriers.size(); i++) {
+                String mcc = carriers.allowedCarriers.get(i).mcc;
+                String mnc = carriers.allowedCarriers.get(i).mnc;
+                String spn = null, imsi = null, gid1 = null, gid2 = null;
+                int matchType = carriers.allowedCarriers.get(i).matchType;
+                String matchData = carriers.allowedCarriers.get(i).matchData;
+                if (matchType == CarrierIdentifier.MatchType.SPN) {
+                    spn = matchData;
+                } else if (matchType == CarrierIdentifier.MatchType.IMSI_PREFIX) {
+                    imsi = matchData;
+                } else if (matchType == CarrierIdentifier.MatchType.GID1) {
+                    gid1 = matchData;
+                } else if (matchType == CarrierIdentifier.MatchType.GID2) {
+                    gid2 = matchData;
+                }
+                ret.add(new CarrierIdentifier(mcc, mnc, spn, imsi, gid1, gid2));
+            }
+            if (responseInfo.error == RadioError.NONE) {
+                /* TODO: Handle excluded carriers */
+                sendMessageResponse(rr.mResult, ret);
+            }
+            mRil.processResponseDone(rr, responseInfo, ret);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/RestrictedState.java b/src/java/com/android/internal/telephony/RestrictedState.java
index 1c377e7..f09af8a 100644
--- a/src/java/com/android/internal/telephony/RestrictedState.java
+++ b/src/java/com/android/internal/telephony/RestrictedState.java
@@ -83,6 +83,10 @@
         return mCsNormalRestricted && mCsEmergencyRestricted;
     }
 
+    public boolean isAnyCsRestricted() {
+        return mCsNormalRestricted || mCsEmergencyRestricted;
+    }
+
     @Override
     public boolean equals (Object o) {
         RestrictedState s;
diff --git a/src/java/com/android/internal/telephony/RetryManager.java b/src/java/com/android/internal/telephony/RetryManager.java
index 684de9a..23c3498 100644
--- a/src/java/com/android/internal/telephony/RetryManager.java
+++ b/src/java/com/android/internal/telephony/RetryManager.java
@@ -27,8 +27,6 @@
 
 import com.android.internal.telephony.dataconnection.ApnSetting;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Random;
 
@@ -112,6 +110,11 @@
     private static final long DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING = 3000;
 
     /**
+     * The default value (in milliseconds) for retrying APN after disconnect
+     */
+    private static final long DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY = 10000;
+
+    /**
      * The value indicating no retry is needed
      */
     public static final long NO_RETRY = -1;
@@ -141,6 +144,12 @@
     private long mFailFastInterApnDelay;
 
     /**
+     * The delay (in milliseconds) for APN retrying after disconnect (e.g. Modem suddenly reports
+     * data call lost)
+     */
+    private long mApnRetryAfterDisconnectDelay;
+
+    /**
      * Modem suggested delay for retrying the current APN
      */
     private long mModemSuggestedDelay = NO_SUGGESTED_RETRY_DELAY;
@@ -337,6 +346,9 @@
             mFailFastInterApnDelay = b.getLong(
                     CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG,
                     DEFAULT_INTER_APN_DELAY_FOR_PROVISIONING);
+            mApnRetryAfterDisconnectDelay = b.getLong(
+                    CarrierConfigManager.KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG,
+                    DEFAULT_APN_RETRY_AFTER_DISCONNECT_DELAY);
 
             // Load all retry patterns for all different APNs.
             String[] allConfigStrings = b.getStringArray(
@@ -645,44 +657,21 @@
     }
 
     /**
-     * Get the delay between APN setting trying. This is the fixed delay used for APN setting trying
-     * within the same round, comparing to the exponential delay used for different rounds.
-     * @param failFastEnabled True if fail fast mode enabled, which a shorter delay will be used
+     * Get the delay in milliseconds for APN retry after disconnect
      * @return The delay in milliseconds
      */
-    public long getInterApnDelay(boolean failFastEnabled) {
-        return (failFastEnabled) ? mFailFastInterApnDelay : mInterApnDelay;
+    public long getRetryAfterDisconnectDelay() {
+        return mApnRetryAfterDisconnectDelay;
     }
 
     public String toString() {
-        return "mApnType=" + mApnType + " mRetryCount=" + mRetryCount +
-                " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex +
-                " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay=" +
-                mModemSuggestedDelay + " mRetryForever=" + mRetryForever +
-                " mConfig={" + mConfig + "}";
-    }
-
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("  RetryManager");
-        pw.println("***************************************");
-
-        pw.println("    config = " + mConfig);
-        pw.println("    mApnType = " + mApnType);
-        pw.println("    mCurrentApnIndex = " + mCurrentApnIndex);
-        pw.println("    mRetryCount = " + mRetryCount);
-        pw.println("    mMaxRetryCount = " + mMaxRetryCount);
-        pw.println("    mSameApnRetryCount = " + mSameApnRetryCount);
-        pw.println("    mModemSuggestedDelay = " + mModemSuggestedDelay);
-
-        if (mWaitingApns != null) {
-            pw.println("    APN list: ");
-            for (int i = 0; i < mWaitingApns.size(); i++) {
-                pw.println("      [" + i + "]=" + mWaitingApns.get(i));
-            }
-        }
-
-        pw.println("***************************************");
-        pw.flush();
+        if (mConfig == null) return "";
+        return "RetryManager: mApnType=" + mApnType + " mRetryCount=" + mRetryCount
+                + " mMaxRetryCount=" + mMaxRetryCount + " mCurrentApnIndex=" + mCurrentApnIndex
+                + " mSameApnRtryCount=" + mSameApnRetryCount + " mModemSuggestedDelay="
+                + mModemSuggestedDelay + " mRetryForever=" + mRetryForever + " mInterApnDelay="
+                + mInterApnDelay + " mApnRetryAfterDisconnectDelay=" + mApnRetryAfterDisconnectDelay
+                + " mConfig={" + mConfig + "}";
     }
 
     private void log(String s) {
diff --git a/src/java/com/android/internal/telephony/RilWakelockInfo.java b/src/java/com/android/internal/telephony/RilWakelockInfo.java
new file mode 100644
index 0000000..5d9e54b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RilWakelockInfo.java
@@ -0,0 +1,110 @@
+/*
+ * 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.annotation.TargetApi;
+import android.os.Build;
+import android.telephony.Rlog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+@TargetApi(8)
+public class RilWakelockInfo {
+    private final String LOG_TAG = RilWakelockInfo.class.getSimpleName();
+    private int mRilRequestSent;
+    private int mTokenNumber;
+    private long mRequestTime;
+    private long mResponseTime;
+
+    /* If there are n requests waiting for a response for time t, the time attributed to
+    each request will be t/n. If the number of outstanding requests changes at time t1,
+    then we will compute the wakelock time till t1 and store it in mWakelockTimeAttributedSoFar
+    and update mConcurrentRequests. mLastAggregatedTime will be set to t1 and used to
+    compute the time taken for this request using the new mConcurrentRequests
+     */
+    private long mWakelockTimeAttributedSoFar;
+    private long mLastAggregatedTime;
+    private int mConcurrentRequests;
+
+    @VisibleForTesting
+    public int getConcurrentRequests() {
+        return mConcurrentRequests;
+    }
+
+    RilWakelockInfo(int rilRequest, int tokenNumber, int concurrentRequests, long requestTime) {
+        concurrentRequests = validateConcurrentRequests(concurrentRequests);
+        this.mRilRequestSent = rilRequest;
+        this.mTokenNumber = tokenNumber;
+        this.mConcurrentRequests = concurrentRequests;
+        this.mRequestTime = requestTime;
+        this.mWakelockTimeAttributedSoFar = 0;
+        this.mLastAggregatedTime = requestTime;
+    }
+
+    private int validateConcurrentRequests(int concurrentRequests) {
+        if(concurrentRequests <= 0) {
+            if(Build.IS_DEBUGGABLE) {
+                IllegalArgumentException e = new IllegalArgumentException(
+                    "concurrentRequests should always be greater than 0.");
+                Rlog.e(LOG_TAG, e.toString());
+                throw e;
+            } else {
+                concurrentRequests = 1;
+            }
+        }
+        return concurrentRequests;
+    }
+
+    int getTokenNumber() {
+        return mTokenNumber;
+    }
+
+    int getRilRequestSent() {
+        return mRilRequestSent;
+    }
+
+    void setResponseTime(long responseTime) {
+        updateTime(responseTime);
+        this.mResponseTime = responseTime;
+    }
+
+    void updateConcurrentRequests(int concurrentRequests, long time) {
+        concurrentRequests = validateConcurrentRequests(concurrentRequests);
+        updateTime(time);
+        mConcurrentRequests = concurrentRequests;
+    }
+
+    synchronized void updateTime(long time) {
+        mWakelockTimeAttributedSoFar += (time - mLastAggregatedTime) / mConcurrentRequests;
+        mLastAggregatedTime = time;
+    }
+
+    long getWakelockTimeAttributedToClient() {
+        return mWakelockTimeAttributedSoFar;
+    }
+
+    @Override
+    public String toString() {
+        return "WakelockInfo{" +
+                "rilRequestSent=" + mRilRequestSent +
+                ", tokenNumber=" + mTokenNumber +
+                ", requestTime=" + mRequestTime +
+                ", responseTime=" + mResponseTime +
+                ", mWakelockTimeAttributed=" + mWakelockTimeAttributedSoFar +
+                '}';
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 476bbad..2ec5101 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -15,7 +15,19 @@
  */
 
 package com.android.internal.telephony;
+
+import static android.Manifest.permission.SEND_SMS_NO_CONFIRMATION;
+import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
+import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
+import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
+import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
+import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED;
+
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
@@ -64,6 +76,7 @@
 import android.widget.TextView;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccController;
@@ -75,14 +88,6 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import static android.Manifest.permission.SEND_SMS_NO_CONFIRMATION;
-import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE;
-import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
-import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED;
-import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE;
-import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU;
-import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF;
-
 public abstract class SMSDispatcher extends Handler {
     static final String TAG = "SMSDispatcher";    // accessed from inner class
     static final boolean DBG = false;
@@ -311,7 +316,25 @@
         case EVENT_STOP_SENDING:
         {
             SmsTracker tracker = (SmsTracker) msg.obj;
-            tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
+            if (msg.arg1 == ConfirmDialogListener.SHORT_CODE_MSG) {
+                if (msg.arg2 == ConfirmDialogListener.NEVER_ALLOW) {
+                    tracker.onFailed(mContext,
+                            RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, 0/*errorCode*/);
+                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                            + "sending SHORT_CODE_NEVER_ALLOWED error code.");
+                } else {
+                    tracker.onFailed(mContext,
+                            RESULT_ERROR_SHORT_CODE_NOT_ALLOWED, 0/*errorCode*/);
+                    Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                            + "sending SHORT_CODE_NOT_ALLOWED error code.");
+                }
+            } else if (msg.arg1 == ConfirmDialogListener.RATE_LIMIT) {
+                tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/);
+                Rlog.d(TAG, "SMSDispatcher: EVENT_STOP_SENDING - "
+                        + "sending LIMIT_EXCEEDED error code.");
+            } else {
+                Rlog.e(TAG, "SMSDispatcher: EVENT_STOP_SENDING - unexpected cases.");
+            }
             mPendingTrackerCount--;
             break;
         }
@@ -948,7 +971,8 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      * -param destAddr the destination phone number (for short code confirmation)
      */
-    protected void sendRawPdu(SmsTracker tracker) {
+    @VisibleForTesting
+    public void sendRawPdu(SmsTracker tracker) {
         HashMap map = tracker.getData();
         byte pdu[] = (byte[]) map.get("pdu");
 
@@ -979,7 +1003,8 @@
         PackageInfo appInfo;
         try {
             // XXX this is lossy- apps can share a UID
-            appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
+            appInfo = pm.getPackageInfoAsUser(
+                    packageNames[0], PackageManager.GET_SIGNATURES, tracker.mUserId);
         } catch (PackageManager.NameNotFoundException e) {
             Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS");
             tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
@@ -1052,7 +1077,7 @@
 
             // Wait for user confirmation unless the user has set permission to always allow/deny
             int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
-                    tracker.mAppInfo.packageName);
+                    tracker.getAppPackageName());
             if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
                 // First time trying to send to premium SMS.
                 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER;
@@ -1065,7 +1090,10 @@
 
                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW:
                     Rlog.w(TAG, "User denied this app from sending to premium SMS");
-                    sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker));
+                    Message msg = obtainMessage(EVENT_STOP_SENDING, tracker);
+                    msg.arg1 = ConfirmDialogListener.SHORT_CODE_MSG;
+                    msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
+                    sendMessage(msg);
                     return false;   // reject this message
 
                 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER:
@@ -1104,10 +1132,10 @@
      * @param appPackage the package name of the app requesting to send an SMS
      * @return the label for the specified app, or the package name if getApplicationInfo() fails
      */
-    private CharSequence getAppLabel(String appPackage) {
+    private CharSequence getAppLabel(String appPackage, @UserIdInt int userId) {
         PackageManager pm = mContext.getPackageManager();
         try {
-            ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0);
+            ApplicationInfo appInfo = pm.getApplicationInfoAsUser(appPackage, 0, userId);
             return appInfo.loadSafeLabel(pm);
         } catch (PackageManager.NameNotFoundException e) {
             Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage);
@@ -1124,11 +1152,13 @@
             return;     // queue limit reached; error was returned to caller
         }
 
-        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
+        CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
         Resources r = Resources.getSystem();
         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel));
 
-        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null);
+        // Construct ConfirmDialogListenter for Rate Limit handling
+        ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null,
+                ConfirmDialogListener.RATE_LIMIT);
 
         AlertDialog d = new AlertDialog.Builder(mContext)
                 .setTitle(R.string.sms_control_title)
@@ -1160,7 +1190,7 @@
             detailsId = R.string.sms_short_code_details;
         }
 
-        CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName);
+        CharSequence appLabel = getAppLabel(tracker.getAppPackageName(), tracker.mUserId);
         Resources r = Resources.getSystem();
         Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message,
                 appLabel, tracker.mDestAddress));
@@ -1169,8 +1199,10 @@
                 Context.LAYOUT_INFLATER_SERVICE);
         View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null);
 
+        // Construct ConfirmDialogListenter for short code message sending
         ConfirmDialogListener listener = new ConfirmDialogListener(tracker,
-                (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction));
+                (TextView) layout.findViewById(R.id.sms_short_code_remember_undo_instruction),
+                ConfirmDialogListener.SHORT_CODE_MSG);
 
 
         TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message);
@@ -1329,11 +1361,14 @@
 
         private boolean mPersistMessage;
 
+        // User who sends the SMS.
+        private final @UserIdInt int mUserId;
+
         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 persistMessage) {
+                boolean isText, boolean persistMessage, int userId) {
             mData = data;
             mSentIntent = sentIntent;
             mDeliveryIntent = deliveryIntent;
@@ -1352,6 +1387,7 @@
             mSubId = subId;
             mIsText = isText;
             mPersistMessage = persistMessage;
+            mUserId = userId;
         }
 
         /**
@@ -1367,6 +1403,14 @@
         }
 
         /**
+         * Get the App package name
+         * @return App package name info
+         */
+        public String getAppPackageName() {
+            return mAppInfo != null ? mAppInfo.packageName : null;
+        }
+
+        /**
          * Update the status of this message if we persisted it
          */
         public void updateSentMessageStatus(Context context, int status) {
@@ -1565,11 +1609,13 @@
         String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
 
         // Get package info via packagemanager
+        final int userId = UserHandle.getCallingUserId();
         PackageInfo appInfo = null;
         if (packageNames != null && packageNames.length > 0) {
             try {
                 // XXX this is lossy- apps can share a UID
-                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
+                appInfo = pm.getPackageInfoAsUser(
+                        packageNames[0], PackageManager.GET_SIGNATURES, userId);
             } catch (PackageManager.NameNotFoundException e) {
                 // error will be logged in sendRawPdu
             }
@@ -1579,7 +1625,7 @@
         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, persistMessage);
+                fullMessageText, getSubId(), isText, persistMessage, userId);
     }
 
     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
@@ -1625,10 +1671,15 @@
         private Button mNegativeButton;
         private boolean mRememberChoice;    // default is unchecked
         private final TextView mRememberUndoInstruction;
+        private int mConfirmationType;  // 0 - Short Code Msg Sending; 1 - Rate Limit Exceeded
+        private static final int SHORT_CODE_MSG = 0; // Short Code Msg
+        private static final int RATE_LIMIT = 1; // Rate Limit Exceeded
+        private static final int NEVER_ALLOW = 1; // Never Allow
 
-        ConfirmDialogListener(SmsTracker tracker, TextView textView) {
+        ConfirmDialogListener(SmsTracker tracker, TextView textView, int confirmationType) {
             mTracker = tracker;
             mRememberUndoInstruction = textView;
+            mConfirmationType = confirmationType;
         }
 
         void setPositiveButton(Button button) {
@@ -1661,18 +1712,23 @@
                 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER,
                                     mTracker.mAppInfo.applicationInfo == null ?
                                     -1 :  mTracker.mAppInfo.applicationInfo.uid);
-                sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+                Message msg = obtainMessage(EVENT_STOP_SENDING, mTracker);
+                msg.arg1 = mConfirmationType;
                 if (mRememberChoice) {
                     newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
+                    msg.arg2 = ConfirmDialogListener.NEVER_ALLOW;
                 }
+                sendMessage(msg);
             }
-            setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission);
+            setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
         }
 
         @Override
         public void onCancel(DialogInterface dialog) {
             Rlog.d(TAG, "dialog dismissed: don't send SMS");
-            sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker));
+            Message msg = obtainMessage(EVENT_STOP_SENDING, mTracker);
+            msg.arg1 = mConfirmationType;
+            sendMessage(msg);
         }
 
         @Override
@@ -1734,8 +1790,13 @@
 
         List<String> carrierPackages = card.getCarrierPackageNamesForIntent(
             mContext.getPackageManager(), new Intent(CarrierMessagingService.SERVICE_INTERFACE));
-        return (carrierPackages != null && carrierPackages.size() == 1) ?
-                carrierPackages.get(0) : null;
+        if (carrierPackages != null && carrierPackages.size() == 1) {
+            return carrierPackages.get(0);
+        }
+        // If there is no carrier package which implements CarrierMessagingService, then lookup if
+        // for a carrierImsPackage that implements CarrierMessagingService.
+        return CarrierSmsUtils.getCarrierImsPackageForIntent(mContext, mPhone,
+                new Intent(CarrierMessagingService.SERVICE_INTERFACE));
     }
 
     protected int getSubId() {
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 0ec699f..fb8231e 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -16,6 +16,11 @@
 
 package com.android.internal.telephony;
 
+import static android.provider.Telephony.ServiceStateTable.getContentValuesForServiceState;
+import static android.provider.Telephony.ServiceStateTable.getUriForSubscriptionId;
+
+import static com.android.internal.telephony.CarrierActionAgent.CARRIER_ACTION_SET_RADIO_ENABLED;
+
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -28,21 +33,23 @@
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.ContentObserver;
+import android.hardware.radio.V1_0.CellInfoType;
+import android.hardware.radio.V1_0.DataRegStateResult;
+import android.hardware.radio.V1_0.RegState;
+import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.os.AsyncResult;
 import android.os.BaseBundle;
 import android.os.Build;
 import android.os.Handler;
-import android.os.Looper;
 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;
+import android.os.WorkSource;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
@@ -50,12 +57,10 @@
 import android.telephony.CellIdentityLte;
 import android.telephony.CellIdentityWcdma;
 import android.telephony.CellInfo;
-import android.telephony.CellInfoCdma;
 import android.telephony.CellInfoGsm;
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellLocation;
-import android.telephony.CellSignalStrengthLte;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -70,16 +75,6 @@
 import android.util.Pair;
 import android.util.TimeUtils;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.TimeZone;
-import java.util.concurrent.atomic.AtomicInteger;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriInfo;
@@ -91,8 +86,21 @@
 import com.android.internal.telephony.uicc.SIMRecords;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.util.NotificationChannelController;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.PatternSyntaxException;
+
 /**
  * {@hide}
  */
@@ -122,16 +130,6 @@
     // TODO - this should not be public, right now used externally GsmConnetion.
     public RestrictedState mRestrictedState;
 
-    /* The otaspMode passed to PhoneStateListener#onOtaspChanged */
-    static public final int OTASP_UNINITIALIZED = 0;
-    static public final int OTASP_UNKNOWN = 1;
-    static public final int OTASP_NEEDED = 2;
-    static public final int OTASP_NOT_NEEDED = 3;
-    /**
-     * OtaUtil has conflict enum 4: OtaUtils.OTASP_FAILURE_SPC_RETRIES
-     */
-    static public final int OTASP_SIM_UNPROVISIONED = 5;
-
     /**
      * A unique identifier to track requests associated with a poll
      * and ignore stale responses.  The value is a count-down of
@@ -155,6 +153,7 @@
     protected RegistrantList mDetachedRegistrants = new RegistrantList();
     private RegistrantList mDataRegStateOrRatChangedRegistrants = new RegistrantList();
     private RegistrantList mNetworkAttachedRegistrants = new RegistrantList();
+    private RegistrantList mNetworkDetachedRegistrants = new RegistrantList();
     private RegistrantList mPsRestrictEnabledRegistrants = new RegistrantList();
     private RegistrantList mPsRestrictDisabledRegistrants = new RegistrantList();
 
@@ -178,7 +177,6 @@
     protected static final int EVENT_POLL_SIGNAL_STRENGTH              = 10;
     protected static final int EVENT_NITZ_TIME                         = 11;
     protected static final int EVENT_SIGNAL_STRENGTH_UPDATE            = 12;
-    protected static final int EVENT_RADIO_AVAILABLE                   = 13;
     protected static final int EVENT_POLL_STATE_NETWORK_SELECTION_MODE = 14;
     protected static final int EVENT_GET_LOC_DONE                      = 15;
     protected static final int EVENT_SIM_RECORDS_LOADED                = 16;
@@ -211,6 +209,7 @@
     protected static final int EVENT_IMS_CAPABILITY_CHANGED            = 48;
     protected static final int EVENT_ALL_DATA_DISCONNECTED             = 49;
     protected static final int EVENT_PHONE_TYPE_SWITCHED               = 50;
+    protected static final int EVENT_RADIO_POWER_FROM_CARRIER          = 51;
 
     protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
@@ -283,6 +282,9 @@
     private final LocalLog mAttachLog = new LocalLog(10);
     private final LocalLog mPhoneTypeLog = new LocalLog(10);
     private final LocalLog mRatLog = new LocalLog(20);
+    private final LocalLog mRadioPowerLog = new LocalLog(20);
+    private final LocalLog mTimeLog = new LocalLog(15);
+    private final LocalLog mTimeZoneLog = new LocalLog(15);
 
     private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
         public final AtomicInteger mPreviousSubId =
@@ -402,6 +404,14 @@
     private int mNewMaxDataCalls = 1;
     private int mReasonDataDenied = -1;
     private int mNewReasonDataDenied = -1;
+
+    /**
+     * The code of the rejection cause that is sent by network when the CS
+     * registration is rejected. It should be shown to the user as a notification.
+     */
+    private int mRejectCode;
+    private int mNewRejectCode;
+
     /**
      * GSM roaming status solely based on TS 27.007 7.2 CREG. Only used by
      * handlePollStateResult to store CREG roaming result.
@@ -422,6 +432,8 @@
     private boolean mStartedGprsRegCheck;
     /** Already sent the event-log for no gprs register. */
     private boolean mReportedGprsNoReg;
+
+    private CarrierServiceStateTracker mCSST;
     /**
      * The Notification object given to the NotificationManager.
      */
@@ -433,12 +445,22 @@
     public static final int CS_DISABLED = 1004;           // Access Control enables all voice/sms service
     public static final int CS_NORMAL_ENABLED = 1005;     // Access Control blocks normal voice/sms service
     public static final int CS_EMERGENCY_ENABLED = 1006;  // Access Control blocks emergency call service
+    public static final int CS_REJECT_CAUSE_ENABLED = 2001;     // Notify MM rejection cause
+    public static final int CS_REJECT_CAUSE_DISABLED = 2002;    // Cancel MM rejection cause
     /** Notification id. */
     public static final int PS_NOTIFICATION = 888;  // Id to update and cancel PS restricted
     public static final int CS_NOTIFICATION = 999;  // Id to update and cancel CS restricted
+    public static final int CS_REJECT_CAUSE_NOTIFICATION = 111; // Id to update and cancel MM
+                                                                // rejection cause
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(
+                    CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+                updateLteEarfcnLists();
+                return;
+            }
+
             if (!mPhone.isPhoneTypeGsm()) {
                 loge("Ignoring intent " + intent + " received on CDMA phone");
                 return;
@@ -460,7 +482,7 @@
     public static final String UNACTIVATED_MIN2_VALUE = "000000";
     public static final String UNACTIVATED_MIN_VALUE = "1111110111";
     // Current Otasp value
-    private int mCurrentOtaspMode = OTASP_UNINITIALIZED;
+    private int mCurrentOtaspMode = TelephonyManager.OTASP_UNINITIALIZED;
     /** if time between NITZ updates is less than mNitzUpdateSpacing the update may be ignored. */
     public static final int NITZ_UPDATE_SPACING_DEFAULT = 1000 * 60 * 10;
     private int mNitzUpdateSpacing = SystemProperties.getInt("ro.nitz_update_spacing",
@@ -493,6 +515,16 @@
     private String mRegistrationDeniedReason;
     private String mCurrentCarrier = null;
 
+    /* list of LTE EARFCNs (E-UTRA Absolute Radio Frequency Channel Number,
+     * Reference: 3GPP TS 36.104 5.4.3)
+     * inclusive ranges for which the lte rsrp boost is applied */
+    private ArrayList<Pair<Integer, Integer>> mEarfcnPairListForRsrpBoost = null;
+
+    private int mLteRsrpBoost = 0; // offset which is reduced from the rsrp threshold
+                                   // while calculating signal strength level.
+    private final Object mLteRsrpBoostLock = new Object();
+    private static final int INVALID_LTE_EARFCN = -1;
+
     public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
         mPhone = phone;
         mCi = ci;
@@ -510,6 +542,7 @@
         mSubscriptionManager = SubscriptionManager.from(phone.getContext());
         mSubscriptionManager
                 .addOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
+        mRestrictedState = new RestrictedState();
 
         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
 
@@ -518,7 +551,7 @@
         mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
 
         mCi.registerForRadioStateChanged(this, EVENT_RADIO_STATE_CHANGED, null);
-        mCi.registerForVoiceNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
+        mCi.registerForNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
         mCi.setOnNITZTime(this, EVENT_NITZ_TIME, null);
 
         mCr = phone.getContext().getContentResolver();
@@ -527,6 +560,9 @@
         int enableCellularOnBoot = Settings.Global.getInt(mCr,
                 Settings.Global.ENABLE_CELLULAR_ON_BOOT, 1);
         mDesiredPowerState = (enableCellularOnBoot > 0) && ! (airplaneMode > 0);
+        mRadioPowerLog.log("init : airplane mode = " + airplaneMode + " enableCellularOnBoot = " +
+                enableCellularOnBoot);
+
 
         mCr.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME), true,
@@ -535,6 +571,8 @@
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
                 mAutoTimeZoneObserver);
         setSignalStrengthDefaultValues();
+        mPhone.getCarrierActionAgent().registerForCarrierAction(CARRIER_ACTION_SET_RADIO_ENABLED,
+                this, EVENT_RADIO_POWER_FROM_CARRIER, null, false);
 
         // Monitor locale change
         Context context = mPhone.getContext();
@@ -544,20 +582,51 @@
         filter = new IntentFilter();
         filter.addAction(ACTION_RADIO_OFF);
         context.registerReceiver(mIntentReceiver, filter);
+        filter = new IntentFilter();
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        context.registerReceiver(mIntentReceiver, filter);
 
-        mPhone.notifyOtaspChanged(OTASP_UNINITIALIZED);
+        mPhone.notifyOtaspChanged(TelephonyManager.OTASP_UNINITIALIZED);
 
+        mCi.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
         updatePhoneType();
+
+        mCSST = new CarrierServiceStateTracker(phone, this);
+
+        registerForNetworkAttached(mCSST,
+                CarrierServiceStateTracker.CARRIER_EVENT_VOICE_REGISTRATION, null);
+        registerForNetworkDetached(mCSST,
+                CarrierServiceStateTracker.CARRIER_EVENT_VOICE_DEREGISTRATION, null);
+        registerForDataConnectionAttached(mCSST,
+                CarrierServiceStateTracker.CARRIER_EVENT_DATA_REGISTRATION, null);
+        registerForDataConnectionDetached(mCSST,
+                CarrierServiceStateTracker.CARRIER_EVENT_DATA_DEREGISTRATION, null);
     }
 
     @VisibleForTesting
     public void updatePhoneType() {
+        // If we are previously voice roaming, we need to notify that roaming status changed before
+        // we change back to non-roaming.
+        if (mSS != null && mSS.getVoiceRoaming()) {
+            mVoiceRoamingOffRegistrants.notifyRegistrants();
+        }
+
+        // If we are previously data roaming, we need to notify that roaming status changed before
+        // we change back to non-roaming.
+        if (mSS != null && mSS.getDataRoaming()) {
+            mDataRoamingOffRegistrants.notifyRegistrants();
+        }
+
+        // If we are previously in service, we need to notify that we are out of service now.
+        if (mSS != null && mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE) {
+            mDetachedRegistrants.notifyRegistrants();
+        }
+
         mSS = new ServiceState();
         mNewSS = new ServiceState();
         mLastCellInfoListTime = 0;
         mLastCellInfoList = null;
         mSignalStrength = new SignalStrength();
-        mRestrictedState = new RestrictedState();
         mStartedGprsRegCheck = false;
         mReportedGprsNoReg = false;
         mMdn = null;
@@ -582,13 +651,7 @@
 
             mCellLoc = new GsmCellLocation();
             mNewCellLoc = new GsmCellLocation();
-            mCi.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);
-            mCi.setOnRestrictedStateChanged(this, EVENT_RESTRICTED_STATE_CHANGED, null);
         } else {
-            //clear GSM regsitrations first
-            mCi.unregisterForAvailable(this);
-            mCi.unSetOnRestrictedStateChanged(this);
-
             if (mPhone.isPhoneTypeCdmaLte()) {
                 mPhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
             }
@@ -623,11 +686,7 @@
 
         logPhoneTypeChange();
 
-        // Tell everybody that we've thrown away state and are starting over with
-        // empty, detached ServiceStates.
-        mVoiceRoamingOffRegistrants.notifyRegistrants();
-        mDataRoamingOffRegistrants.notifyRegistrants();
-        mDetachedRegistrants.notifyRegistrants();
+        // Tell everybody that the registration state and RAT have changed.
         notifyDataRegStateRilRadioTechnologyChanged();
     }
 
@@ -646,6 +705,8 @@
         mSubscriptionManager
             .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangedListener);
         mCi.unregisterForImsNetworkStateChanged(this);
+        mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
+                CARRIER_ACTION_SET_RADIO_ENABLED);
     }
 
     public boolean getDesiredPowerState() {
@@ -660,6 +721,7 @@
             try {
                 mPhone.notifySignalStrength();
                 notified = true;
+                mLastSignalStrength = mSignalStrength;
             } catch (NullPointerException ex) {
                 loge("updateSignalStrength() Phone already destroyed: " + ex
                         + "SignalStrength not notified");
@@ -693,7 +755,7 @@
                 log("useDataRegStateForDataOnlyDevice: VoiceRegState=" + mNewSS.getVoiceRegState()
                     + " DataRegState=" + mNewSS.getDataRegState());
             }
-            // TODO: Consider not lying and instead have callers know the difference. 
+            // TODO: Consider not lying and instead have callers know the difference.
             mNewSS.setVoiceRegState(mNewSS.getDataRegState());
         }
     }
@@ -782,12 +844,13 @@
      * @param h handler to notify
      * @param what what code of message when delivered
      * @param obj placed in Message.obj
+     * @param notifyNow notify upon registration if data roaming is off
      */
-    public void registerForDataRoamingOff(Handler h, int what, Object obj) {
+    public void registerForDataRoamingOff(Handler h, int what, Object obj, boolean notifyNow) {
         Registrant r = new Registrant(h, what, obj);
         mDataRoamingOffRegistrants.add(r);
 
-        if (!mSS.getDataRoaming()) {
+        if (notifyNow && !mSS.getDataRoaming()) {
             r.notifyRegistrant();
         }
     }
@@ -866,11 +929,123 @@
         }
     }
 
+    private void processCellLocationInfo(CellLocation cellLocation,
+                                         VoiceRegStateResult voiceRegStateResult) {
+        if (mPhone.isPhoneTypeGsm()) {
+            int psc = -1;
+            int cid = -1;
+            int lac = -1;
+            switch(voiceRegStateResult.cellIdentity.cellInfoType) {
+                case CellInfoType.GSM: {
+                    if (voiceRegStateResult.cellIdentity.cellIdentityGsm.size() == 1) {
+                        android.hardware.radio.V1_0.CellIdentityGsm cellIdentityGsm =
+                                voiceRegStateResult.cellIdentity.cellIdentityGsm.get(0);
+                        cid = cellIdentityGsm.cid;
+                        lac = cellIdentityGsm.lac;
+                    }
+                    break;
+                }
+                case CellInfoType.WCDMA: {
+                    if (voiceRegStateResult.cellIdentity.cellIdentityWcdma.size() == 1) {
+                        android.hardware.radio.V1_0.CellIdentityWcdma cellIdentityWcdma =
+                                voiceRegStateResult.cellIdentity.cellIdentityWcdma.get(0);
+                        cid = cellIdentityWcdma.cid;
+                        lac = cellIdentityWcdma.lac;
+                        psc = cellIdentityWcdma.psc;
+                    }
+                    break;
+                }
+                case CellInfoType.TD_SCDMA: {
+                    if (voiceRegStateResult.cellIdentity.cellIdentityTdscdma.size() == 1) {
+                        android.hardware.radio.V1_0.CellIdentityTdscdma
+                                cellIdentityTdscdma =
+                                voiceRegStateResult.cellIdentity.cellIdentityTdscdma.get(0);
+                        cid = cellIdentityTdscdma.cid;
+                        lac = cellIdentityTdscdma.lac;
+                    }
+                    break;
+                }
+                case CellInfoType.LTE: {
+                    if (voiceRegStateResult.cellIdentity.cellIdentityLte.size() == 1) {
+                        android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
+                                voiceRegStateResult.cellIdentity.cellIdentityLte.get(0);
+                        cid = cellIdentityLte.ci;
+
+                        /* Continuing the historical behaviour of using tac as lac. */
+                        lac = cellIdentityLte.tac;
+                    }
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
+            // LAC and CID are -1 if not avail
+            ((GsmCellLocation) cellLocation).setLacAndCid(lac, cid);
+            ((GsmCellLocation) cellLocation).setPsc(psc);
+        } else {
+            int baseStationId = -1;
+            int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
+            int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
+            int systemId = 0;
+            int networkId = 0;
+
+            switch(voiceRegStateResult.cellIdentity.cellInfoType) {
+                case CellInfoType.CDMA: {
+                    if (voiceRegStateResult.cellIdentity.cellIdentityCdma.size() == 1) {
+                        android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
+                                voiceRegStateResult.cellIdentity.cellIdentityCdma.get(0);
+                        baseStationId = cellIdentityCdma.baseStationId;
+                        baseStationLatitude = cellIdentityCdma.latitude;
+                        baseStationLongitude = cellIdentityCdma.longitude;
+                        systemId = cellIdentityCdma.systemId;
+                        networkId = cellIdentityCdma.networkId;
+                    }
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
+
+            // Some carriers only return lat-lngs of 0,0
+            if (baseStationLatitude == 0 && baseStationLongitude == 0) {
+                baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
+                baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
+            }
+
+            // Values are -1 if not available.
+            ((CdmaCellLocation) cellLocation).setCellLocationData(baseStationId,
+                    baseStationLatitude, baseStationLongitude, systemId, networkId);
+        }
+    }
+
+    private int getLteEarfcn(DataRegStateResult dataRegStateResult) {
+        int lteEarfcn = INVALID_LTE_EARFCN;
+        switch(dataRegStateResult.cellIdentity.cellInfoType) {
+            case CellInfoType.LTE: {
+                if (dataRegStateResult.cellIdentity.cellIdentityLte.size() == 1) {
+                    android.hardware.radio.V1_0.CellIdentityLte cellIdentityLte =
+                            dataRegStateResult.cellIdentity.cellIdentityLte.get(0);
+                    lteEarfcn = cellIdentityLte.earfcn;
+                }
+                break;
+            }
+            default: {
+                break;
+            }
+        }
+
+        return lteEarfcn;
+    }
+
     @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
         int[] ints;
         Message message;
+
+        if (VDBG) log("received event " + msg.what);
         switch (msg.what) {
             case EVENT_SET_RADIO_POWER_OFF:
                 synchronized(this) {
@@ -941,12 +1116,7 @@
                 }
                 break;
 
-            //GSM
-            case EVENT_RADIO_AVAILABLE:
-                //this is unnecessary
-                //setPowerStateToDesired();
-                break;
-
+            // GSM
             case EVENT_SIM_READY:
                 // Reset the mPreviousSubId so we treat a SIM power bounce
                 // as a first boot.  See b/19194287
@@ -991,62 +1161,8 @@
 
             case EVENT_GET_LOC_DONE:
                 ar = (AsyncResult) msg.obj;
-
                 if (ar.exception == null) {
-                    String states[] = (String[])ar.result;
-                    if (mPhone.isPhoneTypeGsm()) {
-                        int lac = -1;
-                        int cid = -1;
-                        if (states.length >= 3) {
-                            try {
-                                if (states[1] != null && states[1].length() > 0) {
-                                    lac = (int)Long.parseLong(states[1], 16);
-                                }
-                                if (states[2] != null && states[2].length() > 0) {
-                                    cid = (int)Long.parseLong(states[2], 16);
-                                }
-                            } catch (NumberFormatException ex) {
-                                Rlog.w(LOG_TAG, "error parsing location: " + ex);
-                            }
-                        }
-                        ((GsmCellLocation)mCellLoc).setLacAndCid(lac, cid);
-                    } else {
-                        int baseStationId = -1;
-                        int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
-                        int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
-                        int systemId = -1;
-                        int networkId = -1;
-
-                        if (states.length > 9) {
-                            try {
-                                if (states[4] != null) {
-                                    baseStationId = Integer.parseInt(states[4]);
-                                }
-                                if (states[5] != null) {
-                                    baseStationLatitude = Integer.parseInt(states[5]);
-                                }
-                                if (states[6] != null) {
-                                    baseStationLongitude = Integer.parseInt(states[6]);
-                                }
-                                // Some carriers only return lat-lngs of 0,0
-                                if (baseStationLatitude == 0 && baseStationLongitude == 0) {
-                                    baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
-                                    baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
-                                }
-                                if (states[8] != null) {
-                                    systemId = Integer.parseInt(states[8]);
-                                }
-                                if (states[9] != null) {
-                                    networkId = Integer.parseInt(states[9]);
-                                }
-                            } catch (NumberFormatException ex) {
-                                loge("error parsing cell location data: " + ex);
-                            }
-                        }
-
-                        ((CdmaCellLocation)mCellLoc).setCellLocationData(baseStationId,
-                                baseStationLatitude, baseStationLongitude, systemId, networkId);
-                    }
+                    processCellLocationInfo(mCellLoc, (VoiceRegStateResult) ar.result);
                     mPhone.notifyLocationChanged();
                 }
 
@@ -1335,6 +1451,15 @@
                 }
                 break;
 
+            case EVENT_RADIO_POWER_FROM_CARRIER:
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception == null) {
+                    boolean enable = (boolean) ar.result;
+                    if (DBG) log("EVENT_RADIO_POWER_FROM_CARRIER: " + enable);
+                    setRadioPowerFromCarrier(enable);
+                }
+                break;
+
             default:
                 log("Unhandled message with number: " + msg.what);
                 break;
@@ -1412,27 +1537,27 @@
         // if sim is not loaded, return otasp uninitialized
         if(!mPhone.getIccRecordsLoaded()) {
             if(DBG) log("getOtasp: otasp uninitialized due to sim not loaded");
-            return OTASP_UNINITIALIZED;
+            return TelephonyManager.OTASP_UNINITIALIZED;
         }
         // if voice tech is Gsm, return otasp not needed
         if(mPhone.isPhoneTypeGsm()) {
             if(DBG) log("getOtasp: otasp not needed for GSM");
-            return OTASP_NOT_NEEDED;
+            return TelephonyManager.OTASP_NOT_NEEDED;
         }
         // for ruim, min is null means require otasp.
         if (mIsSubscriptionFromRuim && mMin == null) {
-            return OTASP_NEEDED;
+            return TelephonyManager.OTASP_NEEDED;
         }
         if (mMin == null || (mMin.length() < 6)) {
             if (DBG) log("getOtasp: bad mMin='" + mMin + "'");
-            provisioningState = OTASP_UNKNOWN;
+            provisioningState = TelephonyManager.OTASP_UNKNOWN;
         } else {
             if ((mMin.equals(UNACTIVATED_MIN_VALUE)
                     || mMin.substring(0,6).equals(UNACTIVATED_MIN2_VALUE))
                     || SystemProperties.getBoolean("test_cdma_setup", false)) {
-                provisioningState = OTASP_NEEDED;
+                provisioningState = TelephonyManager.OTASP_NEEDED;
             } else {
-                provisioningState = OTASP_NOT_NEEDED;
+                provisioningState = TelephonyManager.OTASP_NOT_NEEDED;
             }
         }
         if (DBG) log("getOtasp: state=" + provisioningState);
@@ -1526,14 +1651,45 @@
 
                 // Setting SS Roaming (general)
                 if (mIsSubscriptionFromRuim) {
-                    mNewSS.setVoiceRoaming(isRoamingBetweenOperators(mNewSS.getVoiceRoaming(), mNewSS));
+                    boolean isRoamingBetweenOperators = isRoamingBetweenOperators(
+                            mNewSS.getVoiceRoaming(), mNewSS);
+                    if (isRoamingBetweenOperators != mNewSS.getVoiceRoaming()) {
+                        log("isRoamingBetweenOperators=" + isRoamingBetweenOperators
+                                + ". Override CDMA voice roaming to " + isRoamingBetweenOperators);
+                        mNewSS.setVoiceRoaming(isRoamingBetweenOperators);
+                    }
                 }
-                // For CDMA, voice and data should have the same roaming status
-                final boolean isVoiceInService =
-                        (mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
-                final int dataRegType = mNewSS.getRilDataRadioTechnology();
-                if (isVoiceInService && ServiceState.isCdma(dataRegType)) {
-                    mNewSS.setDataRoaming(mNewSS.getVoiceRoaming());
+                /**
+                 * For CDMA, voice and data should have the same roaming status.
+                 * If voice is not in service, use TSB58 roaming indicator to set
+                 * data roaming status. If TSB58 roaming indicator is not in the
+                 * carrier-specified list of ERIs for home system then set roaming.
+                 */
+                final int dataRat = mNewSS.getRilDataRadioTechnology();
+                if (ServiceState.isCdma(dataRat)) {
+                    final boolean isVoiceInService =
+                            (mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE);
+                    if (isVoiceInService) {
+                        boolean isVoiceRoaming = mNewSS.getVoiceRoaming();
+                        if (mNewSS.getDataRoaming() != isVoiceRoaming) {
+                            log("Data roaming != Voice roaming. Override data roaming to "
+                                    + isVoiceRoaming);
+                            mNewSS.setDataRoaming(isVoiceRoaming);
+                        }
+                    } else {
+                        /**
+                         * As per VoiceRegStateResult from radio types.hal the TSB58
+                         * Roaming Indicator shall be sent if device is registered
+                         * on a CDMA or EVDO system.
+                         */
+                        boolean isRoamIndForHomeSystem = isRoamIndForHomeSystem(
+                                Integer.toString(mRoamingIndicator));
+                        if (mNewSS.getDataRoaming() == isRoamIndForHomeSystem) {
+                            log("isRoamIndForHomeSystem=" + isRoamIndForHomeSystem
+                                    + ", override data roaming to " + !isRoamIndForHomeSystem);
+                            mNewSS.setDataRoaming(!isRoamIndForHomeSystem);
+                        }
+                    }
                 }
 
                 // Setting SS CdmaRoamingIndicator and CdmaDefaultRoamingIndicator
@@ -1607,155 +1763,99 @@
         return cdmaRoaming && !isSameOperatorNameFromSimAndSS(s);
     }
 
+    private int getRegStateFromHalRegState(int regState) {
+        switch (regState) {
+            case RegState.NOT_REG_MT_NOT_SEARCHING_OP:
+                return ServiceState.RIL_REG_STATE_NOT_REG;
+            case RegState.REG_HOME:
+                return ServiceState.RIL_REG_STATE_HOME;
+            case RegState.NOT_REG_MT_SEARCHING_OP:
+                return ServiceState.RIL_REG_STATE_SEARCHING;
+            case RegState.REG_DENIED:
+                return ServiceState.RIL_REG_STATE_DENIED;
+            case RegState.UNKNOWN:
+                return ServiceState.RIL_REG_STATE_UNKNOWN;
+            case RegState.REG_ROAMING:
+                return ServiceState.RIL_REG_STATE_ROAMING;
+            case RegState.NOT_REG_MT_NOT_SEARCHING_OP_EM:
+                return ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED;
+            case RegState.NOT_REG_MT_SEARCHING_OP_EM:
+                return ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED;
+            case RegState.REG_DENIED_EM:
+                return ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED;
+            case RegState.UNKNOWN_EM:
+                return ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED;
+            default:
+                return ServiceState.REGISTRATION_STATE_NOT_REGISTERED_AND_NOT_SEARCHING;
+        }
+    }
+
     void handlePollStateResultMessage(int what, AsyncResult ar) {
         int ints[];
-        String states[];
         switch (what) {
             case EVENT_POLL_STATE_REGISTRATION: {
+                VoiceRegStateResult voiceRegStateResult = (VoiceRegStateResult) ar.result;
+                int registrationState = getRegStateFromHalRegState(voiceRegStateResult.regState);
+                int cssIndicator = voiceRegStateResult.cssSupported ? 1 : 0;
+
+                mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
+                mNewSS.setCssIndicator(cssIndicator);
+                mNewSS.setRilVoiceRadioTechnology(voiceRegStateResult.rat);
+
+                //Denial reason if registrationState = 3
+                int reasonForDenial = voiceRegStateResult.reasonForDenial;
                 if (mPhone.isPhoneTypeGsm()) {
-                    states = (String[]) ar.result;
-                    int lac = -1;
-                    int cid = -1;
-                    int type = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
-                    int reasonRegStateDenied = -1;
-                    int psc = -1;
-                    if (states.length > 0) {
-                        try {
-                            regState = Integer.parseInt(states[0]);
-                            if (states.length >= 3) {
-                                if (states[1] != null && states[1].length() > 0) {
-                                    lac = (int)Long.parseLong(states[1], 16);
-                                }
-                                if (states[2] != null && states[2].length() > 0) {
-                                    cid = (int)Long.parseLong(states[2], 16);
-                                }
 
-                                // states[3] (if present) is the current radio technology
-                                if (states.length >= 4 && states[3] != null) {
-                                    type = Integer.parseInt(states[3]);
-                                }
-                            }
-                            if (states.length > 14) {
-                                if (states[14] != null && states[14].length() > 0) {
-                                    psc = (int)Long.parseLong(states[14], 16);
-                                }
-                            }
-                        } catch (NumberFormatException ex) {
-                            loge("error parsing RegistrationState: " + ex);
-                        }
-                    }
-
-                    mGsmRoaming = regCodeIsRoaming(regState);
-                    mNewSS.setVoiceRegState(regCodeToServiceState(regState));
-                    mNewSS.setRilVoiceRadioTechnology(type);
+                    mGsmRoaming = regCodeIsRoaming(registrationState);
+                    mNewRejectCode = reasonForDenial;
 
                     boolean isVoiceCapable = mPhone.getContext().getResources()
                             .getBoolean(com.android.internal.R.bool.config_voice_capable);
-                    if ((regState == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED
-                            || regState == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED
-                            || regState == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED
-                            || regState == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED)
+                    if (((registrationState
+                            == ServiceState.RIL_REG_STATE_DENIED_EMERGENCY_CALL_ENABLED)
+                            || (registrationState
+                            == ServiceState.RIL_REG_STATE_NOT_REG_EMERGENCY_CALL_ENABLED)
+                            || (registrationState
+                            == ServiceState.RIL_REG_STATE_SEARCHING_EMERGENCY_CALL_ENABLED)
+                            || (registrationState
+                            == ServiceState.RIL_REG_STATE_UNKNOWN_EMERGENCY_CALL_ENABLED))
                             && isVoiceCapable) {
                         mEmergencyOnly = true;
                     } else {
                         mEmergencyOnly = false;
                     }
-
-                    // LAC and CID are -1 if not avail
-                    ((GsmCellLocation)mNewCellLoc).setLacAndCid(lac, cid);
-                    ((GsmCellLocation)mNewCellLoc).setPsc(psc);
                 } else {
-                    states = (String[])ar.result;
+                    int roamingIndicator = voiceRegStateResult.roamingIndicator;
 
-                    int registrationState = 4;     //[0] registrationState
-                    int radioTechnology = -1;      //[3] radioTechnology
-                    int baseStationId = -1;        //[4] baseStationId
-                    //[5] baseStationLatitude
-                    int baseStationLatitude = CdmaCellLocation.INVALID_LAT_LONG;
-                    //[6] baseStationLongitude
-                    int baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
-                    int cssIndicator = 0;          //[7] init with 0, because it is treated as a boolean
-                    int systemId = 0;              //[8] systemId
-                    int networkId = 0;             //[9] networkId
-                    int roamingIndicator = -1;     //[10] Roaming indicator
-                    int systemIsInPrl = 0;         //[11] Indicates if current system is in PRL
-                    int defaultRoamingIndicator = 0;  //[12] Is default roaming indicator from PRL
-                    int reasonForDenial = 0;       //[13] Denial reason if registrationState = 3
+                    //Indicates if current system is in PR
+                    int systemIsInPrl = voiceRegStateResult.systemIsInPrl;
 
-                    if (states.length >= 14) {
-                        try {
-                            if (states[0] != null) {
-                                registrationState = Integer.parseInt(states[0]);
-                            }
-                            if (states[3] != null) {
-                                radioTechnology = Integer.parseInt(states[3]);
-                            }
-                            if (states[4] != null) {
-                                baseStationId = Integer.parseInt(states[4]);
-                            }
-                            if (states[5] != null) {
-                                baseStationLatitude = Integer.parseInt(states[5]);
-                            }
-                            if (states[6] != null) {
-                                baseStationLongitude = Integer.parseInt(states[6]);
-                            }
-                            // Some carriers only return lat-lngs of 0,0
-                            if (baseStationLatitude == 0 && baseStationLongitude == 0) {
-                                baseStationLatitude  = CdmaCellLocation.INVALID_LAT_LONG;
-                                baseStationLongitude = CdmaCellLocation.INVALID_LAT_LONG;
-                            }
-                            if (states[7] != null) {
-                                cssIndicator = Integer.parseInt(states[7]);
-                            }
-                            if (states[8] != null) {
-                                systemId = Integer.parseInt(states[8]);
-                            }
-                            if (states[9] != null) {
-                                networkId = Integer.parseInt(states[9]);
-                            }
-                            if (states[10] != null) {
-                                roamingIndicator = Integer.parseInt(states[10]);
-                            }
-                            if (states[11] != null) {
-                                systemIsInPrl = Integer.parseInt(states[11]);
-                            }
-                            if (states[12] != null) {
-                                defaultRoamingIndicator = Integer.parseInt(states[12]);
-                            }
-                            if (states[13] != null) {
-                                reasonForDenial = Integer.parseInt(states[13]);
-                            }
-                        } catch (NumberFormatException ex) {
-                            loge("EVENT_POLL_STATE_REGISTRATION_CDMA: error parsing: " + ex);
-                        }
-                    } else {
-                        throw new RuntimeException("Warning! Wrong number of parameters returned from "
-                                + "RIL_REQUEST_REGISTRATION_STATE: expected 14 or more "
-                                + "strings and got " + states.length + " strings");
-                    }
+                    //Is default roaming indicator from PRL
+                    int defaultRoamingIndicator = voiceRegStateResult.defaultRoamingIndicator;
 
                     mRegistrationState = registrationState;
                     // When registration state is roaming and TSB58
                     // roaming indicator is not in the carrier-specified
                     // list of ERIs for home system, mCdmaRoaming is true.
                     boolean cdmaRoaming =
-                            regCodeIsRoaming(registrationState) && !isRoamIndForHomeSystem(states[10]);
+                            regCodeIsRoaming(registrationState)
+                                    && !isRoamIndForHomeSystem(
+                                            Integer.toString(roamingIndicator));
                     mNewSS.setVoiceRoaming(cdmaRoaming);
-                    mNewSS.setVoiceRegState(regCodeToServiceState(registrationState));
-
-                    mNewSS.setRilVoiceRadioTechnology(radioTechnology);
-
-                    mNewSS.setCssIndicator(cssIndicator);
-                    mNewSS.setSystemAndNetworkId(systemId, networkId);
                     mRoamingIndicator = roamingIndicator;
                     mIsInPrl = (systemIsInPrl == 0) ? false : true;
                     mDefaultRoamingIndicator = defaultRoamingIndicator;
 
-
-                    // Values are -1 if not available.
-                    ((CdmaCellLocation)mNewCellLoc).setCellLocationData(baseStationId,
-                            baseStationLatitude, baseStationLongitude, systemId, networkId);
+                    int systemId = 0;
+                    int networkId = 0;
+                    if (voiceRegStateResult.cellIdentity.cellInfoType == CellInfoType.CDMA
+                            && voiceRegStateResult.cellIdentity.cellIdentityCdma.size() == 1) {
+                        android.hardware.radio.V1_0.CellIdentityCdma cellIdentityCdma =
+                                voiceRegStateResult.cellIdentity.cellIdentityCdma.get(0);
+                        systemId = cellIdentityCdma.systemId;
+                        networkId = cellIdentityCdma.networkId;
+                    }
+                    mNewSS.setSystemAndNetworkId(systemId, networkId);
 
                     if (reasonForDenial == 0) {
                         mRegistrationDeniedReason = ServiceStateTracker.REGISTRATION_DENIED_GEN;
@@ -1769,100 +1869,52 @@
                         if (DBG) log("Registration denied, " + mRegistrationDeniedReason);
                     }
                 }
+
+                processCellLocationInfo(mNewCellLoc, voiceRegStateResult);
+
+                if (DBG) {
+                    log("handlPollVoiceRegResultMessage: regState=" + registrationState
+                            + " radioTechnology=" + voiceRegStateResult.rat);
+                }
                 break;
             }
 
             case EVENT_POLL_STATE_GPRS: {
+                DataRegStateResult dataRegStateResult = (DataRegStateResult) ar.result;
+                int regState = getRegStateFromHalRegState(dataRegStateResult.regState);
+                int dataRegState = regCodeToServiceState(regState);
+                int newDataRat = dataRegStateResult.rat;
+                mNewSS.setDataRegState(dataRegState);
+                mNewSS.setRilDataRadioTechnology(newDataRat);
+
                 if (mPhone.isPhoneTypeGsm()) {
-                    states = (String[]) ar.result;
 
-                    int type = 0;
-                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
-                    mNewReasonDataDenied = -1;
-                    mNewMaxDataCalls = 1;
-                    if (states.length > 0) {
-                        try {
-                            regState = Integer.parseInt(states[0]);
-
-                            // states[3] (if present) is the current radio technology
-                            if (states.length >= 4 && states[3] != null) {
-                                type = Integer.parseInt(states[3]);
-                            }
-                            if ((states.length >= 5) &&
-                                    (regState == ServiceState.RIL_REG_STATE_DENIED)) {
-                                mNewReasonDataDenied = Integer.parseInt(states[4]);
-                            }
-                            if (states.length >= 6) {
-                                mNewMaxDataCalls = Integer.parseInt(states[5]);
-                            }
-                        } catch (NumberFormatException ex) {
-                            loge("error parsing GprsRegistrationState: " + ex);
-                        }
-                    }
-                    int dataRegState = regCodeToServiceState(regState);
-                    mNewSS.setDataRegState(dataRegState);
+                    mNewReasonDataDenied = dataRegStateResult.reasonDataDenied;
+                    mNewMaxDataCalls = dataRegStateResult.maxDataCalls;
                     mDataRoaming = regCodeIsRoaming(regState);
-                    mNewSS.setRilDataRadioTechnology(type);
+                    // Save the data roaming state reported by modem registration before resource
+                    // overlay or carrier config possibly overrides it.
+                    mNewSS.setDataRoamingFromRegistration(mDataRoaming);
+
                     if (DBG) {
                         log("handlPollStateResultMessage: GsmSST setDataRegState=" + dataRegState
                                 + " regState=" + regState
-                                + " dataRadioTechnology=" + type);
+                                + " dataRadioTechnology=" + newDataRat);
                     }
                 } else if (mPhone.isPhoneTypeCdma()) {
-                    states = (String[])ar.result;
-                    if (DBG) {
-                        log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS states.length=" +
-                                states.length + " states=" + states);
-                    }
 
-                    int regState = ServiceState.RIL_REG_STATE_UNKNOWN;
-                    int dataRadioTechnology = 0;
+                    boolean isDataRoaming = regCodeIsRoaming(regState);
+                    mNewSS.setDataRoaming(isDataRoaming);
+                    // Save the data roaming state reported by modem registration before resource
+                    // overlay or carrier config possibly overrides it.
+                    mNewSS.setDataRoamingFromRegistration(isDataRoaming);
 
-                    if (states.length > 0) {
-                        try {
-                            regState = Integer.parseInt(states[0]);
-
-                            // states[3] (if present) is the current radio technology
-                            if (states.length >= 4 && states[3] != null) {
-                                dataRadioTechnology = Integer.parseInt(states[3]);
-                            }
-                        } catch (NumberFormatException ex) {
-                            loge("handlePollStateResultMessage: error parsing GprsRegistrationState: "
-                                    + ex);
-                        }
-                    }
-
-                    int dataRegState = regCodeToServiceState(regState);
-                    mNewSS.setDataRegState(dataRegState);
-                    mNewSS.setRilDataRadioTechnology(dataRadioTechnology);
-                    mNewSS.setDataRoaming(regCodeIsRoaming(regState));
                     if (DBG) {
                         log("handlPollStateResultMessage: cdma setDataRegState=" + dataRegState
                                 + " regState=" + regState
-                                + " dataRadioTechnology=" + dataRadioTechnology);
+                                + " dataRadioTechnology=" + newDataRat);
                     }
                 } else {
-                    states = (String[])ar.result;
-                    if (DBG) {
-                        log("handlePollStateResultMessage: EVENT_POLL_STATE_GPRS states.length=" +
-                                states.length + " states=" + states);
-                    }
-
-                    int newDataRAT = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
-                    int regState = -1;
-                    if (states.length > 0) {
-                        try {
-                            regState = Integer.parseInt(states[0]);
-
-                            // states[3] (if present) is the current radio technology
-                            if (states.length >= 4 && states[3] != null) {
-                                newDataRAT = Integer.parseInt(states[3]);
-                            }
-                        } catch (NumberFormatException ex) {
-                            loge("handlePollStateResultMessage: error parsing GprsRegistrationState: "
-                                    + ex);
-                        }
-                    }
 
                     // If the unsolicited signal strength comes just before data RAT family changes
                     // (i.e. from UNKNOWN to LTE, CDMA to LTE, LTE to CDMA), the signal bar might
@@ -1871,24 +1923,28 @@
                     // even not come at all.  In order to provide the best user experience, we
                     // query the latest signal information so it will show up on the UI on time.
                     int oldDataRAT = mSS.getRilDataRadioTechnology();
-                    if ((oldDataRAT == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN &&
-                            newDataRAT != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) ||
-                            (ServiceState.isCdma(oldDataRAT) && ServiceState.isLte(newDataRAT)) ||
-                            (ServiceState.isLte(oldDataRAT) && ServiceState.isCdma(newDataRAT))) {
+                    if (((oldDataRAT == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)
+                            && (newDataRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN))
+                            || (ServiceState.isCdma(oldDataRAT) && ServiceState.isLte(newDataRat))
+                            || (ServiceState.isLte(oldDataRAT)
+                            && ServiceState.isCdma(newDataRat))) {
                         mCi.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
                     }
 
-                    mNewSS.setRilDataRadioTechnology(newDataRAT);
-                    int dataRegState = regCodeToServiceState(regState);
-                    mNewSS.setDataRegState(dataRegState);
                     // voice roaming state in done while handling EVENT_POLL_STATE_REGISTRATION_CDMA
-                    mNewSS.setDataRoaming(regCodeIsRoaming(regState));
+                    boolean isDataRoaming = regCodeIsRoaming(regState);
+                    mNewSS.setDataRoaming(isDataRoaming);
+                    // Save the data roaming state reported by modem registration before resource
+                    // overlay or carrier config possibly overrides it.
+                    mNewSS.setDataRoamingFromRegistration(isDataRoaming);
                     if (DBG) {
-                        log("handlPollStateResultMessage: CdmaLteSST setDataRegState=" + dataRegState
-                                + " regState=" + regState
-                                + " dataRadioTechnology=" + newDataRAT);
+                        log("handlPollStateResultMessage: CdmaLteSST setDataRegState="
+                                + dataRegState + " regState=" + regState + " dataRadioTechnology="
+                                + newDataRat);
                     }
                 }
+
+                updateServiceStateLteEarfcnBoost(mNewSS, getLteEarfcn(dataRegStateResult));
                 break;
             }
 
@@ -1947,7 +2003,7 @@
             case EVENT_POLL_STATE_NETWORK_SELECTION_MODE: {
                 ints = (int[])ar.result;
                 mNewSS.setIsManualSelection(ints[0] == 1);
-                if ((ints[0] == 1) && (!mPhone.isManualNetSelAllowed())) {
+                if ((ints[0] == 1) && (mPhone.shouldForceAutoNetworkSelect())) {
                         /*
                          * modem is currently in manual selection but manual
                          * selection is not allowed in the current mode so
@@ -1974,8 +2030,9 @@
      */
     private boolean isRoamIndForHomeSystem(String roamInd) {
         // retrieve the carrier-specified list of ERIs for home system
-        String[] homeRoamIndicators = mPhone.getContext().getResources()
+        String[] homeRoamIndicators = Resources.getSystem()
                 .getStringArray(com.android.internal.R.array.config_cdma_home_system);
+        log("isRoamIndForHomeSystem: homeRoamIndicators=" + Arrays.toString(homeRoamIndicators));
 
         if (homeRoamIndicators != null) {
             // searches through the comma-separated list for a match,
@@ -1986,10 +2043,12 @@
                 }
             }
             // no matches found against the list!
+            log("isRoamIndForHomeSystem: No match found against list for roamInd=" + roamInd);
             return false;
         }
 
         // no system property found for the roaming indicators for home system
+        log("isRoamIndForHomeSystem: No list found");
         return false;
     }
 
@@ -2013,14 +2072,15 @@
              * agreements and MVNO's.
              */
             boolean roaming = (mGsmRoaming || mDataRoaming);
-            if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS) &&
-                    (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
+
+            if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS)
+                    && (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
+                log("updateRoamingState: resource override set non roaming.isSameNamedOperators="
+                        + isSameNamedOperators(mNewSS) + ",isOperatorConsideredNonRoaming="
+                        + isOperatorConsideredNonRoaming(mNewSS));
                 roaming = false;
             }
 
-            // Save the roaming state before carrier config possibly overrides it.
-            mNewSS.setDataRoamingFromRegistration(roaming);
-
             CarrierConfigManager configLoader = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
 
@@ -2050,9 +2110,6 @@
             mNewSS.setVoiceRoaming(roaming);
             mNewSS.setDataRoaming(roaming);
         } else {
-            // Save the roaming state before carrier config possibly overrides it.
-            mNewSS.setDataRoamingFromRegistration(mNewSS.getDataRoaming());
-
             CarrierConfigManager configLoader = (CarrierConfigManager)
                     mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
             if (configLoader != null) {
@@ -2132,6 +2189,7 @@
             wfcDataSpnFormat = wfcSpnFormats[dataIdx];
         }
 
+        int combinedRegState = getCombinedRegState();
         if (mPhone.isPhoneTypeGsm()) {
             // The values of plmn/showPlmn change in different scenarios.
             // 1) No service but emergency call allowed -> expected
@@ -2155,8 +2213,8 @@
             String plmn = null;
             boolean showPlmn = false;
             int rule = (iccRecords != null) ? iccRecords.getDisplayRule(mSS.getOperatorNumeric()) : 0;
-            if (mSS.getVoiceRegState() == ServiceState.STATE_OUT_OF_SERVICE
-                    || mSS.getVoiceRegState() == ServiceState.STATE_EMERGENCY_ONLY) {
+            if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE
+                    || combinedRegState == ServiceState.STATE_EMERGENCY_ONLY) {
                 showPlmn = true;
                 if (mEmergencyOnly) {
                     // No service but emergency call allowed
@@ -2169,7 +2227,7 @@
                 }
                 if (DBG) log("updateSpnDisplay: radio is on but out " +
                         "of service, set plmn='" + plmn + "'");
-            } else if (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE) {
+            } else if (combinedRegState == ServiceState.STATE_IN_SERVICE) {
                 // In either home or roaming service
                 plmn = mSS.getOperatorAlpha();
                 showPlmn = !TextUtils.isEmpty(plmn) &&
@@ -2250,7 +2308,7 @@
             mCurDataSpn = dataSpn;
             mCurPlmn = plmn;
         } else {
-            // mOperatorAlphaLong contains the ERI text
+            // mOperatorAlpha contains the ERI text
             String plmn = mSS.getOperatorAlpha();
             boolean showPlmn = false;
 
@@ -2276,6 +2334,14 @@
                 plmn = null;
             }
 
+            if (combinedRegState == ServiceState.STATE_OUT_OF_SERVICE) {
+                plmn = Resources.getSystem().getText(com.android.internal.R.string
+                        .lockscreen_carrier_default).toString();
+                if (DBG) {
+                    log("updateSpnDisplay: radio is on but out of svc, set plmn='" + plmn + "'");
+                }
+            }
+
             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
@@ -2309,12 +2375,14 @@
 
     protected void setPowerStateToDesired() {
         if (DBG) {
-            log("mDeviceShuttingDown=" + mDeviceShuttingDown +
+            String tmpLog = "mDeviceShuttingDown=" + mDeviceShuttingDown +
                     ", mDesiredPowerState=" + mDesiredPowerState +
                     ", getRadioState=" + mCi.getRadioState() +
                     ", mPowerOffDelayNeed=" + mPowerOffDelayNeed +
                     ", mAlarmSwitch=" + mAlarmSwitch +
-                    ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier);
+                    ", mRadioDisabledByCarrier=" + mRadioDisabledByCarrier;
+            log(tmpLog);
+            mRadioPowerLog.log(tmpLog);
         }
 
         if (mPhone.isPhoneTypeGsm() && mAlarmSwitch) {
@@ -2430,16 +2498,13 @@
      * that could support voice and data simultaneously.
      */
     public boolean isConcurrentVoiceAndDataAllowed() {
-        if (mPhone.isPhoneTypeGsm()) {
-            return (mSS.getRilVoiceRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
-        } else if (mPhone.isPhoneTypeCdma()) {
-            // Note: it needs to be confirmed which CDMA network types
-            // can support voice and data calls concurrently.
-            // For the time-being, the return value will be false.
-            return false;
+        if (mSS.getCssIndicator() == 1) {
+            // Checking the Concurrent Service Supported flag first for all phone types.
+            return true;
+        } else if (mPhone.isPhoneTypeGsm()) {
+            return (mSS.getRilDataRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
         } else {
-            // Using the Conncurrent Service Supported flag for CdmaLte devices.
-            return mSS.getCssIndicator() == 1;
+            return false;
         }
     }
 
@@ -2494,6 +2559,8 @@
         mPollingContext = new int[1];
         mPollingContext[0] = 0;
 
+        log("pollState: modemTriggered=" + modemTriggered);
+
         switch (mCi.getRadioState()) {
             case RADIO_UNAVAILABLE:
                 mNewSS.setStateOutOfService();
@@ -2510,11 +2577,11 @@
                 setSignalStrengthDefaultValues();
                 mGotCountryCode = false;
                 mNitzUpdatedTime = false;
-                // don't poll for state when the radio is off
-                // EXCEPT, if the poll was modemTrigged (they sent us new radio data)
-                // or we're on IWLAN
-                if (!modemTriggered && ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                        != mSS.getRilDataRadioTechnology()) {
+                // don't poll when device is shutting down or the poll was not modemTrigged
+                // (they sent us new radio data) and current network is not IWLAN
+                if (mDeviceShuttingDown ||
+                        (!modemTriggered && ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        != mSS.getRilDataRadioTechnology())) {
                     pollStateDone();
                     break;
                 }
@@ -2541,18 +2608,11 @@
         }
     }
 
-    //todo: try to merge pollstate functions
     private void pollStateDone() {
-        if (mPhone.isPhoneTypeGsm()) {
-            pollStateDoneGsm();
-        } else if (mPhone.isPhoneTypeCdma()) {
-            pollStateDoneCdma();
-        } else {
-            pollStateDoneCdmaLte();
+        if (!mPhone.isPhoneTypeGsm()) {
+            updateRoamingState();
         }
-    }
 
-    private void pollStateDoneGsm() {
         if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
             mNewSS.setVoiceRoaming(true);
             mNewSS.setDataRoaming(true);
@@ -2561,12 +2621,12 @@
         resetServiceStateInIwlanMode();
 
         if (DBG) {
-            log("Poll ServiceState done: " +
-                    " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]" +
-                    " oldMaxDataCalls=" + mMaxDataCalls +
-                    " mNewMaxDataCalls=" + mNewMaxDataCalls +
-                    " oldReasonDataDenied=" + mReasonDataDenied +
-                    " mNewReasonDataDenied=" + mNewReasonDataDenied);
+            log("Poll ServiceState done: "
+                    + " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]"
+                    + " oldMaxDataCalls=" + mMaxDataCalls
+                    + " mNewMaxDataCalls=" + mNewMaxDataCalls
+                    + " oldReasonDataDenied=" + mReasonDataDenied
+                    + " mNewReasonDataDenied=" + mNewReasonDataDenied);
         }
 
         boolean hasRegistered =
@@ -2577,11 +2637,11 @@
                 mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
                         && mNewSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
 
-        boolean hasGprsAttached =
+        boolean hasDataAttached =
                 mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
                         && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
 
-        boolean hasGprsDetached =
+        boolean hasDataDetached =
                 mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
                         && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
 
@@ -2595,7 +2655,7 @@
 
         // ratchet the new tech up through it's rat family but don't drop back down
         // until cell change
-        if (hasLocationChanged == false) {
+        if (!hasLocationChanged) {
             mRatRatcheter.ratchetRat(mSS, mNewSS);
         }
 
@@ -2615,37 +2675,99 @@
 
         boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
 
-        TelephonyManager tm =
-                (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        boolean hasRejectCauseChanged = mRejectCode != mNewRejectCode;
+
+        boolean hasCssIndicatorChanged = (mSS.getCssIndicator() != mNewSS.getCssIndicator());
+
+        boolean has4gHandoff = false;
+        boolean hasMultiApnSupport = false;
+        boolean hasLostMultiApnSupport = false;
+        if (mPhone.isPhoneTypeCdmaLte()) {
+            has4gHandoff = mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
+                    && ((ServiceState.isLte(mSS.getRilDataRadioTechnology())
+                    && (mNewSS.getRilDataRadioTechnology()
+                    == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD))
+                    ||
+                    ((mSS.getRilDataRadioTechnology()
+                            == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)
+                            && ServiceState.isLte(mNewSS.getRilDataRadioTechnology())));
+
+            hasMultiApnSupport = ((ServiceState.isLte(mNewSS.getRilDataRadioTechnology())
+                    || (mNewSS.getRilDataRadioTechnology()
+                    == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD))
+                    &&
+                    (!ServiceState.isLte(mSS.getRilDataRadioTechnology())
+                            && (mSS.getRilDataRadioTechnology()
+                            != ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)));
+
+            hasLostMultiApnSupport =
+                    ((mNewSS.getRilDataRadioTechnology()
+                            >= ServiceState.RIL_RADIO_TECHNOLOGY_IS95A)
+                            && (mNewSS.getRilDataRadioTechnology()
+                            <= ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A));
+        }
+
+        if (DBG) {
+            log("pollStateDone:"
+                    + " hasRegistered=" + hasRegistered
+                    + " hasDeregistered=" + hasDeregistered
+                    + " hasDataAttached=" + hasDataAttached
+                    + " hasDataDetached=" + hasDataDetached
+                    + " hasDataRegStateChanged=" + hasDataRegStateChanged
+                    + " hasRilVoiceRadioTechnologyChanged= " + hasRilVoiceRadioTechnologyChanged
+                    + " hasRilDataRadioTechnologyChanged=" + hasRilDataRadioTechnologyChanged
+                    + " hasChanged=" + hasChanged
+                    + " hasVoiceRoamingOn=" + hasVoiceRoamingOn
+                    + " hasVoiceRoamingOff=" + hasVoiceRoamingOff
+                    + " hasDataRoamingOn=" + hasDataRoamingOn
+                    + " hasDataRoamingOff=" + hasDataRoamingOff
+                    + " hasLocationChanged=" + hasLocationChanged
+                    + " has4gHandoff = " + has4gHandoff
+                    + " hasMultiApnSupport=" + hasMultiApnSupport
+                    + " hasLostMultiApnSupport=" + hasLostMultiApnSupport
+                    + " hasCssIndicatorChanged=" + hasCssIndicatorChanged);
+        }
 
         // Add an event log when connection state changes
         if (hasVoiceRegStateChanged || hasDataRegStateChanged) {
-            EventLog.writeEvent(EventLogTags.GSM_SERVICE_STATE_CHANGE,
+            EventLog.writeEvent(mPhone.isPhoneTypeGsm() ? EventLogTags.GSM_SERVICE_STATE_CHANGE :
+                            EventLogTags.CDMA_SERVICE_STATE_CHANGE,
                     mSS.getVoiceRegState(), mSS.getDataRegState(),
                     mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
         }
 
-        // Add an event log when network type switched
-        // TODO: we may add filtering to reduce the event logged,
-        // i.e. check preferred network setting, only switch to 2G, etc
-        if (hasRilVoiceRadioTechnologyChanged) {
-            int cid = -1;
-            GsmCellLocation loc = (GsmCellLocation)mNewCellLoc;
-            if (loc != null) cid = loc.getCid();
-            // NOTE: this code was previously located after mSS and mNewSS are swapped, so
-            // existing logs were incorrectly using the new state for "network_from"
-            // and STATE_OUT_OF_SERVICE for "network_to". To avoid confusion, use a new log tag
-            // to record the correct states.
-            EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED_NEW, cid,
-                    mSS.getRilVoiceRadioTechnology(),
-                    mNewSS.getRilVoiceRadioTechnology());
-            if (DBG) {
-                log("RAT switched "
-                        + ServiceState.rilRadioTechnologyToString(mSS.getRilVoiceRadioTechnology())
-                        + " -> "
-                        + ServiceState.rilRadioTechnologyToString(
-                        mNewSS.getRilVoiceRadioTechnology()) + " at cell " + cid);
+        if (mPhone.isPhoneTypeGsm()) {
+            // Add an event log when network type switched
+            // TODO: we may add filtering to reduce the event logged,
+            // i.e. check preferred network setting, only switch to 2G, etc
+            if (hasRilVoiceRadioTechnologyChanged) {
+                int cid = -1;
+                GsmCellLocation loc = (GsmCellLocation) mNewCellLoc;
+                if (loc != null) cid = loc.getCid();
+                // NOTE: this code was previously located after mSS and mNewSS are swapped, so
+                // existing logs were incorrectly using the new state for "network_from"
+                // and STATE_OUT_OF_SERVICE for "network_to". To avoid confusion, use a new log tag
+                // to record the correct states.
+                EventLog.writeEvent(EventLogTags.GSM_RAT_SWITCHED_NEW, cid,
+                        mSS.getRilVoiceRadioTechnology(),
+                        mNewSS.getRilVoiceRadioTechnology());
+                if (DBG) {
+                    log("RAT switched "
+                            + ServiceState.rilRadioTechnologyToString(
+                            mSS.getRilVoiceRadioTechnology())
+                            + " -> "
+                            + ServiceState.rilRadioTechnologyToString(
+                            mNewSS.getRilVoiceRadioTechnology()) + " at cell " + cid);
+                }
             }
+
+            if (hasCssIndicatorChanged) {
+                mPhone.notifyDataConnection(Phone.REASON_CSS_INDICATOR_CHANGED);
+            }
+
+            mReasonDataDenied = mNewReasonDataDenied;
+            mMaxDataCalls = mNewMaxDataCalls;
+            mRejectCode = mNewRejectCode;
         }
 
         // swap mSS and mNewSS to put new state in mSS
@@ -2656,17 +2778,17 @@
         mNewSS.setStateOutOfService();
 
         // swap mCellLoc and mNewCellLoc to put new state in mCellLoc
-        GsmCellLocation tcl = (GsmCellLocation)mCellLoc;
+        CellLocation tcl = mCellLoc;
         mCellLoc = mNewCellLoc;
         mNewCellLoc = tcl;
 
-        mReasonDataDenied = mNewReasonDataDenied;
-        mMaxDataCalls = mNewMaxDataCalls;
-
         if (hasRilVoiceRadioTechnologyChanged) {
             updatePhoneObject();
         }
 
+        TelephonyManager tm =
+                (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+
         if (hasRilDataRadioTechnologyChanged) {
             tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
 
@@ -2680,71 +2802,70 @@
             mNetworkAttachedRegistrants.notifyRegistrants();
 
             if (DBG) {
-                log("pollStateDone: registering current mNitzUpdatedTime=" +
-                        mNitzUpdatedTime + " changing to false");
+                log("pollStateDone: registering current mNitzUpdatedTime=" + mNitzUpdatedTime
+                        + " changing to false");
             }
             mNitzUpdatedTime = false;
         }
 
-        if (hasChanged) {
-            String operatorNumeric;
+        if (hasDeregistered) {
+            mNetworkDetachedRegistrants.notifyRegistrants();
+        }
 
+        if (hasRejectCauseChanged) {
+            setNotification(mRejectCode == 0 ? CS_REJECT_CAUSE_DISABLED : CS_REJECT_CAUSE_ENABLED);
+        }
+
+        if (hasChanged) {
             updateSpnDisplay();
 
             tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlpha());
 
             String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
-            operatorNumeric = mSS.getOperatorNumeric();
+            String operatorNumeric = mSS.getOperatorNumeric();
+
+            if (!mPhone.isPhoneTypeGsm()) {
+                // try to fix the invalid Operator Numeric
+                if (isInvalidOperatorNumeric(operatorNumeric)) {
+                    int sid = mSS.getSystemId();
+                    operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
+                }
+            }
+
             tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
             updateCarrierMccMncConfiguration(operatorNumeric,
                     prevOperatorNumeric, mPhone.getContext());
-            if (operatorNumeric == null) {
-                if (DBG) log("operatorNumeric is null");
+            if (isInvalidOperatorNumeric(operatorNumeric)) {
+                if (DBG) log("operatorNumeric " + operatorNumeric + " is invalid");
                 tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
                 mGotCountryCode = false;
                 mNitzUpdatedTime = false;
-            } else {
+            } else if (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
+                // Update time zone, ISO, and IDD.
+                //
+                // If the device is on IWLAN, modems manufacture a ServiceState with the MCC/MNC of
+                // the SIM as if we were talking to towers. Telephony code then uses that with
+                // mccTable to suggest a timezone. We shouldn't do that if the MCC/MNC is from IWLAN
+
                 String iso = "";
                 String mcc = "";
-                try{
+                try {
                     mcc = operatorNumeric.substring(0, 3);
                     iso = MccTable.countryCodeForMcc(Integer.parseInt(mcc));
-                } catch ( NumberFormatException ex){
-                    loge("pollStateDone: countryCodeForMcc error" + ex);
-                } catch ( StringIndexOutOfBoundsException ex) {
-                    loge("pollStateDone: countryCodeForMcc error" + ex);
+                } catch (NumberFormatException | StringIndexOutOfBoundsException ex) {
+                    loge("pollStateDone: countryCodeForMcc error: " + ex);
                 }
 
                 tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), iso);
                 mGotCountryCode = true;
 
-                TimeZone zone = null;
+                if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso)
+                        && getAutoTimeZone()) {
+                    updateTimeZoneByNetworkCountryCode(iso);
+                }
 
-                if (!mNitzUpdatedTime && !mcc.equals("000") && !TextUtils.isEmpty(iso) &&
-                        getAutoTimeZone()) {
-
-                    // Test both paths if ignore nitz is true
-                    boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
-                            TelephonyProperties.PROPERTY_IGNORE_NITZ, false) &&
-                            ((SystemClock.uptimeMillis() & 1) == 0);
-
-                    ArrayList<TimeZone> uniqueZones = TimeUtils.getTimeZonesWithUniqueOffsets(iso);
-                    if ((uniqueZones.size() == 1) || testOneUniqueOffsetPath) {
-                        zone = uniqueZones.get(0);
-                        if (DBG) {
-                            log("pollStateDone: no nitz but one TZ for iso-cc=" + iso +
-                                    " with zone.getID=" + zone.getID() +
-                                    " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
-                        }
-                        setAndBroadcastNetworkSetTimeZone(zone.getID());
-                    } else {
-                        if (DBG) {
-                            log("pollStateDone: there are " + uniqueZones.size() +
-                                    " unique offsets for iso-cc='" + iso +
-                                    " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath +
-                                    "', do nothing");
-                        }
-                    }
+                if (!mPhone.isPhoneTypeGsm()) {
+                    setOperatorIdd(operatorNumeric);
                 }
 
                 if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
@@ -2753,24 +2874,33 @@
                 }
             }
 
-            tm.setNetworkRoamingForPhone(mPhone.getPhoneId(), mSS.getVoiceRoaming());
+            tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
+                    mPhone.isPhoneTypeGsm() ? mSS.getVoiceRoaming() :
+                            (mSS.getVoiceRoaming() || mSS.getDataRoaming()));
 
             setRoamingType(mSS);
             log("Broadcasting ServiceState : " + mSS);
+            // notify using PhoneStateListener and the legacy intent ACTION_SERVICE_STATE_CHANGED
             mPhone.notifyServiceStateChanged(mSS);
 
+            // insert into ServiceStateProvider. This will trigger apps to wake through JobScheduler
+            mPhone.getContext().getContentResolver()
+                    .insert(getUriForSubscriptionId(mPhone.getSubId()),
+                            getContentValuesForServiceState(mSS));
+
             TelephonyMetrics.getInstance().writeServiceStateChanged(mPhone.getPhoneId(), mSS);
         }
 
-        if (hasGprsAttached || hasGprsDetached || hasRegistered || hasDeregistered) {
+        if (hasDataAttached || has4gHandoff || hasDataDetached || hasRegistered
+                || hasDeregistered) {
             logAttachChange();
         }
 
-        if (hasGprsAttached) {
+        if (hasDataAttached || has4gHandoff) {
             mAttachedRegistrants.notifyRegistrants();
         }
 
-        if (hasGprsDetached) {
+        if (hasDataDetached) {
             mDetachedRegistrants.notifyRegistrants();
         }
 
@@ -2813,447 +2943,22 @@
             mPhone.notifyLocationChanged();
         }
 
-        if (!isGprsConsistent(mSS.getDataRegState(), mSS.getVoiceRegState())) {
-            if (!mStartedGprsRegCheck && !mReportedGprsNoReg) {
-                mStartedGprsRegCheck = true;
+        if (mPhone.isPhoneTypeGsm()) {
+            if (!isGprsConsistent(mSS.getDataRegState(), mSS.getVoiceRegState())) {
+                if (!mStartedGprsRegCheck && !mReportedGprsNoReg) {
+                    mStartedGprsRegCheck = true;
 
-                int check_period = Settings.Global.getInt(
-                        mPhone.getContext().getContentResolver(),
-                        Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
-                        DEFAULT_GPRS_CHECK_PERIOD_MILLIS);
-                sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS),
-                        check_period);
-            }
-        } else {
-            mReportedGprsNoReg = false;
-        }
-    }
-
-    protected void pollStateDoneCdma() {
-        updateRoamingState();
-
-        useDataRegStateForDataOnlyDevices();
-        resetServiceStateInIwlanMode();
-        if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
-
-        boolean hasRegistered =
-                mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
-                        && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
-
-        boolean hasCdmaDataConnectionAttached =
-                mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
-                        && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
-
-        boolean hasCdmaDataConnectionDetached =
-                mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
-                        && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
-
-        boolean hasCdmaDataConnectionChanged =
-                mSS.getDataRegState() != mNewSS.getDataRegState();
-
-        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
-
-        // ratchet the new tech up through it's rat family but don't drop back down
-        // until cell change
-        if (hasLocationChanged == false) {
-            mRatRatcheter.ratchetRat(mSS, mNewSS);
-        }
-
-        boolean hasRilVoiceRadioTechnologyChanged =
-                mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
-
-        boolean hasRilDataRadioTechnologyChanged =
-                mSS.getRilDataRadioTechnology() != mNewSS.getRilDataRadioTechnology();
-
-        boolean hasChanged = !mNewSS.equals(mSS);
-
-        boolean hasVoiceRoamingOn = !mSS.getVoiceRoaming() && mNewSS.getVoiceRoaming();
-
-        boolean hasVoiceRoamingOff = mSS.getVoiceRoaming() && !mNewSS.getVoiceRoaming();
-
-        boolean hasDataRoamingOn = !mSS.getDataRoaming() && mNewSS.getDataRoaming();
-
-        boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
-
-        TelephonyManager tm =
-                (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
-
-        // Add an event log when connection state changes
-        if (mSS.getVoiceRegState() != mNewSS.getVoiceRegState() ||
-                mSS.getDataRegState() != mNewSS.getDataRegState()) {
-            EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE,
-                    mSS.getVoiceRegState(), mSS.getDataRegState(),
-                    mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
-        }
-
-        ServiceState tss;
-        tss = mSS;
-        mSS = mNewSS;
-        mNewSS = tss;
-        // clean slate for next time
-        mNewSS.setStateOutOfService();
-
-        CdmaCellLocation tcl = (CdmaCellLocation)mCellLoc;
-        mCellLoc = mNewCellLoc;
-        mNewCellLoc = tcl;
-
-        if (hasRilVoiceRadioTechnologyChanged) {
-            updatePhoneObject();
-        }
-
-        if (hasRilDataRadioTechnologyChanged) {
-            tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
-
-            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                    == mSS.getRilDataRadioTechnology()) {
-                log("pollStateDone: IWLAN enabled");
-            }
-        }
-
-        if (hasRegistered) {
-            mNetworkAttachedRegistrants.notifyRegistrants();
-        }
-
-        if (hasChanged) {
-            updateSpnDisplay();
-
-            String operatorNumeric;
-
-            tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlpha());
-
-            String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
-            operatorNumeric = mSS.getOperatorNumeric();
-
-            // try to fix the invalid Operator Numeric
-            if (isInvalidOperatorNumeric(operatorNumeric)) {
-                int sid = mSS.getSystemId();
-                operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
-            }
-
-            tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
-            updateCarrierMccMncConfiguration(operatorNumeric,
-                    prevOperatorNumeric, mPhone.getContext());
-
-            if (isInvalidOperatorNumeric(operatorNumeric)) {
-                if (DBG) log("operatorNumeric "+ operatorNumeric +"is invalid");
-                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
-                mGotCountryCode = false;
-            } else {
-                String isoCountryCode = "";
-                String mcc = operatorNumeric.substring(0, 3);
-                try{
-                    isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(
-                            operatorNumeric.substring(0, 3)));
-                } catch ( NumberFormatException ex){
-                    loge("pollStateDone: countryCodeForMcc error" + ex);
-                } catch ( StringIndexOutOfBoundsException ex) {
-                    loge("pollStateDone: countryCodeForMcc error" + ex);
+                    int check_period = Settings.Global.getInt(
+                            mPhone.getContext().getContentResolver(),
+                            Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
+                            DEFAULT_GPRS_CHECK_PERIOD_MILLIS);
+                    sendMessageDelayed(obtainMessage(EVENT_CHECK_REPORT_GPRS),
+                            check_period);
                 }
-
-                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), isoCountryCode);
-                mGotCountryCode = true;
-
-                setOperatorIdd(operatorNumeric);
-
-                if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
-                        mNeedFixZoneAfterNitz)) {
-                    fixTimeZone(isoCountryCode);
-                }
-            }
-
-            tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
-                    (mSS.getVoiceRoaming() || mSS.getDataRoaming()));
-
-            // set roaming type
-            setRoamingType(mSS);
-            log("Broadcasting ServiceState : " + mSS);
-            mPhone.notifyServiceStateChanged(mSS);
-        }
-
-        if (hasCdmaDataConnectionAttached || hasCdmaDataConnectionDetached || hasRegistered) {
-            logAttachChange();
-        }
-
-        if (hasCdmaDataConnectionAttached) {
-            mAttachedRegistrants.notifyRegistrants();
-        }
-
-        if (hasCdmaDataConnectionDetached) {
-            mDetachedRegistrants.notifyRegistrants();
-        }
-
-        if (hasRilDataRadioTechnologyChanged || hasRilVoiceRadioTechnologyChanged) {
-            logRatChange();
-        }
-
-        if (hasCdmaDataConnectionChanged || hasRilDataRadioTechnologyChanged) {
-            notifyDataRegStateRilRadioTechnologyChanged();
-            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                    == mSS.getRilDataRadioTechnology()) {
-                mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
             } else {
-                mPhone.notifyDataConnection(null);
+                mReportedGprsNoReg = false;
             }
         }
-
-        if (hasVoiceRoamingOn) {
-            mVoiceRoamingOnRegistrants.notifyRegistrants();
-        }
-
-        if (hasVoiceRoamingOff) {
-            mVoiceRoamingOffRegistrants.notifyRegistrants();
-        }
-
-        if (hasVoiceRoamingOn || hasVoiceRoamingOff || hasDataRoamingOn || hasDataRoamingOff) {
-            logRoamingChange();
-        }
-
-        if (hasDataRoamingOn) {
-            mDataRoamingOnRegistrants.notifyRegistrants();
-        }
-
-        if (hasDataRoamingOff) {
-            mDataRoamingOffRegistrants.notifyRegistrants();
-        }
-
-        if (hasLocationChanged) {
-            mPhone.notifyLocationChanged();
-        }
-        // TODO: Add CdmaCellIdenity updating, see CdmaLteServiceStateTracker.
-    }
-
-    protected void pollStateDoneCdmaLte() {
-        updateRoamingState();
-
-        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
-            mNewSS.setVoiceRoaming(true);
-            mNewSS.setDataRoaming(true);
-        }
-
-        useDataRegStateForDataOnlyDevices();
-        resetServiceStateInIwlanMode();
-        log("pollStateDone: lte 1 ss=[" + mSS + "] newSS=[" + mNewSS + "]");
-
-        boolean hasRegistered = mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
-                && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
-
-        boolean hasDeregistered = mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE
-                && mNewSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE;
-
-        boolean hasCdmaDataConnectionAttached =
-                mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE
-                        && mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE;
-
-        boolean hasCdmaDataConnectionDetached =
-                mSS.getDataRegState() == ServiceState.STATE_IN_SERVICE
-                        && mNewSS.getDataRegState() != ServiceState.STATE_IN_SERVICE;
-
-        boolean hasCdmaDataConnectionChanged =
-                mSS.getDataRegState() != mNewSS.getDataRegState();
-
-        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
-
-        // ratchet the new tech up through it's rat family but don't drop back down
-        // until cell change
-        if (hasLocationChanged == false) {
-            mRatRatcheter.ratchetRat(mSS, mNewSS);
-        }
-
-        boolean hasVoiceRadioTechnologyChanged = mSS.getRilVoiceRadioTechnology()
-                != mNewSS.getRilVoiceRadioTechnology();
-
-        boolean hasDataRadioTechnologyChanged = mSS.getRilDataRadioTechnology()
-                != mNewSS.getRilDataRadioTechnology();
-
-        boolean hasChanged = !mNewSS.equals(mSS);
-
-        boolean hasVoiceRoamingOn = !mSS.getVoiceRoaming() && mNewSS.getVoiceRoaming();
-
-        boolean hasVoiceRoamingOff = mSS.getVoiceRoaming() && !mNewSS.getVoiceRoaming();
-
-        boolean hasDataRoamingOn = !mSS.getDataRoaming() && mNewSS.getDataRoaming();
-
-        boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
-
-        boolean has4gHandoff =
-                mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE &&
-                ((ServiceState.isLte(mSS.getRilDataRadioTechnology()) &&
-                (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) ||
-                ((mSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD) &&
-                ServiceState.isLte(mNewSS.getRilDataRadioTechnology())));
-
-        boolean hasMultiApnSupport =
-                ((ServiceState.isLte(mNewSS.getRilDataRadioTechnology()) ||
-                (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) &&
-                (!ServiceState.isLte(mSS.getRilDataRadioTechnology()) &&
-                (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)));
-
-        boolean hasLostMultiApnSupport =
-                ((mNewSS.getRilDataRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_IS95A) &&
-                (mNewSS.getRilDataRadioTechnology() <= ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A));
-
-        TelephonyManager tm =
-                (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
-
-        if (DBG) {
-            log("pollStateDone:"
-                    + " hasRegistered=" + hasRegistered
-                    + " hasDeegistered=" + hasDeregistered
-                    + " hasCdmaDataConnectionAttached=" + hasCdmaDataConnectionAttached
-                    + " hasCdmaDataConnectionDetached=" + hasCdmaDataConnectionDetached
-                    + " hasCdmaDataConnectionChanged=" + hasCdmaDataConnectionChanged
-                    + " hasVoiceRadioTechnologyChanged= " + hasVoiceRadioTechnologyChanged
-                    + " hasDataRadioTechnologyChanged=" + hasDataRadioTechnologyChanged
-                    + " hasChanged=" + hasChanged
-                    + " hasVoiceRoamingOn=" + hasVoiceRoamingOn
-                    + " hasVoiceRoamingOff=" + hasVoiceRoamingOff
-                    + " hasDataRoamingOn=" + hasDataRoamingOn
-                    + " hasDataRoamingOff=" + hasDataRoamingOff
-                    + " hasLocationChanged=" + hasLocationChanged
-                    + " has4gHandoff = " + has4gHandoff
-                    + " hasMultiApnSupport=" + hasMultiApnSupport
-                    + " hasLostMultiApnSupport=" + hasLostMultiApnSupport);
-        }
-        // Add an event log when connection state changes
-        if (mSS.getVoiceRegState() != mNewSS.getVoiceRegState()
-                || mSS.getDataRegState() != mNewSS.getDataRegState()) {
-            EventLog.writeEvent(EventLogTags.CDMA_SERVICE_STATE_CHANGE, mSS.getVoiceRegState(),
-                    mSS.getDataRegState(), mNewSS.getVoiceRegState(), mNewSS.getDataRegState());
-        }
-
-        ServiceState tss;
-        tss = mSS;
-        mSS = mNewSS;
-        mNewSS = tss;
-        // clean slate for next time
-        mNewSS.setStateOutOfService();
-
-        CdmaCellLocation tcl = (CdmaCellLocation)mCellLoc;
-        mCellLoc = mNewCellLoc;
-        mNewCellLoc = tcl;
-
-        mNewSS.setStateOutOfService(); // clean slate for next time
-
-        if (hasVoiceRadioTechnologyChanged) {
-            updatePhoneObject();
-        }
-
-        if (hasDataRadioTechnologyChanged) {
-            tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
-
-            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                    == mSS.getRilDataRadioTechnology()) {
-                log("pollStateDone: IWLAN enabled");
-            }
-        }
-
-        if (hasRegistered) {
-            mNetworkAttachedRegistrants.notifyRegistrants();
-        }
-
-        if (hasChanged) {
-            updateSpnDisplay();
-
-            String operatorNumeric;
-
-            tm.setNetworkOperatorNameForPhone(mPhone.getPhoneId(), mSS.getOperatorAlphaLong());
-
-            String prevOperatorNumeric = tm.getNetworkOperatorForPhone(mPhone.getPhoneId());
-            operatorNumeric = mSS.getOperatorNumeric();
-            // try to fix the invalid Operator Numeric
-            if (isInvalidOperatorNumeric(operatorNumeric)) {
-                int sid = mSS.getSystemId();
-                operatorNumeric = fixUnknownMcc(operatorNumeric, sid);
-            }
-            tm.setNetworkOperatorNumericForPhone(mPhone.getPhoneId(), operatorNumeric);
-            updateCarrierMccMncConfiguration(operatorNumeric,
-                    prevOperatorNumeric, mPhone.getContext());
-
-            if (isInvalidOperatorNumeric(operatorNumeric)) {
-                if (DBG) log("operatorNumeric is null");
-                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), "");
-                mGotCountryCode = false;
-            } else {
-                String isoCountryCode = "";
-                String mcc = operatorNumeric.substring(0, 3);
-                try {
-                    isoCountryCode = MccTable.countryCodeForMcc(Integer.parseInt(operatorNumeric
-                            .substring(0, 3)));
-                } catch (NumberFormatException ex) {
-                    loge("countryCodeForMcc error" + ex);
-                } catch (StringIndexOutOfBoundsException ex) {
-                    loge("countryCodeForMcc error" + ex);
-                }
-
-                tm.setNetworkCountryIsoForPhone(mPhone.getPhoneId(), isoCountryCode);
-                mGotCountryCode = true;
-
-                setOperatorIdd(operatorNumeric);
-
-                if (shouldFixTimeZoneNow(mPhone, operatorNumeric, prevOperatorNumeric,
-                        mNeedFixZoneAfterNitz)) {
-                    fixTimeZone(isoCountryCode);
-                }
-            }
-
-            tm.setNetworkRoamingForPhone(mPhone.getPhoneId(),
-                    (mSS.getVoiceRoaming() || mSS.getDataRoaming()));
-
-            setRoamingType(mSS);
-            log("Broadcasting ServiceState : " + mSS);
-            mPhone.notifyServiceStateChanged(mSS);
-        }
-
-        if (hasCdmaDataConnectionAttached || has4gHandoff || hasCdmaDataConnectionDetached ||
-                hasRegistered || hasDeregistered) {
-            logAttachChange();
-        }
-
-        if (hasCdmaDataConnectionAttached || has4gHandoff) {
-            mAttachedRegistrants.notifyRegistrants();
-        }
-
-        if (hasCdmaDataConnectionDetached) {
-            mDetachedRegistrants.notifyRegistrants();
-        }
-
-        if (hasDataRadioTechnologyChanged || hasVoiceRadioTechnologyChanged) {
-            logRatChange();
-        }
-
-        if ((hasCdmaDataConnectionChanged || hasDataRadioTechnologyChanged)) {
-            notifyDataRegStateRilRadioTechnologyChanged();
-            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                    == mSS.getRilDataRadioTechnology()) {
-                mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
-            } else {
-                mPhone.notifyDataConnection(null);
-            }
-        }
-
-        if (hasVoiceRoamingOn || hasVoiceRoamingOff || hasDataRoamingOn || hasDataRoamingOff) {
-            logRoamingChange();
-        }
-
-        if (hasVoiceRoamingOn) {
-            mVoiceRoamingOnRegistrants.notifyRegistrants();
-        }
-
-        if (hasVoiceRoamingOff) {
-            mVoiceRoamingOffRegistrants.notifyRegistrants();
-        }
-
-        if (hasDataRoamingOn) {
-            mDataRoamingOnRegistrants.notifyRegistrants();
-        }
-
-        if (hasDataRoamingOff) {
-            mDataRoamingOffRegistrants.notifyRegistrants();
-        }
-
-        if (hasLocationChanged) {
-            mPhone.notifyLocationChanged();
-        }
     }
 
     private void updateOperatorNameFromEri() {
@@ -3300,7 +3005,7 @@
             }
 
             if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY &&
-                    mIccRecords != null && (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)
+                    mIccRecords != null && getCombinedRegState() == ServiceState.STATE_IN_SERVICE
                     && !ServiceState.isLte(mSS.getRilVoiceRadioTechnology())) {
                 // 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. Note that ERI, SID,
@@ -3400,7 +3105,7 @@
         TimeZone zone = null;
         // If the offset is (0, false) and the time zone property
         // is set, use the time zone property rather than GMT.
-        String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
+        final String zoneName = SystemProperties.get(TIMEZONE_PROPERTY);
         if (DBG) {
             log("fixTimeZone zoneName='" + zoneName +
                     "' mZoneOffset=" + mZoneOffset + " mZoneDst=" + mZoneDst +
@@ -3441,7 +3146,10 @@
             if (DBG) log("fixTimeZone: using getTimeZone(off, dst, time, iso)");
         }
 
-        mNeedFixZoneAfterNitz = false;
+        final String tmpLog = "fixTimeZone zoneName=" + zoneName + " mZoneOffset=" + mZoneOffset
+                + " mZoneDst=" + mZoneDst + " iso-cc=" + isoCountryCode + " mNeedFixZoneAfterNitz="
+                + mNeedFixZoneAfterNitz + " zone=" + (zone != null ? zone.getID() : "NULL");
+        mTimeZoneLog.log(tmpLog);
 
         if (zone != null) {
             log("fixTimeZone: zone != null zone.getID=" + zone.getID());
@@ -3450,10 +3158,13 @@
             } else {
                 log("fixTimeZone: skip changing zone as getAutoTimeZone was false");
             }
-            saveNitzTimeZone(zone.getID());
+            if (mNeedFixZoneAfterNitz) {
+                saveNitzTimeZone(zone.getID());
+            }
         } else {
             log("fixTimeZone: zone == null, do nothing for zone");
         }
+        mNeedFixZoneAfterNitz = false;
     }
 
     /**
@@ -3501,25 +3212,14 @@
         return guess;
     }
 
-    /** code is registration state 0-5 from TS 27.007 7.2 */
+    /** convert ServiceState registration code
+     * to service state */
     private int regCodeToServiceState(int code) {
         switch (code) {
-            case 0:
-            case 2: // 2 is "searching"
-            case 3: // 3 is "registration denied"
-            case 4: // 4 is "unknown" no vaild in current baseband
-            case 10:// same as 0, but indicates that emergency call is possible.
-            case 12:// same as 2, but indicates that emergency call is possible.
-            case 13:// same as 3, but indicates that emergency call is possible.
-            case 14:// same as 4, but indicates that emergency call is possible.
-                return ServiceState.STATE_OUT_OF_SERVICE;
-
-            case 1:
-            case 5: // 5 is "registered, roaming"
+            case ServiceState.RIL_REG_STATE_HOME:
+            case ServiceState.RIL_REG_STATE_ROAMING:
                 return ServiceState.STATE_IN_SERVICE;
-
             default:
-                loge("regCodeToServiceState: unexpected service state " + code);
                 return ServiceState.STATE_OUT_OF_SERVICE;
         }
     }
@@ -3583,7 +3283,8 @@
     /**
      * Do not set roaming state in case of oprators considered non-roaming.
      *
-     * Can use mcc or mcc+mnc as item of config_operatorConsideredNonRoaming.
+     * Can use mcc or mcc+mnc as item of
+     * {@link CarrierConfigManager#KEY_NON_ROAMING_OPERATOR_STRING_ARRAY}.
      * For example, 302 or 21407. If mcc or mcc+mnc match with operator,
      * don't set roaming state.
      *
@@ -3592,15 +3293,22 @@
      */
     private boolean isOperatorConsideredNonRoaming(ServiceState s) {
         String operatorNumeric = s.getOperatorNumeric();
-        String[] numericArray = mPhone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_operatorConsideredNonRoaming);
-
-        if (numericArray.length == 0 || operatorNumeric == null) {
+        final CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        String[] numericArray = null;
+        if (configManager != null) {
+            PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+            if (config != null) {
+                numericArray = config.getStringArray(
+                        CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY);
+            }
+        }
+        if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
             return false;
         }
 
         for (String numeric : numericArray) {
-            if (operatorNumeric.startsWith(numeric)) {
+            if (!TextUtils.isEmpty(numeric) && operatorNumeric.startsWith(numeric)) {
                 return true;
             }
         }
@@ -3609,15 +3317,22 @@
 
     private boolean isOperatorConsideredRoaming(ServiceState s) {
         String operatorNumeric = s.getOperatorNumeric();
-        String[] numericArray = mPhone.getContext().getResources().getStringArray(
-                com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming);
-
-        if (numericArray.length == 0 || operatorNumeric == null) {
+        final CarrierConfigManager configManager = (CarrierConfigManager) mPhone.getContext()
+                .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        String[] numericArray = null;
+        if (configManager != null) {
+            PersistableBundle config = configManager.getConfigForSubId(mPhone.getSubId());
+            if (config != null) {
+                numericArray = config.getStringArray(
+                        CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY);
+            }
+        }
+        if (ArrayUtils.isEmpty(numericArray) || operatorNumeric == null) {
             return false;
         }
 
         for (String numeric : numericArray) {
-            if (operatorNumeric.startsWith(numeric)) {
+            if (!TextUtils.isEmpty(numeric) && operatorNumeric.startsWith(numeric)) {
                 return true;
             }
         }
@@ -3636,9 +3351,8 @@
 
         if (DBG) log("onRestrictedStateChanged: E rs "+ mRestrictedState);
 
-        if (ar.exception == null) {
-            int[] ints = (int[])ar.result;
-            int state = ints[0];
+        if (ar.exception == null && ar.result != null) {
+            int state = (int)ar.result;
 
             newRs.setCsEmergencyRestricted(
                     ((state & RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY) != 0) ||
@@ -3668,7 +3382,7 @@
              * and we only need to notify when state is changed.
              */
             if (mRestrictedState.isCsRestricted()) {
-                if (!newRs.isCsRestricted()) {
+                if (!newRs.isAnyCsRestricted()) {
                     // remove all restriction
                     setNotification(CS_DISABLED);
                 } else if (!newRs.isCsNormalRestricted()) {
@@ -3680,7 +3394,7 @@
                 }
             } else if (mRestrictedState.isCsEmergencyRestricted() &&
                     !mRestrictedState.isCsNormalRestricted()) {
-                if (!newRs.isCsRestricted()) {
+                if (!newRs.isAnyCsRestricted()) {
                     // remove all restriction
                     setNotification(CS_DISABLED);
                 } else if (newRs.isCsRestricted()) {
@@ -3692,7 +3406,7 @@
                 }
             } else if (!mRestrictedState.isCsEmergencyRestricted() &&
                     mRestrictedState.isCsNormalRestricted()) {
-                if (!newRs.isCsRestricted()) {
+                if (!newRs.isAnyCsRestricted()) {
                     // remove all restriction
                     setNotification(CS_DISABLED);
                 } else if (newRs.isCsRestricted()) {
@@ -3721,16 +3435,17 @@
     }
 
     /**
+     * @param workSource calling WorkSource
      * @return the current cell location information. Prefer Gsm location
      * information if available otherwise return LTE location information
      */
-    public CellLocation getCellLocation() {
+    public CellLocation getCellLocation(WorkSource workSource) {
         if (((GsmCellLocation)mCellLoc).getLac() >= 0 &&
                 ((GsmCellLocation)mCellLoc).getCid() >= 0) {
-            if (DBG) log("getCellLocation(): X good mCellLoc=" + mCellLoc);
+            if (VDBG) log("getCellLocation(): X good mCellLoc=" + mCellLoc);
             return mCellLoc;
         } else {
-            List<CellInfo> result = getAllCellInfo();
+            List<CellInfo> result = getAllCellInfo(workSource);
             if (result != null) {
                 // A hack to allow tunneling of LTE information via GsmCellLocation
                 // so that older Network Location Providers can return some information
@@ -3752,7 +3467,7 @@
                         cellLocOther.setLacAndCid(cellIdentityGsm.getLac(),
                                 cellIdentityGsm.getCid());
                         cellLocOther.setPsc(cellIdentityGsm.getPsc());
-                        if (DBG) log("getCellLocation(): X ret GSM info=" + cellLocOther);
+                        if (VDBG) log("getCellLocation(): X ret GSM info=" + cellLocOther);
                         return cellLocOther;
                     } else if (ci instanceof CellInfoWcdma) {
                         CellInfoWcdma cellInfoWcdma = (CellInfoWcdma)ci;
@@ -3760,7 +3475,7 @@
                         cellLocOther.setLacAndCid(cellIdentityWcdma.getLac(),
                                 cellIdentityWcdma.getCid());
                         cellLocOther.setPsc(cellIdentityWcdma.getPsc());
-                        if (DBG) log("getCellLocation(): X ret WCDMA info=" + cellLocOther);
+                        if (VDBG) log("getCellLocation(): X ret WCDMA info=" + cellLocOther);
                         return cellLocOther;
                     } else if ((ci instanceof CellInfoLte) &&
                             ((cellLocOther.getLac() < 0) || (cellLocOther.getCid() < 0))) {
@@ -3772,18 +3487,18 @@
                             cellLocOther.setLacAndCid(cellIdentityLte.getTac(),
                                     cellIdentityLte.getCi());
                             cellLocOther.setPsc(0);
-                            if (DBG) {
+                            if (VDBG) {
                                 log("getCellLocation(): possible LTE cellLocOther=" + cellLocOther);
                             }
                         }
                     }
                 }
-                if (DBG) {
+                if (VDBG) {
                     log("getCellLocation(): X ret best answer cellLocOther=" + cellLocOther);
                 }
                 return cellLocOther;
             } else {
-                if (DBG) {
+                if (VDBG) {
                     log("getCellLocation(): X empty mCellLoc and CellInfo mCellLoc=" + mCellLoc);
                 }
                 return mCellLoc;
@@ -3799,8 +3514,9 @@
         // tz is in number of quarter-hours
 
         long start = SystemClock.elapsedRealtime();
-        if (DBG) {log("NITZ: " + nitz + "," + nitzReceiveTime +
-                " start=" + start + " delay=" + (start - nitzReceiveTime));
+        if (DBG) {
+            log("NITZ: " + nitz + "," + nitzReceiveTime
+                    + " start=" + start + " delay=" + (start - nitzReceiveTime));
         }
 
         try {
@@ -3893,12 +3609,17 @@
                 mZoneDst     = dst != 0;
                 mZoneTime    = c.getTimeInMillis();
             }
+
+            String tmpLog = "NITZ: nitz=" + nitz + " nitzReceiveTime=" + nitzReceiveTime
+                    + " tzOffset=" + tzOffset + " dst=" + dst + " zone="
+                    + (zone != null ? zone.getID() : "NULL")
+                    + " iso=" + iso + " mGotCountryCode=" + mGotCountryCode
+                    + " mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz
+                    + " getAutoTimeZone()=" + getAutoTimeZone();
             if (DBG) {
-                log("NITZ: tzOffset=" + tzOffset + " dst=" + dst + " zone=" +
-                        (zone!=null ? zone.getID() : "NULL") +
-                        " iso=" + iso + " mGotCountryCode=" + mGotCountryCode +
-                        " mNeedFixZoneAfterNitz=" + mNeedFixZoneAfterNitz);
+                log(tmpLog);
             }
+            mTimeZoneLog.log(tmpLog);
 
             if (zone != null) {
                 if (getAutoTimeZone()) {
@@ -3943,13 +3664,16 @@
                     // Note: with range checks above, cast to int is safe
                     c.add(Calendar.MILLISECOND, (int)millisSinceNitzReceived);
 
+                    tmpLog = "NITZ: nitz=" + nitz + " nitzReceiveTime=" + nitzReceiveTime
+                            + " Setting time of day to " + c.getTime()
+                            + " NITZ receive delay(ms): " + millisSinceNitzReceived
+                            + " gained(ms): "
+                            + (c.getTimeInMillis() - System.currentTimeMillis())
+                            + " from " + nitz;
                     if (DBG) {
-                        log("NITZ: Setting time of day to " + c.getTime()
-                                + " NITZ receive delay(ms): " + millisSinceNitzReceived
-                                + " gained(ms): "
-                                + (c.getTimeInMillis() - System.currentTimeMillis())
-                                + " from " + nitz);
+                        log(tmpLog);
                     }
+                    mTimeLog.log(tmpLog);
                     if (mPhone.isPhoneTypeGsm()) {
                         setAndBroadcastNetworkSetTime(c.getTimeInMillis());
                         Rlog.i(LOG_TAG, "NITZ: after Setting time of day");
@@ -4071,8 +3795,10 @@
                     mSavedAtTime);
         }
         if (mSavedTime != 0 && mSavedAtTime != 0) {
-            setAndBroadcastNetworkSetTime(mSavedTime
-                    + (SystemClock.elapsedRealtime() - mSavedAtTime));
+            long currTime = SystemClock.elapsedRealtime();
+            mTimeLog.log("Reverting to NITZ time, currTime=" + currTime
+                    + " mSavedAtTime=" + mSavedAtTime + " mSavedTime=" + mSavedTime);
+            setAndBroadcastNetworkSetTime(mSavedTime + (currTime - mSavedAtTime));
         }
     }
 
@@ -4080,18 +3806,28 @@
         if (Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 0) == 0) {
             return;
         }
-        if (DBG) log("Reverting to NITZ TimeZone: tz='" + mSavedTimeZone);
+        String tmpLog = "Reverting to NITZ TimeZone: tz=" + mSavedTimeZone;
+        if (DBG) log(tmpLog);
+        mTimeZoneLog.log(tmpLog);
         if (mSavedTimeZone != null) {
             setAndBroadcastNetworkSetTimeZone(mSavedTimeZone);
+        } else {
+            String iso = ((TelephonyManager) mPhone.getContext().getSystemService(
+                    Context.TELEPHONY_SERVICE)).getNetworkCountryIsoForPhone(mPhone.getPhoneId());
+            if (!TextUtils.isEmpty(iso)) {
+                updateTimeZoneByNetworkCountryCode(iso);
+            }
         }
     }
 
     /**
-     * Post a notification to NotificationManager for restricted state
+     * Post a notification to NotificationManager for restricted state and
+     * rejection cause for cs registration
      *
      * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE
      */
-    private void setNotification(int notifyType) {
+    @VisibleForTesting
+    public void setNotification(int notifyType) {
         if (DBG) log("setNotification: create notification " + notifyType);
 
         // Needed because sprout RIL sends these when they shouldn't?
@@ -4104,10 +3840,26 @@
 
         Context context = mPhone.getContext();
 
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager != null) {
+            PersistableBundle bundle = configManager.getConfig();
+            if (bundle != null) {
+                boolean disableVoiceBarringNotification = bundle.getBoolean(
+                        CarrierConfigManager.KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
+                if(disableVoiceBarringNotification && (notifyType == CS_ENABLED
+                        || notifyType == CS_NORMAL_ENABLED
+                        || notifyType == CS_EMERGENCY_ENABLED)) {
+                    if (DBG) log("Voice/emergency call barred notification disabled");
+                    return;
+                }
+            }
+        }
 
         CharSequence details = "";
-        CharSequence title = context.getText(com.android.internal.R.string.RestrictedOnData);
+        CharSequence title = "";
         int notificationId = CS_NOTIFICATION;
+        int icon = com.android.internal.R.drawable.stat_sys_warning;
 
         switch (notifyType) {
             case PS_ENABLED:
@@ -4116,41 +3868,68 @@
                     return;
                 }
                 notificationId = PS_NOTIFICATION;
-                details = context.getText(com.android.internal.R.string.RestrictedOnData);
+                title = context.getText(com.android.internal.R.string.RestrictedOnDataTitle);
+                details = context.getText(com.android.internal.R.string.RestrictedStateContent);
                 break;
             case PS_DISABLED:
                 notificationId = PS_NOTIFICATION;
                 break;
             case CS_ENABLED:
-                details = context.getText(com.android.internal.R.string.RestrictedOnAllVoice);
+                title = context.getText(com.android.internal.R.string.RestrictedOnAllVoiceTitle);
+                details = context.getText(
+                        com.android.internal.R.string.RestrictedStateContent);
                 break;
             case CS_NORMAL_ENABLED:
-                details = context.getText(com.android.internal.R.string.RestrictedOnNormal);
+                title = context.getText(com.android.internal.R.string.RestrictedOnNormalTitle);
+                details = context.getText(com.android.internal.R.string.RestrictedStateContent);
                 break;
             case CS_EMERGENCY_ENABLED:
-                details = context.getText(com.android.internal.R.string.RestrictedOnEmergency);
+                title = context.getText(com.android.internal.R.string.RestrictedOnEmergencyTitle);
+                details = context.getText(
+                        com.android.internal.R.string.RestrictedStateContent);
                 break;
             case CS_DISABLED:
                 // do nothing and cancel the notification later
                 break;
+            case CS_REJECT_CAUSE_ENABLED:
+                notificationId = CS_REJECT_CAUSE_NOTIFICATION;
+                int resId = selectResourceForRejectCode(mRejectCode);
+                if (0 == resId) {
+                    // cancel notification because current reject code is not handled.
+                    notifyType = CS_REJECT_CAUSE_DISABLED;
+                } else {
+                    icon = com.android.internal.R.drawable.stat_notify_mmcc_indication_icn;
+                    title = Resources.getSystem().getString(resId);
+                    details = null;
+                }
+                break;
+            case CS_REJECT_CAUSE_DISABLED:
+                notificationId = CS_REJECT_CAUSE_NOTIFICATION;
+                break;
         }
 
-        if (DBG) log("setNotification: put notification " + title + " / " +details);
+        if (DBG) {
+            log("setNotification, create notification, notifyType: " + notifyType
+                    + ", title: " + title + ", details: " + details);
+        }
+
         mNotification = new Notification.Builder(context)
                 .setWhen(System.currentTimeMillis())
                 .setAutoCancel(true)
-                .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+                .setSmallIcon(icon)
                 .setTicker(title)
                 .setColor(context.getResources().getColor(
                         com.android.internal.R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setContentText(details)
+                .setChannel(NotificationChannelController.CHANNEL_ID_ALERT)
                 .build();
 
         NotificationManager notificationManager = (NotificationManager)
                 context.getSystemService(Context.NOTIFICATION_SERVICE);
 
-        if (notifyType == PS_DISABLED || notifyType == CS_DISABLED) {
+        if (notifyType == PS_DISABLED || notifyType == CS_DISABLED
+                || notifyType == CS_REJECT_CAUSE_DISABLED) {
             // cancel previous post notification
             notificationManager.cancel(notificationId);
         } else {
@@ -4159,6 +3938,34 @@
         }
     }
 
+    /**
+     * Selects the resource ID, which depends on rejection cause that is sent by the network when CS
+     * registration is rejected.
+     *
+     * @param rejCode should be compatible with TS 24.008.
+     */
+    private int selectResourceForRejectCode(int rejCode) {
+        int rejResourceId = 0;
+        switch (rejCode) {
+            case 1:// Authentication reject
+                rejResourceId = com.android.internal.R.string.mmcc_authentication_reject;
+                break;
+            case 2:// IMSI unknown in HLR
+                rejResourceId = com.android.internal.R.string.mmcc_imsi_unknown_in_hlr;
+                break;
+            case 3:// Illegal MS
+                rejResourceId = com.android.internal.R.string.mmcc_illegal_ms;
+                break;
+            case 6:// Illegal ME
+                rejResourceId = com.android.internal.R.string.mmcc_illegal_me;
+                break;
+            default:
+                // The other codes are not defined or not required by operators till now.
+                break;
+        }
+        return rejResourceId;
+    }
+
     private UiccCardApplication getUiccCardApplication() {
         if (mPhone.isPhoneTypeGsm()) {
             return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
@@ -4262,11 +4069,31 @@
             r.notifyRegistrant();
         }
     }
+
     public void unregisterForNetworkAttached(Handler h) {
         mNetworkAttachedRegistrants.remove(h);
     }
 
     /**
+     * Registration point for transition into network detached.
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj in Message.obj
+     */
+    public void registerForNetworkDetached(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+
+        mNetworkDetachedRegistrants.add(r);
+        if (mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE) {
+            r.notifyRegistrant();
+        }
+    }
+
+    public void unregisterForNetworkDetached(Handler h) {
+        mNetworkDetachedRegistrants.remove(h);
+    }
+
+    /**
      * Registration point for transition into packet service restricted zone.
      * @param h handler to notify
      * @param what what code of message when delivered
@@ -4417,6 +4244,97 @@
     }
 
     /**
+     * Checks if the provided earfcn falls withing the range of earfcns.
+     *
+     * return true if earfcn falls within the provided range; false otherwise.
+     */
+    private boolean containsEarfcnInEarfcnRange(ArrayList<Pair<Integer, Integer>> earfcnPairList,
+            int earfcn) {
+        if (earfcnPairList != null) {
+            for (Pair<Integer, Integer> earfcnPair : earfcnPairList) {
+                if ((earfcn >= earfcnPair.first) && (earfcn <= earfcnPair.second)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Convert the earfcnStringArray to list of pairs.
+     *
+     * Format of the earfcnsList is expected to be {"erafcn1_start-earfcn1_end",
+     * "earfcn2_start-earfcn2_end" ... }
+     */
+    ArrayList<Pair<Integer, Integer>> convertEarfcnStringArrayToPairList(String[] earfcnsList) {
+        ArrayList<Pair<Integer, Integer>> earfcnPairList = new ArrayList<Pair<Integer, Integer>>();
+
+        if (earfcnsList != null) {
+            int earfcnStart;
+            int earfcnEnd;
+            for (int i = 0; i < earfcnsList.length; i++) {
+                try {
+                    String[] earfcns = earfcnsList[i].split("-");
+                    if (earfcns.length != 2) {
+                        if (VDBG) {
+                            log("Invalid earfcn range format");
+                        }
+                        return null;
+                    }
+
+                    earfcnStart = Integer.parseInt(earfcns[0]);
+                    earfcnEnd = Integer.parseInt(earfcns[1]);
+
+                    if (earfcnStart > earfcnEnd) {
+                        if (VDBG) {
+                            log("Invalid earfcn range format");
+                        }
+                        return null;
+                    }
+
+                    earfcnPairList.add(new Pair<Integer, Integer>(earfcnStart, earfcnEnd));
+                } catch (PatternSyntaxException pse) {
+                    if (VDBG) {
+                        log("Invalid earfcn range format");
+                    }
+                    return null;
+                } catch (NumberFormatException nfe) {
+                    if (VDBG) {
+                        log("Invalid earfcn number format");
+                    }
+                    return null;
+                }
+            }
+        }
+
+        return earfcnPairList;
+    }
+    private void updateLteEarfcnLists() {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        PersistableBundle b = configManager.getConfigForSubId(mPhone.getSubId());
+        synchronized (mLteRsrpBoostLock) {
+            mLteRsrpBoost = b.getInt(CarrierConfigManager.KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
+            String[] earfcnsStringArrayForRsrpBoost = b.getStringArray(
+                    CarrierConfigManager.KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY);
+            mEarfcnPairListForRsrpBoost = convertEarfcnStringArrayToPairList(
+                    earfcnsStringArrayForRsrpBoost);
+        }
+    }
+
+    private void updateServiceStateLteEarfcnBoost(ServiceState serviceState, int lteEarfcn) {
+        synchronized (mLteRsrpBoostLock) {
+            if ((lteEarfcn != INVALID_LTE_EARFCN)
+                    && containsEarfcnInEarfcnRange(mEarfcnPairListForRsrpBoost, lteEarfcn)) {
+                serviceState.setLteEarfcnRsrpBoost(mLteRsrpBoost);
+            } else {
+                serviceState.setLteEarfcnRsrpBoost(0);
+            }
+        }
+    }
+
+    /**
      * send signal-strength-changed notification if changed Called both for
      * solicited and unsolicited signal strength updates
      *
@@ -4424,10 +4342,15 @@
      */
     protected boolean onSignalStrengthResult(AsyncResult ar) {
         boolean isGsm = false;
-        //override isGsm for CDMA LTE
-        if (mPhone.isPhoneTypeGsm() ||
-                (mPhone.isPhoneTypeCdmaLte() &&
-                        ServiceState.isLte(mSS.getRilDataRadioTechnology()))) {
+        int dataRat = mSS.getRilDataRadioTechnology();
+        int voiceRat = mSS.getRilVoiceRadioTechnology();
+
+        // Override isGsm based on currently camped data and voice RATs
+        // Set isGsm to true if the RAT belongs to GSM family and not IWLAN
+        if ((dataRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                && ServiceState.isGsm(dataRat))
+                || (voiceRat != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                && ServiceState.isGsm(voiceRat))) {
             isGsm = true;
         }
 
@@ -4438,6 +4361,7 @@
             mSignalStrength = (SignalStrength) ar.result;
             mSignalStrength.validateInput();
             mSignalStrength.setGsm(isGsm);
+            mSignalStrength.setLteRsrpBoost(mSS.getLteEarfcnRsrpBoost());
         } else {
             log("onSignalStrengthResult() Exception from RIL : " + ar.exception);
             mSignalStrength = new SignalStrength(isGsm);
@@ -4533,7 +4457,7 @@
     /**
      * @return all available cell information or null if none.
      */
-    public List<CellInfo> getAllCellInfo() {
+    public List<CellInfo> getAllCellInfo(WorkSource workSource) {
         CellInfoResult result = new CellInfoResult();
         if (VDBG) log("SST.getAllCellInfo(): E");
         int ver = mCi.getRilVersion();
@@ -4544,7 +4468,7 @@
                     Message msg = obtainMessage(EVENT_GET_CELL_INFO_LIST, result);
                     synchronized(result.lockObj) {
                         result.list = null;
-                        mCi.getCellInfoList(msg);
+                        mCi.getCellInfoList(msg, workSource);
                         try {
                             result.lockObj.wait(5000);
                         } catch (InterruptedException e) {
@@ -4633,6 +4557,24 @@
         }
     }
 
+    private void dumpEarfcnPairList(PrintWriter pw) {
+        pw.print(" mEarfcnPairListForRsrpBoost={");
+        if (mEarfcnPairListForRsrpBoost != null) {
+            int i = mEarfcnPairListForRsrpBoost.size();
+            for (Pair<Integer, Integer> earfcnPair : mEarfcnPairListForRsrpBoost) {
+                pw.print("(");
+                pw.print(earfcnPair.first);
+                pw.print(",");
+                pw.print(earfcnPair.second);
+                pw.print(")");
+                if ((--i) != 0) {
+                    pw.print(",");
+                }
+            }
+        }
+        pw.println("}");
+    }
+
     private void dumpCellInfoList(PrintWriter pw) {
         pw.print(" mLastCellInfoList={");
         if(mLastCellInfoList != null) {
@@ -4664,8 +4606,8 @@
         pw.println(" mRestrictedState=" + mRestrictedState);
         pw.println(" mPendingRadioPowerOffAfterDataOff=" + mPendingRadioPowerOffAfterDataOff);
         pw.println(" mPendingRadioPowerOffAfterDataOffTag=" + mPendingRadioPowerOffAfterDataOffTag);
-        pw.println(" mCellLoc=" + mCellLoc);
-        pw.println(" mNewCellLoc=" + mNewCellLoc);
+        pw.println(" mCellLoc=" + Rlog.pii(VDBG, mCellLoc));
+        pw.println(" mNewCellLoc=" + Rlog.pii(VDBG, mNewCellLoc));
         pw.println(" mLastCellInfoListTime=" + mLastCellInfoListTime);
         dumpCellInfoList(pw);
         pw.flush();
@@ -4721,6 +4663,8 @@
         pw.println(" mPowerOffDelayNeed=" + mPowerOffDelayNeed);
         pw.println(" mDeviceShuttingDown=" + mDeviceShuttingDown);
         pw.println(" mSpnUpdatePending=" + mSpnUpdatePending);
+        pw.println(" mLteRsrpBoost=" + mLteRsrpBoost);
+        dumpEarfcnPairList(pw);
 
         pw.println(" Roaming Log:");
         IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
@@ -4742,6 +4686,20 @@
         ipw.increaseIndent();
         mRatLog.dump(fd, ipw, args);
         ipw.decreaseIndent();
+
+        ipw.println(" Radio power Log:");
+        ipw.increaseIndent();
+        mRadioPowerLog.dump(fd, ipw, args);
+
+        ipw.println(" Time Logs:");
+        ipw.increaseIndent();
+        mTimeLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+
+        ipw.println(" Time zone Logs:");
+        ipw.increaseIndent();
+        mTimeZoneLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
     }
 
     public boolean isImsRegistered() {
@@ -5000,4 +4958,50 @@
     public boolean isDeviceShuttingDown() {
         return mDeviceShuttingDown;
     }
+
+    /**
+     * Consider dataRegState if voiceRegState is OOS to determine SPN to be displayed
+     */
+    protected int getCombinedRegState() {
+        int regState = mSS.getVoiceRegState();
+        int dataRegState = mSS.getDataRegState();
+        if ((regState == ServiceState.STATE_OUT_OF_SERVICE
+                || regState == ServiceState.STATE_POWER_OFF)
+                && (dataRegState == ServiceState.STATE_IN_SERVICE)) {
+            log("getCombinedRegState: return STATE_IN_SERVICE as Data is in service");
+            regState = dataRegState;
+        }
+        return regState;
+    }
+
+    /**
+     * Update time zone by network country code, works on countries which only have one time zone.
+     * @param iso Country code from network MCC
+     */
+    private void updateTimeZoneByNetworkCountryCode(String iso) {
+        // Test both paths if ignore nitz is true
+        boolean testOneUniqueOffsetPath = SystemProperties.getBoolean(
+                TelephonyProperties.PROPERTY_IGNORE_NITZ, false)
+                && ((SystemClock.uptimeMillis() & 1) == 0);
+
+        List<String> uniqueZoneIds = TimeUtils.getTimeZoneIdsWithUniqueOffsets(iso);
+        if ((uniqueZoneIds.size() == 1) || testOneUniqueOffsetPath) {
+            String zoneId = uniqueZoneIds.get(0);
+            if (DBG) {
+                log("updateTimeZoneByNetworkCountryCode: no nitz but one TZ for iso-cc=" + iso
+                        + " with zone.getID=" + zoneId
+                        + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath);
+            }
+            mTimeZoneLog.log("updateTimeZoneByNetworkCountryCode: set time zone=" + zoneId
+                    + " iso=" + iso);
+            setAndBroadcastNetworkSetTimeZone(zoneId);
+        } else {
+            if (DBG) {
+                log("updateTimeZoneByNetworkCountryCode: there are " + uniqueZoneIds.size()
+                        + " unique offsets for iso-cc='" + iso
+                        + " testOneUniqueOffsetPath=" + testOneUniqueOffsetPath
+                        + "', do nothing");
+            }
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SettingsObserver.java b/src/java/com/android/internal/telephony/SettingsObserver.java
new file mode 100644
index 0000000..2253c36
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SettingsObserver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.telephony.Rlog;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The class to describe settings observer
+ */
+public class SettingsObserver extends ContentObserver {
+    private final Map<Uri, Integer> mUriEventMap;
+    private final Context mContext;
+    private final Handler mHandler;
+    private static final String TAG = "SettingsObserver";
+
+    public SettingsObserver(Context context, Handler handler) {
+        super(null);
+        mUriEventMap = new HashMap<>();
+        mContext = context;
+        mHandler = handler;
+    }
+
+    /**
+     * Start observing a content.
+     * @param uri Content URI
+     * @param what The event to fire if the content changes
+     */
+    public void observe(Uri uri, int what) {
+        mUriEventMap.put(uri, what);
+        final ContentResolver resolver = mContext.getContentResolver();
+        resolver.registerContentObserver(uri, false, this);
+    }
+
+    /**
+     * Stop observing a content.
+     */
+    public void unobserve() {
+        final ContentResolver resolver = mContext.getContentResolver();
+        resolver.unregisterContentObserver(this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        Rlog.e(TAG, "Should never be reached.");
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        final Integer what = mUriEventMap.get(uri);
+        if (what != null) {
+            mHandler.obtainMessage(what.intValue()).sendToTarget();
+        } else {
+            Rlog.e(TAG, "No matching event to send for URI=" + uri);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SimActivationTracker.java b/src/java/com/android/internal/telephony/SimActivationTracker.java
new file mode 100644
index 0000000..8fd6eed
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SimActivationTracker.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2016 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.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.telephony.Rlog;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.InvalidParameterException;
+
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATED;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_RESTRICTED;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATING;
+
+public class SimActivationTracker {
+    /**
+     * SimActivationTracker(SAT) serves as a central place to keep track of all knowledge of
+     * voice & data activation state which is set by custom/default carrier apps.
+     * Each phone object maintains a single activation tracker.
+     */
+    private static final boolean DBG = true;
+    private static final String LOG_TAG = "SAT";
+    private static final boolean VDBG = Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+    private Phone mPhone;
+
+    /**
+     * Voice Activation State
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     */
+    private int mVoiceActivationState;
+
+    /**
+     * Data Activation State
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED
+     * @see android.telephony.TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED
+     */
+    private int mDataActivationState;
+    private final LocalLog mVoiceActivationStateLog = new LocalLog(10);
+    private final LocalLog mDataActivationStateLog = new LocalLog(10);
+    private final BroadcastReceiver mReceiver;
+
+    public SimActivationTracker(Phone phone) {
+        mPhone = phone;
+        mVoiceActivationState = SIM_ACTIVATION_STATE_UNKNOWN;
+        mDataActivationState = SIM_ACTIVATION_STATE_UNKNOWN;
+
+        mReceiver = new  BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                if (VDBG) log("action: " + action);
+                if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)){
+                    if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(
+                            intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE))) {
+                        if (DBG) log("onSimAbsent, reset activation state to UNKNOWN");
+                        setVoiceActivationState(SIM_ACTIVATION_STATE_UNKNOWN);
+                        setDataActivationState(SIM_ACTIVATION_STATE_UNKNOWN);
+                    }
+                }
+            }
+        };
+
+        IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        mPhone.getContext().registerReceiver(mReceiver, intentFilter);
+    }
+
+    public void setVoiceActivationState(int state) {
+        if (!isValidActivationState(state) || (SIM_ACTIVATION_STATE_RESTRICTED == state)) {
+            throw new IllegalArgumentException("invalid voice activation state: " + state);
+        }
+        if (DBG) log("setVoiceActivationState=" + state);
+        mVoiceActivationState = state;
+        mVoiceActivationStateLog.log(toString(state));
+        mPhone.notifyVoiceActivationStateChanged(state);
+    }
+
+    public void setDataActivationState(int state) {
+        if (!isValidActivationState(state)) {
+            throw new IllegalArgumentException("invalid data activation state: " + state);
+        }
+        if (DBG) log("setDataActivationState=" + state);
+        mDataActivationState = state;
+        mDataActivationStateLog.log(toString(state));
+        mPhone.notifyDataActivationStateChanged(state);
+    }
+
+    public int getVoiceActivationState() {
+        return mVoiceActivationState;
+    }
+
+    public int getDataActivationState() {
+        return mDataActivationState;
+    }
+
+    private static boolean isValidActivationState(int state) {
+        switch (state) {
+            case SIM_ACTIVATION_STATE_UNKNOWN:
+            case SIM_ACTIVATION_STATE_ACTIVATING:
+            case SIM_ACTIVATION_STATE_ACTIVATED:
+            case SIM_ACTIVATION_STATE_DEACTIVATED:
+            case SIM_ACTIVATION_STATE_RESTRICTED:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private static String toString(int state) {
+        switch (state) {
+            case SIM_ACTIVATION_STATE_UNKNOWN:
+                return "unknown";
+            case SIM_ACTIVATION_STATE_ACTIVATING:
+                return "activating";
+            case SIM_ACTIVATION_STATE_ACTIVATED:
+                return "activated";
+            case SIM_ACTIVATION_STATE_DEACTIVATED:
+                return "deactivated";
+            case SIM_ACTIVATION_STATE_RESTRICTED:
+                return "restricted";
+            default:
+                return "invalid";
+        }
+    }
+
+    private void log(String s) {
+        Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "]" + s);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        pw.println(" mVoiceActivationState Log:");
+        ipw.increaseIndent();
+        mVoiceActivationStateLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+
+        pw.println(" mDataActivationState Log:");
+        ipw.increaseIndent();
+        mDataActivationStateLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
+    }
+
+    public void dispose() {
+        mPhone.getContext().unregisterReceiver(mReceiver);
+    }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/src/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
deleted file mode 100644
index 439eaea..0000000
--- a/src/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2014 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.telephony.Rlog;
-import android.os.Build;
-import android.util.SparseIntArray;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.telephony.SmsManager;
-import android.telephony.TelephonyManager;
-
-import com.android.internal.util.XmlUtils;
-import com.android.internal.telephony.cdma.sms.UserData;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-public class Sms7BitEncodingTranslator {
-    private static final String TAG = "Sms7BitEncodingTranslator";
-    private static final boolean DBG = Build.IS_DEBUGGABLE ;
-    private static boolean mIs7BitTranslationTableLoaded = false;
-    private static SparseIntArray mTranslationTable = null;
-    private static SparseIntArray mTranslationTableCommon = null;
-    private static SparseIntArray mTranslationTableGSM = null;
-    private static SparseIntArray mTranslationTableCDMA = null;
-
-    // Parser variables
-    private static final String XML_START_TAG = "SmsEnforce7BitTranslationTable";
-    private static final String XML_TRANSLATION_TYPE_TAG = "TranslationType";
-    private static final String XML_CHARACTOR_TAG = "Character";
-    private static final String XML_FROM_TAG = "from";
-    private static final String XML_TO_TAG = "to";
-
-    /**
-     * Translates each message character that is not supported by GSM 7bit
-     * alphabet into a supported one
-     *
-     * @param message
-     *            message to be translated
-     * @param throwsException
-     *            if true and some error occurs during translation, an exception
-     *            is thrown; otherwise a null String is returned
-     * @return translated message or null if some error occur
-     */
-    public static String translate(CharSequence message) {
-        if (message == null) {
-            Rlog.w(TAG, "Null message can not be translated");
-            return null;
-        }
-
-        int size = message.length();
-        if (size <= 0) {
-            return "";
-        }
-
-        if (!mIs7BitTranslationTableLoaded) {
-            mTranslationTableCommon = new SparseIntArray();
-            mTranslationTableGSM = new SparseIntArray();
-            mTranslationTableCDMA = new SparseIntArray();
-            load7BitTranslationTableFromXml();
-            mIs7BitTranslationTableLoaded = true;
-        }
-
-        if ((mTranslationTableCommon != null && mTranslationTableCommon.size() > 0) ||
-                (mTranslationTableGSM != null && mTranslationTableGSM.size() > 0) ||
-                (mTranslationTableCDMA != null && mTranslationTableCDMA.size() > 0)) {
-            char[] output = new char[size];
-            boolean isCdmaFormat = useCdmaFormatForMoSms();
-            for (int i = 0; i < size; i++) {
-                output[i] = translateIfNeeded(message.charAt(i), isCdmaFormat);
-            }
-
-            return String.valueOf(output);
-        }
-
-        return null;
-    }
-
-    /**
-     * Translates a single character into its corresponding acceptable one, if
-     * needed, based on GSM 7-bit alphabet
-     *
-     * @param c
-     *            character to be translated
-     * @return original character, if it's present on GSM 7-bit alphabet; a
-     *         corresponding character, based on the translation table or white
-     *         space, if no mapping is found in the translation table for such
-     *         character
-     */
-    private static char translateIfNeeded(char c, boolean isCdmaFormat) {
-        if (noTranslationNeeded(c, isCdmaFormat)) {
-            if (DBG) {
-                Rlog.v(TAG, "No translation needed for " + Integer.toHexString(c));
-            }
-            return c;
-        }
-
-        /*
-         * Trying to translate unicode to Gsm 7-bit alphabet; If c is not
-         * present on translation table, c does not belong to Unicode Latin-1
-         * (Basic + Supplement), so we don't know how to translate it to a Gsm
-         * 7-bit character! We replace c for an empty space and advises the user
-         * about it.
-         */
-        int translation = -1;
-
-        if (mTranslationTableCommon != null) {
-            translation = mTranslationTableCommon.get(c, -1);
-        }
-
-        if (translation == -1) {
-            if (isCdmaFormat) {
-                if (mTranslationTableCDMA != null) {
-                    translation = mTranslationTableCDMA.get(c, -1);
-                }
-            } else {
-                if (mTranslationTableGSM != null) {
-                    translation = mTranslationTableGSM.get(c, -1);
-                }
-            }
-        }
-
-        if (translation != -1) {
-            if (DBG) {
-                Rlog.v(TAG, Integer.toHexString(c) + " (" + c + ")" + " translated to "
-                        + Integer.toHexString(translation) + " (" + (char) translation + ")");
-            }
-            return (char) translation;
-        } else {
-            if (DBG) {
-                Rlog.w(TAG, "No translation found for " + Integer.toHexString(c)
-                        + "! Replacing for empty space");
-            }
-            return ' ';
-        }
-    }
-
-    private static boolean noTranslationNeeded(char c, boolean isCdmaFormat) {
-        if (isCdmaFormat) {
-            return GsmAlphabet.isGsmSeptets(c) && UserData.charToAscii.get(c, -1) != -1;
-        }
-        else {
-            return GsmAlphabet.isGsmSeptets(c);
-        }
-    }
-
-    private static boolean useCdmaFormatForMoSms() {
-        if (!SmsManager.getDefault().isImsSmsSupported()) {
-            // use Voice technology to determine SMS format.
-            return TelephonyManager.getDefault().getCurrentPhoneType()
-                    == PhoneConstants.PHONE_TYPE_CDMA;
-        }
-        // IMS is registered with SMS support, check the SMS format supported
-        return (SmsConstants.FORMAT_3GPP2.equals(SmsManager.getDefault().getImsSmsFormat()));
-    }
-
-    /**
-     * Load the whole translation table file from the framework resource
-     * encoded in XML.
-     */
-    private static void load7BitTranslationTableFromXml() {
-        XmlResourceParser parser = null;
-        Resources r = Resources.getSystem();
-
-        if (parser == null) {
-            if (DBG) Rlog.d(TAG, "load7BitTranslationTableFromXml: open normal file");
-            parser = r.getXml(com.android.internal.R.xml.sms_7bit_translation_table);
-        }
-
-        try {
-            XmlUtils.beginDocument(parser, XML_START_TAG);
-            while (true)  {
-                XmlUtils.nextElement(parser);
-                String tag = parser.getName();
-                if (DBG) {
-                    Rlog.d(TAG, "tag: " + tag);
-                }
-                if (XML_TRANSLATION_TYPE_TAG.equals(tag)) {
-                    String type = parser.getAttributeValue(null, "Type");
-                    if (DBG) {
-                        Rlog.d(TAG, "type: " + type);
-                    }
-                    if (type.equals("common")) {
-                        mTranslationTable = mTranslationTableCommon;
-                    } else if (type.equals("gsm")) {
-                        mTranslationTable = mTranslationTableGSM;
-                    } else if (type.equals("cdma")) {
-                        mTranslationTable = mTranslationTableCDMA;
-                    } else {
-                        Rlog.e(TAG, "Error Parsing 7BitTranslationTable: found incorrect type" + type);
-                    }
-                } else if (XML_CHARACTOR_TAG.equals(tag) && mTranslationTable != null) {
-                    int from = parser.getAttributeUnsignedIntValue(null,
-                            XML_FROM_TAG, -1);
-                    int to = parser.getAttributeUnsignedIntValue(null,
-                            XML_TO_TAG, -1);
-                    if ((from != -1) && (to != -1)) {
-                        if (DBG) {
-                            Rlog.d(TAG, "Loading mapping " + Integer.toHexString(from)
-                                    .toUpperCase() + " -> " + Integer.toHexString(to)
-                                    .toUpperCase());
-                        }
-                        mTranslationTable.put (from, to);
-                    } else {
-                        Rlog.d(TAG, "Invalid translation table file format");
-                    }
-                } else {
-                    break;
-                }
-            }
-            if (DBG) Rlog.d(TAG, "load7BitTranslationTableFromXml: parsing successful, file loaded");
-        } catch (Exception e) {
-            Rlog.e(TAG, "Got exception while loading 7BitTranslationTable file.", e);
-        } finally {
-            if (parser instanceof XmlResourceParser) {
-                ((XmlResourceParser)parser).close();
-            }
-        }
-    }
-}
diff --git a/src/java/com/android/internal/telephony/SmsAddress.java b/src/java/com/android/internal/telephony/SmsAddress.java
deleted file mode 100644
index b3892cb..0000000
--- a/src/java/com/android/internal/telephony/SmsAddress.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2008 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 abstract class SmsAddress {
-    // From TS 23.040 9.1.2.5 and TS 24.008 table 10.5.118
-    // and C.S0005-D table 2.7.1.3.2.4-2
-    public static final int TON_UNKNOWN = 0;
-    public static final int TON_INTERNATIONAL = 1;
-    public static final int TON_NATIONAL = 2;
-    public static final int TON_NETWORK = 3;
-    public static final int TON_SUBSCRIBER = 4;
-    public static final int TON_ALPHANUMERIC = 5;
-    public static final int TON_ABBREVIATED = 6;
-
-    public int ton;
-    public String address;
-    public byte[] origBytes;
-
-    /**
-     * Returns the address of the SMS message in String form or null if unavailable
-     */
-    public String getAddressString() {
-        return address;
-    }
-
-    /**
-     * Returns true if this is an alphanumeric address
-     */
-    public boolean isAlphanumeric() {
-        return ton == TON_ALPHANUMERIC;
-    }
-
-    /**
-     * Returns true if this is a network address
-     */
-    public boolean isNetworkSpecific() {
-        return ton == TON_NETWORK;
-    }
-
-    public boolean couldBeEmailGateway() {
-        // Some carriers seems to send email gateway messages in this form:
-        // from: an UNKNOWN TON, 3 or 4 digits long, beginning with a 5
-        // PID: 0x00, Data coding scheme 0x03
-        // So we just attempt to treat any message from an address length <= 4
-        // as an email gateway
-
-        return address.length() <= 4;
-    }
-
-}
diff --git a/src/java/com/android/internal/telephony/SmsApplication.java b/src/java/com/android/internal/telephony/SmsApplication.java
deleted file mode 100644
index bf90350..0000000
--- a/src/java/com/android/internal/telephony/SmsApplication.java
+++ /dev/null
@@ -1,924 +0,0 @@
-/*
- * Copyright (C) 2013 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.Manifest.permission;
-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.ApplicationInfo;
-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.res.Resources;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.Debug;
-import android.os.Process;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Telephony;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.Rlog;
-import android.telephony.SmsManager;
-import android.telephony.TelephonyManager;
-import android.util.Log;
-
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.MetricsProto.MetricsEvent;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Class for managing the primary application that we will deliver SMS/MMS messages to
- *
- * {@hide}
- */
-public final class SmsApplication {
-    static final String LOG_TAG = "SmsApplication";
-    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";
-    private static final String SCHEME_MMS = "mms";
-    private static final String SCHEME_MMSTO = "mmsto";
-    private static final boolean DEBUG_MULTIUSER = false;
-
-    private static SmsPackageMonitor sSmsPackageMonitor = null;
-
-    public static class SmsApplicationData {
-        /**
-         * Name of this SMS app for display.
-         */
-        private String mApplicationName;
-
-        /**
-         * Package name for this SMS app.
-         */
-        public String mPackageName;
-
-        /**
-         * The class name of the SMS_DELIVER_ACTION receiver in this app.
-         */
-        public String mSmsReceiverClass;
-
-        /**
-         * The class name of the WAP_PUSH_DELIVER_ACTION receiver in this app.
-         */
-        public String mMmsReceiverClass;
-
-        /**
-         * The class name of the ACTION_RESPOND_VIA_MESSAGE intent in this app.
-         */
-        public String mRespondViaMessageClass;
-
-        /**
-         * The class name of the ACTION_SENDTO intent in this app.
-         */
-        public String mSendToClass;
-
-        /**
-         * The class name of the ACTION_DEFAULT_SMS_PACKAGE_CHANGED receiver in this app.
-         */
-        public String mSmsAppChangedReceiverClass;
-
-        /**
-         * The class name of the ACTION_EXTERNAL_PROVIDER_CHANGE receiver in this app.
-         */
-        public String mProviderChangedReceiverClass;
-
-        /**
-         * The user-id for this application
-         */
-        public int mUid;
-
-        /**
-         * Returns true if this SmsApplicationData is complete (all intents handled).
-         * @return
-         */
-        public boolean isComplete() {
-            return (mSmsReceiverClass != null && mMmsReceiverClass != null
-                    && mRespondViaMessageClass != null && mSendToClass != null);
-        }
-
-        public SmsApplicationData(String packageName, int uid) {
-            mPackageName = packageName;
-            mUid = uid;
-        }
-
-        public String getApplicationName(Context context) {
-            if (mApplicationName == null) {
-                PackageManager pm = context.getPackageManager();
-                ApplicationInfo appInfo;
-                try {
-                    appInfo = pm.getApplicationInfoAsUser(mPackageName, 0,
-                            UserHandle.getUserId(mUid));
-                } catch (NameNotFoundException e) {
-                    return null;
-                }
-                if (appInfo != null) {
-                    CharSequence label  = pm.getApplicationLabel(appInfo);
-                    mApplicationName = (label == null) ? null : label.toString();
-                }
-            }
-            return mApplicationName;
-        }
-
-        @Override
-        public String toString() {
-            return " mPackageName: " + mPackageName +
-                    " mSmsReceiverClass: " + mSmsReceiverClass +
-                    " mMmsReceiverClass: " + mMmsReceiverClass +
-                    " mRespondViaMessageClass: " + mRespondViaMessageClass +
-                    " mSendToClass: " + mSendToClass +
-                    " mSmsAppChangedClass: " + mSmsAppChangedReceiverClass +
-                    " mProviderChangedReceiverClass: " + mProviderChangedReceiverClass +
-                    " mUid: " + mUid;
-        }
-    }
-
-    /**
-     * Returns the userId of the Context object, if called from a system app,
-     * otherwise it returns the caller's userId
-     * @param context The context object passed in by the caller.
-     * @return
-     */
-    private static int getIncomingUserId(Context context) {
-        int contextUserId = context.getUserId();
-        final int callingUid = Binder.getCallingUid();
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getIncomingUserHandle caller=" + callingUid + ", myuid="
-                    + android.os.Process.myUid() + "\n\t" + Debug.getCallers(4));
-        }
-        if (UserHandle.getAppId(callingUid)
-                < android.os.Process.FIRST_APPLICATION_UID) {
-            return contextUserId;
-        } else {
-            return UserHandle.getUserId(callingUid);
-        }
-    }
-
-    /**
-     * Returns the list of available SMS apps defined as apps that are registered for both the
-     * SMS_RECEIVED_ACTION (SMS) and WAP_PUSH_RECEIVED_ACTION (MMS) broadcasts (and their broadcast
-     * receivers are enabled)
-     *
-     * Requirements to be an SMS application:
-     * Implement SMS_DELIVER_ACTION broadcast receiver.
-     * Require BROADCAST_SMS permission.
-     *
-     * Implement WAP_PUSH_DELIVER_ACTION broadcast receiver.
-     * Require BROADCAST_WAP_PUSH permission.
-     *
-     * Implement RESPOND_VIA_MESSAGE intent.
-     * Support smsto Uri scheme.
-     * Require SEND_RESPOND_VIA_MESSAGE permission.
-     *
-     * Implement ACTION_SENDTO intent.
-     * Support smsto Uri scheme.
-     */
-    public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
-        int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            return getApplicationCollectionInternal(context, userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private static Collection<SmsApplicationData> getApplicationCollectionInternal(
-            Context context, int userId) {
-        PackageManager packageManager = context.getPackageManager();
-
-        // Get the list of apps registered for SMS
-        Intent intent = new Intent(Intents.SMS_DELIVER_ACTION);
-        List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, 0,
-                userId);
-
-        HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>();
-
-        // Add one entry to the map for every sms receiver (ignoring duplicate sms receivers)
-        for (ResolveInfo resolveInfo : smsReceivers) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            if (!permission.BROADCAST_SMS.equals(activityInfo.permission)) {
-                continue;
-            }
-            final String packageName = activityInfo.packageName;
-            if (!receivers.containsKey(packageName)) {
-                final SmsApplicationData smsApplicationData = new SmsApplicationData(packageName,
-                        activityInfo.applicationInfo.uid);
-                smsApplicationData.mSmsReceiverClass = activityInfo.name;
-                receivers.put(packageName, smsApplicationData);
-            }
-        }
-
-        // Update any existing entries with mms receiver class
-        intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION);
-        intent.setDataAndType(null, "application/vnd.wap.mms-message");
-        List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceiversAsUser(intent, 0,
-                userId);
-        for (ResolveInfo resolveInfo : mmsReceivers) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            if (!permission.BROADCAST_WAP_PUSH.equals(activityInfo.permission)) {
-                continue;
-            }
-            final String packageName = activityInfo.packageName;
-            final SmsApplicationData smsApplicationData = receivers.get(packageName);
-            if (smsApplicationData != null) {
-                smsApplicationData.mMmsReceiverClass = activityInfo.name;
-            }
-        }
-
-        // Update any existing entries with respond via message intent class.
-        intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE,
-                Uri.fromParts(SCHEME_SMSTO, "", null));
-        List<ResolveInfo> respondServices = packageManager.queryIntentServicesAsUser(intent, 0,
-                userId);
-        for (ResolveInfo resolveInfo : respondServices) {
-            final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
-            if (serviceInfo == null) {
-                continue;
-            }
-            if (!permission.SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
-                continue;
-            }
-            final String packageName = serviceInfo.packageName;
-            final SmsApplicationData smsApplicationData = receivers.get(packageName);
-            if (smsApplicationData != null) {
-                smsApplicationData.mRespondViaMessageClass = serviceInfo.name;
-            }
-        }
-
-        // Update any existing entries with supports send to.
-        intent = new Intent(Intent.ACTION_SENDTO,
-                Uri.fromParts(SCHEME_SMSTO, "", null));
-        List<ResolveInfo> sendToActivities = packageManager.queryIntentActivitiesAsUser(intent, 0,
-                userId);
-        for (ResolveInfo resolveInfo : sendToActivities) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            final String packageName = activityInfo.packageName;
-            final SmsApplicationData smsApplicationData = receivers.get(packageName);
-            if (smsApplicationData != null) {
-                smsApplicationData.mSendToClass = activityInfo.name;
-            }
-        }
-
-        // Update any existing entries with the default sms changed handler.
-        intent = new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
-        List<ResolveInfo> smsAppChangedReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
-                0, userId);
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getApplicationCollectionInternal smsAppChangedActivities=" +
-                    smsAppChangedReceivers);
-        }
-        for (ResolveInfo resolveInfo : smsAppChangedReceivers) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            final String packageName = activityInfo.packageName;
-            final SmsApplicationData smsApplicationData = receivers.get(packageName);
-            if (DEBUG_MULTIUSER) {
-                Log.i(LOG_TAG, "getApplicationCollectionInternal packageName=" +
-                        packageName + " smsApplicationData: " + smsApplicationData +
-                        " activityInfo.name: " + activityInfo.name);
-            }
-            if (smsApplicationData != null) {
-                smsApplicationData.mSmsAppChangedReceiverClass = activityInfo.name;
-            }
-        }
-
-        // Update any existing entries with the external provider changed handler.
-        intent = new Intent(Telephony.Sms.Intents.ACTION_EXTERNAL_PROVIDER_CHANGE);
-        List<ResolveInfo> providerChangedReceivers = packageManager.queryBroadcastReceiversAsUser(intent,
-                0, userId);
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getApplicationCollectionInternal providerChangedActivities=" +
-                    providerChangedReceivers);
-        }
-        for (ResolveInfo resolveInfo : providerChangedReceivers) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            final String packageName = activityInfo.packageName;
-            final SmsApplicationData smsApplicationData = receivers.get(packageName);
-            if (DEBUG_MULTIUSER) {
-                Log.i(LOG_TAG, "getApplicationCollectionInternal packageName=" +
-                        packageName + " smsApplicationData: " + smsApplicationData +
-                        " activityInfo.name: " + activityInfo.name);
-            }
-            if (smsApplicationData != null) {
-                smsApplicationData.mProviderChangedReceiverClass = activityInfo.name;
-            }
-        }
-
-        // Remove any entries for which we did not find all required intents.
-        for (ResolveInfo resolveInfo : smsReceivers) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            final String packageName = activityInfo.packageName;
-            final SmsApplicationData smsApplicationData = receivers.get(packageName);
-            if (smsApplicationData != null) {
-                if (!smsApplicationData.isComplete()) {
-                    receivers.remove(packageName);
-                }
-            }
-        }
-        return receivers.values();
-    }
-
-    /**
-     * Checks to see if we have a valid installed SMS application for the specified package name
-     * @return Data for the specified package name or null if there isn't one
-     */
-    private static SmsApplicationData getApplicationForPackage(
-            Collection<SmsApplicationData> applications, String packageName) {
-        if (packageName == null) {
-            return null;
-        }
-        // Is there an entry in the application list for the specified package?
-        for (SmsApplicationData application : applications) {
-            if (application.mPackageName.contentEquals(packageName)) {
-                return application;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Get the application we will use for delivering SMS/MMS messages.
-     *
-     * We return the preferred sms application with the following order of preference:
-     * (1) User selected SMS app (if selected, and if still valid)
-     * (2) Android Messaging (if installed)
-     * (3) The currently configured highest priority broadcast receiver
-     * (4) Null
-     */
-    private static SmsApplicationData getApplication(Context context, boolean updateIfNeeded,
-            int userId) {
-        TelephonyManager tm = (TelephonyManager)
-                context.getSystemService(Context.TELEPHONY_SERVICE);
-        if (!tm.isSmsCapable()) {
-            // No phone, no SMS
-            return null;
-        }
-
-        Collection<SmsApplicationData> applications = getApplicationCollectionInternal(context,
-                userId);
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getApplication userId=" + userId);
-        }
-        // Determine which application receives the broadcast
-        String defaultApplication = Settings.Secure.getStringForUser(context.getContentResolver(),
-                Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getApplication defaultApp=" + defaultApplication);
-        }
-
-        SmsApplicationData applicationData = null;
-        if (defaultApplication != null) {
-            applicationData = getApplicationForPackage(applications, defaultApplication);
-        }
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getApplication appData=" + applicationData);
-        }
-        // Picking a new SMS app requires AppOps and Settings.Secure permissions, so we only do
-        // this if the caller asked us to.
-        if (updateIfNeeded && applicationData == null) {
-            // Try to find the default SMS package for this device
-            Resources r = context.getResources();
-            String defaultPackage =
-                    r.getString(com.android.internal.R.string.default_sms_application);
-            applicationData = getApplicationForPackage(applications, defaultPackage);
-
-            if (applicationData == null) {
-                // Are there any applications?
-                if (applications.size() != 0) {
-                    applicationData = (SmsApplicationData)applications.toArray()[0];
-                }
-            }
-
-            // If we found a new default app, update the setting
-            if (applicationData != null) {
-                setDefaultApplicationInternal(applicationData.mPackageName, context, userId);
-            }
-        }
-
-        // If we found a package, make sure AppOps permissions are set up correctly
-        if (applicationData != null) {
-            AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
-
-            // We can only call checkOp if we are privileged (updateIfNeeded) or if the app we
-            // are checking is for our current uid. Doing this check from the unprivileged current
-            // SMS app allows us to tell the current SMS app that it is not in a good state and
-            // needs to ask to be the current SMS app again to work properly.
-            if (updateIfNeeded || applicationData.mUid == android.os.Process.myUid()) {
-                // Verify that the SMS app has permissions
-                int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
-                        applicationData.mPackageName);
-                if (mode != AppOpsManager.MODE_ALLOWED) {
-                    Rlog.e(LOG_TAG, applicationData.mPackageName + " lost OP_WRITE_SMS: " +
-                            (updateIfNeeded ? " (fixing)" : " (no permission to fix)"));
-                    if (updateIfNeeded) {
-                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
-                                applicationData.mPackageName, AppOpsManager.MODE_ALLOWED);
-                    } else {
-                        // We can not return a package if permissions are not set up correctly
-                        applicationData = null;
-                    }
-                }
-            }
-
-            // We can only verify the phone and BT app's permissions from a privileged caller
-            if (updateIfNeeded) {
-                // Ensure this component is still configured as the preferred activity. Usually the
-                // current SMS app will already be the preferred activity - but checking whether or
-                // not this is true is just as expensive as reconfiguring the preferred activity so
-                // we just reconfigure every time.
-                PackageManager packageManager = context.getPackageManager();
-                configurePreferredActivity(packageManager, new ComponentName(
-                        applicationData.mPackageName, applicationData.mSendToClass),
-                        userId);
-                // 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);
-                // Give WRITE_SMS AppOps permission to UID 1001 which contains multiple
-                // apps, all of them should be able to write to telephony provider.
-                // This is to allow the proxy package permission check in telephony provider
-                // to pass.
-                assignWriteSmsPermissionToSystemUid(appOps, Process.PHONE_UID);
-            }
-        }
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "getApplication returning appData=" + applicationData);
-        }
-        return applicationData;
-    }
-
-    /**
-     * Sets the specified package as the default SMS/MMS application. The caller of this method
-     * needs to have permission to set AppOps and write to secure settings.
-     */
-    public static void setDefaultApplication(String packageName, Context context) {
-        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
-        if (!tm.isSmsCapable()) {
-            // No phone, no SMS
-            return;
-        }
-
-        final int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            setDefaultApplicationInternal(packageName, context, userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    private static void setDefaultApplicationInternal(String packageName, Context context,
-            int userId) {
-        // Get old package name
-        String oldPackageName = Settings.Secure.getStringForUser(context.getContentResolver(),
-                Settings.Secure.SMS_DEFAULT_APPLICATION, userId);
-
-        if (DEBUG_MULTIUSER) {
-            Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldPackageName +
-                    " new=" + packageName);
-        }
-
-        if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
-            // No change
-            return;
-        }
-
-        // We only make the change if the new package is valid
-        PackageManager packageManager = context.getPackageManager();
-        Collection<SmsApplicationData> applications = getApplicationCollection(context);
-        SmsApplicationData oldAppData = oldPackageName != null ?
-                getApplicationForPackage(applications, oldPackageName) : null;
-        SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
-        if (applicationData != null) {
-            // Ignore OP_WRITE_SMS for the previously configured default SMS app.
-            AppOpsManager appOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
-            if (oldPackageName != null) {
-                try {
-                    PackageInfo info = packageManager.getPackageInfo(oldPackageName,
-                            PackageManager.GET_UNINSTALLED_PACKAGES);
-                    appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                            oldPackageName, AppOpsManager.MODE_IGNORED);
-                } catch (NameNotFoundException e) {
-                    Rlog.w(LOG_TAG, "Old SMS package not found: " + oldPackageName);
-                }
-            }
-
-            // Update the secure setting.
-            Settings.Secure.putStringForUser(context.getContentResolver(),
-                    Settings.Secure.SMS_DEFAULT_APPLICATION, applicationData.mPackageName,
-                    userId);
-
-            // Configure this as the preferred activity for SENDTO sms/mms intents
-            configurePreferredActivity(packageManager, new ComponentName(
-                    applicationData.mPackageName, applicationData.mSendToClass), userId);
-
-            // Allow OP_WRITE_SMS for the newly configured default SMS app.
-            appOps.setMode(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
-                    applicationData.mPackageName, AppOpsManager.MODE_ALLOWED);
-
-            // 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);
-            // Give WRITE_SMS AppOps permission to UID 1001 which contains multiple
-            // apps, all of them should be able to write to telephony provider.
-            // This is to allow the proxy package permission check in telephony provider
-            // to pass.
-            assignWriteSmsPermissionToSystemUid(appOps, Process.PHONE_UID);
-
-            if (DEBUG_MULTIUSER) {
-                Log.i(LOG_TAG, "setDefaultApplicationInternal oldAppData=" + oldAppData);
-            }
-            if (oldAppData != null && oldAppData.mSmsAppChangedReceiverClass != null) {
-                // Notify the old sms app that it's no longer the default
-                final Intent oldAppIntent =
-                        new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
-                final ComponentName component = new ComponentName(oldAppData.mPackageName,
-                        oldAppData.mSmsAppChangedReceiverClass);
-                oldAppIntent.setComponent(component);
-                oldAppIntent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, false);
-                if (DEBUG_MULTIUSER) {
-                    Log.i(LOG_TAG, "setDefaultApplicationInternal old=" + oldAppData.mPackageName);
-                }
-                context.sendBroadcast(oldAppIntent);
-            }
-            // Notify the new sms app that it's now the default (if the new sms app has a receiver
-            // to handle the changed default sms intent).
-            if (DEBUG_MULTIUSER) {
-                Log.i(LOG_TAG, "setDefaultApplicationInternal new applicationData=" +
-                        applicationData);
-            }
-            if (applicationData.mSmsAppChangedReceiverClass != null) {
-                final Intent intent =
-                        new Intent(Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED);
-                final ComponentName component = new ComponentName(applicationData.mPackageName,
-                        applicationData.mSmsAppChangedReceiverClass);
-                intent.setComponent(component);
-                intent.putExtra(Telephony.Sms.Intents.EXTRA_IS_DEFAULT_SMS_APP, true);
-                if (DEBUG_MULTIUSER) {
-                    Log.i(LOG_TAG, "setDefaultApplicationInternal new=" + packageName);
-                }
-                context.sendBroadcast(intent);
-            }
-            MetricsLogger.action(context, MetricsEvent.ACTION_DEFAULT_SMS_APP_CHANGED,
-                    applicationData.mPackageName);
-        }
-    }
-
-    /**
-     * 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);
-        }
-
-    }
-
-    private static void assignWriteSmsPermissionToSystemUid(AppOpsManager appOps, int uid) {
-        appOps.setUidMode(AppOpsManager.OP_WRITE_SMS, uid, AppOpsManager.MODE_ALLOWED);
-    }
-
-    /**
-     * Tracks package changes and ensures that the default SMS app is always configured to be the
-     * preferred activity for SENDTO sms/mms intents.
-     */
-    private static final class SmsPackageMonitor extends PackageMonitor {
-        final Context mContext;
-
-        public SmsPackageMonitor(Context context) {
-            super();
-            mContext = context;
-        }
-
-        @Override
-        public void onPackageDisappeared(String packageName, int reason) {
-            onPackageChanged();
-        }
-
-        @Override
-        public void onPackageAppeared(String packageName, int reason) {
-            onPackageChanged();
-        }
-
-        @Override
-        public void onPackageModified(String packageName) {
-            onPackageChanged();
-        }
-
-        private void onPackageChanged() {
-            PackageManager packageManager = mContext.getPackageManager();
-            Context userContext = mContext;
-            final int userId = getSendingUserId();
-            if (userId != UserHandle.USER_SYSTEM) {
-                try {
-                    userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
-                            new UserHandle(userId));
-                } catch (NameNotFoundException nnfe) {
-                    if (DEBUG_MULTIUSER) {
-                        Log.w(LOG_TAG, "Unable to create package context for user " + userId);
-                    }
-                }
-            }
-            // Ensure this component is still configured as the preferred activity
-            ComponentName componentName = getDefaultSendToApplication(userContext, true);
-            if (componentName != null) {
-                configurePreferredActivity(packageManager, componentName, userId);
-            }
-        }
-    }
-
-    public static void initSmsPackageMonitor(Context context) {
-        sSmsPackageMonitor = new SmsPackageMonitor(context);
-        sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL, false);
-    }
-
-    private static void configurePreferredActivity(PackageManager packageManager,
-            ComponentName componentName, int userId) {
-        // Add the four activity preferences we want to direct to this app.
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMS);
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_SMSTO);
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMS);
-        replacePreferredActivity(packageManager, componentName, userId, SCHEME_MMSTO);
-    }
-
-    /**
-     * Updates the ACTION_SENDTO activity to the specified component for the specified scheme.
-     */
-    private static void replacePreferredActivity(PackageManager packageManager,
-            ComponentName componentName, int userId, String scheme) {
-        // Build the set of existing activities that handle this scheme
-        Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(scheme, "", null));
-        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivitiesAsUser(
-                intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_RESOLVED_FILTER,
-                userId);
-
-        // Build the set of ComponentNames for these activities
-        final int n = resolveInfoList.size();
-        ComponentName[] set = new ComponentName[n];
-        for (int i = 0; i < n; i++) {
-            ResolveInfo info = resolveInfoList.get(i);
-            set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
-        }
-
-        // Update the preferred SENDTO activity for the specified scheme
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_SENDTO);
-        intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
-        intentFilter.addDataScheme(scheme);
-        packageManager.replacePreferredActivityAsUser(intentFilter,
-                IntentFilter.MATCH_CATEGORY_SCHEME + IntentFilter.MATCH_ADJUSTMENT_NORMAL,
-                set, componentName, userId);
-    }
-
-    /**
-     * Returns SmsApplicationData for this package if this package is capable of being set as the
-     * default SMS application.
-     */
-    public static SmsApplicationData getSmsApplicationData(String packageName, Context context) {
-        Collection<SmsApplicationData> applications = getApplicationCollection(context);
-        return getApplicationForPackage(applications, packageName);
-    }
-
-    /**
-     * Gets the default SMS application
-     * @param context context from the calling app
-     * @param updateIfNeeded update the default app if there is no valid default app configured.
-     * @return component name of the app and class to deliver SMS messages to
-     */
-    public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
-        int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            ComponentName component = null;
-            SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
-                    userId);
-            if (smsApplicationData != null) {
-                component = new ComponentName(smsApplicationData.mPackageName,
-                        smsApplicationData.mSmsReceiverClass);
-            }
-            return component;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Gets the default MMS application
-     * @param context context from the calling app
-     * @param updateIfNeeded update the default app if there is no valid default app configured.
-     * @return component name of the app and class to deliver MMS messages to
-     */
-    public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) {
-        int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            ComponentName component = null;
-            SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
-                    userId);
-            if (smsApplicationData != null) {
-                component = new ComponentName(smsApplicationData.mPackageName,
-                        smsApplicationData.mMmsReceiverClass);
-            }
-            return component;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Gets the default Respond Via Message application
-     * @param context context from the calling app
-     * @param updateIfNeeded update the default app if there is no valid default app configured.
-     * @return component name of the app and class to direct Respond Via Message intent to
-     */
-    public static ComponentName getDefaultRespondViaMessageApplication(Context context,
-            boolean updateIfNeeded) {
-        int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            ComponentName component = null;
-            SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
-                    userId);
-            if (smsApplicationData != null) {
-                component = new ComponentName(smsApplicationData.mPackageName,
-                        smsApplicationData.mRespondViaMessageClass);
-            }
-            return component;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Gets the default Send To (smsto) application.
-     * <p>
-     * Caller must pass in the correct user context if calling from a singleton service.
-     * @param context context from the calling app
-     * @param updateIfNeeded update the default app if there is no valid default app configured.
-     * @return component name of the app and class to direct SEND_TO (smsto) intent to
-     */
-    public static ComponentName getDefaultSendToApplication(Context context,
-            boolean updateIfNeeded) {
-        int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            ComponentName component = null;
-            SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
-                    userId);
-            if (smsApplicationData != null) {
-                component = new ComponentName(smsApplicationData.mPackageName,
-                        smsApplicationData.mSendToClass);
-            }
-            return component;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Gets the default application that handles external changes to the SmsProvider and
-     * MmsProvider.
-     * @param context context from the calling app
-     * @param updateIfNeeded update the default app if there is no valid default app configured.
-     * @return component name of the app and class to deliver change intents to
-     */
-    public static ComponentName getDefaultExternalTelephonyProviderChangedApplication(
-            Context context, boolean updateIfNeeded) {
-        int userId = getIncomingUserId(context);
-        final long token = Binder.clearCallingIdentity();
-        try {
-            ComponentName component = null;
-            SmsApplicationData smsApplicationData = getApplication(context, updateIfNeeded,
-                    userId);
-            if (smsApplicationData != null
-                    && smsApplicationData.mProviderChangedReceiverClass != null) {
-                component = new ComponentName(smsApplicationData.mPackageName,
-                        smsApplicationData.mProviderChangedReceiverClass);
-            }
-            return component;
-        } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    /**
-     * Returns whether need to write the SMS message to SMS database for this package.
-     * <p>
-     * Caller must pass in the correct user context if calling from a singleton service.
-     */
-    public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
-        if (SmsManager.getDefault().getAutoPersisting()) {
-            return true;
-        }
-        return !isDefaultSmsApplication(context, packageName);
-    }
-
-    /**
-     * 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;
-        }
-        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/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index dde4d2d..f2f2aa3 100644
--- a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
+++ b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
@@ -60,7 +60,8 @@
             "count",
             "address",
             "_id",
-            "message_body"
+            "message_body",
+            "display_originating_addr"
     };
 
     private static SmsBroadcastUndelivered instance;
@@ -199,7 +200,7 @@
             for (SmsReferenceKey message : oldMultiPartMessages) {
                 // delete permanently
                 int rows = mResolver.delete(InboundSmsHandler.sRawUriPermanentDelete,
-                        InboundSmsHandler.SELECT_BY_REFERENCE, message.getDeleteWhereArgs());
+                        message.getDeleteWhere(), message.getDeleteWhereArgs());
                 if (rows == 0) {
                     Rlog.e(TAG, "No rows were deleted from raw table!");
                 } else if (DBG) {
@@ -242,11 +243,14 @@
         final String mAddress;
         final int mReferenceNumber;
         final int mMessageCount;
+        final String mQuery;
 
         SmsReferenceKey(InboundSmsTracker tracker) {
             mAddress = tracker.getAddress();
             mReferenceNumber = tracker.getReferenceNumber();
             mMessageCount = tracker.getMessageCount();
+            mQuery = tracker.getQueryForSegments();
+
         }
 
         String[] getDeleteWhereArgs() {
@@ -254,6 +258,10 @@
                     Integer.toString(mMessageCount)};
         }
 
+        String getDeleteWhere() {
+            return mQuery;
+        }
+
         @Override
         public int hashCode() {
             return ((mReferenceNumber * 31) + mMessageCount) * 31 + mAddress.hashCode();
diff --git a/src/java/com/android/internal/telephony/SmsHeader.java b/src/java/com/android/internal/telephony/SmsHeader.java
deleted file mode 100644
index b519b70..0000000
--- a/src/java/com/android/internal/telephony/SmsHeader.java
+++ /dev/null
@@ -1,314 +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 com.android.internal.telephony.SmsConstants;
-import com.android.internal.util.HexDump;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-
-import java.util.ArrayList;
-
-/**
- * SMS user data header, as specified in TS 23.040 9.2.3.24.
- */
-public class SmsHeader {
-
-    // TODO(cleanup): this data structure is generally referred to as
-    // the 'user data header' or UDH, and so the class name should
-    // change to reflect this...
-
-    /** SMS user data header information element identifiers.
-     * (see TS 23.040 9.2.3.24)
-     */
-    public static final int ELT_ID_CONCATENATED_8_BIT_REFERENCE       = 0x00;
-    public static final int ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION     = 0x01;
-    public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT  = 0x04;
-    public static final int ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT = 0x05;
-    public static final int ELT_ID_SMSC_CONTROL_PARAMS                = 0x06;
-    public static final int ELT_ID_UDH_SOURCE_INDICATION              = 0x07;
-    public static final int ELT_ID_CONCATENATED_16_BIT_REFERENCE      = 0x08;
-    public static final int ELT_ID_WIRELESS_CTRL_MSG_PROTOCOL         = 0x09;
-    public static final int ELT_ID_TEXT_FORMATTING                    = 0x0A;
-    public static final int ELT_ID_PREDEFINED_SOUND                   = 0x0B;
-    public static final int ELT_ID_USER_DEFINED_SOUND                 = 0x0C;
-    public static final int ELT_ID_PREDEFINED_ANIMATION               = 0x0D;
-    public static final int ELT_ID_LARGE_ANIMATION                    = 0x0E;
-    public static final int ELT_ID_SMALL_ANIMATION                    = 0x0F;
-    public static final int ELT_ID_LARGE_PICTURE                      = 0x10;
-    public static final int ELT_ID_SMALL_PICTURE                      = 0x11;
-    public static final int ELT_ID_VARIABLE_PICTURE                   = 0x12;
-    public static final int ELT_ID_USER_PROMPT_INDICATOR              = 0x13;
-    public static final int ELT_ID_EXTENDED_OBJECT                    = 0x14;
-    public static final int ELT_ID_REUSED_EXTENDED_OBJECT             = 0x15;
-    public static final int ELT_ID_COMPRESSION_CONTROL                = 0x16;
-    public static final int ELT_ID_OBJECT_DISTR_INDICATOR             = 0x17;
-    public static final int ELT_ID_STANDARD_WVG_OBJECT                = 0x18;
-    public static final int ELT_ID_CHARACTER_SIZE_WVG_OBJECT          = 0x19;
-    public static final int ELT_ID_EXTENDED_OBJECT_DATA_REQUEST_CMD   = 0x1A;
-    public static final int ELT_ID_RFC_822_EMAIL_HEADER               = 0x20;
-    public static final int ELT_ID_HYPERLINK_FORMAT_ELEMENT           = 0x21;
-    public static final int ELT_ID_REPLY_ADDRESS_ELEMENT              = 0x22;
-    public static final int ELT_ID_ENHANCED_VOICE_MAIL_INFORMATION    = 0x23;
-    public static final int ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT     = 0x24;
-    public static final int ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT    = 0x25;
-
-    public static final int PORT_WAP_PUSH = 2948;
-    public static final int PORT_WAP_WSP  = 9200;
-
-    public static class PortAddrs {
-        public int destPort;
-        public int origPort;
-        public boolean areEightBits;
-    }
-
-    public static class ConcatRef {
-        public int refNumber;
-        public int seqNumber;
-        public int msgCount;
-        public boolean isEightBits;
-    }
-
-    public static class SpecialSmsMsg {
-        public int msgIndType;
-        public int msgCount;
-    }
-
-    /**
-     * A header element that is not explicitly parsed, meaning not
-     * PortAddrs or ConcatRef or SpecialSmsMsg.
-     */
-    public static class MiscElt {
-        public int id;
-        public byte[] data;
-    }
-
-    public PortAddrs portAddrs;
-    public ConcatRef concatRef;
-    public ArrayList<SpecialSmsMsg> specialSmsMsgList = new ArrayList<SpecialSmsMsg>();
-    public ArrayList<MiscElt> miscEltList = new ArrayList<MiscElt>();
-
-    /** 7 bit national language locking shift table, or 0 for GSM default 7 bit alphabet. */
-    public int languageTable;
-
-    /** 7 bit national language single shift table, or 0 for GSM default 7 bit extension table. */
-    public int languageShiftTable;
-
-    public SmsHeader() {}
-
-    /**
-     * Create structured SmsHeader object from serialized byte array representation.
-     * (see TS 23.040 9.2.3.24)
-     * @param data is user data header bytes
-     * @return SmsHeader object
-     */
-    public static SmsHeader fromByteArray(byte[] data) {
-        ByteArrayInputStream inStream = new ByteArrayInputStream(data);
-        SmsHeader smsHeader = new SmsHeader();
-        while (inStream.available() > 0) {
-            /**
-             * NOTE: as defined in the spec, ConcatRef and PortAddr
-             * fields should not reoccur, but if they do the last
-             * occurrence is to be used.  Also, for ConcatRef
-             * elements, if the count is zero, sequence is zero, or
-             * sequence is larger than count, the entire element is to
-             * be ignored.
-             */
-            int id = inStream.read();
-            int length = inStream.read();
-            ConcatRef concatRef;
-            PortAddrs portAddrs;
-            switch (id) {
-            case ELT_ID_CONCATENATED_8_BIT_REFERENCE:
-                concatRef = new ConcatRef();
-                concatRef.refNumber = inStream.read();
-                concatRef.msgCount = inStream.read();
-                concatRef.seqNumber = inStream.read();
-                concatRef.isEightBits = true;
-                if (concatRef.msgCount != 0 && concatRef.seqNumber != 0 &&
-                        concatRef.seqNumber <= concatRef.msgCount) {
-                    smsHeader.concatRef = concatRef;
-                }
-                break;
-            case ELT_ID_CONCATENATED_16_BIT_REFERENCE:
-                concatRef = new ConcatRef();
-                concatRef.refNumber = (inStream.read() << 8) | inStream.read();
-                concatRef.msgCount = inStream.read();
-                concatRef.seqNumber = inStream.read();
-                concatRef.isEightBits = false;
-                if (concatRef.msgCount != 0 && concatRef.seqNumber != 0 &&
-                        concatRef.seqNumber <= concatRef.msgCount) {
-                    smsHeader.concatRef = concatRef;
-                }
-                break;
-            case ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT:
-                portAddrs = new PortAddrs();
-                portAddrs.destPort = inStream.read();
-                portAddrs.origPort = inStream.read();
-                portAddrs.areEightBits = true;
-                smsHeader.portAddrs = portAddrs;
-                break;
-            case ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT:
-                portAddrs = new PortAddrs();
-                portAddrs.destPort = (inStream.read() << 8) | inStream.read();
-                portAddrs.origPort = (inStream.read() << 8) | inStream.read();
-                portAddrs.areEightBits = false;
-                smsHeader.portAddrs = portAddrs;
-                break;
-            case ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT:
-                smsHeader.languageShiftTable = inStream.read();
-                break;
-            case ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT:
-                smsHeader.languageTable = inStream.read();
-                break;
-            case ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION:
-                SpecialSmsMsg specialSmsMsg = new SpecialSmsMsg();
-                specialSmsMsg.msgIndType = inStream.read();
-                specialSmsMsg.msgCount = inStream.read();
-                smsHeader.specialSmsMsgList.add(specialSmsMsg);
-                break;
-            default:
-                MiscElt miscElt = new MiscElt();
-                miscElt.id = id;
-                miscElt.data = new byte[length];
-                inStream.read(miscElt.data, 0, length);
-                smsHeader.miscEltList.add(miscElt);
-            }
-        }
-        return smsHeader;
-    }
-
-    /**
-     * Create serialized byte array representation from structured SmsHeader object.
-     * (see TS 23.040 9.2.3.24)
-     * @return Byte array representing the SmsHeader
-     */
-    public static byte[] toByteArray(SmsHeader smsHeader) {
-        if ((smsHeader.portAddrs == null) &&
-            (smsHeader.concatRef == null) &&
-            (smsHeader.specialSmsMsgList.isEmpty()) &&
-            (smsHeader.miscEltList.isEmpty()) &&
-            (smsHeader.languageShiftTable == 0) &&
-            (smsHeader.languageTable == 0)) {
-            return null;
-        }
-
-        ByteArrayOutputStream outStream =
-                new ByteArrayOutputStream(SmsConstants.MAX_USER_DATA_BYTES);
-        ConcatRef concatRef = smsHeader.concatRef;
-        if (concatRef != null) {
-            if (concatRef.isEightBits) {
-                outStream.write(ELT_ID_CONCATENATED_8_BIT_REFERENCE);
-                outStream.write(3);
-                outStream.write(concatRef.refNumber);
-            } else {
-                outStream.write(ELT_ID_CONCATENATED_16_BIT_REFERENCE);
-                outStream.write(4);
-                outStream.write(concatRef.refNumber >>> 8);
-                outStream.write(concatRef.refNumber & 0x00FF);
-            }
-            outStream.write(concatRef.msgCount);
-            outStream.write(concatRef.seqNumber);
-        }
-        PortAddrs portAddrs = smsHeader.portAddrs;
-        if (portAddrs != null) {
-            if (portAddrs.areEightBits) {
-                outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_8_BIT);
-                outStream.write(2);
-                outStream.write(portAddrs.destPort);
-                outStream.write(portAddrs.origPort);
-            } else {
-                outStream.write(ELT_ID_APPLICATION_PORT_ADDRESSING_16_BIT);
-                outStream.write(4);
-                outStream.write(portAddrs.destPort >>> 8);
-                outStream.write(portAddrs.destPort & 0x00FF);
-                outStream.write(portAddrs.origPort >>> 8);
-                outStream.write(portAddrs.origPort & 0x00FF);
-            }
-        }
-        if (smsHeader.languageShiftTable != 0) {
-            outStream.write(ELT_ID_NATIONAL_LANGUAGE_SINGLE_SHIFT);
-            outStream.write(1);
-            outStream.write(smsHeader.languageShiftTable);
-        }
-        if (smsHeader.languageTable != 0) {
-            outStream.write(ELT_ID_NATIONAL_LANGUAGE_LOCKING_SHIFT);
-            outStream.write(1);
-            outStream.write(smsHeader.languageTable);
-        }
-        for (SpecialSmsMsg specialSmsMsg : smsHeader.specialSmsMsgList) {
-            outStream.write(ELT_ID_SPECIAL_SMS_MESSAGE_INDICATION);
-            outStream.write(2);
-            outStream.write(specialSmsMsg.msgIndType & 0xFF);
-            outStream.write(specialSmsMsg.msgCount & 0xFF);
-        }
-        for (MiscElt miscElt : smsHeader.miscEltList) {
-            outStream.write(miscElt.id);
-            outStream.write(miscElt.data.length);
-            outStream.write(miscElt.data, 0, miscElt.data.length);
-        }
-        return outStream.toByteArray();
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("UserDataHeader ");
-        builder.append("{ ConcatRef ");
-        if (concatRef == null) {
-            builder.append("unset");
-        } else {
-            builder.append("{ refNumber=" + concatRef.refNumber);
-            builder.append(", msgCount=" + concatRef.msgCount);
-            builder.append(", seqNumber=" + concatRef.seqNumber);
-            builder.append(", isEightBits=" + concatRef.isEightBits);
-            builder.append(" }");
-        }
-        builder.append(", PortAddrs ");
-        if (portAddrs == null) {
-            builder.append("unset");
-        } else {
-            builder.append("{ destPort=" + portAddrs.destPort);
-            builder.append(", origPort=" + portAddrs.origPort);
-            builder.append(", areEightBits=" + portAddrs.areEightBits);
-            builder.append(" }");
-        }
-        if (languageShiftTable != 0) {
-            builder.append(", languageShiftTable=" + languageShiftTable);
-        }
-        if (languageTable != 0) {
-            builder.append(", languageTable=" + languageTable);
-        }
-        for (SpecialSmsMsg specialSmsMsg : specialSmsMsgList) {
-            builder.append(", SpecialSmsMsg ");
-            builder.append("{ msgIndType=" + specialSmsMsg.msgIndType);
-            builder.append(", msgCount=" + specialSmsMsg.msgCount);
-            builder.append(" }");
-        }
-        for (MiscElt miscElt : miscEltList) {
-            builder.append(", MiscElt ");
-            builder.append("{ id=" + miscElt.id);
-            builder.append(", length=" + miscElt.data.length);
-            builder.append(", data=" + HexDump.toHexString(miscElt.data));
-            builder.append(" }");
-        }
-        builder.append(" }");
-        return builder.toString();
-    }
-
-}
diff --git a/src/java/com/android/internal/telephony/SmsMessageBase.java b/src/java/com/android/internal/telephony/SmsMessageBase.java
deleted file mode 100644
index e5821dc..0000000
--- a/src/java/com/android/internal/telephony/SmsMessageBase.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright (C) 2008 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 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;
-import android.text.Emoji;
-
-/**
- * Base class declaring the specific methods and members for SmsMessage.
- * {@hide}
- */
-public abstract class SmsMessageBase {
-    /** {@hide} The address of the SMSC. May be null */
-    protected String mScAddress;
-
-    /** {@hide} The address of the sender */
-    protected SmsAddress mOriginatingAddress;
-
-    /** {@hide} The message body as a string. May be null if the message isn't text */
-    protected String mMessageBody;
-
-    /** {@hide} */
-    protected String mPseudoSubject;
-
-    /** {@hide} Non-null if this is an email gateway message */
-    protected String mEmailFrom;
-
-    /** {@hide} Non-null if this is an email gateway message */
-    protected String mEmailBody;
-
-    /** {@hide} */
-    protected boolean mIsEmail;
-
-    /** {@hide} Time when SC (service centre) received the message */
-    protected long mScTimeMillis;
-
-    /** {@hide} The raw PDU of the message */
-    protected byte[] mPdu;
-
-    /** {@hide} The raw bytes for the user data section of the message */
-    protected byte[] mUserData;
-
-    /** {@hide} */
-    protected SmsHeader mUserDataHeader;
-
-    // "Message Waiting Indication Group"
-    // 23.038 Section 4
-    /** {@hide} */
-    protected boolean mIsMwi;
-
-    /** {@hide} */
-    protected boolean mMwiSense;
-
-    /** {@hide} */
-    protected boolean mMwiDontStore;
-
-    /**
-     * Indicates status for messages stored on the ICC.
-     */
-    protected int mStatusOnIcc = -1;
-
-    /**
-     * Record index of message in the EF.
-     */
-    protected int mIndexOnIcc = -1;
-
-    /** TP-Message-Reference - Message Reference of sent message. @hide */
-    public int mMessageRef;
-
-    // TODO(): This class is duplicated in SmsMessage.java. Refactor accordingly.
-    public static abstract class SubmitPduBase  {
-        public byte[] encodedScAddress; // Null if not applicable.
-        public byte[] encodedMessage;
-
-        @Override
-        public String toString() {
-            return "SubmitPdu: encodedScAddress = "
-                    + Arrays.toString(encodedScAddress)
-                    + ", encodedMessage = "
-                    + Arrays.toString(encodedMessage);
-        }
-    }
-
-    /**
-     * Returns the address of the SMS service center that relayed this message
-     * or null if there is none.
-     */
-    public String getServiceCenterAddress() {
-        return mScAddress;
-    }
-
-    /**
-     * Returns the originating address (sender) of this SMS message in String
-     * form or null if unavailable
-     */
-    public String getOriginatingAddress() {
-        if (mOriginatingAddress == null) {
-            return null;
-        }
-
-        return mOriginatingAddress.getAddressString();
-    }
-
-    /**
-     * Returns the originating address, or email from address if this message
-     * was from an email gateway. Returns null if originating address
-     * unavailable.
-     */
-    public String getDisplayOriginatingAddress() {
-        if (mIsEmail) {
-            return mEmailFrom;
-        } else {
-            return getOriginatingAddress();
-        }
-    }
-
-    /**
-     * Returns the message body as a String, if it exists and is text based.
-     * @return message body is there is one, otherwise null
-     */
-    public String getMessageBody() {
-        return mMessageBody;
-    }
-
-    /**
-     * Returns the class of this message.
-     */
-    public abstract SmsConstants.MessageClass getMessageClass();
-
-    /**
-     * Returns the message body, or email message body if this message was from
-     * an email gateway. Returns null if message body unavailable.
-     */
-    public String getDisplayMessageBody() {
-        if (mIsEmail) {
-            return mEmailBody;
-        } else {
-            return getMessageBody();
-        }
-    }
-
-    /**
-     * Unofficial convention of a subject line enclosed in parens empty string
-     * if not present
-     */
-    public String getPseudoSubject() {
-        return mPseudoSubject == null ? "" : mPseudoSubject;
-    }
-
-    /**
-     * Returns the service centre timestamp in currentTimeMillis() format
-     */
-    public long getTimestampMillis() {
-        return mScTimeMillis;
-    }
-
-    /**
-     * Returns true if message is an email.
-     *
-     * @return true if this message came through an email gateway and email
-     *         sender / subject / parsed body are available
-     */
-    public boolean isEmail() {
-        return mIsEmail;
-    }
-
-    /**
-     * @return if isEmail() is true, body of the email sent through the gateway.
-     *         null otherwise
-     */
-    public String getEmailBody() {
-        return mEmailBody;
-    }
-
-    /**
-     * @return if isEmail() is true, email from address of email sent through
-     *         the gateway. null otherwise
-     */
-    public String getEmailFrom() {
-        return mEmailFrom;
-    }
-
-    /**
-     * Get protocol identifier.
-     */
-    public abstract int getProtocolIdentifier();
-
-    /**
-     * See TS 23.040 9.2.3.9 returns true if this is a "replace short message"
-     * SMS
-     */
-    public abstract boolean isReplace();
-
-    /**
-     * Returns true for CPHS MWI toggle message.
-     *
-     * @return true if this is a CPHS MWI toggle message See CPHS 4.2 section
-     *         B.4.2
-     */
-    public abstract boolean isCphsMwiMessage();
-
-    /**
-     * returns true if this message is a CPHS voicemail / message waiting
-     * indicator (MWI) clear message
-     */
-    public abstract boolean isMWIClearMessage();
-
-    /**
-     * returns true if this message is a CPHS voicemail / message waiting
-     * indicator (MWI) set message
-     */
-    public abstract boolean isMWISetMessage();
-
-    /**
-     * returns true if this message is a "Message Waiting Indication Group:
-     * Discard Message" notification and should not be stored.
-     */
-    public abstract boolean isMwiDontStore();
-
-    /**
-     * returns the user data section minus the user data header if one was
-     * present.
-     */
-    public byte[] getUserData() {
-        return mUserData;
-    }
-
-    /**
-     * Returns an object representing the user data header
-     *
-     * {@hide}
-     */
-    public SmsHeader getUserDataHeader() {
-        return mUserDataHeader;
-    }
-
-    /**
-     * TODO(cleanup): The term PDU is used in a seemingly non-unique
-     * manner -- for example, what is the difference between this byte
-     * array and the contents of SubmitPdu objects.  Maybe a more
-     * illustrative term would be appropriate.
-     */
-
-    /**
-     * Returns the raw PDU for the message.
-     */
-    public byte[] getPdu() {
-        return mPdu;
-    }
-
-    /**
-     * For an SMS-STATUS-REPORT message, this returns the status field from
-     * the status report.  This field indicates the status of a previously
-     * submitted SMS, if requested.  See TS 23.040, 9.2.3.15 TP-Status for a
-     * description of values.
-     *
-     * @return 0 indicates the previously sent message was received.
-     *         See TS 23.040, 9.9.2.3.15 for a description of other possible
-     *         values.
-     */
-    public abstract int getStatus();
-
-    /**
-     * Return true iff the message is a SMS-STATUS-REPORT message.
-     */
-    public abstract boolean isStatusReportMessage();
-
-    /**
-     * Returns true iff the <code>TP-Reply-Path</code> bit is set in
-     * this message.
-     */
-    public abstract boolean isReplyPathPresent();
-
-    /**
-     * Returns the status of the message on the ICC (read, unread, sent, unsent).
-     *
-     * @return the status of the message on the ICC.  These are:
-     *         SmsManager.STATUS_ON_ICC_FREE
-     *         SmsManager.STATUS_ON_ICC_READ
-     *         SmsManager.STATUS_ON_ICC_UNREAD
-     *         SmsManager.STATUS_ON_ICC_SEND
-     *         SmsManager.STATUS_ON_ICC_UNSENT
-     */
-    public int getStatusOnIcc() {
-        return mStatusOnIcc;
-    }
-
-    /**
-     * Returns the record index of the message on the ICC (1-based index).
-     * @return the record index of the message on the ICC, or -1 if this
-     *         SmsMessage was not created from a ICC SMS EF record.
-     */
-    public int getIndexOnIcc() {
-        return mIndexOnIcc;
-    }
-
-    protected void parseMessageBody() {
-        // originatingAddress could be null if this message is from a status
-        // report.
-        if (mOriginatingAddress != null && mOriginatingAddress.couldBeEmailGateway()) {
-            extractEmailAddressFromMessageBody();
-        }
-    }
-
-    /**
-     * Try to parse this message as an email gateway message
-     * There are two ways specified in TS 23.040 Section 3.8 :
-     *  - SMS message "may have its TP-PID set for Internet electronic mail - MT
-     * SMS format: [<from-address><space>]<message> - "Depending on the
-     * nature of the gateway, the destination/origination address is either
-     * derived from the content of the SMS TP-OA or TP-DA field, or the
-     * TP-OA/TP-DA field contains a generic gateway address and the to/from
-     * address is added at the beginning as shown above." (which is supported here)
-     * - Multiple addresses separated by commas, no spaces, Subject field delimited
-     * by '()' or '##' and '#' Section 9.2.3.24.11 (which are NOT supported here)
-     */
-    protected void extractEmailAddressFromMessageBody() {
-
-        /* Some carriers may use " /" delimiter as below
-         *
-         * 1. [x@y][ ]/[subject][ ]/[body]
-         * -or-
-         * 2. [x@y][ ]/[body]
-         */
-         String[] parts = mMessageBody.split("( /)|( )", 2);
-         if (parts.length < 2) return;
-         mEmailFrom = parts[0];
-         mEmailBody = parts[1];
-         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)) {
-                int breakPos = breakIterator.preceding(nextPos);
-                while (breakPos + 4 <= nextPos
-                        && Emoji.isRegionalIndicatorSymbol(
-                            Character.codePointAt(msgBody, breakPos))
-                        && Emoji.isRegionalIndicatorSymbol(
-                            Character.codePointAt(msgBody, breakPos + 2))) {
-                    // skip forward over flags (pairs of Regional Indicator Symbol)
-                    breakPos += 4;
-                }
-                if (breakPos > currentPosition) {
-                    nextPos = breakPos;
-                } else if (Character.isHighSurrogate(msgBody.charAt(nextPos - 1))) {
-                    // no character boundary in this fragment, try to at least land on a code point
-                    nextPos -= 1;
-                }
-            }
-        }
-        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 b468e7c..bf548d6 100644
--- a/src/java/com/android/internal/telephony/SmsNumberUtils.java
+++ b/src/java/com/android/internal/telephony/SmsNumberUtils.java
@@ -16,21 +16,24 @@
 
 package com.android.internal.telephony;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-
 import android.content.Context;
-import android.os.Build;
-import android.text.TextUtils;
 import android.database.Cursor;
 import android.database.SQLException;
+import android.os.Binder;
+import android.os.Build;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
 import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.HbpcdLookup.MccIdd;
 import com.android.internal.telephony.HbpcdLookup.MccLookup;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+
 
  /**
  * This class implements handle the MO SMS target address before sending.
@@ -528,10 +531,11 @@
      *  Filter the destination number if using VZW sim card.
      */
     public static String filterDestAddr(Phone phone, String destAddr) {
-        if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + destAddr + "\"" );
+        if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + Rlog.pii(TAG, destAddr) + "\"" );
 
         if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
-            Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number! Nothing changed.");
+            Rlog.w(TAG, "destAddr" + Rlog.pii(TAG, destAddr) +
+                    " is not a global phone number! Nothing changed.");
             return destAddr;
         }
 
@@ -551,7 +555,8 @@
 
         if (DBG) {
             Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
-            Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? result : destAddr) + "\"" );
+            Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? Rlog.pii(TAG,
+                    result) : Rlog.pii(TAG, destAddr)) + "\"");
         }
         return result != null ? result : destAddr;
     }
@@ -597,28 +602,24 @@
     }
 
     private static boolean needToConvert(Phone phone) {
-        boolean bNeedToConvert  = false;
-        String[] listArray = phone.getContext().getResources()
-                .getStringArray(com.android.internal.R.array
-                .config_sms_convert_destination_number_support);
-        if (listArray != null && listArray.length > 0) {
-            for (int i=0; i<listArray.length; i++) {
-                if (!TextUtils.isEmpty(listArray[i])) {
-                    String[] needToConvertArray = listArray[i].split(";");
-                    if (needToConvertArray != null && needToConvertArray.length > 0) {
-                        if (needToConvertArray.length == 1) {
-                            bNeedToConvert = "true".equalsIgnoreCase(needToConvertArray[0]);
-                        } else if (needToConvertArray.length == 2 &&
-                                !TextUtils.isEmpty(needToConvertArray[1]) &&
-                                compareGid1(phone, needToConvertArray[1])) {
-                            bNeedToConvert = "true".equalsIgnoreCase(needToConvertArray[0]);
-                            break;
-                        }
-                    }
+        // Calling package may not have READ_PHONE_STATE which is required for getConfig().
+        // Clear the calling identity so that it is called as self.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            CarrierConfigManager configManager = (CarrierConfigManager)
+                    phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager != null) {
+                PersistableBundle bundle = configManager.getConfig();
+                if (bundle != null) {
+                    return bundle.getBoolean(CarrierConfigManager
+                            .KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL);
                 }
             }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        return bNeedToConvert;
+        // by default this value is false
+        return false;
     }
 
     private static boolean compareGid1(Phone phone, String serviceGid1) {
diff --git a/src/java/com/android/internal/telephony/SmsStorageMonitor.java b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
index 9faed1e..77ac097 100755
--- a/src/java/com/android/internal/telephony/SmsStorageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsStorageMonitor.java
@@ -137,12 +137,13 @@
     }
 
     /**
-     * Called when SIM_FULL message is received from the RIL.  Notifies interested
-     * parties that SIM storage for SMS messages is full.
+     * Called when SIM_FULL message is received from the RIL. Notifies the default SMS application
+     * that SIM storage for SMS messages is full.
      */
     private void handleIccFull() {
         // broadcast SIM_FULL intent
         Intent intent = new Intent(Intents.SIM_FULL_ACTION);
+        intent.setComponent(SmsApplication.getDefaultSimFullApplication(mContext, false));
         mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
         mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS);
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index 73e9a42..402a5ef 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -29,8 +29,8 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
-import android.util.AtomicFile;
 import android.telephony.Rlog;
+import android.util.AtomicFile;
 import android.util.Xml;
 
 import com.android.internal.util.FastXmlSerializer;
@@ -48,10 +48,10 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.regex.Pattern;
 
 /**
@@ -85,7 +85,7 @@
     static final int CATEGORY_STANDARD_SHORT_CODE = 2;
 
     /** Return value from {@link #checkDestination} for possible premium short codes. */
-    static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
+    public static final int CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE = 3;
 
     /** Return value from {@link #checkDestination} for premium short codes. */
     static final int CATEGORY_PREMIUM_SHORT_CODE = 4;
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 61e6e81..ff42b25 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -38,7 +38,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,11 +46,13 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -58,7 +60,7 @@
  * SubscriptionController to provide an inter-process communication to
  * access Sms in Icc.
  *
- * Any setters which take subId, slotId or phoneId as a parameter will throw an exception if the
+ * Any setters which take subId, slotIndex or phoneId as a parameter will throw an exception if the
  * parameter equals the corresponding INVALID_XXX_ID or DEFAULT_XXX_ID.
  *
  * All getters will lookup the corresponding default if the parameter is DEFAULT_XXX_ID. Ie calling
@@ -66,9 +68,9 @@
  *
  * Finally, any getters which perform the mapping between subscriptions, slots and phones will
  * return the corresponding INVALID_XXX_ID if the parameter is INVALID_XXX_ID. All other getters
- * will fail and return the appropriate error value. Ie calling getSlotId(INVALID_SUBSCRIPTION_ID)
- * will return INVALID_SLOT_ID and calling getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID)
- * will return null.
+ * will fail and return the appropriate error value. Ie calling
+ * getSlotIndex(INVALID_SUBSCRIPTION_ID) will return INVALID_SIM_SLOT_INDEX and calling
+ * getSubInfoForSubscriber(INVALID_SUBSCRIPTION_ID) will return null.
  *
  */
 public class SubscriptionController extends ISub.Stub {
@@ -128,7 +130,7 @@
     private AppOpsManager mAppOps;
 
     // FIXME: Does not allow for multiple subs in a slot and change to SparseArray
-    private static Map<Integer, Integer> sSlotIdxToSubId =
+    private static Map<Integer, Integer> sSlotIndexToSubId =
             new ConcurrentHashMap<Integer, Integer>();
     private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
@@ -185,7 +187,7 @@
     }
 
     private boolean isSubInfoReady() {
-        return sSlotIdxToSubId.size() > 0;
+        return sSlotIndexToSubId.size() > 0;
     }
 
     private SubscriptionController(Phone phone) {
@@ -466,13 +468,13 @@
     }
 
     /**
-     * Get the active SubscriptionInfo associated with the slotIdx
-     * @param slotIdx the slot which the subscription is inserted
+     * Get the active SubscriptionInfo associated with the slotIndex
+     * @param slotIndex 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,
+    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex,
             String callingPackage) {
         if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForSimSlotIndex")) {
             return null;
@@ -485,16 +487,16 @@
                     mContext.getOpPackageName());
             if (subList != null) {
                 for (SubscriptionInfo si : subList) {
-                    if (si.getSimSlotIndex() == slotIdx) {
+                    if (si.getSimSlotIndex() == slotIndex) {
                         if (DBG) {
-                            logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
-                                    + " subId=" + si);
+                            logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex="
+                                    + slotIndex + " subId=" + si);
                         }
                         return si;
                     }
                 }
                 if (DBG) {
-                    logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
+                    logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIndex=" + slotIndex
                             + " subId=null");
                 }
             } else {
@@ -595,8 +597,6 @@
      */
     @Override
     public int getActiveSubInfoCount(String callingPackage) {
-        if (DBG) logd("[getActiveSubInfoCount]+");
-
         if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
             return 0;
         }
@@ -607,10 +607,10 @@
             List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
                     mContext.getOpPackageName());
             if (records == null) {
-                if (DBG) logd("[getActiveSubInfoCount] records null");
+                if (VDBG) logd("[getActiveSubInfoCount] records null");
                 return 0;
             }
-            if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+            if (VDBG) logd("[getActiveSubInfoCount]- count: " + records.size());
             return records.size();
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -666,13 +666,13 @@
     /**
      * Add a new SubInfoRecord to subinfo database if needed
      * @param iccId the IccId of the SIM card
-     * @param slotId the slot which the SIM is inserted
+     * @param slotIndex the slot which the SIM is inserted
      * @return 0 if success, < 0 on error.
      */
     @Override
-    public int addSubInfoRecord(String iccId, int slotId) {
+    public int addSubInfoRecord(String iccId, int slotIndex) {
         if (DBG) logdl("[addSubInfoRecord]+ iccId:" + SubscriptionInfo.givePrintableIccid(iccId) +
-                " slotId:" + slotId);
+                " slotIndex:" + slotIndex);
 
         enforceModifyPhoneState("addSubInfoRecord");
 
@@ -699,7 +699,7 @@
                     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.SIM_SLOT_INDEX, slotIndex);
                     value.put(SubscriptionManager.CARRIER_NAME, "");
                     Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
                     if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
@@ -709,8 +709,8 @@
                     int nameSource = cursor.getInt(2);
                     ContentValues value = new ContentValues();
 
-                    if (slotId != oldSimInfoId) {
-                        value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
+                    if (slotIndex != oldSimInfoId) {
+                        value.put(SubscriptionManager.SIM_SLOT_INDEX, slotIndex);
                     }
 
                     if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
@@ -733,30 +733,31 @@
 
             cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
                     SubscriptionManager.SIM_SLOT_INDEX + "=?",
-                    new String[] {String.valueOf(slotId)}, null);
+                    new String[] {String.valueOf(slotIndex)}, 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 sSlotIndexToSubId already has the same subId for a slotIndex/phoneId,
+                        // do not add it.
+                        Integer currentSubId = sSlotIndexToSubId.get(slotIndex);
                         if (currentSubId == null
+                                || currentSubId != subId
                                 || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
                             // TODO While two subs active, if user deactivats first
                             // one, need to update the default subId with second one.
 
-                            // FIXME: Currently we assume phoneId == slotId which in the future
+                            // FIXME: Currently we assume phoneId == slotIndex which in the future
                             // may not be true, for instance with multiple subs per slot.
                             // But is true at the moment.
-                            sSlotIdxToSubId.put(slotId, subId);
+                            sSlotIndexToSubId.put(slotIndex, subId);
                             int subIdCountMax = getActiveSubInfoCountMax();
                             int defaultSubId = getDefaultSubId();
                             if (DBG) {
                                 logdl("[addSubInfoRecord]"
-                                        + " sSlotIdxToSubId.size=" + sSlotIdxToSubId.size()
-                                        + " slotId=" + slotId + " subId=" + subId
+                                        + " sSlotIndexToSubId.size=" + sSlotIndexToSubId.size()
+                                        + " slotIndex=" + slotIndex + " subId=" + subId
                                         + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
                             }
 
@@ -780,7 +781,7 @@
                                         + " && currentSubId is valid, IGNORE");
                             }
                         }
-                        if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
+                        if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotIndex + "," + subId + ")");
                     } while (cursor.moveToNext());
                 }
             } finally {
@@ -790,37 +791,36 @@
             }
 
             // 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) {
+            int subId = getSubIdUsingPhoneId(slotIndex);
+            if (!SubscriptionManager.isValidSubscriptionId(subId)) {
                 if (DBG) {
-                    logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
-                            + subIds);
+                    logdl("[addSubInfoRecord]- getSubId failed invalid subId = " + subId);
                 }
                 return -1;
             }
             if (setDisplayName) {
-                String simCarrierName = mTelephonyManager.getSimOperatorName(subIds[0]);
+                String simCarrierName = mTelephonyManager.getSimOperatorName(subId);
                 String nameToSet;
 
                 if (!TextUtils.isEmpty(simCarrierName)) {
                     nameToSet = simCarrierName;
                 } else {
-                    nameToSet = "CARD " + Integer.toString(slotId + 1);
+                    nameToSet = "CARD " + Integer.toString(slotIndex + 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);
+                                "=" + Long.toString(subId), null);
 
                 if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
             }
 
             // Once the records are loaded, notify DcTracker
-            sPhones[slotId].updateDataConnectionTracker();
+            sPhones[slotIndex].updateDataConnectionTracker();
 
-            if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIdxToSubId.size());
+            if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubId.size());
 
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -836,14 +836,13 @@
      * @param spn spn to be included in carrier text
      * @return true if carrier text is set, false otherwise
      */
-    public boolean setPlmnSpn(int slotId, boolean showPlmn, String plmn, boolean showSpn,
+    public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,
                               String spn) {
         synchronized (mLock) {
-            int[] subIds = getSubId(slotId);
+            int subId = getSubIdUsingPhoneId(slotIndex);
             if (mContext.getPackageManager().resolveContentProvider(
                     SubscriptionManager.CONTENT_URI.getAuthority(), 0) == null ||
-                    subIds == null ||
-                    !SubscriptionManager.isValidSubscriptionId(subIds[0])) {
+                    !SubscriptionManager.isValidSubscriptionId(subId)) {
                 // No place to store this info. Notify registrants of the change anyway as they
                 // might retrieve the SPN/PLMN text from the SST sticky broadcast.
                 // TODO: This can be removed once SubscriptionController is not running on devices
@@ -867,9 +866,7 @@
             } else if (showSpn) {
                 carrierText = spn;
             }
-            for (int i = 0; i < subIds.length; i++) {
-                setCarrierText(carrierText, subIds[i]);
-            }
+            setCarrierText(carrierText, subId);
             return true;
         }
     }
@@ -1096,37 +1093,37 @@
     }
 
     @Override
-    public int getSlotId(int subId) {
-        if (VDBG) printStackTrace("[getSlotId] subId=" + subId);
+    public int getSlotIndex(int subId) {
+        if (VDBG) printStackTrace("[getSlotIndex] subId=" + subId);
 
         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             subId = getDefaultSubId();
         }
         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
-            if (DBG) logd("[getSlotId]- subId invalid");
+            if (DBG) logd("[getSlotIndex]- subId invalid");
             return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
         }
 
-        int size = sSlotIdxToSubId.size();
+        int size = sSlotIndexToSubId.size();
 
         if (size == 0)
         {
-            if (DBG) logd("[getSlotId]- size == 0, return SIM_NOT_INSERTED instead");
+            if (DBG) logd("[getSlotIndex]- size == 0, return SIM_NOT_INSERTED instead");
             return SubscriptionManager.SIM_NOT_INSERTED;
         }
 
-        for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) {
             int sim = entry.getKey();
             int sub = entry.getValue();
 
             if (subId == sub)
             {
-                if (VDBG) logv("[getSlotId]- return = " + sim);
+                if (VDBG) logv("[getSlotIndex]- return = " + sim);
                 return sim;
             }
         }
 
-        if (DBG) logd("[getSlotId]- return fail");
+        if (DBG) logd("[getSlotIndex]- return fail");
         return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
     }
 
@@ -1136,41 +1133,41 @@
      */
     @Override
     @Deprecated
-    public int[] getSubId(int slotIdx) {
-        if (VDBG) printStackTrace("[getSubId]+ slotIdx=" + slotIdx);
+    public int[] getSubId(int slotIndex) {
+        if (VDBG) printStackTrace("[getSubId]+ slotIndex=" + slotIndex);
 
-        // Map default slotIdx to the current default subId.
+        // Map default slotIndex to the current default subId.
         // TODO: Not used anywhere sp consider deleting as it's somewhat nebulous
         // as a slot maybe used for multiple different type of "connections"
         // such as: voice, data and sms. But we're doing the best we can and using
         // getDefaultSubId which makes a best guess.
-        if (slotIdx == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
-            slotIdx = getSlotId(getDefaultSubId());
-            if (VDBG) logd("[getSubId] map default slotIdx=" + slotIdx);
+        if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
+            slotIndex = getSlotIndex(getDefaultSubId());
+            if (VDBG) logd("[getSubId] map default slotIndex=" + slotIndex);
         }
 
-        // Check that we have a valid SlotIdx
-        if (!SubscriptionManager.isValidSlotId(slotIdx)) {
-            if (DBG) logd("[getSubId]- invalid slotIdx=" + slotIdx);
+        // Check that we have a valid slotIndex
+        if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+            if (DBG) logd("[getSubId]- invalid slotIndex=" + slotIndex);
             return null;
         }
 
-        // Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate.
-        int size = sSlotIdxToSubId.size();
+        // Check if we've got any SubscriptionInfo records using slotIndexToSubId as a surrogate.
+        int size = sSlotIndexToSubId.size();
         if (size == 0) {
             if (VDBG) {
-                logd("[getSubId]- sSlotIdxToSubId.size == 0, return DummySubIds slotIdx="
-                        + slotIdx);
+                logd("[getSubId]- sSlotIndexToSubId.size == 0, return DummySubIds slotIndex="
+                        + slotIndex);
             }
-            return getDummySubIds(slotIdx);
+            return getDummySubIds(slotIndex);
         }
 
         // Create an array of subIds that are in this slot?
         ArrayList<Integer> subIds = new ArrayList<Integer>();
-        for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) {
             int slot = entry.getKey();
             int sub = entry.getValue();
-            if (slotIdx == slot) {
+            if (slotIndex == slot) {
                 subIds.add(sub);
             }
         }
@@ -1185,8 +1182,8 @@
             if (VDBG) logd("[getSubId]- subIdArr=" + subIdArr);
             return subIdArr;
         } else {
-            if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIdx=" + slotIdx);
-            return getDummySubIds(slotIdx);
+            if (DBG) logd("[getSubId]- numSubIds == 0, return DummySubIds slotIndex=" + slotIndex);
+            return getDummySubIds(slotIndex);
         }
     }
 
@@ -1208,15 +1205,15 @@
             return SubscriptionManager.INVALID_PHONE_INDEX;
         }
 
-        int size = sSlotIdxToSubId.size();
+        int size = sSlotIndexToSubId.size();
         if (size == 0) {
             phoneId = mDefaultPhoneId;
             if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
             return phoneId;
         }
 
-        // FIXME: Assumes phoneId == slotId
-        for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
+        // FIXME: Assumes phoneId == slotIndex
+        for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) {
             int sim = entry.getKey();
             int sub = entry.getValue();
 
@@ -1234,7 +1231,7 @@
 
     }
 
-    private int[] getDummySubIds(int slotIdx) {
+    private int[] getDummySubIds(int slotIndex) {
         // FIXME: Remove notion of Dummy SUBSCRIPTION_ID.
         // I tested this returning null as no one appears to care,
         // but no connection came up on sprout with two sims.
@@ -1243,10 +1240,10 @@
         if (numSubs > 0) {
             int[] dummyValues = new int[numSubs];
             for (int i = 0; i < numSubs; i++) {
-                dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx;
+                dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIndex;
             }
             if (VDBG) {
-                logd("getDummySubIds: slotIdx=" + slotIdx
+                logd("getDummySubIds: slotIndex=" + slotIndex
                     + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]);
             }
             return dummyValues;
@@ -1265,14 +1262,14 @@
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
-            int size = sSlotIdxToSubId.size();
+            int size = sSlotIndexToSubId.size();
 
             if (size == 0) {
                 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
                 return 0;
             }
 
-            sSlotIdxToSubId.clear();
+            sSlotIndexToSubId.clear();
             if (DBG) logdl("[clearSubInfo]- clear size=" + size);
             return size;
         } finally {
@@ -1347,9 +1344,11 @@
     private void broadcastDefaultSmsSubIdChanged(int subId) {
         // Broadcast an Intent for default sms sub change
         if (DBG) logdl("[broadcastDefaultSmsSubIdChanged] subId=" + subId);
-        Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        Intent intent = new Intent(SubscriptionManager.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
@@ -1379,7 +1378,8 @@
         // Broadcast an Intent for default voice sub change
         if (DBG) logdl("[broadcastDefaultVoiceSubIdChanged] subId=" + subId);
         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
@@ -1462,7 +1462,8 @@
         // Broadcast an Intent for default data sub change
         if (DBG) logdl("[broadcastDefaultDataSubIdChanged] subId=" + subId);
         Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
-        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
@@ -1488,7 +1489,8 @@
 
                 // Broadcast an Intent for default sub change
                 Intent intent = new Intent(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED);
-                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+                        | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
                 if (DBG) {
                     logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" +
@@ -1554,7 +1556,7 @@
         return true;
     }
 
-    // FIXME: We need we should not be assuming phoneId == slotId as it will not be true
+    // FIXME: We need we should not be assuming phoneId == slotIndex as it will not be true
     // when there are multiple subscriptions per sim and probably for other reasons.
     public int getSubIdUsingPhoneId(int phoneId) {
         int[] subIds = getSubId(phoneId);
@@ -1564,37 +1566,33 @@
         return subIds[0];
     }
 
-    public int[] getSubIdUsingSlotId(int slotId) {
-        return getSubId(slotId);
-    }
-
-    public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck,
-            String callingPackage) {
-        if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
-
-        if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
+    public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotIndex,
+                                                                    boolean needCheck,
+                                                                    String callingPackage) {
+        if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]+ slotIndex:" + slotIndex);
+        if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
             return null;
         }
 
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
-                slotId = getSlotId(getDefaultSubId());
+            if (slotIndex == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
+                slotIndex = getSlotIndex(getDefaultSubId());
             }
-            if (!SubscriptionManager.isValidSlotId(slotId)) {
-                if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
+            if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+                if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]- invalid slotIndex");
                 return null;
             }
 
             if (needCheck && !isSubInfoReady()) {
-                if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
+                if (DBG) logd("[getSubInfoUsingSlotIndexWithCheck]- not ready");
                 return null;
             }
 
             Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
                     null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
-                    new String[]{String.valueOf(slotId)}, null);
+                    new String[]{String.valueOf(slotIndex)}, null);
             ArrayList<SubscriptionInfo> subList = null;
             try {
                 if (cursor != null) {
@@ -1613,7 +1611,7 @@
                     cursor.close();
                 }
             }
-            if (DBG) logd("[getSubInfoUsingSlotId]- null info return");
+            if (DBG) logd("[getSubInfoUsingSlotIndex]- null info return");
 
             return subList;
         } finally {
@@ -1639,7 +1637,7 @@
      */
     @Override
     public int[] getActiveSubIdList() {
-        Set<Entry<Integer, Integer>> simInfoSet = sSlotIdxToSubId.entrySet();
+        Set<Entry<Integer, Integer>> simInfoSet = new HashSet<>(sSlotIndexToSubId.entrySet());
 
         int[] subIdArr = new int[simInfoSet.size()];
         int i = 0;
@@ -1659,25 +1657,25 @@
     @Override
     public boolean isActiveSubId(int subId) {
         boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
-                && sSlotIdxToSubId.containsValue(subId);
+                && sSlotIndexToSubId.containsValue(subId);
 
         if (VDBG) logdl("[isActiveSubId]- " + retVal);
         return retVal;
     }
 
     /**
-     * Get the SIM state for the slot idx
+     * Get the SIM state for the slot index
      * @return SIM state as the ordinal of {@See IccCardConstants.State}
      */
     @Override
-    public int getSimStateForSlotIdx(int slotIdx) {
+    public int getSimStateForSlotIndex(int slotIndex) {
         State simState;
         String err;
-        if (slotIdx < 0) {
+        if (slotIndex < 0) {
             simState = IccCardConstants.State.UNKNOWN;
-            err = "invalid slotIdx";
+            err = "invalid slotIndex";
         } else {
-            Phone phone = PhoneFactory.getPhone(slotIdx);
+            Phone phone = PhoneFactory.getPhone(slotIndex);
             if (phone == null) {
                 simState = IccCardConstants.State.UNKNOWN;
                 err = "phone == null";
@@ -1693,8 +1691,8 @@
             }
         }
         if (VDBG) {
-            logd("getSimStateForSlotIdx: " + err + " simState=" + simState
-                    + " ordinal=" + simState.ordinal() + " slotIdx=" + slotIdx);
+            logd("getSimStateForSlotIndex: " + err + " simState=" + simState
+                    + " ordinal=" + simState.ordinal() + " slotIndex=" + slotIndex);
         }
         return simState.ordinal();
     }
@@ -1747,7 +1745,7 @@
      */
     @Override
     public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
-        if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
+        if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIndexWithCheck")) {
             return null;
         }
         String resultValue = null;
@@ -1827,8 +1825,8 @@
                     .from(mContext).getDefaultSmsPhoneId());
             pw.flush();
 
-            for (Entry<Integer, Integer> entry : sSlotIdxToSubId.entrySet()) {
-                pw.println(" sSlotIdxToSubId[" + entry.getKey() + "]: subId=" + entry.getValue());
+            for (Entry<Integer, Integer> entry : sSlotIndexToSubId.entrySet()) {
+                pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subId=" + entry.getValue());
             }
             pw.flush();
             pw.println("++++++++++++++++++++++++++++++++");
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 1f68ff3..ad217c2 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -16,8 +16,8 @@
 
 package com.android.internal.telephony;
 
-import android.app.ActivityManagerNative;
-import android.app.IUserSwitchObserver;
+import android.app.ActivityManager;
+import android.app.UserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -32,8 +32,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
@@ -51,13 +49,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-
-import static android.Manifest.permission.READ_PHONE_STATE;
-import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
 
 /**
  *@hide
@@ -108,8 +100,6 @@
     private static int[] mInsertSimState = new int[PROJECT_SIM_NUM];
     private SubscriptionManager mSubscriptionManager = null;
     private IPackageManager mPackageManager;
-    private UserManager mUserManager;
-    private Map<Integer, Intent> rebroadcastIntentsOnUnlock = new HashMap<>();
 
     // The current foreground user ID.
     private int mCurrentlyActiveUserId;
@@ -122,11 +112,9 @@
         mPhone = phone;
         mSubscriptionManager = SubscriptionManager.from(mContext);
         mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
-        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
 
         IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
-        intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiver(sReceiver, intentFilter);
 
         mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
@@ -140,8 +128,7 @@
         // -Whenever we switch to a new user
         mCurrentlyActiveUserId = 0;
         try {
-            ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                    new IUserSwitchObserver.Stub() {
+            ActivityManager.getService().registerUserSwitchObserver(new UserSwitchObserver() {
                 @Override
                 public void onUserSwitching(int newUserId, IRemoteCallback reply)
                         throws RemoteException {
@@ -157,18 +144,8 @@
                         }
                     }
                 }
-
-                @Override
-                public void onUserSwitchComplete(int newUserId) {
-                    // Ignore.
-                }
-
-                @Override
-                public void onForegroundProfileSwitch(int newProfileId) throws RemoteException {
-                    // Ignore.
-                }
             }, LOG_TAG);
-            mCurrentlyActiveUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+            mCurrentlyActiveUserId = ActivityManager.getService().getCurrentUser().id;
         } catch (RemoteException e) {
             logd("Couldn't get current user ID; guessing it's 0: " + e.getMessage());
         }
@@ -184,31 +161,16 @@
             String action = intent.getAction();
             logd("Action: " + action);
 
-            if (action.equals(Intent.ACTION_USER_UNLOCKED)) {
-                // broadcast pending intents
-                Iterator iterator = rebroadcastIntentsOnUnlock.entrySet().iterator();
-                while (iterator.hasNext()) {
-                    Map.Entry pair = (Map.Entry) iterator.next();
-                    Intent i = (Intent)pair.getValue();
-                    iterator.remove();
-                    logd("Broadcasting intent ACTION_SIM_STATE_CHANGED for mCardIndex: " +
-                            pair.getKey());
-                    ActivityManagerNative.broadcastStickyIntent(i, READ_PHONE_STATE,
-                            UserHandle.USER_ALL);
-                }
-                logd("[Receiver]-");
-                return;
-            }
-
             if (!action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED) &&
                     !action.equals(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED)) {
                 return;
             }
 
-            int slotId = intent.getIntExtra(PhoneConstants.PHONE_KEY,
+            int slotIndex = intent.getIntExtra(PhoneConstants.PHONE_KEY,
                     SubscriptionManager.INVALID_SIM_SLOT_INDEX);
-            logd("slotId: " + slotId);
-            if (slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            logd("slotIndex: " + slotIndex);
+            if (!SubscriptionManager.isValidSlotIndex(slotIndex)) {
+                logd("ACTION_SIM_STATE_CHANGED contains invalid slotIndex: " + slotIndex);
                 return;
             }
 
@@ -216,15 +178,14 @@
             logd("simStatus: " + simStatus);
 
             if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
-                rebroadcastIntentsOnUnlock.put(slotId, intent);
                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
-                    sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotId, -1));
+                    sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotIndex, -1));
                 } else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {
-                    sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotId, -1));
+                    sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotIndex, -1));
                 } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {
-                    sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotId, -1));
+                    sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotIndex, -1));
                 } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_RESTRICTED.equals(simStatus)) {
-                    sendMessage(obtainMessage(EVENT_SIM_RESTRICTED, slotId, -1));
+                    sendMessage(obtainMessage(EVENT_SIM_RESTRICTED, slotIndex, -1));
                 } else {
                     logd("Ignoring simStatus: " + simStatus);
                 }
@@ -232,9 +193,9 @@
                 if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(simStatus)) {
                     String reason = intent.getStringExtra(
                         IccCardConstants.INTENT_KEY_LOCKED_REASON);
-                    sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotId, -1, reason));
+                    sendMessage(obtainMessage(EVENT_SIM_LOCKED, slotIndex, -1, reason));
                 } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(simStatus)) {
-                    sendMessage(obtainMessage(EVENT_SIM_LOADED, slotId, -1));
+                    sendMessage(obtainMessage(EVENT_SIM_LOADED, slotIndex, -1));
                 } else {
                     logd("Ignoring simStatus: " + simStatus);
                 }
@@ -340,7 +301,7 @@
                 break;
 
             case EVENT_SIM_IO_ERROR:
-                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+                handleSimError(msg.arg1);
                 break;
 
             case EVENT_SIM_RESTRICTED:
@@ -390,111 +351,105 @@
     }
 
     private void handleSimLoaded(int slotId) {
-        logd("handleSimStateLoadedInternal: slotId: " + slotId);
+        logd("handleSimLoaded: slotId: " + slotId);
 
         // The SIM should be loaded at this state, but it is possible in cases such as SIM being
         // removed or a refresh RESET that the IccRecords could be null. The right behavior is to
         // not broadcast the SIM loaded.
         IccRecords records = mPhone[slotId].getIccCard().getIccRecords();
         if (records == null) {  // Possibly a race condition.
-            logd("onRecieve: IccRecords null");
+            logd("handleSimLoaded: IccRecords null");
             return;
         }
         if (records.getIccId() == null) {
-            logd("onRecieve: IccID null");
+            logd("handleSimLoaded: IccID null");
             return;
         }
         mIccId[slotId] = records.getIccId();
 
         if (isAllIccIdQueryDone()) {
             updateSubscriptionInfoByIccId();
-        }
+            int[] subIds = mSubscriptionManager.getActiveSubscriptionIdList();
+            for (int subId : subIds) {
+                TelephonyManager tm = TelephonyManager.getDefault();
 
-        int subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
-        int[] subIds = SubscriptionController.getInstance().getSubId(slotId);
-        if (subIds != null) {   // Why an array?
-            subId = subIds[0];
-        }
+                String operator = tm.getSimOperatorNumeric(subId);
+                slotId = SubscriptionController.getInstance().getPhoneId(subId);
 
-        if (SubscriptionManager.isValidSubscriptionId(subId)) {
-            TelephonyManager tm = TelephonyManager.getDefault();
-
-            String operator = tm.getSimOperatorNumericForPhone(slotId);
-
-            if (!TextUtils.isEmpty(operator)) {
-                if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
-                    MccTable.updateMccMncConfiguration(mContext, operator, false);
-                }
-                SubscriptionController.getInstance().setMccMnc(operator, subId);
-            } else {
-                logd("EVENT_RECORDS_LOADED Operator name is null");
-            }
-
-            String msisdn = tm.getLine1Number(subId);
-            ContentResolver contentResolver = mContext.getContentResolver();
-
-            if (msisdn != null) {
-                ContentValues number = new ContentValues(1);
-                number.put(SubscriptionManager.NUMBER, msisdn);
-                contentResolver.update(SubscriptionManager.CONTENT_URI, number,
-                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
-                        + Long.toString(subId), null);
-            }
-
-            SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
-            String nameToSet;
-            String simCarrierName = tm.getSimOperatorName(subId);
-            ContentValues name = new ContentValues(1);
-
-            if (subInfo != null && subInfo.getNameSource() !=
-                    SubscriptionManager.NAME_SOURCE_USER_INPUT) {
-                if (!TextUtils.isEmpty(simCarrierName)) {
-                    nameToSet = simCarrierName;
+                if (!TextUtils.isEmpty(operator)) {
+                    if (subId == SubscriptionController.getInstance().getDefaultSubId()) {
+                        MccTable.updateMccMncConfiguration(mContext, operator, false);
+                    }
+                    SubscriptionController.getInstance().setMccMnc(operator, subId);
                 } else {
-                    nameToSet = "CARD " + Integer.toString(slotId + 1);
+                    logd("EVENT_RECORDS_LOADED Operator name is null");
                 }
-                name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
-                logd("sim name = " + nameToSet);
-                contentResolver.update(SubscriptionManager.CONTENT_URI, name,
-                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
-                        + "=" + Long.toString(subId), null);
+
+                String msisdn = tm.getLine1Number(subId);
+                ContentResolver contentResolver = mContext.getContentResolver();
+
+                if (msisdn != null) {
+                    ContentValues number = new ContentValues(1);
+                    number.put(SubscriptionManager.NUMBER, msisdn);
+                    contentResolver.update(SubscriptionManager.CONTENT_URI, number,
+                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
+                                    + Long.toString(subId), null);
+                }
+
+                SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
+                String nameToSet;
+                String simCarrierName = tm.getSimOperatorName(subId);
+                ContentValues name = new ContentValues(1);
+
+                if (subInfo != null && subInfo.getNameSource() !=
+                        SubscriptionManager.NAME_SOURCE_USER_INPUT) {
+                    if (!TextUtils.isEmpty(simCarrierName)) {
+                        nameToSet = simCarrierName;
+                    } else {
+                        nameToSet = "CARD " + Integer.toString(slotId + 1);
+                    }
+                    name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
+                    logd("sim name = " + nameToSet);
+                    contentResolver.update(SubscriptionManager.CONTENT_URI, name,
+                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+                                    + "=" + Long.toString(subId), null);
+                }
+
+                /* Update preferred network type and network selection mode on SIM change.
+                 * Storing last subId in SharedPreference for now to detect SIM change. */
+                SharedPreferences sp =
+                        PreferenceManager.getDefaultSharedPreferences(mContext);
+                int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
+
+                if (storedSubId != subId) {
+                    int networkType = RILConstants.PREFERRED_NETWORK_MODE;
+
+                    // Set the modem network mode
+                    mPhone[slotId].setPreferredNetworkType(networkType, null);
+                    Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
+                            Settings.Global.PREFERRED_NETWORK_MODE + subId,
+                            networkType);
+
+                    // Only support automatic selection mode on SIM change.
+                    mPhone[slotId].getNetworkSelectionMode(
+                            obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE,
+                                    new Integer(slotId)));
+
+                    // Update stored subId
+                    SharedPreferences.Editor editor = sp.edit();
+                    editor.putInt(CURR_SUBID + slotId, subId);
+                    editor.apply();
+                }
+
+                // Update set of enabled carrier apps now that the privilege rules may have changed.
+                CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                        mPackageManager, TelephonyManager.getDefault(),
+                        mContext.getContentResolver(), mCurrentlyActiveUserId);
+
+                broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
+                updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
             }
-
-            /* Update preferred network type and network selection mode on SIM change.
-             * Storing last subId in SharedPreference for now to detect SIM change. */
-            SharedPreferences sp =
-                    PreferenceManager.getDefaultSharedPreferences(mContext);
-            int storedSubId = sp.getInt(CURR_SUBID + slotId, -1);
-
-            if (storedSubId != subId) {
-                int networkType = RILConstants.PREFERRED_NETWORK_MODE;
-
-                // Set the modem network mode
-                mPhone[slotId].setPreferredNetworkType(networkType, null);
-                Settings.Global.putInt(mPhone[slotId].getContext().getContentResolver(),
-                        Settings.Global.PREFERRED_NETWORK_MODE + subId,
-                        networkType);
-
-                // Only support automatic selection mode on SIM change.
-                mPhone[slotId].getNetworkSelectionMode(
-                        obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE, new Integer(slotId)));
-
-                // Update stored subId
-                SharedPreferences.Editor editor = sp.edit();
-                editor.putInt(CURR_SUBID + slotId, subId);
-                editor.apply();
-            }
-        } else {
-            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(), mContext.getContentResolver(),
-                mCurrentlyActiveUserId);
-
-        broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
-        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
     }
 
     private void updateCarrierServices(int slotId, String simState) {
@@ -515,6 +470,17 @@
         updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
     }
 
+    private void handleSimError(int slotId) {
+        if (mIccId[slotId] != null && !mIccId[slotId].equals(ICCID_STRING_FOR_NO_SIM)) {
+            logd("SIM" + (slotId + 1) + " Error ");
+        }
+        mIccId[slotId] = ICCID_STRING_FOR_NO_SIM;
+        if (isAllIccIdQueryDone()) {
+            updateSubscriptionInfoByIccId();
+        }
+        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+    }
+
     /**
      * TODO: Simplify more, as no one is interested in what happened
      * only what the current list contains.
@@ -522,8 +488,6 @@
     synchronized private void updateSubscriptionInfoByIccId() {
         logd("updateSubscriptionInfoByIccId:+ Start");
 
-        mSubscriptionManager.clearSubscriptionInfo();
-
         for (int i = 0; i < PROJECT_SIM_NUM; i++) {
             mInsertSimState[i] = SIM_NOT_CHANGE;
         }
@@ -537,6 +501,13 @@
         }
         logd("insertedSimCount = " + insertedSimCount);
 
+        // We only clear the slot-to-sub map when one/some SIM was removed. Note this is a
+        // workaround for some race conditions that the empty map was accessed while we are
+        // rebuilding the map.
+        if (SubscriptionController.getInstance().getActiveSubIdList().length > insertedSimCount) {
+            SubscriptionController.getInstance().clearSubInfo();
+        }
+
         int index = 0;
         for (int i = 0; i < PROJECT_SIM_NUM; i++) {
             if (mInsertSimState[i] == SIM_NOT_INSERT) {
@@ -557,9 +528,9 @@
         for (int i = 0; i < PROJECT_SIM_NUM; i++) {
             oldIccId[i] = null;
             List<SubscriptionInfo> oldSubInfo =
-                    SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false,
+                    SubscriptionController.getInstance().getSubInfoUsingSlotIndexWithCheck(i, false,
                     mContext.getOpPackageName());
-            if (oldSubInfo != null) {
+            if (oldSubInfo != null && oldSubInfo.size() > 0) {
                 oldIccId[i] = oldSubInfo.get(0).getIccId();
                 logd("updateSubscriptionInfoByIccId: oldSubId = "
                         + oldSubInfo.get(0).getSubscriptionId());
@@ -688,8 +659,7 @@
         SubscriptionManager.putPhoneIdAndSubIdExtra(i, slotId);
         logd("Broadcasting intent ACTION_SIM_STATE_CHANGED " + state + " reason " + reason +
              " for mCardIndex: " + slotId);
-        ActivityManagerNative.broadcastStickyIntent(i, READ_PHONE_STATE, UserHandle.USER_ALL);
-        rebroadcastIntentsOnUnlock.put(slotId, i);
+        IntentBroadcaster.getInstance().broadcastStickyIntent(i, slotId);
     }
 
     public void dispose() {
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index e12d02a..193d29e 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -20,17 +20,14 @@
 import android.database.Cursor;
 import android.os.Handler;
 import android.os.IDeviceIdleController;
-import android.os.PowerManager;
 import android.os.ServiceManager;
 
-import com.android.ims.ImsManager;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
 import com.android.internal.telephony.cdma.EriManager;
 import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
-import com.android.internal.telephony.imsphone.ImsPullCall;
 import com.android.internal.telephony.uicc.IccCardProxy;
 
 /**
@@ -64,10 +61,22 @@
         return new ServiceStateTracker(phone, ci);
     }
 
+    public SimActivationTracker makeSimActivationTracker(Phone phone) {
+        return new SimActivationTracker(phone);
+    }
+
     public DcTracker makeDcTracker(Phone phone) {
         return new DcTracker(phone);
     }
 
+    public CarrierSignalAgent makeCarrierSignalAgent(Phone phone) {
+        return new CarrierSignalAgent(phone);
+    }
+
+    public CarrierActionAgent makeCarrierActionAgent(Phone phone) {
+        return new CarrierActionAgent(phone);
+    }
+
     public IccPhoneBookInterfaceManager makeIccPhoneBookInterfaceManager(Phone phone) {
         return new IccPhoneBookInterfaceManager(phone);
     }
@@ -92,19 +101,20 @@
      * Create a tracker for a single-part SMS.
      */
     public InboundSmsTracker makeInboundSmsTracker(byte[] pdu, long timestamp, int destPort,
-            boolean is3gpp2, boolean is3gpp2WapPdu, String address, String messageBody) {
+            boolean is3gpp2, boolean is3gpp2WapPdu, String address, String displayAddr,
+            String messageBody) {
         return new InboundSmsTracker(pdu, timestamp, destPort, is3gpp2, is3gpp2WapPdu, address,
-                messageBody);
+                displayAddr, messageBody);
     }
 
     /**
      * Create a tracker for a multi-part SMS.
      */
     public InboundSmsTracker makeInboundSmsTracker(byte[] pdu, long timestamp, int destPort,
-            boolean is3gpp2, String address, int referenceNumber, int sequenceNumber,
+            boolean is3gpp2, String address, String displayAddr, int referenceNumber, int sequenceNumber,
             int messageCount, boolean is3gpp2WapPdu, String messageBody) {
-        return new InboundSmsTracker(pdu, timestamp, destPort, is3gpp2, address, referenceNumber,
-                sequenceNumber, messageCount, is3gpp2WapPdu, messageBody);
+        return new InboundSmsTracker(pdu, timestamp, destPort, is3gpp2, address, displayAddr,
+                referenceNumber, sequenceNumber, messageCount, is3gpp2WapPdu, messageBody);
     }
 
     /**
@@ -123,6 +133,17 @@
         return new ImsExternalCallTracker(imsPhone);
     }
 
+    /**
+     * Create an AppSmsManager for per-app SMS message.
+     */
+    public AppSmsManager makeAppSmsManager(Context context) {
+        return new AppSmsManager(context);
+    }
+
+    public DeviceStateMonitor makeDeviceStateMonitor(Phone phone) {
+        return new DeviceStateMonitor(phone);
+    }
+
     public CdmaSubscriptionSourceManager
     getCdmaSubscriptionSourceManagerInstance(Context context, CommandsInterface ci, Handler h,
                                              int what, Object obj) {
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index ba59e6d..3608c20 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -20,10 +20,9 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
 import android.net.Uri;
+import android.os.BadParcelableException;
 import android.os.Build;
-import android.preference.PreferenceManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 
@@ -32,10 +31,10 @@
 import com.android.ims.ImsConferenceState;
 import com.android.ims.ImsExternalCallState;
 import com.android.ims.ImsReasonInfo;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
-import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
 import com.android.internal.telephony.test.TestConferenceEventPackageParser;
 
 import java.io.File;
@@ -82,6 +81,17 @@
     private static final String ACTION_TEST_HANDOVER_FAIL =
             "com.android.internal.telephony.TestHandoverFail";
 
+    /**
+     * Test-only intent used to trigger signalling of a
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification} to the {@link ImsPhone}.
+     * Use {@link #EXTRA_CODE} to specify the
+     * {@link com.android.internal.telephony.gsm.SuppServiceNotification#code}.
+     */
+    private static final String ACTION_TEST_SUPP_SRVC_NOTIFICATION =
+            "com.android.internal.telephony.TestSuppSrvcNotification";
+
+    private static final String EXTRA_CODE = "code";
+
     private static List<ImsExternalCallState> mImsExternalCallStates = null;
 
     private Phone mPhone;
@@ -92,24 +102,32 @@
             @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (DBG) log("sIntentReceiver.onReceive: action=" + action);
-            if (action.equals(mPhone.getActionDetached())) {
-                log("simulate detaching");
-                mPhone.getServiceStateTracker().mDetachedRegistrants.notifyRegistrants();
-            } else if (action.equals(mPhone.getActionAttached())) {
-                log("simulate attaching");
-                mPhone.getServiceStateTracker().mAttachedRegistrants.notifyRegistrants();
-            } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) {
-                log("inject simulated conference event package");
-                handleTestConferenceEventPackage(context, intent.getStringExtra(EXTRA_FILENAME));
-            } else if (action.equals(ACTION_TEST_DIALOG_EVENT_PACKAGE)) {
-                log("handle test dialog event package intent");
-                handleTestDialogEventPackageIntent(intent);
-            } else if (action.equals(ACTION_TEST_HANDOVER_FAIL)) {
-                log("handle handover fail test intent");
-                handleHandoverFailedIntent();
-            } else {
-                if (DBG) log("onReceive: unknown action=" + action);
+            try {
+                if (DBG) log("sIntentReceiver.onReceive: action=" + action);
+                if (action.equals(mPhone.getActionDetached())) {
+                    log("simulate detaching");
+                    mPhone.getServiceStateTracker().mDetachedRegistrants.notifyRegistrants();
+                } else if (action.equals(mPhone.getActionAttached())) {
+                    log("simulate attaching");
+                    mPhone.getServiceStateTracker().mAttachedRegistrants.notifyRegistrants();
+                } else if (action.equals(ACTION_TEST_CONFERENCE_EVENT_PACKAGE)) {
+                    log("inject simulated conference event package");
+                    handleTestConferenceEventPackage(context,
+                            intent.getStringExtra(EXTRA_FILENAME));
+                } else if (action.equals(ACTION_TEST_DIALOG_EVENT_PACKAGE)) {
+                    log("handle test dialog event package intent");
+                    handleTestDialogEventPackageIntent(intent);
+                } else if (action.equals(ACTION_TEST_HANDOVER_FAIL)) {
+                    log("handle handover fail test intent");
+                    handleHandoverFailedIntent();
+                } else if (action.equals(ACTION_TEST_SUPP_SRVC_NOTIFICATION)) {
+                    log("handle supp service notification test intent");
+                    sendTestSuppServiceNotification(intent);
+                } else {
+                    if (DBG) log("onReceive: unknown action=" + action);
+                }
+            } catch (BadParcelableException e) {
+                Rlog.w(LOG_TAG, e);
             }
         }
     };
@@ -131,6 +149,7 @@
                 filter.addAction(ACTION_TEST_CONFERENCE_EVENT_PACKAGE);
                 filter.addAction(ACTION_TEST_DIALOG_EVENT_PACKAGE);
                 filter.addAction(ACTION_TEST_HANDOVER_FAIL);
+                filter.addAction(ACTION_TEST_SUPP_SRVC_NOTIFICATION);
                 mImsExternalCallStates = new ArrayList<ImsExternalCallState>();
             }
 
@@ -245,4 +264,18 @@
             mImsExternalCallStates.add(state);
         }
     }
+
+    private void sendTestSuppServiceNotification(Intent intent) {
+        if (intent.hasExtra(EXTRA_CODE)) {
+            int code = intent.getIntExtra(EXTRA_CODE, -1);
+            ImsPhone imsPhone = (ImsPhone) mPhone;
+            if (imsPhone == null) {
+                return;
+            }
+            log("Test supp service notification:" + code);
+            SuppServiceNotification suppServiceNotification = new SuppServiceNotification();
+            suppServiceNotification.code = code;
+            imsPhone.notifySuppSvcNotification(suppServiceNotification);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
index 6844dd3..e6cb972 100755
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -42,16 +42,20 @@
 public class UiccSmsController extends ISms.Stub {
     static final String LOG_TAG = "RIL_UiccSmsController";
 
-    protected Phone[] mPhone;
-
-    protected UiccSmsController(Phone[] phone){
-        mPhone = phone;
-
+    protected UiccSmsController() {
         if (ServiceManager.getService("isms") == null) {
             ServiceManager.addService("isms", this);
         }
     }
 
+    private Phone getPhone(int subId) {
+        Phone phone = PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
+        if (phone == null) {
+            phone = PhoneFactory.getDefaultPhone();
+        }
+        return phone;
+    }
+
     @Override
     public boolean
     updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
@@ -320,45 +324,26 @@
     }
 
     /**
-     * get sms interface manager object based on subscription.
-     **/
+     * Get sms interface manager object based on subscription.
+     * @return ICC SMS manager
+     */
     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)
-                || phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
-            phoneId = 0;
-        }
-
-        try {
-            return (IccSmsInterfaceManager)
-                ((Phone)mPhone[(int)phoneId]).getIccSmsInterfaceManager();
-        } catch (NullPointerException e) {
-            Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
-            e.printStackTrace();
-            return null;
-        } catch (ArrayIndexOutOfBoundsException e) {
-            Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
-            e.printStackTrace();
-            return null;
-        }
+        return getPhone(subId).getIccSmsInterfaceManager();
     }
 
     /**
-       Gets User preferred SMS subscription */
+     * Get User preferred SMS subscription
+     * @return User preferred SMS subscription
+     */
     @Override
     public int getPreferredSmsSubscription() {
         return SubscriptionController.getInstance().getDefaultSmsSubId();
     }
 
     /**
-     * Get SMS prompt property,  enabled or not
-     **/
+     * Get SMS prompt property enabled or not
+     * @return True if SMS prompt is enabled.
+     */
     @Override
     public boolean isSMSPromptEnabled() {
         return PhoneFactory.isSMSPromptEnabled();
@@ -392,11 +377,9 @@
         }
     }
 
-    /*
-     * @return true if the subId is active.
-     */
-    private boolean isActiveSubId(int subId) {
-        return SubscriptionController.getInstance().isActiveSubId(subId);
+    @Override
+    public String createAppSpecificSmsToken(int subId, String callingPkg, PendingIntent intent) {
+        return getPhone(subId).getAppSmsManager().createAppSpecificSmsToken(callingPkg, intent);
     }
 
     private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
index ca1d9e0..1294762 100644
--- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
+++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -16,42 +16,101 @@
 package com.android.internal.telephony;
 
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.provider.VoicemailContract;
+import android.telecom.PhoneAccountHandle;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.SmsMessage;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.VisualVoicemailSms;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 import android.util.ArrayMap;
 import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.VisualVoicemailSmsParser.WrappedMessageData;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetDecoder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
 
+/**
+ * Filters SMS to {@link android.telephony.VisualVoicemailService}, based on the config from {@link
+ * VisualVoicemailSmsFilterSettings}. The SMS is sent to telephony service which will do the actual
+ * dispatching.
+ */
 public class VisualVoicemailSmsFilter {
 
+    /**
+     * Interface to convert subIds so the logic can be replaced in tests.
+     */
+    @VisibleForTesting
+    public interface PhoneAccountHandleConverter {
+
+        /**
+         * Convert the subId to a {@link PhoneAccountHandle}
+         */
+        PhoneAccountHandle fromSubId(int subId);
+    }
+
     private static final String TAG = "VvmSmsFilter";
 
-    private static final String SYSTEM_VVM_CLIENT_PACKAGE = "com.android.phone";
+    private static final String TELEPHONY_SERVICE_PACKAGE = "com.android.phone";
+
+    private static final ComponentName PSTN_CONNECTION_SERVICE_COMPONENT =
+            new ComponentName("com.android.phone",
+                    "com.android.services.telephony.TelephonyConnectionService");
 
     private static Map<String, List<Pattern>> sPatterns;
 
+    private static final PhoneAccountHandleConverter DEFAULT_PHONE_ACCOUNT_HANDLE_CONVERTER =
+            new PhoneAccountHandleConverter() {
+
+                @Override
+                public PhoneAccountHandle fromSubId(int subId) {
+                    if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+                        return null;
+                    }
+                    int phoneId = SubscriptionManager.getPhoneId(subId);
+                    if (phoneId == SubscriptionManager.INVALID_PHONE_INDEX) {
+                        return null;
+                    }
+                    return new PhoneAccountHandle(PSTN_CONNECTION_SERVICE_COMPONENT,
+                            PhoneFactory.getPhone(phoneId).getFullIccSerialNumber());
+                }
+            };
+
+    private static PhoneAccountHandleConverter sPhoneAccountHandleConverter =
+            DEFAULT_PHONE_ACCOUNT_HANDLE_CONVERTER;
+
+    /**
+     * Wrapper to combine multiple PDU into an SMS message
+     */
+    private static class FullMessage {
+        public SmsMessage firstMessage;
+        public String fullMessageBody;
+    }
+
     /**
      * Attempt to parse the incoming SMS as a visual voicemail SMS. If the parsing succeeded, A
-     * {@link VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} intent will be sent to the visual
-     * voicemail client, and the SMS should be dropped.
+     * {@link VoicemailContract#ACTION_VOICEMAIL_SMS_RECEIVED} intent will be sent to telephony
+     * service, and the SMS will be dropped.
      *
      * <p>The accepted format for a visual voicemail SMS is a generalization of the OMTP format:
      *
      * <p>[clientPrefix]:[prefix]:([key]=[value];)*
      *
      * Additionally, if the SMS does not match the format, but matches the regex specified by the
-     * carrier in {@link com.android.internal.R.array.config_vvmSmsFilterRegexes}, the SMS will
-     * still be dropped and a {@link VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED} with {@link
-     * VoicemailContract#EXTRA_VOICEMAIL_SMS_MESSAGE_BODY} will be sent.
+     * carrier in {@link com.android.internal.R.array#config_vvmSmsFilterRegexes}, the SMS will
+     * still be dropped and a {@link VoicemailContract#ACTION_VOICEMAIL_SMS_RECEIVED} will be sent.
      *
      * @return true if the SMS has been parsed to be a visual voicemail SMS and should be dropped
      */
@@ -60,37 +119,65 @@
         TelephonyManager telephonyManager =
                 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
 
-        // TODO: select client package.
-        String vvmClientPackage = SYSTEM_VVM_CLIENT_PACKAGE;
+        VisualVoicemailSmsFilterSettings settings;
+        settings = telephonyManager.getActiveVisualVoicemailSmsFilterSettings(subId);
 
-        VisualVoicemailSmsFilterSettings settings =
-                telephonyManager.getVisualVoicemailSmsFilterSettings(vvmClientPackage, subId);
         if (settings == null) {
             return false;
         }
-        // TODO: filter base on originating number and destination port.
 
-        String messageBody = getFullMessage(pdus, format);
+        PhoneAccountHandle phoneAccountHandle = sPhoneAccountHandleConverter.fromSubId(subId);
 
-        if(messageBody == null){
-            // Verizon WAP push SMS is not recognized by android, which has a ascii PDU.
+        if (phoneAccountHandle == null) {
+            Log.e(TAG, "Unable to convert subId " + subId + " to PhoneAccountHandle");
+            return false;
+        }
+
+        FullMessage fullMessage = getFullMessage(pdus, format);
+
+        if (fullMessage == null) {
+            // Carrier WAP push SMS is not recognized by android, which has a ascii PDU.
             // Attempt to parse it.
             Log.i(TAG, "Unparsable SMS received");
             String asciiMessage = parseAsciiPduMessage(pdus);
             WrappedMessageData messageData = VisualVoicemailSmsParser
-                .parseAlternativeFormat(asciiMessage);
+                    .parseAlternativeFormat(asciiMessage);
             if (messageData != null) {
-                sendVvmSmsBroadcast(context, vvmClientPackage, subId, messageData, null);
+                sendVvmSmsBroadcast(context, phoneAccountHandle, messageData, null);
             }
             // Confidence for what the message actually is is low. Don't remove the message and let
             // system decide. Usually because it is not parsable it will be dropped.
             return false;
         }
+
+        String messageBody = fullMessage.fullMessageBody;
         String clientPrefix = settings.clientPrefix;
         WrappedMessageData messageData = VisualVoicemailSmsParser
-            .parse(clientPrefix, messageBody);
+                .parse(clientPrefix, messageBody);
         if (messageData != null) {
-            sendVvmSmsBroadcast(context, vvmClientPackage, subId, messageData, null);
+            if (settings.destinationPort
+                    == VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS) {
+                if (destPort == -1) {
+                    // Non-data SMS is directed to the port "-1".
+                    Log.i(TAG, "SMS matching VVM format received but is not a DATA SMS");
+                    return false;
+                }
+            } else if (settings.destinationPort
+                    != VisualVoicemailSmsFilterSettings.DESTINATION_PORT_ANY) {
+                if (settings.destinationPort != destPort) {
+                    Log.i(TAG, "SMS matching VVM format received but is not directed to port "
+                            + settings.destinationPort);
+                    return false;
+                }
+            }
+
+            if (!settings.originatingNumbers.isEmpty()
+                    && !isSmsFromNumbers(fullMessage.firstMessage, settings.originatingNumbers)) {
+                Log.i(TAG, "SMS matching VVM format received but is not from originating numbers");
+                return false;
+            }
+
+            sendVvmSmsBroadcast(context, phoneAccountHandle, messageData, null);
             return true;
         }
 
@@ -105,14 +192,27 @@
         for (Pattern pattern : patterns) {
             if (pattern.matcher(messageBody).matches()) {
                 Log.w(TAG, "Incoming SMS matches pattern " + pattern + " but has illegal format, "
-                    + "still dropping as VVM SMS");
-                sendVvmSmsBroadcast(context, vvmClientPackage, subId, null, messageBody);
+                        + "still dropping as VVM SMS");
+                sendVvmSmsBroadcast(context, phoneAccountHandle, null, messageBody);
                 return true;
             }
         }
         return false;
     }
 
+    /**
+     * override how subId is converted to PhoneAccountHandle for tests
+     */
+    @VisibleForTesting
+    public static void setPhoneAccountHandleConverterForTest(
+            PhoneAccountHandleConverter converter) {
+        if (converter == null) {
+            sPhoneAccountHandleConverter = DEFAULT_PHONE_ACCOUNT_HANDLE_CONVERTER;
+        } else {
+            sPhoneAccountHandleConverter = converter;
+        }
+    }
+
     private static void buildPatternsMap(Context context) {
         if (sPatterns != null) {
             return;
@@ -120,7 +220,7 @@
         sPatterns = new ArrayMap<>();
         // TODO(twyen): build from CarrierConfig once public API can be updated.
         for (String entry : context.getResources()
-            .getStringArray(com.android.internal.R.array.config_vvmSmsFilterRegexes)) {
+                .getStringArray(com.android.internal.R.array.config_vvmSmsFilterRegexes)) {
             String[] mccMncList = entry.split(";")[0].split(",");
             Pattern pattern = Pattern.compile(entry.split(";")[1]);
 
@@ -133,19 +233,21 @@
         }
     }
 
-    private static void sendVvmSmsBroadcast(Context context, String vvmClientPackage, int subId,
-        @Nullable WrappedMessageData messageData, @Nullable String messageBody) {
+    private static void sendVvmSmsBroadcast(Context context, PhoneAccountHandle phoneAccountHandle,
+            @Nullable WrappedMessageData messageData, @Nullable String messageBody) {
         Log.i(TAG, "VVM SMS received");
         Intent intent = new Intent(VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED);
+        VisualVoicemailSms.Builder builder = new VisualVoicemailSms.Builder();
         if (messageData != null) {
-            intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_PREFIX, messageData.prefix);
-            intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_FIELDS, messageData.fields);
+            builder.setPrefix(messageData.prefix);
+            builder.setFields(messageData.fields);
         }
         if (messageBody != null) {
-            intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_MESSAGE_BODY, messageBody);
+            builder.setMessageBody(messageBody);
         }
-        intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS_SUBID, subId);
-        intent.setPackage(vvmClientPackage);
+        builder.setPhoneAccountHandle(phoneAccountHandle);
+        intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS, builder.build());
+        intent.setPackage(TELEPHONY_SERVICE_PACKAGE);
         context.sendBroadcast(intent);
     }
 
@@ -153,21 +255,39 @@
      * @return the message body of the SMS, or {@code null} if it can not be parsed.
      */
     @Nullable
-    private static String getFullMessage(byte[][] pdus, String format) {
+    private static FullMessage getFullMessage(byte[][] pdus, String format) {
+        FullMessage result = new FullMessage();
         StringBuilder builder = new StringBuilder();
+        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
         for (byte pdu[] : pdus) {
             SmsMessage message = SmsMessage.createFromPdu(pdu, format);
-
             if (message == null) {
                 // The PDU is not recognized by android
                 return null;
             }
+            if (result.firstMessage == null) {
+                result.firstMessage = message;
+            }
             String body = message.getMessageBody();
+            if (body == null && message.getUserData() != null) {
+                // Attempt to interpret the user data as UTF-8. UTF-8 string over data SMS using
+                // 8BIT data coding scheme is our recommended way to send VVM SMS and is used in CTS
+                // Tests. The OMTP visual voicemail specification does not specify the SMS type and
+                // encoding.
+                ByteBuffer byteBuffer = ByteBuffer.wrap(message.getUserData());
+                try {
+                    body = decoder.decode(byteBuffer).toString();
+                } catch (CharacterCodingException e) {
+                    // User data is not decode-able as UTF-8. Ignoring.
+                    return null;
+                }
+            }
             if (body != null) {
                 builder.append(body);
             }
         }
-        return builder.toString();
+        result.fullMessageBody = builder.toString();
+        return result;
     }
 
     private static String parseAsciiPduMessage(byte[][] pdus) {
@@ -177,4 +297,18 @@
         }
         return builder.toString();
     }
+
+    private static boolean isSmsFromNumbers(SmsMessage message, List<String> numbers) {
+        if (message == null) {
+            Log.e(TAG, "Unable to create SmsMessage from PDU, cannot determine originating number");
+            return false;
+        }
+
+        for (String number : numbers) {
+            if (PhoneNumberUtils.compare(number, message.getOriginatingAddress())) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/cat/AppInterface.java b/src/java/com/android/internal/telephony/cat/AppInterface.java
index 1694d9e..1f2d3a0 100755
--- a/src/java/com/android/internal/telephony/cat/AppInterface.java
+++ b/src/java/com/android/internal/telephony/cat/AppInterface.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.cat;
 
+import android.content.ComponentName;
+
 /**
  * Interface for communication between STK App and CAT Telephony
  *
@@ -28,11 +30,11 @@
      * proactive command, session end, ALPHA during STK CC arrive.
      */
     public static final String CAT_CMD_ACTION =
-                                    "android.intent.action.stk.command";
+                                    "com.android.internal.stk.command";
     public static final String CAT_SESSION_END_ACTION =
-                                    "android.intent.action.stk.session_end";
+                                    "com.android.internal.stk.session_end";
     public static final String CAT_ALPHA_NOTIFY_ACTION =
-                                    "android.intent.action.stk.alpha_notify";
+                                    "com.android.internal.stk.alpha_notify";
 
     //This is used to send ALPHA string from card to STK App.
     public static final String ALPHA_STRING = "alpha_string";
@@ -43,11 +45,16 @@
     public static final String CARD_STATUS = "card_status";
     //Intent's actions are broadcasted by Telephony once IccRefresh occurs.
     public static final String CAT_ICC_STATUS_CHANGE =
-                                    "android.intent.action.stk.icc_status_change";
+                                    "com.android.internal.stk.icc_status_change";
 
     // Permission required by STK command receiver
     public static final String STK_PERMISSION = "android.permission.RECEIVE_STK_COMMANDS";
 
+    // Only forwards cat broadcast to the system default stk app
+    public static ComponentName getDefaultSTKApplication() {
+        return ComponentName.unflattenFromString("com.android.stk/.StkCmdReceiver");
+    }
+
     /*
      * Callback function from app to telephony to pass a result code and user's
      * input back to the ICC.
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 145a2b3..cd7a756 100755
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -264,7 +264,7 @@
             mHandlerThread = null;
             removeCallbacksAndMessages(null);
             if (sInstance != null) {
-                if (SubscriptionManager.isValidSlotId(mSlotId)) {
+                if (SubscriptionManager.isValidSlotIndex(mSlotId)) {
                     sInstance[mSlotId] = null;
                 } else {
                     CatLog.d(this, "error: invaild slot id: " + mSlotId);
@@ -531,6 +531,7 @@
         Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
         intent.putExtra("STK CMD", cmdMsg);
         intent.putExtra("SLOT_ID", mSlotId);
+        intent.setComponent(AppInterface.getDefaultSTKApplication());
         CatLog.d(this, "Sending CmdMsg: " + cmdMsg+ " on slotid:" + mSlotId);
         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
     }
@@ -545,6 +546,7 @@
         mCurrntCmd = mMenuCmd;
         Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION);
         intent.putExtra("SLOT_ID", mSlotId);
+        intent.setComponent(AppInterface.getDefaultSTKApplication());
         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
     }
 
@@ -793,7 +795,7 @@
         int slotId = PhoneConstants.DEFAULT_CARD_INDEX;
         SubscriptionController sControl = SubscriptionController.getInstance();
         if (sControl != null) {
-            slotId = sControl.getSlotId(sControl.getDefaultSubId());
+            slotId = sControl.getSlotIndex(sControl.getDefaultSubId());
         }
         return getInstance(null, null, null, slotId);
     }
@@ -896,8 +898,10 @@
 
         // This sends an intent with CARD_ABSENT (0 - false) /CARD_PRESENT (1 - true).
         intent.putExtra(AppInterface.CARD_STATUS, cardPresent);
+        intent.setComponent(AppInterface.getDefaultSTKApplication());
+        intent.putExtra("SLOT_ID", mSlotId);
         CatLog.d(this, "Sending Card Status: "
-                + cardState + " " + "cardPresent: " + cardPresent);
+                + cardState + " " + "cardPresent: " + cardPresent +  "SLOT_ID: " +  mSlotId);
         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
     }
 
@@ -907,6 +911,7 @@
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(AppInterface.ALPHA_STRING, alphaString);
         intent.putExtra("SLOT_ID", mSlotId);
+        intent.setComponent(AppInterface.getDefaultSTKApplication());
         mContext.sendBroadcast(intent, AppInterface.STK_PERMISSION);
     }
 
@@ -1020,6 +1025,13 @@
                 }
                 break;
             case LAUNCH_BROWSER:
+                if (resMsg.mResCode == ResultCode.LAUNCH_BROWSER_ERROR) {
+                    // Additional info for Default URL unavailable.
+                    resMsg.setAdditionalInfo(0x04);
+                } else {
+                    resMsg.mIncludeAdditionalInfo = false;
+                    resMsg.mAdditionalInfo = 0;
+                }
                 break;
             // 3GPP TS.102.223: Open Channel alpha confirmation should not send TR
             case OPEN_CHANNEL:
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 3daf414..eb92888 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -242,7 +242,9 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
         case MSG_ID_LOAD_ICON_DONE:
-            sendCmdParams(setIcons(msg.obj));
+            if (mIconLoader != null) {
+                sendCmdParams(setIcons(msg.obj));
+            }
             break;
         }
     }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index a51d7ba..5a40c4e 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -170,7 +170,8 @@
 
         if (SmsEnvelope.TELESERVICE_WAP == teleService) {
             return processCdmaWapPdu(sms.getUserData(), sms.mMessageRef,
-                    sms.getOriginatingAddress(), sms.getTimestampMillis());
+                    sms.getOriginatingAddress(), sms.getDisplayOriginatingAddress(),
+                    sms.getTimestampMillis());
         }
 
         return dispatchNormalMessage(smsb);
@@ -257,7 +258,7 @@
      *         {@link Activity#RESULT_OK} if the message has been broadcast
      *         to applications
      */
-    private int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address,
+    private int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address, String dispAddr,
             long timestamp) {
         int index = 0;
 
@@ -302,8 +303,8 @@
         System.arraycopy(pdu, index, userData, 0, pdu.length - index);
 
         InboundSmsTracker tracker = TelephonyComponentFactory.getInstance().makeInboundSmsTracker(
-                userData, timestamp, destinationPort, true, address, referenceNumber, segment,
-                totalSegments, true, HexDump.toHexString(userData));
+                userData, timestamp, destinationPort, true, address, dispAddr, referenceNumber,
+                segment, totalSegments, true, HexDump.toHexString(userData));
 
         // de-duping is done only for text messages
         return addTrackerToRawTableAndSendMessage(tracker, false /* don't de-dup */);
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
index 7531bb9..4987e51 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
@@ -36,6 +36,34 @@
     public static final int RIL_CDMA_T53_RELEASE_INFO_REC = 9;
     public static final int RIL_CDMA_T53_AUDIO_CONTROL_INFO_REC = 10;
 
+    public CdmaInformationRecords(CdmaDisplayInfoRec obj) {
+        record = obj;
+    }
+
+    public CdmaInformationRecords(CdmaNumberInfoRec obj) {
+        record = obj;
+    }
+
+    public CdmaInformationRecords(CdmaSignalInfoRec obj) {
+        record = obj;
+    }
+
+    public CdmaInformationRecords(CdmaRedirectingNumberInfoRec obj) {
+        record = obj;
+    }
+
+    public CdmaInformationRecords(CdmaLineControlInfoRec obj) {
+        record = obj;
+    }
+
+    public CdmaInformationRecords(CdmaT53ClirInfoRec obj) {
+        record = obj;
+    }
+
+    public CdmaInformationRecords(CdmaT53AudioControlInfoRec obj) {
+        record = obj;
+    }
+
     public CdmaInformationRecords(Parcel p) {
         int id = p.readInt();
         switch (id) {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
index b2893d0..ab76f98 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaMmiCode.java
@@ -28,6 +28,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ResultReceiver;
 import android.telephony.Rlog;
 
 import java.util.regex.Pattern;
@@ -207,6 +208,11 @@
         return false;
     }
 
+    @Override
+    public String getDialString() {
+        return null;
+    }
+
     /** Process a MMI PUK code */
     public void
     processCode() {
@@ -368,4 +374,8 @@
         mPhone.onMMIDone(this);
     }
 
+    @Override
+    public ResultReceiver getUssdCallbackReceiver() {
+        return null;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 2e9ab44..1cfdc33 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -217,7 +217,7 @@
 
     @Override
     protected void sendSubmitPdu(SmsTracker tracker) {
-        if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) {
+        if (mPhone.isInEcm()) {
             if (VDBG) {
                 Rlog.d(TAG, "Block SMS in Emergency Callback mode");
             }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
index 81413c6..bcbce53 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaServiceCategoryProgramHandler.java
@@ -31,7 +31,6 @@
 import android.telephony.cdma.CdmaSmsCbProgramResults;
 
 import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.WakeLockStateMachine;
 import com.android.internal.telephony.cdma.sms.BearerData;
 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
@@ -103,6 +102,8 @@
         }
 
         Intent intent = new Intent(Intents.SMS_SERVICE_CATEGORY_PROGRAM_DATA_RECEIVED_ACTION);
+        intent.setPackage(mContext.getResources().getString(
+                com.android.internal.R.string.config_defaultCellBroadcastReceiverPkg));
         intent.putExtra("sender", sms.getOriginatingAddress());
         intent.putParcelableArrayListExtra("program_data", programDataList);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
diff --git a/src/java/com/android/internal/telephony/cdma/SmsMessage.java b/src/java/com/android/internal/telephony/cdma/SmsMessage.java
deleted file mode 100644
index 0b75fa5..0000000
--- a/src/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ /dev/null
@@ -1,1058 +0,0 @@
-/*
- * Copyright (C) 2008 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.cdma;
-
-import android.os.Parcel;
-import android.os.SystemProperties;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.SmsCbLocation;
-import android.telephony.SmsCbMessage;
-import android.telephony.TelephonyManager;
-import android.telephony.cdma.CdmaSmsCbProgramData;
-import android.telephony.Rlog;
-import android.util.Log;
-import android.text.TextUtils;
-import android.content.res.Resources;
-
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.telephony.cdma.sms.BearerData;
-import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
-import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
-import com.android.internal.telephony.cdma.sms.SmsEnvelope;
-import com.android.internal.telephony.cdma.sms.UserData;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.util.BitwiseInputStream;
-import com.android.internal.util.HexDump;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-
-/**
- * TODO(cleanup): these constants are disturbing... are they not just
- * different interpretations on one number?  And if we did not have
- * terrible class name overlap, they would not need to be directly
- * imported like this.  The class in this file could just as well be
- * named CdmaSmsMessage, could it not?
- */
-
-/**
- * TODO(cleanup): internally returning null in many places makes
- * debugging very hard (among many other reasons) and should be made
- * more meaningful (replaced with exceptions for example).  Null
- * returns should only occur at the very outside of the module/class
- * scope.
- */
-
-/**
- * A Short Message Service message.
- *
- */
-public class SmsMessage extends SmsMessageBase {
-    static final String LOG_TAG = "SmsMessage";
-    static private final String LOGGABLE_TAG = "CDMA:SMS";
-    private static final boolean VDBG = false;
-
-    private final static byte TELESERVICE_IDENTIFIER                    = 0x00;
-    private final static byte SERVICE_CATEGORY                          = 0x01;
-    private final static byte ORIGINATING_ADDRESS                       = 0x02;
-    private final static byte ORIGINATING_SUB_ADDRESS                   = 0x03;
-    private final static byte DESTINATION_ADDRESS                       = 0x04;
-    private final static byte DESTINATION_SUB_ADDRESS                   = 0x05;
-    private final static byte BEARER_REPLY_OPTION                       = 0x06;
-    private final static byte CAUSE_CODES                               = 0x07;
-    private final static byte BEARER_DATA                               = 0x08;
-
-    /**
-     *  Status of a previously submitted SMS.
-     *  This field applies to SMS Delivery Acknowledge messages. 0 indicates success;
-     *  Here, the error class is defined by the bits from 9-8, the status code by the bits from 7-0.
-     *  See C.S0015-B, v2.0, 4.5.21 for a detailed description of possible values.
-     */
-    private int status;
-
-    /** Specifies if a return of an acknowledgment is requested for send SMS */
-    private static final int RETURN_NO_ACK  = 0;
-    private static final int RETURN_ACK     = 1;
-
-    private SmsEnvelope mEnvelope;
-    private BearerData mBearerData;
-
-    public static class SubmitPdu extends SubmitPduBase {
-    }
-
-    /**
-     * Create an SmsMessage from a raw PDU.
-     * Note: In CDMA the PDU is just a byte representation of the received Sms.
-     */
-    public static SmsMessage createFromPdu(byte[] pdu) {
-        SmsMessage msg = new SmsMessage();
-
-        try {
-            msg.parsePdu(pdu);
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
-            return null;
-        } catch (OutOfMemoryError e) {
-            Log.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e);
-            return null;
-        }
-    }
-
-    /**
-     *  Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp.
-     *  Note: Only primitive fields are set.
-     */
-    public static SmsMessage newFromParcel(Parcel p) {
-        // Note: Parcel.readByte actually reads one Int and masks to byte
-        SmsMessage msg = new SmsMessage();
-        SmsEnvelope env = new SmsEnvelope();
-        CdmaSmsAddress addr = new CdmaSmsAddress();
-        CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
-        byte[] data;
-        byte count;
-        int countInt;
-        int addressDigitMode;
-
-        //currently not supported by the modem-lib: env.mMessageType
-        env.teleService = p.readInt(); //p_cur->uTeleserviceID
-
-        if (0 != p.readByte()) { //p_cur->bIsServicePresent
-            env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST;
-        }
-        else {
-            if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) {
-                // assume type ACK
-                env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE;
-            } else {
-                env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
-            }
-        }
-        env.serviceCategory = p.readInt(); //p_cur->uServicecategory
-
-        // address
-        addressDigitMode = p.readInt();
-        addr.digitMode = (byte) (0xFF & addressDigitMode); //p_cur->sAddress.digit_mode
-        addr.numberMode = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_mode
-        addr.ton = p.readInt(); //p_cur->sAddress.number_type
-        addr.numberPlan = (byte) (0xFF & p.readInt()); //p_cur->sAddress.number_plan
-        count = p.readByte(); //p_cur->sAddress.number_of_digits
-        addr.numberOfDigits = count;
-        data = new byte[count];
-        //p_cur->sAddress.digits[digitCount]
-        for (int index=0; index < count; index++) {
-            data[index] = p.readByte();
-
-            // convert the value if it is 4-bit DTMF to 8 bit
-            if (addressDigitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
-                data[index] = msg.convertDtmfToAscii(data[index]);
-            }
-        }
-
-        addr.origBytes = data;
-
-        subaddr.type = p.readInt(); // p_cur->sSubAddress.subaddressType
-        subaddr.odd = p.readByte();     // p_cur->sSubAddress.odd
-        count = p.readByte();           // p_cur->sSubAddress.number_of_digits
-
-        if (count < 0) {
-            count = 0;
-        }
-
-        // p_cur->sSubAddress.digits[digitCount] :
-
-        data = new byte[count];
-
-        for (int index = 0; index < count; ++index) {
-            data[index] = p.readByte();
-        }
-
-        subaddr.origBytes = data;
-
-        /* currently not supported by the modem-lib:
-            env.bearerReply
-            env.replySeqNo
-            env.errorClass
-            env.causeCode
-        */
-
-        // bearer data
-        countInt = p.readInt(); //p_cur->uBearerDataLen
-        if (countInt < 0) {
-            countInt = 0;
-        }
-
-        data = new byte[countInt];
-        for (int index=0; index < countInt; index++) {
-            data[index] = p.readByte();
-        }
-        // BD gets further decoded when accessed in SMSDispatcher
-        env.bearerData = data;
-
-        // link the the filled objects to the SMS
-        env.origAddress = addr;
-        env.origSubaddress = subaddr;
-        msg.mOriginatingAddress = addr;
-        msg.mEnvelope = env;
-
-        // create byte stream representation for transportation through the layers.
-        msg.createPdu();
-
-        return msg;
-    }
-
-    /**
-     * Create an SmsMessage from an SMS EF record.
-     *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1.
-     * @param data Record data.
-     * @return An SmsMessage representing the record.
-     *
-     * @hide
-     */
-    public static SmsMessage createFromEfRecord(int index, byte[] data) {
-        try {
-            SmsMessage msg = new SmsMessage();
-
-            msg.mIndexOnIcc = index;
-
-            // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
-            // or STORED_UNSENT
-            // See 3GPP2 C.S0023 3.4.27
-            if ((data[0] & 1) == 0) {
-                Rlog.w(LOG_TAG, "SMS parsing failed: Trying to parse a free record");
-                return null;
-            } else {
-                msg.mStatusOnIcc = data[0] & 0x07;
-            }
-
-            // Second byte is the MSG_LEN, length of the message
-            // See 3GPP2 C.S0023 3.4.27
-            int size = data[1];
-
-            // Note: Data may include trailing FF's.  That's OK; message
-            // should still parse correctly.
-            byte[] pdu = new byte[size];
-            System.arraycopy(data, 2, pdu, 0, size);
-            // the message has to be parsed before it can be displayed
-            // see gsm.SmsMessage
-            msg.parsePduFromEfRecord(pdu);
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
-            return null;
-        }
-
-    }
-
-    /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    public static int getTPLayerLengthForPDU(String pdu) {
-        Rlog.w(LOG_TAG, "getTPLayerLengthForPDU: is not supported in CDMA mode.");
-        return 0;
-    }
-
-    /**
-     * TODO(cleanup): why do getSubmitPdu methods take an scAddr input
-     * and do nothing with it?  GSM allows us to specify a SC (eg,
-     * when responding to an SMS that explicitly requests the response
-     * is sent to a specific SC), or pass null to use the default
-     * value.  Is there no similar notion in CDMA? Or do we just not
-     * have it hooked up?
-     */
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
-     *
-     * @param scAddr                Service Centre address.  Null means use default.
-     * @param destAddr              Address of the recipient.
-     * @param message               String representation of the message payload.
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @param smsHeader             Array containing the data for the User Data Header, preceded
-     *                              by the Element Identifiers.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     * @hide
-     */
-    public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
-            boolean statusReportRequested, SmsHeader smsHeader) {
-
-        /**
-         * TODO(cleanup): Do we really want silent failure like this?
-         * Would it not be much more reasonable to make sure we don't
-         * call this function if we really want nothing done?
-         */
-        if (message == null || destAddr == null) {
-            return null;
-        }
-
-        UserData uData = new UserData();
-        uData.payloadStr = message;
-        uData.userDataHeader = smsHeader;
-        return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address and port.
-     *
-     * @param scAddr Service Centre address. null == use default
-     * @param destAddr the address of the destination for the message
-     * @param destPort the port to deliver the message to at the
-     *        destination
-     * @param data the data for the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     */
-    public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
-            byte[] data, boolean statusReportRequested) {
-
-        /**
-         * TODO(cleanup): this is not a general-purpose SMS creation
-         * method, but rather something specialized to messages
-         * containing OCTET encoded (meaning non-human-readable) user
-         * data.  The name should reflect that, and not just overload.
-         */
-
-        SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
-        portAddrs.destPort = destPort;
-        portAddrs.origPort = 0;
-        portAddrs.areEightBits = false;
-
-        SmsHeader smsHeader = new SmsHeader();
-        smsHeader.portAddrs = portAddrs;
-
-        UserData uData = new UserData();
-        uData.userDataHeader = smsHeader;
-        uData.msgEncoding = UserData.ENCODING_OCTET;
-        uData.msgEncodingSet = true;
-        uData.payload = data;
-
-        return privateGetSubmitPdu(destAddr, statusReportRequested, uData);
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
-     *
-     * @param destAddr the address of the destination for the message
-     * @param userData the data for the message
-     * @param statusReportRequested Indicates whether a report is requested for this message.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     */
-    public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
-            boolean statusReportRequested) {
-        return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
-    }
-
-    /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    @Override
-    public int getProtocolIdentifier() {
-        Rlog.w(LOG_TAG, "getProtocolIdentifier: is not supported in CDMA mode.");
-        // (3GPP TS 23.040): "no interworking, but SME to SME protocol":
-        return 0;
-    }
-
-    /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    @Override
-    public boolean isReplace() {
-        Rlog.w(LOG_TAG, "isReplace: is not supported in CDMA mode.");
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    @Override
-    public boolean isCphsMwiMessage() {
-        Rlog.w(LOG_TAG, "isCphsMwiMessage: is not supported in CDMA mode.");
-        return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isMWIClearMessage() {
-        return ((mBearerData != null) && (mBearerData.numberOfMessages == 0));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isMWISetMessage() {
-        return ((mBearerData != null) && (mBearerData.numberOfMessages > 0));
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean isMwiDontStore() {
-        return ((mBearerData != null) &&
-                (mBearerData.numberOfMessages > 0) &&
-                (mBearerData.userData == null));
-    }
-
-    /**
-     * Returns the status for a previously submitted message.
-     * For not interfering with status codes from GSM, this status code is
-     * shifted to the bits 31-16.
-     */
-    @Override
-    public int getStatus() {
-        return (status << 16);
-    }
-
-    /** Return true iff the bearer data message type is DELIVERY_ACK. */
-    @Override
-    public boolean isStatusReportMessage() {
-        return (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK);
-    }
-
-    /**
-     * Note: This function is a GSM specific functionality which is not supported in CDMA mode.
-     */
-    @Override
-    public boolean isReplyPathPresent() {
-        Rlog.w(LOG_TAG, "isReplyPathPresent: is not supported in CDMA mode.");
-        return false;
-    }
-
-    /**
-     * Calculate the number of septets needed to encode the message.
-     *
-     * @param messageBody the message to encode
-     * @param use7bitOnly ignore (but still count) illegal characters if true
-     * @param isEntireMsg indicates if this is entire msg or a segment in multipart msg
-     * @return TextEncodingDetails
-     */
-    public static TextEncodingDetails calculateLength(CharSequence messageBody,
-            boolean use7bitOnly, boolean isEntireMsg) {
-        CharSequence newMsgBody = null;
-        Resources r = Resources.getSystem();
-        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
-            newMsgBody  = Sms7BitEncodingTranslator.translate(messageBody);
-        }
-        if (TextUtils.isEmpty(newMsgBody)) {
-            newMsgBody = messageBody;
-        }
-        return BearerData.calcTextEncodingDetails(newMsgBody, use7bitOnly, isEntireMsg);
-    }
-
-    /**
-     * Returns the teleservice type of the message.
-     * @return the teleservice:
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET},
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WMT},
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WEMT},
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN},
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP}
-    */
-    public int getTeleService() {
-        return mEnvelope.teleService;
-    }
-
-    /**
-     * Returns the message type of the message.
-     * @return the message type:
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_POINT_TO_POINT},
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_BROADCAST},
-     *  {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_ACKNOWLEDGE},
-    */
-    public int getMessageType() {
-        // NOTE: mEnvelope.messageType is not set correctly for cell broadcasts with some RILs.
-        // Use the service category parameter to detect CMAS and other cell broadcast messages.
-        if (mEnvelope.serviceCategory != 0) {
-            return SmsEnvelope.MESSAGE_TYPE_BROADCAST;
-        } else {
-            return SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
-        }
-    }
-
-    /**
-     * Decodes pdu to an empty SMS object.
-     * In the CDMA case the pdu is just an internal byte stream representation
-     * of the SMS Java-object.
-     * @see #createPdu()
-     */
-    private void parsePdu(byte[] pdu) {
-        ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
-        DataInputStream dis = new DataInputStream(bais);
-        int length;
-        int bearerDataLength;
-        SmsEnvelope env = new SmsEnvelope();
-        CdmaSmsAddress addr = new CdmaSmsAddress();
-
-        try {
-            env.messageType = dis.readInt();
-            env.teleService = dis.readInt();
-            env.serviceCategory = dis.readInt();
-
-            addr.digitMode = dis.readByte();
-            addr.numberMode = dis.readByte();
-            addr.ton = dis.readByte();
-            addr.numberPlan = dis.readByte();
-
-            length = dis.readUnsignedByte();
-            addr.numberOfDigits = length;
-
-            // sanity check on the length
-            if (length > pdu.length) {
-                throw new RuntimeException(
-                        "createFromPdu: Invalid pdu, addr.numberOfDigits " + length
-                        + " > pdu len " + pdu.length);
-            }
-            addr.origBytes = new byte[length];
-            dis.read(addr.origBytes, 0, length); // digits
-
-            env.bearerReply = dis.readInt();
-            // CauseCode values:
-            env.replySeqNo = dis.readByte();
-            env.errorClass = dis.readByte();
-            env.causeCode = dis.readByte();
-
-            //encoded BearerData:
-            bearerDataLength = dis.readInt();
-            // sanity check on the length
-            if (bearerDataLength > pdu.length) {
-                throw new RuntimeException(
-                        "createFromPdu: Invalid pdu, bearerDataLength " + bearerDataLength
-                        + " > pdu len " + pdu.length);
-            }
-            env.bearerData = new byte[bearerDataLength];
-            dis.read(env.bearerData, 0, bearerDataLength);
-            dis.close();
-        } catch (IOException ex) {
-            throw new RuntimeException(
-                    "createFromPdu: conversion from byte array to object failed: " + ex, ex);
-        } catch (Exception ex) {
-            Rlog.e(LOG_TAG, "createFromPdu: conversion from byte array to object failed: " + ex);
-        }
-
-        // link the filled objects to this SMS
-        mOriginatingAddress = addr;
-        env.origAddress = addr;
-        mEnvelope = env;
-        mPdu = pdu;
-
-        parseSms();
-    }
-
-    /**
-     * Decodes 3GPP2 sms stored in CSIM/RUIM cards As per 3GPP2 C.S0015-0
-     */
-    private void parsePduFromEfRecord(byte[] pdu) {
-        ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
-        DataInputStream dis = new DataInputStream(bais);
-        SmsEnvelope env = new SmsEnvelope();
-        CdmaSmsAddress addr = new CdmaSmsAddress();
-        CdmaSmsSubaddress subAddr = new CdmaSmsSubaddress();
-
-        try {
-            env.messageType = dis.readByte();
-
-            while (dis.available() > 0) {
-                int parameterId = dis.readByte();
-                int parameterLen = dis.readUnsignedByte();
-                byte[] parameterData = new byte[parameterLen];
-
-                switch (parameterId) {
-                    case TELESERVICE_IDENTIFIER:
-                        /*
-                         * 16 bit parameter that identifies which upper layer
-                         * service access point is sending or should receive
-                         * this message
-                         */
-                        env.teleService = dis.readUnsignedShort();
-                        Rlog.i(LOG_TAG, "teleservice = " + env.teleService);
-                        break;
-                    case SERVICE_CATEGORY:
-                        /*
-                         * 16 bit parameter that identifies type of service as
-                         * in 3GPP2 C.S0015-0 Table 3.4.3.2-1
-                         */
-                        env.serviceCategory = dis.readUnsignedShort();
-                        break;
-                    case ORIGINATING_ADDRESS:
-                    case DESTINATION_ADDRESS:
-                        dis.read(parameterData, 0, parameterLen);
-                        BitwiseInputStream addrBis = new BitwiseInputStream(parameterData);
-                        addr.digitMode = addrBis.read(1);
-                        addr.numberMode = addrBis.read(1);
-                        int numberType = 0;
-                        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-                            numberType = addrBis.read(3);
-                            addr.ton = numberType;
-
-                            if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK)
-                                addr.numberPlan = addrBis.read(4);
-                        }
-
-                        addr.numberOfDigits = addrBis.read(8);
-
-                        byte[] data = new byte[addr.numberOfDigits];
-                        byte b = 0x00;
-
-                        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
-                            /* As per 3GPP2 C.S0005-0 Table 2.7.1.3.2.4-4 */
-                            for (int index = 0; index < addr.numberOfDigits; index++) {
-                                b = (byte) (0xF & addrBis.read(4));
-                                // convert the value if it is 4-bit DTMF to 8
-                                // bit
-                                data[index] = convertDtmfToAscii(b);
-                            }
-                        } else if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-                            if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK) {
-                                for (int index = 0; index < addr.numberOfDigits; index++) {
-                                    b = (byte) (0xFF & addrBis.read(8));
-                                    data[index] = b;
-                                }
-
-                            } else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
-                                if (numberType == 2)
-                                    Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
-                                else
-                                    Rlog.e(LOG_TAG,
-                                          "TODO: Originating Addr is data network address");
-                            } else {
-                                Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
-                            }
-                        } else {
-                            Rlog.e(LOG_TAG, "Incorrect Digit mode");
-                        }
-                        addr.origBytes = data;
-                        Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
-                        break;
-                    case ORIGINATING_SUB_ADDRESS:
-                    case DESTINATION_SUB_ADDRESS:
-                        dis.read(parameterData, 0, parameterLen);
-                        BitwiseInputStream subAddrBis = new BitwiseInputStream(parameterData);
-                        subAddr.type = subAddrBis.read(3);
-                        subAddr.odd = subAddrBis.readByteArray(1)[0];
-                        int subAddrLen = subAddrBis.read(8);
-                        byte[] subdata = new byte[subAddrLen];
-                        for (int index = 0; index < subAddrLen; index++) {
-                            b = (byte) (0xFF & subAddrBis.read(4));
-                            // convert the value if it is 4-bit DTMF to 8 bit
-                            subdata[index] = convertDtmfToAscii(b);
-                        }
-                        subAddr.origBytes = subdata;
-                        break;
-                    case BEARER_REPLY_OPTION:
-                        dis.read(parameterData, 0, parameterLen);
-                        BitwiseInputStream replyOptBis = new BitwiseInputStream(parameterData);
-                        env.bearerReply = replyOptBis.read(6);
-                        break;
-                    case CAUSE_CODES:
-                        dis.read(parameterData, 0, parameterLen);
-                        BitwiseInputStream ccBis = new BitwiseInputStream(parameterData);
-                        env.replySeqNo = ccBis.readByteArray(6)[0];
-                        env.errorClass = ccBis.readByteArray(2)[0];
-                        if (env.errorClass != 0x00)
-                            env.causeCode = ccBis.readByteArray(8)[0];
-                        break;
-                    case BEARER_DATA:
-                        dis.read(parameterData, 0, parameterLen);
-                        env.bearerData = parameterData;
-                        break;
-                    default:
-                        throw new Exception("unsupported parameterId (" + parameterId + ")");
-                }
-            }
-            bais.close();
-            dis.close();
-        } catch (Exception ex) {
-            Rlog.e(LOG_TAG, "parsePduFromEfRecord: conversion from pdu to SmsMessage failed" + ex);
-        }
-
-        // link the filled objects to this SMS
-        mOriginatingAddress = addr;
-        env.origAddress = addr;
-        env.origSubaddress = subAddr;
-        mEnvelope = env;
-        mPdu = pdu;
-
-        parseSms();
-    }
-
-    /**
-     * Parses a SMS message from its BearerData stream. (mobile-terminated only)
-     */
-    public void parseSms() {
-        // Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
-        // It contains only an 8-bit number with the number of messages waiting
-        if (mEnvelope.teleService == SmsEnvelope.TELESERVICE_MWI) {
-            mBearerData = new BearerData();
-            if (mEnvelope.bearerData != null) {
-                mBearerData.numberOfMessages = 0x000000FF & mEnvelope.bearerData[0];
-            }
-            if (VDBG) {
-                Rlog.d(LOG_TAG, "parseSms: get MWI " +
-                      Integer.toString(mBearerData.numberOfMessages));
-            }
-            return;
-        }
-        mBearerData = BearerData.decode(mEnvelope.bearerData);
-        if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
-            Rlog.d(LOG_TAG, "MT raw BearerData = '" +
-                      HexDump.toHexString(mEnvelope.bearerData) + "'");
-            Rlog.d(LOG_TAG, "MT (decoded) BearerData = " + mBearerData);
-        }
-        mMessageRef = mBearerData.messageId;
-        if (mBearerData.userData != null) {
-            mUserData = mBearerData.userData.payload;
-            mUserDataHeader = mBearerData.userData.userDataHeader;
-            mMessageBody = mBearerData.userData.payloadStr;
-        }
-
-        if (mOriginatingAddress != null) {
-            mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
-            if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
-                if (mOriginatingAddress.address.charAt(0) != '+') {
-                    mOriginatingAddress.address = "+" + mOriginatingAddress.address;
-                }
-            }
-            if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
-                    + mOriginatingAddress.address);
-        }
-
-        if (mBearerData.msgCenterTimeStamp != null) {
-            mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
-        }
-
-        if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
-
-        // Message Type (See 3GPP2 C.S0015-B, v2, 4.5.1)
-        if (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK) {
-            // The BearerData MsgStatus subparameter should only be
-            // included for DELIVERY_ACK messages.  If it occurred for
-            // other messages, it would be unclear what the status
-            // being reported refers to.  The MsgStatus subparameter
-            // is primarily useful to indicate error conditions -- a
-            // message without this subparameter is assumed to
-            // indicate successful delivery (status == 0).
-            if (! mBearerData.messageStatusSet) {
-                Rlog.d(LOG_TAG, "DELIVERY_ACK message without msgStatus (" +
-                        (mUserData == null ? "also missing" : "does have") +
-                        " userData).");
-                status = 0;
-            } else {
-                status = mBearerData.errorClass << 8;
-                status |= mBearerData.messageStatus;
-            }
-        } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
-            throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
-        }
-
-        if (mMessageBody != null) {
-            if (VDBG) Rlog.v(LOG_TAG, "SMS message body: '" + mMessageBody + "'");
-            parseMessageBody();
-        } else if ((mUserData != null) && VDBG) {
-            Rlog.v(LOG_TAG, "SMS payload: '" + IccUtils.bytesToHexString(mUserData) + "'");
-        }
-    }
-
-    /**
-     * Parses a broadcast SMS, possibly containing a CMAS alert.
-     */
-    public SmsCbMessage parseBroadcastSms() {
-        BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
-        if (bData == null) {
-            Rlog.w(LOG_TAG, "BearerData.decode() returned null");
-            return null;
-        }
-
-        if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
-            Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData));
-        }
-
-        String plmn = TelephonyManager.getDefault().getNetworkOperator();
-        SmsCbLocation location = new SmsCbLocation(plmn);
-
-        return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
-                SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE, bData.messageId, location,
-                mEnvelope.serviceCategory, bData.getLanguage(), bData.userData.payloadStr,
-                bData.priority, null, bData.cmasWarningInfo);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public SmsConstants.MessageClass getMessageClass() {
-        if (BearerData.DISPLAY_MODE_IMMEDIATE == mBearerData.displayMode ) {
-            return SmsConstants.MessageClass.CLASS_0;
-        } else {
-            return SmsConstants.MessageClass.UNKNOWN;
-        }
-    }
-
-    /**
-     * Calculate the next message id, starting at 1 and iteratively
-     * incrementing within the range 1..65535 remembering the state
-     * via a persistent system property.  (See C.S0015-B, v2.0,
-     * 4.3.1.5) Since this routine is expected to be accessed via via
-     * binder-call, and hence should be thread-safe, it has been
-     * synchronized.
-     */
-    public synchronized static int getNextMessageId() {
-        // Testing and dialog with partners has indicated that
-        // msgId==0 is (sometimes?) treated specially by lower levels.
-        // Specifically, the ID is not preserved for delivery ACKs.
-        // Hence, avoid 0 -- constraining the range to 1..65535.
-        int msgId = SystemProperties.getInt(TelephonyProperties.PROPERTY_CDMA_MSG_ID, 1);
-        String nextMsgId = Integer.toString((msgId % 0xFFFF) + 1);
-        try{
-            SystemProperties.set(TelephonyProperties.PROPERTY_CDMA_MSG_ID, nextMsgId);
-            if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
-                Rlog.d(LOG_TAG, "next " + TelephonyProperties.PROPERTY_CDMA_MSG_ID + " = " + nextMsgId);
-                Rlog.d(LOG_TAG, "readback gets " +
-                        SystemProperties.get(TelephonyProperties.PROPERTY_CDMA_MSG_ID));
-            }
-        } catch(RuntimeException ex) {
-            Rlog.e(LOG_TAG, "set nextMessage ID failed: " + ex);
-        }
-        return msgId;
-    }
-
-    /**
-     * Creates BearerData and Envelope from parameters for a Submit SMS.
-     * @return byte stream for SubmitPdu.
-     */
-    private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
-            UserData userData) {
-
-        /**
-         * TODO(cleanup): give this function a more meaningful name.
-         */
-
-        /**
-         * TODO(cleanup): Make returning null from the getSubmitPdu
-         * variations meaningful -- clean up the error feedback
-         * mechanism, and avoid null pointer exceptions.
-         */
-
-        /**
-         * North America Plus Code :
-         * Convert + code to 011 and dial out for international SMS
-         */
-        CdmaSmsAddress destAddr = CdmaSmsAddress.parse(
-                PhoneNumberUtils.cdmaCheckAndProcessPlusCodeForSms(destAddrStr));
-        if (destAddr == null) return null;
-
-        BearerData bearerData = new BearerData();
-        bearerData.messageType = BearerData.MESSAGE_TYPE_SUBMIT;
-
-        bearerData.messageId = getNextMessageId();
-
-        bearerData.deliveryAckReq = statusReportRequested;
-        bearerData.userAckReq = false;
-        bearerData.readAckReq = false;
-        bearerData.reportReq = false;
-
-        bearerData.userData = userData;
-
-        byte[] encodedBearerData = BearerData.encode(bearerData);
-        if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
-            Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
-            Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
-        }
-        if (encodedBearerData == null) return null;
-
-        int teleservice = bearerData.hasUserDataHeader ?
-                SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
-
-        SmsEnvelope envelope = new SmsEnvelope();
-        envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
-        envelope.teleService = teleservice;
-        envelope.destAddress = destAddr;
-        envelope.bearerReply = RETURN_ACK;
-        envelope.bearerData = encodedBearerData;
-
-        /**
-         * TODO(cleanup): envelope looks to be a pointless class, get
-         * rid of it.  Also -- most of the envelope fields set here
-         * are ignored, why?
-         */
-
-        try {
-            /**
-             * TODO(cleanup): reference a spec and get rid of the ugly comments
-             */
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
-            DataOutputStream dos = new DataOutputStream(baos);
-            dos.writeInt(envelope.teleService);
-            dos.writeInt(0); //servicePresent
-            dos.writeInt(0); //serviceCategory
-            dos.write(destAddr.digitMode);
-            dos.write(destAddr.numberMode);
-            dos.write(destAddr.ton); // number_type
-            dos.write(destAddr.numberPlan);
-            dos.write(destAddr.numberOfDigits);
-            dos.write(destAddr.origBytes, 0, destAddr.origBytes.length); // digits
-            // Subaddress is not supported.
-            dos.write(0); //subaddressType
-            dos.write(0); //subaddr_odd
-            dos.write(0); //subaddr_nbr_of_digits
-            dos.write(encodedBearerData.length);
-            dos.write(encodedBearerData, 0, encodedBearerData.length);
-            dos.close();
-
-            SubmitPdu pdu = new SubmitPdu();
-            pdu.encodedMessage = baos.toByteArray();
-            pdu.encodedScAddress = null;
-            return pdu;
-        } catch(IOException ex) {
-            Rlog.e(LOG_TAG, "creating SubmitPdu failed: " + ex);
-        }
-        return null;
-    }
-
-    /**
-     * Creates byte array (pseudo pdu) from SMS object.
-     * Note: Do not call this method more than once per object!
-     */
-    private void createPdu() {
-        SmsEnvelope env = mEnvelope;
-        CdmaSmsAddress addr = env.origAddress;
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
-        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(baos));
-
-        try {
-            dos.writeInt(env.messageType);
-            dos.writeInt(env.teleService);
-            dos.writeInt(env.serviceCategory);
-
-            dos.writeByte(addr.digitMode);
-            dos.writeByte(addr.numberMode);
-            dos.writeByte(addr.ton);
-            dos.writeByte(addr.numberPlan);
-            dos.writeByte(addr.numberOfDigits);
-            dos.write(addr.origBytes, 0, addr.origBytes.length); // digits
-
-            dos.writeInt(env.bearerReply);
-            // CauseCode values:
-            dos.writeByte(env.replySeqNo);
-            dos.writeByte(env.errorClass);
-            dos.writeByte(env.causeCode);
-            //encoded BearerData:
-            dos.writeInt(env.bearerData.length);
-            dos.write(env.bearerData, 0, env.bearerData.length);
-            dos.close();
-
-            /**
-             * TODO(cleanup) -- The mPdu field is managed in
-             * a fragile manner, and it would be much nicer if
-             * accessing the serialized representation used a less
-             * fragile mechanism.  Maybe the getPdu method could
-             * generate a representation if there was not yet one?
-             */
-
-            mPdu = baos.toByteArray();
-        } catch (IOException ex) {
-            Rlog.e(LOG_TAG, "createPdu: conversion from object to byte array failed: " + ex);
-        }
-    }
-
-    /**
-     * Converts a 4-Bit DTMF encoded symbol from the calling address number to ASCII character
-     */
-    private byte convertDtmfToAscii(byte dtmfDigit) {
-        byte asciiDigit;
-
-        switch (dtmfDigit) {
-        case  0: asciiDigit = 68; break; // 'D'
-        case  1: asciiDigit = 49; break; // '1'
-        case  2: asciiDigit = 50; break; // '2'
-        case  3: asciiDigit = 51; break; // '3'
-        case  4: asciiDigit = 52; break; // '4'
-        case  5: asciiDigit = 53; break; // '5'
-        case  6: asciiDigit = 54; break; // '6'
-        case  7: asciiDigit = 55; break; // '7'
-        case  8: asciiDigit = 56; break; // '8'
-        case  9: asciiDigit = 57; break; // '9'
-        case 10: asciiDigit = 48; break; // '0'
-        case 11: asciiDigit = 42; break; // '*'
-        case 12: asciiDigit = 35; break; // '#'
-        case 13: asciiDigit = 65; break; // 'A'
-        case 14: asciiDigit = 66; break; // 'B'
-        case 15: asciiDigit = 67; break; // 'C'
-        default:
-            asciiDigit = 32; // Invalid DTMF code
-            break;
-        }
-
-        return asciiDigit;
-    }
-
-    /** This function  shall be called to get the number of voicemails.
-     * @hide
-     */
-    public int getNumOfVoicemails() {
-        return mBearerData.numberOfMessages;
-    }
-
-    /**
-     * Returns a byte array that can be use to uniquely identify a received SMS message.
-     * C.S0015-B  4.3.1.6 Unique Message Identification.
-     *
-     * @return byte array uniquely identifying the message.
-     * @hide
-     */
-    public byte[] getIncomingSmsFingerprint() {
-        ByteArrayOutputStream output = new ByteArrayOutputStream();
-
-        output.write(mEnvelope.serviceCategory);
-        output.write(mEnvelope.teleService);
-        output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length);
-        output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length);
-        output.write(mEnvelope.origSubaddress.origBytes, 0,
-                mEnvelope.origSubaddress.origBytes.length);
-
-        return output.toByteArray();
-    }
-
-    /**
-     * Returns the list of service category program data, if present.
-     * @return a list of CdmaSmsCbProgramData objects, or null if not present
-     * @hide
-     */
-    public ArrayList<CdmaSmsCbProgramData> getSmsCbProgramData() {
-        return mBearerData.serviceCategoryProgramData;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/cdma/SmsMessageConverter.java b/src/java/com/android/internal/telephony/cdma/SmsMessageConverter.java
new file mode 100644
index 0000000..4ac2dea
--- /dev/null
+++ b/src/java/com/android/internal/telephony/cdma/SmsMessageConverter.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2008 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.cdma;
+
+import android.hardware.radio.V1_0.CdmaSmsMessage;
+import android.os.Parcel;
+import android.os.SystemProperties;
+import android.telephony.PhoneNumberUtils;
+import android.telephony.SmsCbLocation;
+import android.telephony.SmsCbMessage;
+import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaSmsCbProgramData;
+import android.telephony.Rlog;
+import android.util.Log;
+import android.text.TextUtils;
+import android.content.res.Resources;
+
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.cdma.sms.BearerData;
+import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
+import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
+import com.android.internal.telephony.cdma.sms.SmsEnvelope;
+import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.util.BitwiseInputStream;
+import com.android.internal.util.HexDump;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * A Factory class to convert from RIL to Framework SMS
+ *
+ */
+public class SmsMessageConverter {
+    static final String LOG_TAG = "SmsMessageConverter";
+    static private final String LOGGABLE_TAG = "CDMA:SMS";
+    private static final boolean VDBG = false;
+
+    /**
+     *  Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp.
+     *  Note: Only primitive fields are set.
+     */
+    public static SmsMessage newCdmaSmsMessageFromRil(
+            CdmaSmsMessage cdmaSmsMessage) {
+        // Note: Parcel.readByte actually reads one Int and masks to byte
+        SmsEnvelope env = new SmsEnvelope();
+        CdmaSmsAddress addr = new CdmaSmsAddress();
+        CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress();
+        byte[] data;
+        byte count;
+        int countInt;
+        int addressDigitMode;
+
+        //currently not supported by the modem-lib: env.mMessageType
+        env.teleService = cdmaSmsMessage.teleserviceId;
+
+        if (cdmaSmsMessage.isServicePresent) {
+            env.messageType = SmsEnvelope.MESSAGE_TYPE_BROADCAST;
+        }
+        else {
+            if (SmsEnvelope.TELESERVICE_NOT_SET == env.teleService) {
+                // assume type ACK
+                env.messageType = SmsEnvelope.MESSAGE_TYPE_ACKNOWLEDGE;
+            } else {
+                env.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
+            }
+        }
+        env.serviceCategory = cdmaSmsMessage.serviceCategory;
+
+        // address
+        addressDigitMode = cdmaSmsMessage.address.digitMode;
+        addr.digitMode = (byte) (0xFF & addressDigitMode);
+        addr.numberMode = (byte) (0xFF & cdmaSmsMessage.address.numberMode);
+        addr.ton = cdmaSmsMessage.address.numberType;
+        addr.numberPlan = (byte) (0xFF & cdmaSmsMessage.address.numberPlan);
+        count = (byte) cdmaSmsMessage.address.digits.size();
+        addr.numberOfDigits = count;
+        data = new byte[count];
+        for (int index=0; index < count; index++) {
+            data[index] = cdmaSmsMessage.address.digits.get(index);
+
+            // convert the value if it is 4-bit DTMF to 8 bit
+            if (addressDigitMode == CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF) {
+                data[index] = SmsMessage.convertDtmfToAscii(data[index]);
+            }
+        }
+
+        addr.origBytes = data;
+
+        subaddr.type = cdmaSmsMessage.subAddress.subaddressType;
+        subaddr.odd = (byte) (cdmaSmsMessage.subAddress.odd ? 1 : 0);
+        count = (byte) cdmaSmsMessage.subAddress.digits.size();
+
+        if (count < 0) {
+            count = 0;
+        }
+
+        // p_cur->sSubAddress.digits[digitCount] :
+
+        data = new byte[count];
+
+        for (int index = 0; index < count; ++index) {
+            data[index] = cdmaSmsMessage.subAddress.digits.get(index);
+        }
+
+        subaddr.origBytes = data;
+
+        /* currently not supported by the modem-lib:
+            env.bearerReply
+            env.replySeqNo
+            env.errorClass
+            env.causeCode
+        */
+
+        // bearer data
+        countInt = cdmaSmsMessage.bearerData.size();
+        if (countInt < 0) {
+            countInt = 0;
+        }
+
+        data = new byte[countInt];
+        for (int index=0; index < countInt; index++) {
+            data[index] = cdmaSmsMessage.bearerData.get(index);
+        }
+        // BD gets further decoded when accessed in SMSDispatcher
+        env.bearerData = data;
+
+        // link the the filled objects to the SMS
+        env.origAddress = addr;
+        env.origSubaddress = subaddr;
+
+        SmsMessage msg = new SmsMessage(addr, env);
+
+        return msg;
+    }
+
+    public static android.telephony.SmsMessage newSmsMessageFromCdmaSmsMessage(
+            CdmaSmsMessage msg) {
+        return new android.telephony.SmsMessage((SmsMessageBase)newCdmaSmsMessageFromRil(msg));
+    }
+}
diff --git a/src/java/com/android/internal/telephony/cdma/sms/BearerData.java b/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
deleted file mode 100644
index 1de72db..0000000
--- a/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ /dev/null
@@ -1,2000 +0,0 @@
-/*
- * Copyright (C) 2008 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.cdma.sms;
-
-import android.content.res.Resources;
-import android.telephony.SmsCbCmasInfo;
-import android.telephony.cdma.CdmaSmsCbProgramData;
-import android.telephony.cdma.CdmaSmsCbProgramResults;
-import android.text.format.Time;
-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.SmsMessageBase;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.util.BitwiseInputStream;
-import com.android.internal.util.BitwiseOutputStream;
-
-import java.util.ArrayList;
-import java.util.TimeZone;
-
-/**
- * An object to encode and decode CDMA SMS bearer data.
- */
-public final class BearerData {
-    private final static String LOG_TAG = "BearerData";
-
-    /**
-     * Bearer Data Subparameter Identifiers
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5-1)
-     * NOTE: Commented subparameter types are not implemented.
-     */
-    private final static byte SUBPARAM_MESSAGE_IDENTIFIER               = 0x00;
-    private final static byte SUBPARAM_USER_DATA                        = 0x01;
-    private final static byte SUBPARAM_USER_RESPONSE_CODE               = 0x02;
-    private final static byte SUBPARAM_MESSAGE_CENTER_TIME_STAMP        = 0x03;
-    private final static byte SUBPARAM_VALIDITY_PERIOD_ABSOLUTE         = 0x04;
-    private final static byte SUBPARAM_VALIDITY_PERIOD_RELATIVE         = 0x05;
-    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE  = 0x06;
-    private final static byte SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE  = 0x07;
-    private final static byte SUBPARAM_PRIORITY_INDICATOR               = 0x08;
-    private final static byte SUBPARAM_PRIVACY_INDICATOR                = 0x09;
-    private final static byte SUBPARAM_REPLY_OPTION                     = 0x0A;
-    private final static byte SUBPARAM_NUMBER_OF_MESSAGES               = 0x0B;
-    private final static byte SUBPARAM_ALERT_ON_MESSAGE_DELIVERY        = 0x0C;
-    private final static byte SUBPARAM_LANGUAGE_INDICATOR               = 0x0D;
-    private final static byte SUBPARAM_CALLBACK_NUMBER                  = 0x0E;
-    private final static byte SUBPARAM_MESSAGE_DISPLAY_MODE             = 0x0F;
-    //private final static byte SUBPARAM_MULTIPLE_ENCODING_USER_DATA      = 0x10;
-    private final static byte SUBPARAM_MESSAGE_DEPOSIT_INDEX            = 0x11;
-    private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA    = 0x12;
-    private final static byte SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS = 0x13;
-    private final static byte SUBPARAM_MESSAGE_STATUS                   = 0x14;
-    //private final static byte SUBPARAM_TP_FAILURE_CAUSE                 = 0x15;
-    //private final static byte SUBPARAM_ENHANCED_VMN                     = 0x16;
-    //private final static byte SUBPARAM_ENHANCED_VMN_ACK                 = 0x17;
-
-    // All other values after this are reserved.
-    private final static byte SUBPARAM_ID_LAST_DEFINED                    = 0x17;
-
-    /**
-     * Supported message types for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.1-1)
-     */
-    public static final int MESSAGE_TYPE_DELIVER        = 0x01;
-    public static final int MESSAGE_TYPE_SUBMIT         = 0x02;
-    public static final int MESSAGE_TYPE_CANCELLATION   = 0x03;
-    public static final int MESSAGE_TYPE_DELIVERY_ACK   = 0x04;
-    public static final int MESSAGE_TYPE_USER_ACK       = 0x05;
-    public static final int MESSAGE_TYPE_READ_ACK       = 0x06;
-    public static final int MESSAGE_TYPE_DELIVER_REPORT = 0x07;
-    public static final int MESSAGE_TYPE_SUBMIT_REPORT  = 0x08;
-
-    public int messageType;
-
-    /**
-     * 16-bit value indicating the message ID, which increments modulo 65536.
-     * (Special rules apply for WAP-messages.)
-     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
-     */
-    public int messageId;
-
-    /**
-     * Supported priority modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1)
-     */
-    public static final int PRIORITY_NORMAL        = 0x0;
-    public static final int PRIORITY_INTERACTIVE   = 0x1;
-    public static final int PRIORITY_URGENT        = 0x2;
-    public static final int PRIORITY_EMERGENCY     = 0x3;
-
-    public boolean priorityIndicatorSet = false;
-    public int priority = PRIORITY_NORMAL;
-
-    /**
-     * Supported privacy modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.10-1)
-     */
-    public static final int PRIVACY_NOT_RESTRICTED = 0x0;
-    public static final int PRIVACY_RESTRICTED     = 0x1;
-    public static final int PRIVACY_CONFIDENTIAL   = 0x2;
-    public static final int PRIVACY_SECRET         = 0x3;
-
-    public boolean privacyIndicatorSet = false;
-    public int privacy = PRIVACY_NOT_RESTRICTED;
-
-    /**
-     * Supported alert priority modes for CDMA SMS messages
-     * (See 3GPP2 C.S0015-B, v2.0, table 4.5.13-1)
-     */
-    public static final int ALERT_DEFAULT          = 0x0;
-    public static final int ALERT_LOW_PRIO         = 0x1;
-    public static final int ALERT_MEDIUM_PRIO      = 0x2;
-    public static final int ALERT_HIGH_PRIO        = 0x3;
-
-    public boolean alertIndicatorSet = false;
-    public int alert = ALERT_DEFAULT;
-
-    /**
-     * Supported display modes for CDMA SMS messages.  Display mode is
-     * a 2-bit value used to indicate to the mobile station when to
-     * display the received message.  (See 3GPP2 C.S0015-B, v2,
-     * 4.5.16)
-     */
-    public static final int DISPLAY_MODE_IMMEDIATE      = 0x0;
-    public static final int DISPLAY_MODE_DEFAULT        = 0x1;
-    public static final int DISPLAY_MODE_USER           = 0x2;
-
-    public boolean displayModeSet = false;
-    public int displayMode = DISPLAY_MODE_DEFAULT;
-
-    /**
-     * Language Indicator values.  NOTE: the spec (3GPP2 C.S0015-B,
-     * v2, 4.5.14) is ambiguous as to the meaning of this field, as it
-     * refers to C.R1001-D but that reference has been crossed out.
-     * It would seem reasonable to assume the values from C.R1001-F
-     * (table 9.2-1) are to be used instead.
-     */
-    public static final int LANGUAGE_UNKNOWN  = 0x00;
-    public static final int LANGUAGE_ENGLISH  = 0x01;
-    public static final int LANGUAGE_FRENCH   = 0x02;
-    public static final int LANGUAGE_SPANISH  = 0x03;
-    public static final int LANGUAGE_JAPANESE = 0x04;
-    public static final int LANGUAGE_KOREAN   = 0x05;
-    public static final int LANGUAGE_CHINESE  = 0x06;
-    public static final int LANGUAGE_HEBREW   = 0x07;
-
-    public boolean languageIndicatorSet = false;
-    public int language = LANGUAGE_UNKNOWN;
-
-    /**
-     * SMS Message Status Codes.  The first component of the Message
-     * status indicates if an error has occurred and whether the error
-     * is considered permanent or temporary.  The second component of
-     * the Message status indicates the cause of the error (if any).
-     * (See 3GPP2 C.S0015-B, v2.0, 4.5.21)
-     */
-    /* no-error codes */
-    public static final int ERROR_NONE                   = 0x00;
-    public static final int STATUS_ACCEPTED              = 0x00;
-    public static final int STATUS_DEPOSITED_TO_INTERNET = 0x01;
-    public static final int STATUS_DELIVERED             = 0x02;
-    public static final int STATUS_CANCELLED             = 0x03;
-    /* temporary-error and permanent-error codes */
-    public static final int ERROR_TEMPORARY              = 0x02;
-    public static final int STATUS_NETWORK_CONGESTION    = 0x04;
-    public static final int STATUS_NETWORK_ERROR         = 0x05;
-    public static final int STATUS_UNKNOWN_ERROR         = 0x1F;
-    /* permanent-error codes */
-    public static final int ERROR_PERMANENT              = 0x03;
-    public static final int STATUS_CANCEL_FAILED         = 0x06;
-    public static final int STATUS_BLOCKED_DESTINATION   = 0x07;
-    public static final int STATUS_TEXT_TOO_LONG         = 0x08;
-    public static final int STATUS_DUPLICATE_MESSAGE     = 0x09;
-    public static final int STATUS_INVALID_DESTINATION   = 0x0A;
-    public static final int STATUS_MESSAGE_EXPIRED       = 0x0D;
-    /* undefined-status codes */
-    public static final int ERROR_UNDEFINED              = 0xFF;
-    public static final int STATUS_UNDEFINED             = 0xFF;
-
-    public boolean messageStatusSet = false;
-    public int errorClass = ERROR_UNDEFINED;
-    public int messageStatus = STATUS_UNDEFINED;
-
-    /**
-     * 1-bit value that indicates whether a User Data Header (UDH) is present.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.1)
-     *
-     * NOTE: during encoding, this value will be set based on the
-     * presence of a UDH in the structured data, any existing setting
-     * will be overwritten.
-     */
-    public boolean hasUserDataHeader;
-
-    /**
-     * provides the information for the user data
-     * (e.g. padding bits, user data, user data header, etc)
-     * (See 3GPP2 C.S.0015-B, v2, 4.5.2)
-     */
-    public UserData userData;
-
-    /**
-     * The User Response Code subparameter is used in the SMS User
-     * Acknowledgment Message to respond to previously received short
-     * messages. This message center-specific element carries the
-     * identifier of a predefined response. (See 3GPP2 C.S.0015-B, v2,
-     * 4.5.3)
-     */
-    public boolean userResponseCodeSet = false;
-    public int userResponseCode;
-
-    /**
-     * 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4
-     */
-    public static class TimeStamp extends Time {
-
-        public TimeStamp() {
-            super(TimeZone.getDefault().getID());   // 3GPP2 timestamps use the local timezone
-        }
-
-        public static TimeStamp fromByteArray(byte[] data) {
-            TimeStamp ts = new TimeStamp();
-            // C.S0015-B v2.0, 4.5.4: range is 1996-2095
-            int year = IccUtils.cdmaBcdByteToInt(data[0]);
-            if (year > 99 || year < 0) return null;
-            ts.year = year >= 96 ? year + 1900 : year + 2000;
-            int month = IccUtils.cdmaBcdByteToInt(data[1]);
-            if (month < 1 || month > 12) return null;
-            ts.month = month - 1;
-            int day = IccUtils.cdmaBcdByteToInt(data[2]);
-            if (day < 1 || day > 31) return null;
-            ts.monthDay = day;
-            int hour = IccUtils.cdmaBcdByteToInt(data[3]);
-            if (hour < 0 || hour > 23) return null;
-            ts.hour = hour;
-            int minute = IccUtils.cdmaBcdByteToInt(data[4]);
-            if (minute < 0 || minute > 59) return null;
-            ts.minute = minute;
-            int second = IccUtils.cdmaBcdByteToInt(data[5]);
-            if (second < 0 || second > 59) return null;
-            ts.second = second;
-            return ts;
-        }
-
-        @Override
-        public String toString() {
-            StringBuilder builder = new StringBuilder();
-            builder.append("TimeStamp ");
-            builder.append("{ year=" + year);
-            builder.append(", month=" + month);
-            builder.append(", day=" + monthDay);
-            builder.append(", hour=" + hour);
-            builder.append(", minute=" + minute);
-            builder.append(", second=" + second);
-            builder.append(" }");
-            return builder.toString();
-        }
-    }
-
-    public TimeStamp msgCenterTimeStamp;
-    public TimeStamp validityPeriodAbsolute;
-    public TimeStamp deferredDeliveryTimeAbsolute;
-
-    /**
-     * Relative time is specified as one byte, the value of which
-     * falls into a series of ranges, as specified below.  The idea is
-     * that shorter time intervals allow greater precision -- the
-     * value means minutes from zero until the MINS_LIMIT (inclusive),
-     * upon which it means hours until the HOURS_LIMIT, and so
-     * forth. (See 3GPP2 C.S0015-B, v2, 4.5.6-1)
-     */
-    public static final int RELATIVE_TIME_MINS_LIMIT      = 143;
-    public static final int RELATIVE_TIME_HOURS_LIMIT     = 167;
-    public static final int RELATIVE_TIME_DAYS_LIMIT      = 196;
-    public static final int RELATIVE_TIME_WEEKS_LIMIT     = 244;
-    public static final int RELATIVE_TIME_INDEFINITE      = 245;
-    public static final int RELATIVE_TIME_NOW             = 246;
-    public static final int RELATIVE_TIME_MOBILE_INACTIVE = 247;
-    public static final int RELATIVE_TIME_RESERVED        = 248;
-
-    public boolean validityPeriodRelativeSet;
-    public int validityPeriodRelative;
-    public boolean deferredDeliveryTimeRelativeSet;
-    public int deferredDeliveryTimeRelative;
-
-    /**
-     * The Reply Option subparameter contains 1-bit values which
-     * indicate whether SMS acknowledgment is requested or not.  (See
-     * 3GPP2 C.S0015-B, v2, 4.5.11)
-     */
-    public boolean userAckReq;
-    public boolean deliveryAckReq;
-    public boolean readAckReq;
-    public boolean reportReq;
-
-    /**
-     * The Number of Messages subparameter (8-bit value) is a decimal
-     * number in the 0 to 99 range representing the number of messages
-     * stored at the Voice Mail System. This element is used by the
-     * Voice Mail Notification service.  (See 3GPP2 C.S0015-B, v2,
-     * 4.5.12)
-     */
-    public int numberOfMessages;
-
-    /**
-     * The Message Deposit Index subparameter is assigned by the
-     * message center as a unique index to the contents of the User
-     * Data subparameter in each message sent to a particular mobile
-     * station. The mobile station, when replying to a previously
-     * received short message which included a Message Deposit Index
-     * subparameter, may include the Message Deposit Index of the
-     * received message to indicate to the message center that the
-     * original contents of the message are to be included in the
-     * reply.  (See 3GPP2 C.S0015-B, v2, 4.5.18)
-     */
-    public int depositIndex;
-
-    /**
-     * 4-bit or 8-bit value that indicates the number to be dialed in reply to a
-     * received SMS message.
-     * (See 3GPP2 C.S0015-B, v2, 4.5.15)
-     */
-    public CdmaSmsAddress callbackNumber;
-
-    /**
-     * CMAS warning notification information.
-     * @see #decodeCmasUserData(BearerData, int)
-     */
-    public SmsCbCmasInfo cmasWarningInfo;
-
-    /**
-     * The Service Category Program Data subparameter is used to enable and disable
-     * SMS broadcast service categories to display. If this subparameter is present,
-     * this field will contain a list of one or more
-     * {@link android.telephony.cdma.CdmaSmsCbProgramData} objects containing the
-     * operation(s) to perform.
-     */
-    public ArrayList<CdmaSmsCbProgramData> serviceCategoryProgramData;
-
-    /**
-     * The Service Category Program Results subparameter informs the message center
-     * of the results of a Service Category Program Data request.
-     */
-    public ArrayList<CdmaSmsCbProgramResults> serviceCategoryProgramResults;
-
-
-    private static class CodingException extends Exception {
-        public CodingException(String s) {
-            super(s);
-        }
-    }
-
-    /**
-     * Returns the language indicator as a two-character ISO 639 string.
-     * @return a two character ISO 639 language code
-     */
-    public String getLanguage() {
-        return getLanguageCodeForValue(language);
-    }
-
-    /**
-     * Converts a CDMA language indicator value to an ISO 639 two character language code.
-     * @param languageValue the CDMA language value to convert
-     * @return the two character ISO 639 language code for the specified value, or null if unknown
-     */
-    private static String getLanguageCodeForValue(int languageValue) {
-        switch (languageValue) {
-            case LANGUAGE_ENGLISH:
-                return "en";
-
-            case LANGUAGE_FRENCH:
-                return "fr";
-
-            case LANGUAGE_SPANISH:
-                return "es";
-
-            case LANGUAGE_JAPANESE:
-                return "ja";
-
-            case LANGUAGE_KOREAN:
-                return "ko";
-
-            case LANGUAGE_CHINESE:
-                return "zh";
-
-            case LANGUAGE_HEBREW:
-                return "he";
-
-            default:
-                return null;
-        }
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("BearerData ");
-        builder.append("{ messageType=" + messageType);
-        builder.append(", messageId=" + messageId);
-        builder.append(", priority=" + (priorityIndicatorSet ? priority : "unset"));
-        builder.append(", privacy=" + (privacyIndicatorSet ? privacy : "unset"));
-        builder.append(", alert=" + (alertIndicatorSet ? alert : "unset"));
-        builder.append(", displayMode=" + (displayModeSet ? displayMode : "unset"));
-        builder.append(", language=" + (languageIndicatorSet ? language : "unset"));
-        builder.append(", errorClass=" + (messageStatusSet ? errorClass : "unset"));
-        builder.append(", msgStatus=" + (messageStatusSet ? messageStatus : "unset"));
-        builder.append(", msgCenterTimeStamp=" +
-                ((msgCenterTimeStamp != null) ? msgCenterTimeStamp : "unset"));
-        builder.append(", validityPeriodAbsolute=" +
-                ((validityPeriodAbsolute != null) ? validityPeriodAbsolute : "unset"));
-        builder.append(", validityPeriodRelative=" +
-                ((validityPeriodRelativeSet) ? validityPeriodRelative : "unset"));
-        builder.append(", deferredDeliveryTimeAbsolute=" +
-                ((deferredDeliveryTimeAbsolute != null) ? deferredDeliveryTimeAbsolute : "unset"));
-        builder.append(", deferredDeliveryTimeRelative=" +
-                ((deferredDeliveryTimeRelativeSet) ? deferredDeliveryTimeRelative : "unset"));
-        builder.append(", userAckReq=" + userAckReq);
-        builder.append(", deliveryAckReq=" + deliveryAckReq);
-        builder.append(", readAckReq=" + readAckReq);
-        builder.append(", reportReq=" + reportReq);
-        builder.append(", numberOfMessages=" + numberOfMessages);
-        builder.append(", callbackNumber=" + Rlog.pii(LOG_TAG, callbackNumber));
-        builder.append(", depositIndex=" + depositIndex);
-        builder.append(", hasUserDataHeader=" + hasUserDataHeader);
-        builder.append(", userData=" + userData);
-        builder.append(" }");
-        return builder.toString();
-    }
-
-    private static void encodeMessageId(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 3);
-        outStream.write(4, bData.messageType);
-        outStream.write(8, bData.messageId >> 8);
-        outStream.write(8, bData.messageId);
-        outStream.write(1, bData.hasUserDataHeader ? 1 : 0);
-        outStream.skip(3);
-    }
-
-    private static int countAsciiSeptets(CharSequence msg, boolean force) {
-        int msgLen = msg.length();
-        if (force) return msgLen;
-        for (int i = 0; i < msgLen; i++) {
-            if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) {
-                return -1;
-            }
-        }
-        return msgLen;
-    }
-
-    /**
-     * Calculate the message text encoding length, fragmentation, and other details.
-     *
-     * @param msg message text
-     * @param force7BitEncoding ignore (but still count) illegal characters if true
-     * @param isEntireMsg indicates if this is entire msg or a segment in multipart msg
-     * @return septet count, or -1 on failure
-     */
-    public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg,
-            boolean force7BitEncoding, boolean isEntireMsg) {
-        TextEncodingDetails ted;
-        int septets = countAsciiSeptets(msg, force7BitEncoding);
-        if (septets != -1 && septets <= SmsConstants.MAX_USER_DATA_SEPTETS) {
-            ted = new TextEncodingDetails();
-            ted.msgCount = 1;
-            ted.codeUnitCount = septets;
-            ted.codeUnitsRemaining = SmsConstants.MAX_USER_DATA_SEPTETS - septets;
-            ted.codeUnitSize = SmsConstants.ENCODING_7BIT;
-        } else {
-            ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength(
-                    msg, force7BitEncoding);
-            if (ted.msgCount == 1 && ted.codeUnitSize == SmsConstants.ENCODING_7BIT &&
-                    isEntireMsg) {
-                // We don't support single-segment EMS, so calculate for 16-bit
-                // TODO: Consider supporting single-segment EMS
-                return SmsMessageBase.calcUnicodeEncodingDetails(msg);
-            }
-        }
-        return ted;
-    }
-
-    private static byte[] encode7bitAscii(String msg, boolean force)
-        throws CodingException
-    {
-        try {
-            BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length());
-            int msgLen = msg.length();
-            for (int i = 0; i < msgLen; i++) {
-                int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
-                if (charCode == -1) {
-                    if (force) {
-                        outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
-                    } else {
-                        throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
-                    }
-                } else {
-                    outStream.write(7, charCode);
-                }
-            }
-            return outStream.toByteArray();
-        } catch (BitwiseOutputStream.AccessException ex) {
-            throw new CodingException("7bit ASCII encode failed: " + ex);
-        }
-    }
-
-    private static byte[] encodeUtf16(String msg)
-        throws CodingException
-    {
-        try {
-            return msg.getBytes("utf-16be");
-        } catch (java.io.UnsupportedEncodingException ex) {
-            throw new CodingException("UTF-16 encode failed: " + ex);
-        }
-    }
-
-    private static class Gsm7bitCodingResult {
-        int septets;
-        byte[] data;
-    }
-
-    private static Gsm7bitCodingResult encode7bitGsm(String msg, int septetOffset, boolean force)
-        throws CodingException
-    {
-        try {
-            /*
-             * TODO(cleanup): It would be nice if GsmAlphabet provided
-             * an option to produce just the data without prepending
-             * the septet count, as this function is really just a
-             * wrapper to strip that off.  Not to mention that the
-             * septet count is generally known prior to invocation of
-             * the encoder.  Note that it cannot be derived from the
-             * resulting array length, since that cannot distinguish
-             * if the last contains either 1 or 8 valid bits.
-             *
-             * TODO(cleanup): The BitwiseXStreams could also be
-             * extended with byte-wise reversed endianness read/write
-             * routines to allow a corresponding implementation of
-             * stringToGsm7BitPacked, and potentially directly support
-             * access to the main bitwise stream from encode/decode.
-             */
-            byte[] fullData = GsmAlphabet.stringToGsm7BitPacked(msg, septetOffset, !force, 0, 0);
-            Gsm7bitCodingResult result = new Gsm7bitCodingResult();
-            result.data = new byte[fullData.length - 1];
-            System.arraycopy(fullData, 1, result.data, 0, fullData.length - 1);
-            result.septets = fullData[0] & 0x00FF;
-            return result;
-        } catch (com.android.internal.telephony.EncodeException ex) {
-            throw new CodingException("7bit GSM encode failed: " + ex);
-        }
-    }
-
-    private static void encode7bitEms(UserData uData, byte[] udhData, boolean force)
-        throws CodingException
-    {
-        int udhBytes = udhData.length + 1;  // Add length octet.
-        int udhSeptets = ((udhBytes * 8) + 6) / 7;
-        Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, udhSeptets, force);
-        uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
-        uData.msgEncodingSet = true;
-        uData.numFields = gcr.septets;
-        uData.payload = gcr.data;
-        uData.payload[0] = (byte)udhData.length;
-        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
-    }
-
-    private static void encode16bitEms(UserData uData, byte[] udhData)
-        throws CodingException
-    {
-        byte[] payload = encodeUtf16(uData.payloadStr);
-        int udhBytes = udhData.length + 1;  // Add length octet.
-        int udhCodeUnits = (udhBytes + 1) / 2;
-        int payloadCodeUnits = payload.length / 2;
-        uData.msgEncoding = UserData.ENCODING_UNICODE_16;
-        uData.msgEncodingSet = true;
-        uData.numFields = udhCodeUnits + payloadCodeUnits;
-        uData.payload = new byte[uData.numFields * 2];
-        uData.payload[0] = (byte)udhData.length;
-        System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
-        System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
-    }
-
-    private static void encodeEmsUserDataPayload(UserData uData)
-        throws CodingException
-    {
-        byte[] headerData = SmsHeader.toByteArray(uData.userDataHeader);
-        if (uData.msgEncodingSet) {
-            if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
-                encode7bitEms(uData, headerData, true);
-            } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
-                encode16bitEms(uData, headerData);
-            } else {
-                throw new CodingException("unsupported EMS user data encoding (" +
-                                          uData.msgEncoding + ")");
-            }
-        } else {
-            try {
-                encode7bitEms(uData, headerData, false);
-            } catch (CodingException ex) {
-                encode16bitEms(uData, headerData);
-            }
-        }
-    }
-
-    private static byte[] encodeShiftJis(String msg) throws CodingException {
-        try {
-            return msg.getBytes("Shift_JIS");
-        } catch (java.io.UnsupportedEncodingException ex) {
-            throw new CodingException("Shift-JIS encode failed: " + ex);
-        }
-    }
-
-    private static void encodeUserDataPayload(UserData uData)
-        throws CodingException
-    {
-        if ((uData.payloadStr == null) && (uData.msgEncoding != UserData.ENCODING_OCTET)) {
-            Rlog.e(LOG_TAG, "user data with null payloadStr");
-            uData.payloadStr = "";
-        }
-
-        if (uData.userDataHeader != null) {
-            encodeEmsUserDataPayload(uData);
-            return;
-        }
-
-        if (uData.msgEncodingSet) {
-            if (uData.msgEncoding == UserData.ENCODING_OCTET) {
-                if (uData.payload == null) {
-                    Rlog.e(LOG_TAG, "user data with octet encoding but null payload");
-                    uData.payload = new byte[0];
-                    uData.numFields = 0;
-                } else {
-                    uData.numFields = uData.payload.length;
-                }
-            } else {
-                if (uData.payloadStr == null) {
-                    Rlog.e(LOG_TAG, "non-octet user data with null payloadStr");
-                    uData.payloadStr = "";
-                }
-                if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) {
-                    Gsm7bitCodingResult gcr = encode7bitGsm(uData.payloadStr, 0, true);
-                    uData.payload = gcr.data;
-                    uData.numFields = gcr.septets;
-                } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
-                    uData.payload = encode7bitAscii(uData.payloadStr, true);
-                    uData.numFields = uData.payloadStr.length();
-                } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
-                    uData.payload = encodeUtf16(uData.payloadStr);
-                    uData.numFields = uData.payloadStr.length();
-                } else if (uData.msgEncoding == UserData.ENCODING_SHIFT_JIS) {
-                    uData.payload = encodeShiftJis(uData.payloadStr);
-                    uData.numFields = uData.payload.length;
-                } else {
-                    throw new CodingException("unsupported user data encoding (" +
-                                              uData.msgEncoding + ")");
-                }
-            }
-        } else {
-            try {
-                uData.payload = encode7bitAscii(uData.payloadStr, false);
-                uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
-            } catch (CodingException ex) {
-                uData.payload = encodeUtf16(uData.payloadStr);
-                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
-            }
-            uData.numFields = uData.payloadStr.length();
-            uData.msgEncodingSet = true;
-        }
-    }
-
-    private static void encodeUserData(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException, CodingException
-    {
-        /*
-         * TODO(cleanup): Do we really need to set userData.payload as
-         * a side effect of encoding?  If not, we could avoid data
-         * copies by passing outStream directly.
-         */
-        encodeUserDataPayload(bData.userData);
-        bData.hasUserDataHeader = bData.userData.userDataHeader != null;
-
-        if (bData.userData.payload.length > SmsConstants.MAX_USER_DATA_BYTES) {
-            throw new CodingException("encoded user data too large (" +
-                                      bData.userData.payload.length +
-                                      " > " + SmsConstants.MAX_USER_DATA_BYTES + " bytes)");
-        }
-
-        /*
-         * TODO(cleanup): figure out what the right answer is WRT paddingBits field
-         *
-         *   userData.paddingBits = (userData.payload.length * 8) - (userData.numFields * 7);
-         *   userData.paddingBits = 0; // XXX this seems better, but why?
-         *
-         */
-        int dataBits = (bData.userData.payload.length * 8) - bData.userData.paddingBits;
-        int paramBits = dataBits + 13;
-        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
-            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
-            paramBits += 8;
-        }
-        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
-        int paddingBits = (paramBytes * 8) - paramBits;
-        outStream.write(8, paramBytes);
-        outStream.write(5, bData.userData.msgEncoding);
-        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
-            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
-            outStream.write(8, bData.userData.msgType);
-        }
-        outStream.write(8, bData.userData.numFields);
-        outStream.writeByteArray(dataBits, bData.userData.payload);
-        if (paddingBits > 0) outStream.write(paddingBits, 0);
-    }
-
-    private static void encodeReplyOption(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(1, bData.userAckReq     ? 1 : 0);
-        outStream.write(1, bData.deliveryAckReq ? 1 : 0);
-        outStream.write(1, bData.readAckReq     ? 1 : 0);
-        outStream.write(1, bData.reportReq      ? 1 : 0);
-        outStream.write(4, 0);
-    }
-
-    private static byte[] encodeDtmfSmsAddress(String address) {
-        int digits = address.length();
-        int dataBits = digits * 4;
-        int dataBytes = (dataBits / 8);
-        dataBytes += (dataBits % 8) > 0 ? 1 : 0;
-        byte[] rawData = new byte[dataBytes];
-        for (int i = 0; i < digits; i++) {
-            char c = address.charAt(i);
-            int val = 0;
-            if ((c >= '1') && (c <= '9')) val = c - '0';
-            else if (c == '0') val = 10;
-            else if (c == '*') val = 11;
-            else if (c == '#') val = 12;
-            else return null;
-            rawData[i / 2] |= val << (4 - ((i % 2) * 4));
-        }
-        return rawData;
-    }
-
-    /*
-     * TODO(cleanup): CdmaSmsAddress encoding should make use of
-     * CdmaSmsAddress.parse provided that DTMF encoding is unified,
-     * and the difference in 4-bit vs. 8-bit is resolved.
-     */
-
-    private static void encodeCdmaSmsAddress(CdmaSmsAddress addr) throws CodingException {
-        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-            try {
-                addr.origBytes = addr.address.getBytes("US-ASCII");
-            } catch (java.io.UnsupportedEncodingException ex) {
-                throw new CodingException("invalid SMS address, cannot convert to ASCII");
-            }
-        } else {
-            addr.origBytes = encodeDtmfSmsAddress(addr.address);
-        }
-    }
-
-    private static void encodeCallbackNumber(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException, CodingException
-    {
-        CdmaSmsAddress addr = bData.callbackNumber;
-        encodeCdmaSmsAddress(addr);
-        int paramBits = 9;
-        int dataBits = 0;
-        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-            paramBits += 7;
-            dataBits = addr.numberOfDigits * 8;
-        } else {
-            dataBits = addr.numberOfDigits * 4;
-        }
-        paramBits += dataBits;
-        int paramBytes = (paramBits / 8) + ((paramBits % 8) > 0 ? 1 : 0);
-        int paddingBits = (paramBytes * 8) - paramBits;
-        outStream.write(8, paramBytes);
-        outStream.write(1, addr.digitMode);
-        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-            outStream.write(3, addr.ton);
-            outStream.write(4, addr.numberPlan);
-        }
-        outStream.write(8, addr.numberOfDigits);
-        outStream.writeByteArray(dataBits, addr.origBytes);
-        if (paddingBits > 0) outStream.write(paddingBits, 0);
-    }
-
-    private static void encodeMsgStatus(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(2, bData.errorClass);
-        outStream.write(6, bData.messageStatus);
-    }
-
-    private static void encodeMsgCount(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(8, bData.numberOfMessages);
-    }
-
-    private static void encodeValidityPeriodRel(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(8, bData.validityPeriodRelative);
-    }
-
-    private static void encodePrivacyIndicator(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(2, bData.privacy);
-        outStream.skip(6);
-    }
-
-    private static void encodeLanguageIndicator(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(8, bData.language);
-    }
-
-    private static void encodeDisplayMode(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(2, bData.displayMode);
-        outStream.skip(6);
-    }
-
-    private static void encodePriorityIndicator(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(2, bData.priority);
-        outStream.skip(6);
-    }
-
-    private static void encodeMsgDeliveryAlert(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        outStream.write(8, 1);
-        outStream.write(2, bData.alert);
-        outStream.skip(6);
-    }
-
-    private static void encodeScpResults(BearerData bData, BitwiseOutputStream outStream)
-        throws BitwiseOutputStream.AccessException
-    {
-        ArrayList<CdmaSmsCbProgramResults> results = bData.serviceCategoryProgramResults;
-        outStream.write(8, (results.size() * 4));   // 4 octets per program result
-        for (CdmaSmsCbProgramResults result : results) {
-            int category = result.getCategory();
-            outStream.write(8, category >> 8);
-            outStream.write(8, category);
-            outStream.write(8, result.getLanguage());
-            outStream.write(4, result.getCategoryResult());
-            outStream.skip(4);
-        }
-    }
-
-    /**
-     * Create serialized representation for BearerData object.
-     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
-     *
-     * @param bData an instance of BearerData.
-     *
-     * @return byte array of raw encoded SMS bearer data.
-     */
-    public static byte[] encode(BearerData bData) {
-        bData.hasUserDataHeader = ((bData.userData != null) &&
-                (bData.userData.userDataHeader != null));
-        try {
-            BitwiseOutputStream outStream = new BitwiseOutputStream(200);
-            outStream.write(8, SUBPARAM_MESSAGE_IDENTIFIER);
-            encodeMessageId(bData, outStream);
-            if (bData.userData != null) {
-                outStream.write(8, SUBPARAM_USER_DATA);
-                encodeUserData(bData, outStream);
-            }
-            if (bData.callbackNumber != null) {
-                outStream.write(8, SUBPARAM_CALLBACK_NUMBER);
-                encodeCallbackNumber(bData, outStream);
-            }
-            if (bData.userAckReq || bData.deliveryAckReq || bData.readAckReq || bData.reportReq) {
-                outStream.write(8, SUBPARAM_REPLY_OPTION);
-                encodeReplyOption(bData, outStream);
-            }
-            if (bData.numberOfMessages != 0) {
-                outStream.write(8, SUBPARAM_NUMBER_OF_MESSAGES);
-                encodeMsgCount(bData, outStream);
-            }
-            if (bData.validityPeriodRelativeSet) {
-                outStream.write(8, SUBPARAM_VALIDITY_PERIOD_RELATIVE);
-                encodeValidityPeriodRel(bData, outStream);
-            }
-            if (bData.privacyIndicatorSet) {
-                outStream.write(8, SUBPARAM_PRIVACY_INDICATOR);
-                encodePrivacyIndicator(bData, outStream);
-            }
-            if (bData.languageIndicatorSet) {
-                outStream.write(8, SUBPARAM_LANGUAGE_INDICATOR);
-                encodeLanguageIndicator(bData, outStream);
-            }
-            if (bData.displayModeSet) {
-                outStream.write(8, SUBPARAM_MESSAGE_DISPLAY_MODE);
-                encodeDisplayMode(bData, outStream);
-            }
-            if (bData.priorityIndicatorSet) {
-                outStream.write(8, SUBPARAM_PRIORITY_INDICATOR);
-                encodePriorityIndicator(bData, outStream);
-            }
-            if (bData.alertIndicatorSet) {
-                outStream.write(8, SUBPARAM_ALERT_ON_MESSAGE_DELIVERY);
-                encodeMsgDeliveryAlert(bData, outStream);
-            }
-            if (bData.messageStatusSet) {
-                outStream.write(8, SUBPARAM_MESSAGE_STATUS);
-                encodeMsgStatus(bData, outStream);
-            }
-            if (bData.serviceCategoryProgramResults != null) {
-                outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
-                encodeScpResults(bData, outStream);
-            }
-            return outStream.toByteArray();
-        } catch (BitwiseOutputStream.AccessException ex) {
-            Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
-        } catch (CodingException ex) {
-            Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
-        }
-        return null;
-   }
-
-    private static boolean decodeMessageId(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 3 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.messageType = inStream.read(4);
-            bData.messageId = inStream.read(8) << 8;
-            bData.messageId |= inStream.read(8);
-            bData.hasUserDataHeader = (inStream.read(1) == 1);
-            inStream.skip(3);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static boolean decodeReserved(
-            BearerData bData, BitwiseInputStream inStream, int subparamId)
-        throws BitwiseInputStream.AccessException, CodingException
-    {
-        boolean decodeSuccess = false;
-        int subparamLen = inStream.read(8); // SUBPARAM_LEN
-        int paramBits = subparamLen * 8;
-        if (paramBits <= inStream.available()) {
-            decodeSuccess = true;
-            inStream.skip(paramBits);
-        }
-        Rlog.d(LOG_TAG, "RESERVED bearer data subparameter " + subparamId + " decode "
-                + (decodeSuccess ? "succeeded" : "failed") + " (param bits = " + paramBits + ")");
-        if (!decodeSuccess) {
-            throw new CodingException("RESERVED bearer data subparameter " + subparamId
-                    + " had invalid SUBPARAM_LEN " + subparamLen);
-        }
-
-        return decodeSuccess;
-    }
-
-    private static boolean decodeUserData(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException
-    {
-        int paramBits = inStream.read(8) * 8;
-        bData.userData = new UserData();
-        bData.userData.msgEncoding = inStream.read(5);
-        bData.userData.msgEncodingSet = true;
-        bData.userData.msgType = 0;
-        int consumedBits = 5;
-        if ((bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) ||
-            (bData.userData.msgEncoding == UserData.ENCODING_GSM_DCS)) {
-            bData.userData.msgType = inStream.read(8);
-            consumedBits += 8;
-        }
-        bData.userData.numFields = inStream.read(8);
-        consumedBits += 8;
-        int dataBits = paramBits - consumedBits;
-        bData.userData.payload = inStream.readByteArray(dataBits);
-        return true;
-    }
-
-    private static String decodeUtf8(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        return decodeCharset(data, offset, numFields, 1, "UTF-8");
-    }
-
-    private static String decodeUtf16(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        // Subtract header and possible padding byte (at end) from num fields.
-        int padding = offset % 2;
-        numFields -= (offset + padding) / 2;
-        return decodeCharset(data, offset, numFields, 2, "utf-16be");
-    }
-
-    private static String decodeCharset(byte[] data, int offset, int numFields, int width,
-            String charset) throws CodingException
-    {
-        if (numFields < 0 || (numFields * width + offset) > data.length) {
-            // Try to decode the max number of characters in payload
-            int padding = offset % width;
-            int maxNumFields = (data.length - offset - padding) / width;
-            if (maxNumFields < 0) {
-                throw new CodingException(charset + " decode failed: offset out of range");
-            }
-            Rlog.e(LOG_TAG, charset + " decode error: offset = " + offset + " numFields = "
-                    + numFields + " data.length = " + data.length + " maxNumFields = "
-                    + maxNumFields);
-            numFields = maxNumFields;
-        }
-        try {
-            return new String(data, offset, numFields * width, charset);
-        } catch (java.io.UnsupportedEncodingException ex) {
-            throw new CodingException(charset + " decode failed: " + ex);
-        }
-    }
-
-    private static String decode7bitAscii(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        try {
-            offset *= 8;
-            StringBuffer strBuf = new StringBuffer(numFields);
-            BitwiseInputStream inStream = new BitwiseInputStream(data);
-            int wantedBits = (offset * 8) + (numFields * 7);
-            if (inStream.available() < wantedBits) {
-                throw new CodingException("insufficient data (wanted " + wantedBits +
-                                          " bits, but only have " + inStream.available() + ")");
-            }
-            inStream.skip(offset);
-            for (int i = 0; i < numFields; i++) {
-                int charCode = inStream.read(7);
-                if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
-                        (charCode <= UserData.ASCII_MAP_MAX_INDEX)) {
-                    strBuf.append(UserData.ASCII_MAP[charCode - UserData.ASCII_MAP_BASE_INDEX]);
-                } else if (charCode == UserData.ASCII_NL_INDEX) {
-                    strBuf.append('\n');
-                } else if (charCode == UserData.ASCII_CR_INDEX) {
-                    strBuf.append('\r');
-                } else {
-                    /* For other charCodes, they are unprintable, and so simply use SPACE. */
-                    strBuf.append(' ');
-                }
-            }
-            return strBuf.toString();
-        } catch (BitwiseInputStream.AccessException ex) {
-            throw new CodingException("7bit ASCII decode failed: " + ex);
-        }
-    }
-
-    private static String decode7bitGsm(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        // Start reading from the next 7-bit aligned boundary after offset.
-        int offsetBits = offset * 8;
-        int offsetSeptets = (offsetBits + 6) / 7;
-        numFields -= offsetSeptets;
-        int paddingBits = (offsetSeptets * 7) - offsetBits;
-        String result = GsmAlphabet.gsm7BitPackedToString(data, offset, numFields, paddingBits,
-                0, 0);
-        if (result == null) {
-            throw new CodingException("7bit GSM decoding failed");
-        }
-        return result;
-    }
-
-    private static String decodeLatin(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        return decodeCharset(data, offset, numFields, 1, "ISO-8859-1");
-    }
-
-    private static String decodeShiftJis(byte[] data, int offset, int numFields)
-        throws CodingException
-    {
-        return decodeCharset(data, offset, numFields, 1, "Shift_JIS");
-    }
-
-    private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
-        throws CodingException
-    {
-        int offset = 0;
-        if (hasUserDataHeader) {
-            int udhLen = userData.payload[0] & 0x00FF;
-            offset += udhLen + 1;
-            byte[] headerData = new byte[udhLen];
-            System.arraycopy(userData.payload, 1, headerData, 0, udhLen);
-            userData.userDataHeader = SmsHeader.fromByteArray(headerData);
-        }
-        switch (userData.msgEncoding) {
-        case UserData.ENCODING_OCTET:
-            /*
-            *  Octet decoding depends on the carrier service.
-            */
-            boolean decodingtypeUTF8 = Resources.getSystem()
-                    .getBoolean(com.android.internal.R.bool.config_sms_utf8_support);
-
-            // Strip off any padding bytes, meaning any differences between the length of the
-            // array and the target length specified by numFields.  This is to avoid any
-            // confusion by code elsewhere that only considers the payload array length.
-            byte[] payload = new byte[userData.numFields];
-            int copyLen = userData.numFields < userData.payload.length
-                    ? userData.numFields : userData.payload.length;
-
-            System.arraycopy(userData.payload, 0, payload, 0, copyLen);
-            userData.payload = payload;
-
-            if (!decodingtypeUTF8) {
-                // There are many devices in the market that send 8bit text sms (latin encoded) as
-                // octet encoded.
-                userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
-            } else {
-                userData.payloadStr = decodeUtf8(userData.payload, offset, userData.numFields);
-            }
-            break;
-
-        case UserData.ENCODING_IA5:
-        case UserData.ENCODING_7BIT_ASCII:
-            userData.payloadStr = decode7bitAscii(userData.payload, offset, userData.numFields);
-            break;
-        case UserData.ENCODING_UNICODE_16:
-            userData.payloadStr = decodeUtf16(userData.payload, offset, userData.numFields);
-            break;
-        case UserData.ENCODING_GSM_7BIT_ALPHABET:
-            userData.payloadStr = decode7bitGsm(userData.payload, offset, userData.numFields);
-            break;
-        case UserData.ENCODING_LATIN:
-            userData.payloadStr = decodeLatin(userData.payload, offset, userData.numFields);
-            break;
-        case UserData.ENCODING_SHIFT_JIS:
-            userData.payloadStr = decodeShiftJis(userData.payload, offset, userData.numFields);
-            break;
-        default:
-            throw new CodingException("unsupported user data encoding ("
-                                      + userData.msgEncoding + ")");
-        }
-    }
-
-    /**
-     * IS-91 Voice Mail message decoding
-     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
-     * (For character encodings, see TIA/EIA/IS-91, Annex B)
-     *
-     * Protocol Summary: The user data payload may contain 3-14
-     * characters.  The first two characters are parsed as a number
-     * and indicate the number of voicemails.  The third character is
-     * either a SPACE or '!' to indicate normal or urgent priority,
-     * respectively.  Any following characters are treated as normal
-     * text user data payload.
-     *
-     * Note that the characters encoding is 6-bit packed.
-     */
-    private static void decodeIs91VoicemailStatus(BearerData bData)
-        throws BitwiseInputStream.AccessException, CodingException
-    {
-        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
-        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
-        int numFields = bData.userData.numFields;
-        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
-            throw new CodingException("IS-91 voicemail status decoding failed");
-        }
-        try {
-            StringBuffer strbuf = new StringBuffer(dataLen);
-            while (inStream.available() >= 6) {
-                strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
-            }
-            String data = strbuf.toString();
-            bData.numberOfMessages = Integer.parseInt(data.substring(0, 2));
-            char prioCode = data.charAt(2);
-            if (prioCode == ' ') {
-                bData.priority = PRIORITY_NORMAL;
-            } else if (prioCode == '!') {
-                bData.priority = PRIORITY_URGENT;
-            } else {
-                throw new CodingException("IS-91 voicemail status decoding failed: " +
-                        "illegal priority setting (" + prioCode + ")");
-            }
-            bData.priorityIndicatorSet = true;
-            bData.userData.payloadStr = data.substring(3, numFields - 3);
-       } catch (java.lang.NumberFormatException ex) {
-            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
-        } catch (java.lang.IndexOutOfBoundsException ex) {
-            throw new CodingException("IS-91 voicemail status decoding failed: " + ex);
-        }
-    }
-
-    /**
-     * IS-91 Short Message decoding
-     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
-     * (For character encodings, see TIA/EIA/IS-91, Annex B)
-     *
-     * Protocol Summary: The user data payload may contain 1-14
-     * characters, which are treated as normal text user data payload.
-     * Note that the characters encoding is 6-bit packed.
-     */
-    private static void decodeIs91ShortMessage(BearerData bData)
-        throws BitwiseInputStream.AccessException, CodingException
-    {
-        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
-        int dataLen = inStream.available() / 6;  // 6-bit packed character encoding.
-        int numFields = bData.userData.numFields;
-        // dataLen may be > 14 characters due to octet padding
-        if ((numFields > 14) || (dataLen < numFields)) {
-            throw new CodingException("IS-91 short message decoding failed");
-        }
-        StringBuffer strbuf = new StringBuffer(dataLen);
-        for (int i = 0; i < numFields; i++) {
-            strbuf.append(UserData.ASCII_MAP[inStream.read(6)]);
-        }
-        bData.userData.payloadStr = strbuf.toString();
-    }
-
-    /**
-     * IS-91 CLI message (callback number) decoding
-     * (See 3GPP2 C.S0015-A, Table 4.3.1.4.1-1)
-     *
-     * Protocol Summary: The data payload may contain 1-32 digits,
-     * encoded using standard 4-bit DTMF, which are treated as a
-     * callback number.
-     */
-    private static void decodeIs91Cli(BearerData bData) throws CodingException {
-        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
-        int dataLen = inStream.available() / 4;  // 4-bit packed DTMF digit encoding.
-        int numFields = bData.userData.numFields;
-        if ((dataLen > 14) || (dataLen < 3) || (dataLen < numFields)) {
-            throw new CodingException("IS-91 voicemail status decoding failed");
-        }
-        CdmaSmsAddress addr = new CdmaSmsAddress();
-        addr.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
-        addr.origBytes = bData.userData.payload;
-        addr.numberOfDigits = (byte)numFields;
-        decodeSmsAddress(addr);
-        bData.callbackNumber = addr;
-    }
-
-    private static void decodeIs91(BearerData bData)
-        throws BitwiseInputStream.AccessException, CodingException
-    {
-        switch (bData.userData.msgType) {
-        case UserData.IS91_MSG_TYPE_VOICEMAIL_STATUS:
-            decodeIs91VoicemailStatus(bData);
-            break;
-        case UserData.IS91_MSG_TYPE_CLI:
-            decodeIs91Cli(bData);
-            break;
-        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE_FULL:
-        case UserData.IS91_MSG_TYPE_SHORT_MESSAGE:
-            decodeIs91ShortMessage(bData);
-            break;
-        default:
-            throw new CodingException("unsupported IS-91 message type (" +
-                    bData.userData.msgType + ")");
-        }
-    }
-
-    private static boolean decodeReplyOption(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.userAckReq     = (inStream.read(1) == 1);
-            bData.deliveryAckReq = (inStream.read(1) == 1);
-            bData.readAckReq     = (inStream.read(1) == 1);
-            bData.reportReq      = (inStream.read(1) == 1);
-            inStream.skip(4);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static boolean decodeMsgCount(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static boolean decodeDepositIndex(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 2 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static String decodeDtmfSmsAddress(byte[] rawData, int numFields)
-        throws CodingException
-    {
-        /* DTMF 4-bit digit encoding, defined in at
-         * 3GPP2 C.S005-D, v2.0, table 2.7.1.3.2.4-4 */
-        StringBuffer strBuf = new StringBuffer(numFields);
-        for (int i = 0; i < numFields; i++) {
-            int val = 0x0F & (rawData[i / 2] >>> (4 - ((i % 2) * 4)));
-            if ((val >= 1) && (val <= 9)) strBuf.append(Integer.toString(val, 10));
-            else if (val == 10) strBuf.append('0');
-            else if (val == 11) strBuf.append('*');
-            else if (val == 12) strBuf.append('#');
-            else throw new CodingException("invalid SMS address DTMF code (" + val + ")");
-        }
-        return strBuf.toString();
-    }
-
-    private static void decodeSmsAddress(CdmaSmsAddress addr) throws CodingException {
-        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-            try {
-                /* As specified in 3GPP2 C.S0015-B, v2, 4.5.15 -- actually
-                 * just 7-bit ASCII encoding, with the MSB being zero. */
-                addr.address = new String(addr.origBytes, 0, addr.origBytes.length, "US-ASCII");
-            } catch (java.io.UnsupportedEncodingException ex) {
-                throw new CodingException("invalid SMS address ASCII code");
-            }
-        } else {
-            addr.address = decodeDtmfSmsAddress(addr.origBytes, addr.numberOfDigits);
-        }
-    }
-
-    private static boolean decodeCallbackNumber(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException, CodingException
-    {
-        final int EXPECTED_PARAM_SIZE = 1 * 8; //at least
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits < EXPECTED_PARAM_SIZE) {
-            inStream.skip(paramBits);
-            return false;
-        }
-        CdmaSmsAddress addr = new CdmaSmsAddress();
-        addr.digitMode = inStream.read(1);
-        byte fieldBits = 4;
-        byte consumedBits = 1;
-        if (addr.digitMode == CdmaSmsAddress.DIGIT_MODE_8BIT_CHAR) {
-            addr.ton = inStream.read(3);
-            addr.numberPlan = inStream.read(4);
-            fieldBits = 8;
-            consumedBits += 7;
-        }
-        addr.numberOfDigits = inStream.read(8);
-        consumedBits += 8;
-        int remainingBits = paramBits - consumedBits;
-        int dataBits = addr.numberOfDigits * fieldBits;
-        int paddingBits = remainingBits - dataBits;
-        if (remainingBits < dataBits) {
-            throw new CodingException("CALLBACK_NUMBER subparam encoding size error (" +
-                                      "remainingBits + " + remainingBits + ", dataBits + " +
-                                      dataBits + ", paddingBits + " + paddingBits + ")");
-        }
-        addr.origBytes = inStream.readByteArray(dataBits);
-        inStream.skip(paddingBits);
-        decodeSmsAddress(addr);
-        bData.callbackNumber = addr;
-        return true;
-    }
-
-    private static boolean decodeMsgStatus(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.errorClass = inStream.read(2);
-            bData.messageStatus = inStream.read(6);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.messageStatusSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeMsgCenterTimeStamp(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 6 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static boolean decodeValidityAbs(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 6 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static boolean decodeDeferredDeliveryAbs(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 6 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
-                    inStream.readByteArray(6 * 8));
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        return decodeSuccess;
-    }
-
-    private static boolean decodeValidityRel(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.deferredDeliveryTimeRelative = inStream.read(8);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.deferredDeliveryTimeRelativeSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeDeferredDeliveryRel(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.validityPeriodRelative = inStream.read(8);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.validityPeriodRelativeSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodePrivacyIndicator(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.privacy = inStream.read(2);
-            inStream.skip(6);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.privacyIndicatorSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeLanguageIndicator(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.language = inStream.read(8);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.languageIndicatorSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeDisplayMode(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.displayMode = inStream.read(2);
-            inStream.skip(6);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.displayModeSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodePriorityIndicator(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.priority = inStream.read(2);
-            inStream.skip(6);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.priorityIndicatorSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeMsgDeliveryAlert(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.alert = inStream.read(2);
-            inStream.skip(6);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.alertIndicatorSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeUserResponseCode(BearerData bData, BitwiseInputStream inStream)
-        throws BitwiseInputStream.AccessException {
-        final int EXPECTED_PARAM_SIZE = 1 * 8;
-        boolean decodeSuccess = false;
-        int paramBits = inStream.read(8) * 8;
-        if (paramBits >= EXPECTED_PARAM_SIZE) {
-            paramBits -= EXPECTED_PARAM_SIZE;
-            decodeSuccess = true;
-            bData.userResponseCode = inStream.read(8);
-        }
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ")");
-        }
-        inStream.skip(paramBits);
-        bData.userResponseCodeSet = decodeSuccess;
-        return decodeSuccess;
-    }
-
-    private static boolean decodeServiceCategoryProgramData(BearerData bData,
-            BitwiseInputStream inStream) throws BitwiseInputStream.AccessException, CodingException
-    {
-        if (inStream.available() < 13) {
-            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
-                    + inStream.available() + " bits available");
-        }
-
-        int paramBits = inStream.read(8) * 8;
-        int msgEncoding = inStream.read(5);
-        paramBits -= 5;
-
-        if (inStream.available() < paramBits) {
-            throw new CodingException("SERVICE_CATEGORY_PROGRAM_DATA decode failed: only "
-                    + inStream.available() + " bits available (" + paramBits + " bits expected)");
-        }
-
-        ArrayList<CdmaSmsCbProgramData> programDataList = new ArrayList<CdmaSmsCbProgramData>();
-
-        final int CATEGORY_FIELD_MIN_SIZE = 6 * 8;
-        boolean decodeSuccess = false;
-        while (paramBits >= CATEGORY_FIELD_MIN_SIZE) {
-            int operation = inStream.read(4);
-            int category = (inStream.read(8) << 8) | inStream.read(8);
-            int language = inStream.read(8);
-            int maxMessages = inStream.read(8);
-            int alertOption = inStream.read(4);
-            int numFields = inStream.read(8);
-            paramBits -= CATEGORY_FIELD_MIN_SIZE;
-
-            int textBits = getBitsForNumFields(msgEncoding, numFields);
-            if (paramBits < textBits) {
-                throw new CodingException("category name is " + textBits + " bits in length,"
-                        + " but there are only " + paramBits + " bits available");
-            }
-
-            UserData userData = new UserData();
-            userData.msgEncoding = msgEncoding;
-            userData.msgEncodingSet = true;
-            userData.numFields = numFields;
-            userData.payload = inStream.readByteArray(textBits);
-            paramBits -= textBits;
-
-            decodeUserDataPayload(userData, false);
-            String categoryName = userData.payloadStr;
-            CdmaSmsCbProgramData programData = new CdmaSmsCbProgramData(operation, category,
-                    language, maxMessages, alertOption, categoryName);
-            programDataList.add(programData);
-
-            decodeSuccess = true;
-        }
-
-        if ((! decodeSuccess) || (paramBits > 0)) {
-            Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
-                      (decodeSuccess ? "succeeded" : "failed") +
-                      " (extra bits = " + paramBits + ')');
-        }
-
-        inStream.skip(paramBits);
-        bData.serviceCategoryProgramData = programDataList;
-        return decodeSuccess;
-    }
-
-    private static int serviceCategoryToCmasMessageClass(int serviceCategory) {
-        switch (serviceCategory) {
-            case SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT:
-                return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
-
-            case SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT:
-                return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
-
-            case SmsEnvelope.SERVICE_CATEGORY_CMAS_SEVERE_THREAT:
-                return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
-
-            case SmsEnvelope.SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY:
-                return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
-
-            case SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
-
-            default:
-                return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
-        }
-    }
-
-    /**
-     * Calculates the number of bits to read for the specified number of encoded characters.
-     * @param msgEncoding the message encoding to use
-     * @param numFields the number of characters to read. For Shift-JIS and Korean encodings,
-     *  this is the number of bytes to read.
-     * @return the number of bits to read from the stream
-     * @throws CodingException if the specified encoding is not supported
-     */
-    private static int getBitsForNumFields(int msgEncoding, int numFields)
-            throws CodingException {
-        switch (msgEncoding) {
-            case UserData.ENCODING_OCTET:
-            case UserData.ENCODING_SHIFT_JIS:
-            case UserData.ENCODING_KOREAN:
-            case UserData.ENCODING_LATIN:
-            case UserData.ENCODING_LATIN_HEBREW:
-                return numFields * 8;
-
-            case UserData.ENCODING_IA5:
-            case UserData.ENCODING_7BIT_ASCII:
-            case UserData.ENCODING_GSM_7BIT_ALPHABET:
-                return numFields * 7;
-
-            case UserData.ENCODING_UNICODE_16:
-                return numFields * 16;
-
-            default:
-                throw new CodingException("unsupported message encoding (" + msgEncoding + ')');
-        }
-    }
-
-    /**
-     * CMAS message decoding.
-     * (See TIA-1149-0-1, CMAS over CDMA)
-     *
-     * @param serviceCategory is the service category from the SMS envelope
-     */
-    private static void decodeCmasUserData(BearerData bData, int serviceCategory)
-            throws BitwiseInputStream.AccessException, CodingException {
-        BitwiseInputStream inStream = new BitwiseInputStream(bData.userData.payload);
-        if (inStream.available() < 8) {
-            throw new CodingException("emergency CB with no CMAE_protocol_version");
-        }
-        int protocolVersion = inStream.read(8);
-        if (protocolVersion != 0) {
-            throw new CodingException("unsupported CMAE_protocol_version " + protocolVersion);
-        }
-
-        int messageClass = serviceCategoryToCmasMessageClass(serviceCategory);
-        int category = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN;
-        int responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN;
-        int severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
-        int urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
-        int certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
-
-        while (inStream.available() >= 16) {
-            int recordType = inStream.read(8);
-            int recordLen = inStream.read(8);
-            switch (recordType) {
-                case 0:     // Type 0 elements (Alert text)
-                    UserData alertUserData = new UserData();
-                    alertUserData.msgEncoding = inStream.read(5);
-                    alertUserData.msgEncodingSet = true;
-                    alertUserData.msgType = 0;
-
-                    int numFields;                          // number of chars to decode
-                    switch (alertUserData.msgEncoding) {
-                        case UserData.ENCODING_OCTET:
-                        case UserData.ENCODING_LATIN:
-                            numFields = recordLen - 1;      // subtract 1 byte for encoding
-                            break;
-
-                        case UserData.ENCODING_IA5:
-                        case UserData.ENCODING_7BIT_ASCII:
-                        case UserData.ENCODING_GSM_7BIT_ALPHABET:
-                            numFields = ((recordLen * 8) - 5) / 7;  // subtract 5 bits for encoding
-                            break;
-
-                        case UserData.ENCODING_UNICODE_16:
-                            numFields = (recordLen - 1) / 2;
-                            break;
-
-                        default:
-                            numFields = 0;      // unsupported encoding
-                    }
-
-                    alertUserData.numFields = numFields;
-                    alertUserData.payload = inStream.readByteArray(recordLen * 8 - 5);
-                    decodeUserDataPayload(alertUserData, false);
-                    bData.userData = alertUserData;
-                    break;
-
-                case 1:     // Type 1 elements
-                    category = inStream.read(8);
-                    responseType = inStream.read(8);
-                    severity = inStream.read(4);
-                    urgency = inStream.read(4);
-                    certainty = inStream.read(4);
-                    inStream.skip(recordLen * 8 - 28);
-                    break;
-
-                default:
-                    Rlog.w(LOG_TAG, "skipping unsupported CMAS record type " + recordType);
-                    inStream.skip(recordLen * 8);
-                    break;
-            }
-        }
-
-        bData.cmasWarningInfo = new SmsCbCmasInfo(messageClass, category, responseType, severity,
-                urgency, certainty);
-    }
-
-    /**
-     * Create BearerData object from serialized representation.
-     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
-     *
-     * @param smsData byte array of raw encoded SMS bearer data.
-     * @return an instance of BearerData.
-     */
-    public static BearerData decode(byte[] smsData) {
-        return decode(smsData, 0);
-    }
-
-    private static boolean isCmasAlertCategory(int category) {
-        return category >= SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT
-                && category <= SmsEnvelope.SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE;
-    }
-
-    /**
-     * Create BearerData object from serialized representation.
-     * (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
-     *
-     * @param smsData byte array of raw encoded SMS bearer data.
-     * @param serviceCategory the envelope service category (for CMAS alert handling)
-     * @return an instance of BearerData.
-     */
-    public static BearerData decode(byte[] smsData, int serviceCategory) {
-        try {
-            BitwiseInputStream inStream = new BitwiseInputStream(smsData);
-            BearerData bData = new BearerData();
-            int foundSubparamMask = 0;
-            while (inStream.available() > 0) {
-                int subparamId = inStream.read(8);
-                int subparamIdBit = 1 << subparamId;
-                // int is 4 bytes. This duplicate check has a limit to Id number up to 32 (4*8)
-                // as 32th bit is the max bit in int.
-                // Per 3GPP2 C.S0015-B Table 4.5-1 Bearer Data Subparameter Identifiers:
-                // last defined subparam ID is 23 (00010111 = 0x17 = 23).
-                // Only do duplicate subparam ID check if subparam is within defined value as
-                // reserved subparams are just skipped.
-                if ((foundSubparamMask & subparamIdBit) != 0 &&
-                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
-                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
-                    throw new CodingException("illegal duplicate subparameter (" +
-                                              subparamId + ")");
-                }
-                boolean decodeSuccess;
-                switch (subparamId) {
-                case SUBPARAM_MESSAGE_IDENTIFIER:
-                    decodeSuccess = decodeMessageId(bData, inStream);
-                    break;
-                case SUBPARAM_USER_DATA:
-                    decodeSuccess = decodeUserData(bData, inStream);
-                    break;
-                case SUBPARAM_USER_RESPONSE_CODE:
-                    decodeSuccess = decodeUserResponseCode(bData, inStream);
-                    break;
-                case SUBPARAM_REPLY_OPTION:
-                    decodeSuccess = decodeReplyOption(bData, inStream);
-                    break;
-                case SUBPARAM_NUMBER_OF_MESSAGES:
-                    decodeSuccess = decodeMsgCount(bData, inStream);
-                    break;
-                case SUBPARAM_CALLBACK_NUMBER:
-                    decodeSuccess = decodeCallbackNumber(bData, inStream);
-                    break;
-                case SUBPARAM_MESSAGE_STATUS:
-                    decodeSuccess = decodeMsgStatus(bData, inStream);
-                    break;
-                case SUBPARAM_MESSAGE_CENTER_TIME_STAMP:
-                    decodeSuccess = decodeMsgCenterTimeStamp(bData, inStream);
-                    break;
-                case SUBPARAM_VALIDITY_PERIOD_ABSOLUTE:
-                    decodeSuccess = decodeValidityAbs(bData, inStream);
-                    break;
-                case SUBPARAM_VALIDITY_PERIOD_RELATIVE:
-                    decodeSuccess = decodeValidityRel(bData, inStream);
-                    break;
-                case SUBPARAM_DEFERRED_DELIVERY_TIME_ABSOLUTE:
-                    decodeSuccess = decodeDeferredDeliveryAbs(bData, inStream);
-                    break;
-                case SUBPARAM_DEFERRED_DELIVERY_TIME_RELATIVE:
-                    decodeSuccess = decodeDeferredDeliveryRel(bData, inStream);
-                    break;
-                case SUBPARAM_PRIVACY_INDICATOR:
-                    decodeSuccess = decodePrivacyIndicator(bData, inStream);
-                    break;
-                case SUBPARAM_LANGUAGE_INDICATOR:
-                    decodeSuccess = decodeLanguageIndicator(bData, inStream);
-                    break;
-                case SUBPARAM_MESSAGE_DISPLAY_MODE:
-                    decodeSuccess = decodeDisplayMode(bData, inStream);
-                    break;
-                case SUBPARAM_PRIORITY_INDICATOR:
-                    decodeSuccess = decodePriorityIndicator(bData, inStream);
-                    break;
-                case SUBPARAM_ALERT_ON_MESSAGE_DELIVERY:
-                    decodeSuccess = decodeMsgDeliveryAlert(bData, inStream);
-                    break;
-                case SUBPARAM_MESSAGE_DEPOSIT_INDEX:
-                    decodeSuccess = decodeDepositIndex(bData, inStream);
-                    break;
-                case SUBPARAM_SERVICE_CATEGORY_PROGRAM_DATA:
-                    decodeSuccess = decodeServiceCategoryProgramData(bData, inStream);
-                    break;
-                default:
-                    decodeSuccess = decodeReserved(bData, inStream, subparamId);
-                }
-                if (decodeSuccess &&
-                        (subparamId >= SUBPARAM_MESSAGE_IDENTIFIER &&
-                        subparamId <= SUBPARAM_ID_LAST_DEFINED)) {
-                    foundSubparamMask |= subparamIdBit;
-                }
-            }
-            if ((foundSubparamMask & (1 << SUBPARAM_MESSAGE_IDENTIFIER)) == 0) {
-                throw new CodingException("missing MESSAGE_IDENTIFIER subparam");
-            }
-            if (bData.userData != null) {
-                if (isCmasAlertCategory(serviceCategory)) {
-                    decodeCmasUserData(bData, serviceCategory);
-                } else if (bData.userData.msgEncoding == UserData.ENCODING_IS91_EXTENDED_PROTOCOL) {
-                    if ((foundSubparamMask ^
-                             (1 << SUBPARAM_MESSAGE_IDENTIFIER) ^
-                             (1 << SUBPARAM_USER_DATA))
-                            != 0) {
-                        Rlog.e(LOG_TAG, "IS-91 must occur without extra subparams (" +
-                              foundSubparamMask + ")");
-                    }
-                    decodeIs91(bData);
-                } else {
-                    decodeUserDataPayload(bData.userData, bData.hasUserDataHeader);
-                }
-            }
-            return bData;
-        } catch (BitwiseInputStream.AccessException ex) {
-            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
-        } catch (CodingException ex) {
-            Rlog.e(LOG_TAG, "BearerData decode failed: " + ex);
-        }
-        return null;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java b/src/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
deleted file mode 100644
index 5f2e561..0000000
--- a/src/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2008 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.cdma.sms;
-
-import android.util.SparseBooleanArray;
-
-import com.android.internal.telephony.SmsAddress;
-import com.android.internal.telephony.cdma.sms.UserData;
-import com.android.internal.util.HexDump;
-
-public class CdmaSmsAddress extends SmsAddress {
-
-    /**
-     * Digit Mode Indicator is a 1-bit value that indicates whether
-     * the address digits are 4-bit DTMF codes or 8-bit codes.  (See
-     * 3GPP2 C.S0015-B, v2, 3.4.3.3)
-     */
-    static public final int DIGIT_MODE_4BIT_DTMF              = 0x00;
-    static public final int DIGIT_MODE_8BIT_CHAR              = 0x01;
-
-    public int digitMode;
-
-    /**
-     * Number Mode Indicator is 1-bit value that indicates whether the
-     * address type is a data network address or not.  (See 3GPP2
-     * C.S0015-B, v2, 3.4.3.3)
-     */
-    static public final int NUMBER_MODE_NOT_DATA_NETWORK      = 0x00;
-    static public final int NUMBER_MODE_DATA_NETWORK          = 0x01;
-
-    public int numberMode;
-
-    /**
-     * Number Types for data networks.
-     * (See 3GPP2 C.S005-D, table2.7.1.3.2.4-2 for complete table)
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3 for data network subset)
-     * NOTE: value is stored in the parent class ton field.
-     */
-    static public final int TON_UNKNOWN                   = 0x00;
-    static public final int TON_INTERNATIONAL_OR_IP       = 0x01;
-    static public final int TON_NATIONAL_OR_EMAIL         = 0x02;
-    static public final int TON_NETWORK                   = 0x03;
-    static public final int TON_SUBSCRIBER                = 0x04;
-    static public final int TON_ALPHANUMERIC              = 0x05;
-    static public final int TON_ABBREVIATED               = 0x06;
-    static public final int TON_RESERVED                  = 0x07;
-
-    /**
-     * Maximum lengths for fields as defined in ril_cdma_sms.h.
-     */
-    static public final int SMS_ADDRESS_MAX          =  36;
-    static public final int SMS_SUBADDRESS_MAX       =  36;
-
-    /**
-     * This field shall be set to the number of address digits
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
-     */
-    public int numberOfDigits;
-
-    /**
-     * Numbering Plan identification is a 0 or 4-bit value that
-     * indicates which numbering plan identification is set.  (See
-     * 3GPP2, C.S0015-B, v2, 3.4.3.3 and C.S005-D, table2.7.1.3.2.4-3)
-     */
-    static public final int NUMBERING_PLAN_UNKNOWN           = 0x0;
-    static public final int NUMBERING_PLAN_ISDN_TELEPHONY    = 0x1;
-    //static protected final int NUMBERING_PLAN_DATA              = 0x3;
-    //static protected final int NUMBERING_PLAN_TELEX             = 0x4;
-    //static protected final int NUMBERING_PLAN_PRIVATE           = 0x9;
-
-    public int numberPlan;
-
-    /**
-     * NOTE: the parsed string address and the raw byte array values
-     * are stored in the parent class address and origBytes fields,
-     * respectively.
-     */
-
-    public CdmaSmsAddress(){
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("CdmaSmsAddress ");
-        builder.append("{ digitMode=" + digitMode);
-        builder.append(", numberMode=" + numberMode);
-        builder.append(", numberPlan=" + numberPlan);
-        builder.append(", numberOfDigits=" + numberOfDigits);
-        builder.append(", ton=" + ton);
-        builder.append(", address=\"" + address + "\"");
-        builder.append(", origBytes=" + HexDump.toHexString(origBytes));
-        builder.append(" }");
-        return builder.toString();
-    }
-
-    /*
-     * TODO(cleanup): Refactor the parsing for addresses to better
-     * share code and logic with GSM.  Also, gather all DTMF/BCD
-     * processing code in one place.
-     */
-
-    private static byte[] parseToDtmf(String address) {
-        int digits = address.length();
-        byte[] result = new byte[digits];
-        for (int i = 0; i < digits; i++) {
-            char c = address.charAt(i);
-            int val = 0;
-            if ((c >= '1') && (c <= '9')) val = c - '0';
-            else if (c == '0') val = 10;
-            else if (c == '*') val = 11;
-            else if (c == '#') val = 12;
-            else return null;
-            result[i] = (byte)val;
-        }
-        return result;
-    }
-
-    private static final char[] numericCharsDialable = {
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*', '#'
-    };
-
-    private static final char[] numericCharsSugar = {
-        '(', ')', ' ', '-', '+', '.', '/', '\\'
-    };
-
-    private static final SparseBooleanArray numericCharDialableMap = new SparseBooleanArray (
-            numericCharsDialable.length + numericCharsSugar.length);
-    static {
-        for (int i = 0; i < numericCharsDialable.length; i++) {
-            numericCharDialableMap.put(numericCharsDialable[i], true);
-        }
-        for (int i = 0; i < numericCharsSugar.length; i++) {
-            numericCharDialableMap.put(numericCharsSugar[i], false);
-        }
-    }
-
-    /**
-     * Given a numeric address string, return the string without
-     * syntactic sugar, meaning parens, spaces, hyphens/minuses, or
-     * plus signs.  If the input string contains non-numeric
-     * non-punctuation characters, return null.
-     */
-    private static String filterNumericSugar(String address) {
-        StringBuilder builder = new StringBuilder();
-        int len = address.length();
-        for (int i = 0; i < len; i++) {
-            char c = address.charAt(i);
-            int mapIndex = numericCharDialableMap.indexOfKey(c);
-            if (mapIndex < 0) return null;
-            if (! numericCharDialableMap.valueAt(mapIndex)) continue;
-            builder.append(c);
-        }
-        return builder.toString();
-    }
-
-    /**
-     * Given a string, return the string without whitespace,
-     * including CR/LF.
-     */
-    private static String filterWhitespace(String address) {
-        StringBuilder builder = new StringBuilder();
-        int len = address.length();
-        for (int i = 0; i < len; i++) {
-            char c = address.charAt(i);
-            if ((c == ' ') || (c == '\r') || (c == '\n') || (c == '\t')) continue;
-            builder.append(c);
-        }
-        return builder.toString();
-    }
-
-    /**
-     * Given a string, create a corresponding CdmaSmsAddress object.
-     *
-     * The result will be null if the input string is not
-     * representable using printable ASCII.
-     *
-     * For numeric addresses, the string is cleaned up by removing
-     * common punctuation.  For alpha addresses, the string is cleaned
-     * up by removing whitespace.
-     */
-    public static CdmaSmsAddress parse(String address) {
-        CdmaSmsAddress addr = new CdmaSmsAddress();
-        addr.address = address;
-        addr.ton = CdmaSmsAddress.TON_UNKNOWN;
-        byte[] origBytes = null;
-        String filteredAddr = filterNumericSugar(address);
-        if (filteredAddr != null) {
-            origBytes = parseToDtmf(filteredAddr);
-        }
-        if (origBytes != null) {
-            addr.digitMode = DIGIT_MODE_4BIT_DTMF;
-            addr.numberMode = NUMBER_MODE_NOT_DATA_NETWORK;
-            if (address.indexOf('+') != -1) {
-                addr.ton = TON_INTERNATIONAL_OR_IP;
-            }
-        } else {
-            filteredAddr = filterWhitespace(address);
-            origBytes = UserData.stringToAscii(filteredAddr);
-            if (origBytes == null) {
-                return null;
-            }
-            addr.digitMode = DIGIT_MODE_8BIT_CHAR;
-            addr.numberMode = NUMBER_MODE_DATA_NETWORK;
-            if (address.indexOf('@') != -1) {
-                addr.ton = TON_NATIONAL_OR_EMAIL;
-            }
-        }
-        addr.origBytes = origBytes;
-        addr.numberOfDigits = origBytes.length;
-        return addr;
-    }
-
-}
diff --git a/src/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java b/src/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
deleted file mode 100644
index 0d5b502..0000000
--- a/src/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project. All rights reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony.cdma.sms;
-
-public class CdmaSmsSubaddress {
-    public int type;
-
-    public byte odd;
-
-    public byte[] origBytes;
-}
-
diff --git a/src/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/src/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
deleted file mode 100644
index f73df56..0000000
--- a/src/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2008 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.cdma.sms;
-
-
-import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
-
-public final class SmsEnvelope {
-    /**
-     * Message Types
-     * (See 3GPP2 C.S0015-B 3.4.1)
-     */
-    static public final int MESSAGE_TYPE_POINT_TO_POINT   = 0x00;
-    static public final int MESSAGE_TYPE_BROADCAST        = 0x01;
-    static public final int MESSAGE_TYPE_ACKNOWLEDGE      = 0x02;
-
-    /**
-     * Supported Teleservices
-     * (See 3GPP2 N.S0005 and TIA-41)
-     */
-    static public final int TELESERVICE_NOT_SET           = 0x0000;
-    static public final int TELESERVICE_WMT               = 0x1002;
-    static public final int TELESERVICE_VMN               = 0x1003;
-    static public final int TELESERVICE_WAP               = 0x1004;
-    static public final int TELESERVICE_WEMT              = 0x1005;
-    static public final int TELESERVICE_SCPT              = 0x1006;
-
-    /**
-     * The following are defined as extensions to the standard teleservices
-     */
-    // Voice mail notification through Message Waiting Indication in CDMA mode or Analog mode.
-    // Defined in 3GPP2 C.S-0005, 3.7.5.6, an Info Record containing an 8-bit number with the
-    // number of messages waiting, it's used by some CDMA carriers for a voice mail count.
-    static public final int TELESERVICE_MWI               = 0x40000;
-
-    // Service Categories for Cell Broadcast, see 3GPP2 C.R1001 table 9.3.1-1
-    // static final int SERVICE_CATEGORY_EMERGENCY      = 0x0001;
-    //...
-
-    // CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
-    public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT  = 0x1000;
-    public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT            = 0x1001;
-    public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT             = 0x1002;
-    public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = 0x1003;
-    public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE              = 0x1004;
-    public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE       = 0x10ff;
-
-    /**
-     * Provides the type of a SMS message like point to point, broadcast or acknowledge
-     */
-    public int messageType;
-
-    /**
-     * The 16-bit Teleservice parameter identifies which upper layer service access point is sending
-     * or receiving the message.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.1)
-     */
-    public int teleService = TELESERVICE_NOT_SET;
-
-    /**
-     * The 16-bit service category parameter identifies the type of service provided
-     * by the SMS message.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.2)
-     */
-    public int serviceCategory;
-
-    /**
-     * The origination address identifies the originator of the SMS message.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
-     */
-    public CdmaSmsAddress origAddress;
-
-    /**
-     * The destination address identifies the target of the SMS message.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
-     */
-    public CdmaSmsAddress destAddress;
-
-    /**
-     * The origination subaddress identifies the originator of the SMS message.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.4)
-     */
-    public CdmaSmsSubaddress origSubaddress;
-
-    /**
-     * The 6-bit bearer reply parameter is used to request the return of a
-     * SMS Acknowledge Message.
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.5)
-     */
-    public int bearerReply;
-
-    /**
-     * Cause Code values:
-     * The cause code parameters are an indication whether an SMS error has occurred and if so,
-     * whether the condition is considered temporary or permanent.
-     * ReplySeqNo 6-bit value,
-     * ErrorClass 2-bit value,
-     * CauseCode 0-bit or 8-bit value
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.6)
-     */
-    public byte replySeqNo;
-    public byte errorClass;
-    public byte causeCode;
-
-    /**
-     * encoded bearer data
-     * (See 3GPP2 C.S0015-B, v2, 3.4.3.7)
-     */
-    public byte[] bearerData;
-
-    public SmsEnvelope() {
-        // nothing to see here
-    }
-
-}
-
diff --git a/src/java/com/android/internal/telephony/cdma/sms/UserData.java b/src/java/com/android/internal/telephony/cdma/sms/UserData.java
deleted file mode 100644
index 599c2b3..0000000
--- a/src/java/com/android/internal/telephony/cdma/sms/UserData.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2008 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.cdma.sms;
-
-import android.util.SparseIntArray;
-
-import com.android.internal.telephony.SmsHeader;
-import com.android.internal.util.HexDump;
-
-public class UserData {
-
-    /**
-     * User data encoding types.
-     * (See 3GPP2 C.R1001-F, v1.0, table 9.1-1)
-     */
-    public static final int ENCODING_OCTET                      = 0x00;
-    public static final int ENCODING_IS91_EXTENDED_PROTOCOL     = 0x01;
-    public static final int ENCODING_7BIT_ASCII                 = 0x02;
-    public static final int ENCODING_IA5                        = 0x03;
-    public static final int ENCODING_UNICODE_16                 = 0x04;
-    public static final int ENCODING_SHIFT_JIS                  = 0x05;
-    public static final int ENCODING_KOREAN                     = 0x06;
-    public static final int ENCODING_LATIN_HEBREW               = 0x07;
-    public static final int ENCODING_LATIN                      = 0x08;
-    public static final int ENCODING_GSM_7BIT_ALPHABET          = 0x09;
-    public static final int ENCODING_GSM_DCS                    = 0x0A;
-
-    /**
-     * IS-91 message types.
-     * (See TIA/EIS/IS-91-A-ENGL 1999, table 3.7.1.1-3)
-     */
-    public static final int IS91_MSG_TYPE_VOICEMAIL_STATUS   = 0x82;
-    public static final int IS91_MSG_TYPE_SHORT_MESSAGE_FULL = 0x83;
-    public static final int IS91_MSG_TYPE_CLI                = 0x84;
-    public static final int IS91_MSG_TYPE_SHORT_MESSAGE      = 0x85;
-
-    /**
-     * US ASCII character mapping table.
-     *
-     * This table contains only the printable ASCII characters, with a
-     * 0x20 offset, meaning that the ASCII SPACE character is at index
-     * 0, with the resulting code of 0x20.
-     *
-     * Note this mapping is also equivalent to that used by both the
-     * IA5 and the IS-91 encodings.  For the former this is defined
-     * using CCITT Rec. T.50 Tables 1 and 3.  For the latter IS 637 B,
-     * Table 4.3.1.4.1-1 -- and note the encoding uses only 6 bits,
-     * and hence only maps entries up to the '_' character.
-     *
-     */
-    public static final char[] ASCII_MAP = {
-        ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
-        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
-        '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
-        'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
-        '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-        'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~'};
-
-    /**
-     * Character to use when forced to encode otherwise unencodable
-     * characters, meaning those not in the respective ASCII or GSM
-     * 7-bit encoding tables.  Current choice is SPACE, which is 0x20
-     * in both the GSM-7bit and ASCII-7bit encodings.
-     */
-    static final byte UNENCODABLE_7_BIT_CHAR = 0x20;
-
-    /**
-     * Only elements between these indices in the ASCII table are printable.
-     */
-    public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
-    public static final int ASCII_NL_INDEX = 0x0A;
-    public static final int ASCII_CR_INDEX = 0x0D;
-    public static final SparseIntArray charToAscii = new SparseIntArray();
-    static {
-        for (int i = 0; i < ASCII_MAP.length; i++) {
-            charToAscii.put(ASCII_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i);
-        }
-        charToAscii.put('\n', ASCII_NL_INDEX);
-        charToAscii.put('\r', ASCII_CR_INDEX);
-    }
-
-    /*
-     * TODO(cleanup): Move this very generic functionality somewhere
-     * more general.
-     */
-    /**
-     * Given a string generate a corresponding ASCII-encoded byte
-     * array, but limited to printable characters.  If the input
-     * contains unprintable characters, return null.
-     */
-    public static byte[] stringToAscii(String str) {
-        int len = str.length();
-        byte[] result = new byte[len];
-        for (int i = 0; i < len; i++) {
-            int charCode = charToAscii.get(str.charAt(i), -1);
-            if (charCode == -1) return null;
-            result[i] = (byte)charCode;
-        }
-        return result;
-    }
-
-    /**
-     * Mapping for ASCII values less than 32 are flow control signals
-     * and not used here.
-     */
-    public static final int ASCII_MAP_BASE_INDEX = 0x20;
-    public static final int ASCII_MAP_MAX_INDEX = ASCII_MAP_BASE_INDEX + ASCII_MAP.length - 1;
-
-    /**
-     * Contains the data header of the user data
-     */
-    public SmsHeader userDataHeader;
-
-    /**
-     * Contains the data encoding type for the SMS message
-     */
-    public int msgEncoding;
-    public boolean msgEncodingSet = false;
-
-    public int msgType;
-
-    /**
-     * Number of invalid bits in the last byte of data.
-     */
-    public int paddingBits;
-
-    public int numFields;
-
-    /**
-     * Contains the user data of a SMS message
-     * (See 3GPP2 C.S0015-B, v2, 4.5.2)
-     */
-    public byte[] payload;
-    public String payloadStr;
-
-    @Override
-    public String toString() {
-        StringBuilder builder = new StringBuilder();
-        builder.append("UserData ");
-        builder.append("{ msgEncoding=" + (msgEncodingSet ? msgEncoding : "unset"));
-        builder.append(", msgType=" + msgType);
-        builder.append(", paddingBits=" + paddingBits);
-        builder.append(", numFields=" + numFields);
-        builder.append(", userDataHeader=" + userDataHeader);
-        builder.append(", payload='" + HexDump.toHexString(payload) + "'");
-        builder.append(", payloadStr='" + payloadStr + "'");
-        builder.append(" }");
-        return builder.toString();
-    }
-
-}
diff --git a/src/java/com/android/internal/telephony/cdma/sms/package.html b/src/java/com/android/internal/telephony/cdma/sms/package.html
deleted file mode 100644
index b2bc736..0000000
--- a/src/java/com/android/internal/telephony/cdma/sms/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<HTML>
-<BODY>
-Provides CDMA-specific features for text/data/PDU SMS messages
-@hide
-</BODY>
-</HTML>
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 7865bc4..3cd804d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -17,7 +17,6 @@
 package com.android.internal.telephony.dataconnection;
 
 import android.app.PendingIntent;
-import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
@@ -536,8 +535,8 @@
         return mConnectionGeneration.get();
     }
 
-    public long getInterApnDelay(boolean failFastEnabled) {
-        return mRetryManager.getInterApnDelay(failFastEnabled || isFastRetryReason());
+    long getRetryAfterDisconnectDelay() {
+        return mRetryManager.getRetryAfterDisconnectDelay();
     }
 
     public static int apnIdForType(int networkType) {
@@ -726,7 +725,7 @@
                 l.dump(fd, pw, args);
             }
             pw.decreaseIndent();
-            pw.println("mRetryManager={" + mRetryManager.toString() + "}");
+            pw.println(mRetryManager);
         }
     }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index 5e5a003..ce8318d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -17,22 +17,24 @@
 package com.android.internal.telephony.dataconnection;
 
 import android.content.Context;
+import android.hardware.radio.V1_0.ApnTypes;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.text.TextUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
 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.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Locale;
+import java.util.Objects;
 
 /**
  * This class represents a apn setting for create PDP link
@@ -42,6 +44,7 @@
     static final String LOG_TAG = "ApnSetting";
 
     private static final boolean DBG = false;
+    private static final boolean VDBG = false;
 
     static final String V2_FORMAT_REGEX = "^\\[ApnSettingV2\\]\\s*";
     static final String V3_FORMAT_REGEX = "^\\[ApnSettingV3\\]\\s*";
@@ -57,6 +60,7 @@
     public final String password;
     public final int authType;
     public final String[] types;
+    public final int typesBitmap;
     public final int id;
     public final String numeric;
     public final String protocol;
@@ -113,12 +117,13 @@
     public boolean permanentFailed = false;
 
     public ApnSetting(int id, String numeric, String carrier, String apn,
-            String proxy, String port,
-            String mmsc, String mmsProxy, String mmsPort,
-            String user, String password, int authType, String[] types,
-            String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
-            int bearerBitmask, int profileId, boolean modemCognitive, int maxConns, int waitTime,
-            int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
+                      String proxy, String port,
+                      String mmsc, String mmsProxy, String mmsPort,
+                      String user, String password, int authType, String[] types,
+                      String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
+                      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;
@@ -132,9 +137,12 @@
         this.password = password;
         this.authType = authType;
         this.types = new String[types.length];
+        int apnBitmap = 0;
         for (int i = 0; i < types.length; i++) {
-            this.types[i] = types[i].toLowerCase(Locale.ROOT);
+            this.types[i] = types[i].toLowerCase();
+            apnBitmap |= getApnBitmask(this.types[i]);
         }
+        this.typesBitmap = apnBitmap;
         this.protocol = protocol;
         this.roamingProtocol = roamingProtocol;
         this.carrierEnabled = carrierEnabled;
@@ -340,10 +348,12 @@
 
     public boolean canHandleType(String type) {
         if (!carrierEnabled) return false;
+        boolean wildcardable = true;
+        if (PhoneConstants.APN_TYPE_IA.equalsIgnoreCase(type)) wildcardable = false;
         for (String t : types) {
             // DEFAULT handles all, and HIPRI is handled by DEFAULT
             if (t.equalsIgnoreCase(type) ||
-                    t.equalsIgnoreCase(PhoneConstants.APN_TYPE_ALL) ||
+                    (wildcardable && t.equalsIgnoreCase(PhoneConstants.APN_TYPE_ALL)) ||
                     (t.equalsIgnoreCase(PhoneConstants.APN_TYPE_DEFAULT) &&
                     type.equalsIgnoreCase(PhoneConstants.APN_TYPE_HIPRI))) {
                 return true;
@@ -398,15 +408,41 @@
         return false;
     }
 
-    public static boolean isMeteredApnType(String type, Context context, int subId,
-                                           boolean isRoaming) {
+    /**
+     * Check if this APN type is metered.
+     *
+     * @param type The APN type
+     * @param phone The phone object
+     * @return True if the APN type is metered, otherwise false.
+     */
+    public static boolean isMeteredApnType(String type, Phone phone) {
+        if (phone == null) {
+            return true;
+        }
 
-        String carrierConfig = (isRoaming) ?
-                CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS :
-                CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
+        boolean isRoaming = phone.getServiceState().getDataRoaming();
+        boolean isIwlan = phone.getServiceState().getRilDataRadioTechnology()
+                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+        int subId = phone.getSubId();
+
+        String carrierConfig;
+        // First check if the device is in IWLAN mode. If yes, use the IWLAN metered APN list. Then
+        // check if the device is roaming. If yes, use the roaming metered APN list. Otherwise, use
+        // the normal metered APN list.
+        if (isIwlan) {
+            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS;
+        } else if (isRoaming) {
+            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS;
+        } else {
+            carrierConfig = CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
+        }
+
+        if (DBG) {
+            Rlog.d(LOG_TAG, "isMeteredApnType: isRoaming=" + isRoaming + ", isIwlan=" + isIwlan);
+        }
 
         CarrierConfigManager configManager = (CarrierConfigManager)
-                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
         if (configManager == null) {
             Rlog.e(LOG_TAG, "Carrier config service is not available");
             return true;
@@ -426,44 +462,49 @@
 
         HashSet<String> meteredApnSet = new HashSet<>(Arrays.asList(meteredApnTypes));
         if (DBG) {
-            Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are " +
-                    Arrays.toString(meteredApnSet.toArray()) +
-                    " isRoaming: " + isRoaming);
+            Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are "
+                    + Arrays.toString(meteredApnSet.toArray()));
         }
 
         // If all types of APN are metered, then this APN setting must be metered.
         if (meteredApnSet.contains(PhoneConstants.APN_TYPE_ALL)) {
-            if (DBG) Rlog.d(LOG_TAG, "All APN types are metered. isRoaming: " + isRoaming);
+            if (DBG) Rlog.d(LOG_TAG, "All APN types are metered.");
             return true;
         }
 
         if (meteredApnSet.contains(type)) {
-            if (DBG) Rlog.d(LOG_TAG, type + " is metered. isRoaming: " + isRoaming);
+            if (DBG) Rlog.d(LOG_TAG, type + " is metered.");
             return true;
         } else if (type.equals(PhoneConstants.APN_TYPE_ALL)) {
             // Assuming no configuration error, if at least one APN type is
             // metered, then this APN setting is metered.
             if (meteredApnSet.size() > 0) {
-                if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered. isRoaming: " +
-                        isRoaming);
+                if (DBG) Rlog.d(LOG_TAG, "APN_TYPE_ALL APN is metered.");
                 return true;
             }
         }
 
-        if (DBG) Rlog.d(LOG_TAG, type + " is not metered. isRoaming: " + isRoaming);
+        if (DBG) Rlog.d(LOG_TAG, type + " is not metered.");
         return false;
     }
 
-    public boolean isMetered(Context context, int subId, boolean isRoaming ) {
+    /**
+     * Check if this APN setting is metered.
+     *
+     * @param phone The phone object
+     * @return True if this APN setting is metered, otherwise false.
+     */
+    public boolean isMetered(Phone phone) {
+        if (phone == null) {
+            return true;
+        }
+
         for (String type : types) {
             // If one of the APN type is metered, then this APN setting is metered.
-            if (isMeteredApnType(type, context, subId, isRoaming)) {
-                if (DBG) Rlog.d(LOG_TAG, "Metered. APN = " + toString() +
-                        "isRoaming: " + isRoaming);
+            if (isMeteredApnType(type, phone)) {
                 return true;
             }
         }
-        if (DBG) Rlog.d(LOG_TAG, "Not metered. APN = " + toString() + "isRoaming: " + isRoaming);
         return false;
     }
 
@@ -477,31 +518,160 @@
 
         ApnSetting other = (ApnSetting) o;
 
-        return carrier.equals(other.carrier) &&
-                id == other.id &&
-                numeric.equals(other.numeric) &&
-                apn.equals(other.apn) &&
-                proxy.equals(other.proxy) &&
-                mmsc.equals(other.mmsc) &&
-                mmsProxy.equals(other.mmsProxy) &&
-                TextUtils.equals(mmsPort, other.mmsPort) &&
-                port.equals(other.port) &&
-                TextUtils.equals(user, other.user) &&
-                TextUtils.equals(password, other.password) &&
-                authType == other.authType &&
-                Arrays.deepEquals(types, other.types) &&
-                protocol.equals(other.protocol) &&
-                roamingProtocol.equals(other.roamingProtocol) &&
-                carrierEnabled == other.carrierEnabled &&
-                bearer == other.bearer &&
-                bearerBitmask == other.bearerBitmask &&
-                profileId == other.profileId &&
-                modemCognitive == other.modemCognitive &&
-                maxConns == other.maxConns &&
-                waitTime == other.waitTime &&
-                maxConnsTime == other.maxConnsTime &&
-                mtu == other.mtu &&
-                mvnoType.equals(other.mvnoType) &&
-                mvnoMatchData.equals(other.mvnoMatchData);
+        return carrier.equals(other.carrier)
+                && id == other.id
+                && numeric.equals(other.numeric)
+                && apn.equals(other.apn)
+                && proxy.equals(other.proxy)
+                && mmsc.equals(other.mmsc)
+                && mmsProxy.equals(other.mmsProxy)
+                && TextUtils.equals(mmsPort, other.mmsPort)
+                && port.equals(other.port)
+                && TextUtils.equals(user, other.user)
+                && TextUtils.equals(password, other.password)
+                && authType == other.authType
+                && Arrays.deepEquals(types, other.types)
+                && typesBitmap == other.typesBitmap
+                && protocol.equals(other.protocol)
+                && roamingProtocol.equals(other.roamingProtocol)
+                && carrierEnabled == other.carrierEnabled
+                && bearer == other.bearer
+                && bearerBitmask == other.bearerBitmask
+                && profileId == other.profileId
+                && modemCognitive == other.modemCognitive
+                && maxConns == other.maxConns
+                && waitTime == other.waitTime
+                && maxConnsTime == other.maxConnsTime
+                && mtu == other.mtu
+                && mvnoType.equals(other.mvnoType)
+                && mvnoMatchData.equals(other.mvnoMatchData);
+    }
+
+    /**
+     * Compare two APN settings
+     *
+     * Note: This method does not compare 'id', 'bearer', 'bearerBitmask'. We only use this for
+     * determining if tearing a data call is needed when conditions change. See
+     * cleanUpConnectionsOnUpdatedApns in DcTracker.
+     *
+     * @param o the other object to compare
+     * @param isDataRoaming True if the device is on data roaming
+     * @return True if the two APN settings are same
+     */
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public boolean equals(Object o, boolean isDataRoaming) {
+        if (!(o instanceof ApnSetting)) {
+            return false;
+        }
+
+        ApnSetting other = (ApnSetting) o;
+
+        return carrier.equals(other.carrier)
+                && numeric.equals(other.numeric)
+                && apn.equals(other.apn)
+                && proxy.equals(other.proxy)
+                && mmsc.equals(other.mmsc)
+                && mmsProxy.equals(other.mmsProxy)
+                && TextUtils.equals(mmsPort, other.mmsPort)
+                && port.equals(other.port)
+                && TextUtils.equals(user, other.user)
+                && TextUtils.equals(password, other.password)
+                && authType == other.authType
+                && Arrays.deepEquals(types, other.types)
+                && typesBitmap == other.typesBitmap
+                && (isDataRoaming || protocol.equals(other.protocol))
+                && (!isDataRoaming || roamingProtocol.equals(other.roamingProtocol))
+                && carrierEnabled == other.carrierEnabled
+                && profileId == other.profileId
+                && modemCognitive == other.modemCognitive
+                && maxConns == other.maxConns
+                && waitTime == other.waitTime
+                && maxConnsTime == other.maxConnsTime
+                && mtu == other.mtu
+                && mvnoType.equals(other.mvnoType)
+                && mvnoMatchData.equals(other.mvnoMatchData);
+    }
+
+    /**
+     * Check if neither mention DUN and are substantially similar
+     *
+     * @param other The other APN settings to compare
+     * @return True if two APN settings are similar
+     */
+    public boolean similar(ApnSetting other) {
+        return (!this.canHandleType(PhoneConstants.APN_TYPE_DUN)
+                && !other.canHandleType(PhoneConstants.APN_TYPE_DUN)
+                && Objects.equals(this.apn, other.apn)
+                && !typeSameAny(this, other)
+                && xorEquals(this.proxy, other.proxy)
+                && xorEquals(this.port, other.port)
+                && xorEquals(this.protocol, other.protocol)
+                && xorEquals(this.roamingProtocol, other.roamingProtocol)
+                && this.carrierEnabled == other.carrierEnabled
+                && this.bearerBitmask == other.bearerBitmask
+                && this.profileId == other.profileId
+                && Objects.equals(this.mvnoType, other.mvnoType)
+                && Objects.equals(this.mvnoMatchData, other.mvnoMatchData)
+                && xorEquals(this.mmsc, other.mmsc)
+                && xorEquals(this.mmsProxy, other.mmsProxy)
+                && xorEquals(this.mmsPort, other.mmsPort));
+    }
+
+    // check whether the types of two APN same (even only one type of each APN is same)
+    private boolean typeSameAny(ApnSetting first, ApnSetting second) {
+        if (VDBG) {
+            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
+            for (int index1 = 0; index1 < first.types.length; index1++) {
+                apnType1.append(first.types[index1]);
+                apnType1.append(",");
+            }
+
+            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
+            for (int index1 = 0; index1 < second.types.length; index1++) {
+                apnType2.append(second.types[index1]);
+                apnType2.append(",");
+            }
+            Rlog.d(LOG_TAG, "APN1: is " + apnType1);
+            Rlog.d(LOG_TAG, "APN2: is " + apnType2);
+        }
+
+        for (int index1 = 0; index1 < first.types.length; index1++) {
+            for (int index2 = 0; index2 < second.types.length; index2++) {
+                if (first.types[index1].equals(PhoneConstants.APN_TYPE_ALL)
+                        || second.types[index2].equals(PhoneConstants.APN_TYPE_ALL)
+                        || first.types[index1].equals(second.types[index2])) {
+                    if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return true");
+                    return true;
+                }
+            }
+        }
+
+        if (VDBG) Rlog.d(LOG_TAG, "typeSameAny: return false");
+        return false;
+    }
+
+    // equal or one is not specified
+    private boolean xorEquals(String first, String second) {
+        return (Objects.equals(first, second)
+                || TextUtils.isEmpty(first)
+                || TextUtils.isEmpty(second));
+    }
+
+    // Helper function to convert APN string into a 32-bit bitmask.
+    private static int getApnBitmask(String apn) {
+        switch (apn) {
+            case PhoneConstants.APN_TYPE_DEFAULT: return ApnTypes.DEFAULT;
+            case PhoneConstants.APN_TYPE_MMS: return ApnTypes.MMS;
+            case PhoneConstants.APN_TYPE_SUPL: return ApnTypes.SUPL;
+            case PhoneConstants.APN_TYPE_DUN: return ApnTypes.DUN;
+            case PhoneConstants.APN_TYPE_HIPRI: return ApnTypes.HIPRI;
+            case PhoneConstants.APN_TYPE_FOTA: return ApnTypes.FOTA;
+            case PhoneConstants.APN_TYPE_IMS: return ApnTypes.IMS;
+            case PhoneConstants.APN_TYPE_CBS: return ApnTypes.CBS;
+            case PhoneConstants.APN_TYPE_IA: return ApnTypes.IA;
+            case PhoneConstants.APN_TYPE_EMERGENCY: return ApnTypes.EMERGENCY;
+            case PhoneConstants.APN_TYPE_ALL: return ApnTypes.ALL;
+            default: return ApnTypes.NONE;
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java b/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
index 6934e9d..bc02b97 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataCallResponse.java
@@ -23,9 +23,7 @@
 import android.net.RouteInfo;
 import android.os.SystemProperties;
 import android.telephony.Rlog;
-
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.dataconnection.DcFailCause;
+import android.text.TextUtils;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -38,18 +36,18 @@
     private final boolean DBG = true;
     private final String LOG_TAG = "DataCallResponse";
 
-    public int version = 0;
-    public int status = 0;
-    public int cid = 0;
-    public int active = 0;
-    public String type = "";
-    public String ifname = "";
-    public String [] addresses = new String[0];
-    public String [] dnses = new String[0];
-    public String[] gateways = new String[0];
-    public int suggestedRetryTime = -1;
-    public String [] pcscf = new String[0];
-    public int mtu = PhoneConstants.UNSET_MTU;
+    public final int status;
+    public final int suggestedRetryTime;
+    public final int cid;
+    public final int active;
+    public final String type;
+    public final String ifname;
+    public final String [] addresses;
+    public final String [] dnses;
+    // TODO: Change this to final if possible.
+    public String[] gateways;
+    public final String [] pcscf;
+    public final int mtu;
 
     /**
      * Class returned by onSetupConnectionCompleted.
@@ -74,11 +72,29 @@
         }
     }
 
+    public DataCallResponse(int status, int suggestedRetryTime, int cid, int active, String type,
+                            String ifname, String addresses, String dnses, String gateways,
+                            String pcscf, int mtu) {
+        this.status = status;
+        this.suggestedRetryTime = suggestedRetryTime;
+        this.cid = cid;
+        this.active = active;
+        this.type = (type == null) ? "" : type;
+        this.ifname = (ifname == null) ? "" : ifname;
+        if ((status == DcFailCause.NONE.getErrorCode()) && TextUtils.isEmpty(ifname)) {
+            throw new RuntimeException("DataCallResponse, no ifname");
+        }
+        this.addresses = TextUtils.isEmpty(addresses) ? new String[0] : addresses.split(" ");
+        this.dnses = TextUtils.isEmpty(dnses) ? new String[0] : dnses.split(" ");
+        this.gateways = TextUtils.isEmpty(gateways) ? new String[0] : gateways.split(" ");
+        this.pcscf = TextUtils.isEmpty(pcscf) ? new String[0] : pcscf.split(" ");
+        this.mtu = mtu;
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer();
         sb.append("DataCallResponse: {")
-           .append("version=").append(version)
            .append(" status=").append(status)
            .append(" retry=").append(suggestedRetryTime)
            .append(" cid=").append(cid)
@@ -242,11 +258,7 @@
                 result = SetupResult.ERR_UnacceptableParameter;
             }
         } else {
-            if (version < 4) {
-                result = SetupResult.ERR_GetLastErrorFromRil;
-            } else {
-                result = SetupResult.ERR_RilError;
-            }
+            result = SetupResult.ERR_RilError;
         }
 
         // An error occurred so clear properties
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 8243735..6f7a13b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -16,20 +16,6 @@
 
 package com.android.internal.telephony.dataconnection;
 
-import com.android.internal.telephony.CallTracker;
-import com.android.internal.telephony.CarrierSignalAgent;
-import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.Phone;
-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.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.StateMachine;
-
 import android.app.PendingIntent;
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -39,6 +25,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkMisc;
 import android.net.ProxyInfo;
+import android.net.StringNetworkSpecifier;
 import android.os.AsyncResult;
 import android.os.Looper;
 import android.os.Message;
@@ -49,18 +36,33 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Pair;
-import android.util.Patterns;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CallTracker;
+import com.android.internal.telephony.CarrierSignalAgent;
+import com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.DctConstants;
+import com.android.internal.telephony.Phone;
+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.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Locale;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * {@hide}
@@ -102,14 +104,17 @@
         ApnContext mApnContext;
         int mProfileId;
         int mRilRat;
+        final boolean mUnmeteredUseOnly;
         Message mOnCompletedMsg;
         final int mConnectionGeneration;
 
-        ConnectionParams(ApnContext apnContext, int profileId,
-                int rilRadioTechnology, Message onCompletedMsg, int connectionGeneration) {
+        ConnectionParams(ApnContext apnContext, int profileId, int rilRadioTechnology,
+                         boolean unmeteredUseOnly,  Message onCompletedMsg,
+                         int connectionGeneration) {
             mApnContext = apnContext;
             mProfileId = profileId;
             mRilRat = rilRadioTechnology;
+            mUnmeteredUseOnly = unmeteredUseOnly;
             mOnCompletedMsg = onCompletedMsg;
             mConnectionGeneration = connectionGeneration;
         }
@@ -119,6 +124,7 @@
             return "{mTag=" + mTag + " mApnContext=" + mApnContext
                     + " mProfileId=" + mProfileId
                     + " mRat=" + mRilRat
+                    + " mUnmeteredUseOnly=" + mUnmeteredUseOnly
                     + " mOnCompletedMsg=" + msgToString(mOnCompletedMsg) + "}";
         }
     }
@@ -253,10 +259,6 @@
 
     /* Getter functions */
 
-    NetworkCapabilities getCopyNetworkCapabilities() {
-        return makeNetworkCapabilities();
-    }
-
     LinkProperties getCopyLinkProperties() {
         return new LinkProperties(mLinkProperties);
     }
@@ -439,20 +441,10 @@
 
         // Check if we should fake an error.
         if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
-            DataCallResponse response = new DataCallResponse();
-            response.version = mPhone.mCi.getRilVersion();
-            response.status = mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause.getErrorCode();
-            response.cid = 0;
-            response.active = 0;
-            response.type = "";
-            response.ifname = "";
-            response.addresses = new String[0];
-            response.dnses = new String[0];
-            response.gateways = new String[0];
-            response.suggestedRetryTime =
-                    mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime;
-            response.pcscf = new String[0];
-            response.mtu = PhoneConstants.UNSET_MTU;
+            DataCallResponse response = new DataCallResponse(
+                    mDcTesterFailBringUpAll.getDcFailBringUp().mFailCause.getErrorCode(),
+                    mDcTesterFailBringUpAll.getDcFailBringUp().mSuggestedRetryTime, 0, 0, "", "",
+                    "", "", "", "", PhoneConstants.UNSET_MTU);
 
             Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
             AsyncResult.forMessage(msg, response, null);
@@ -473,25 +465,20 @@
         Message msg = obtainMessage(EVENT_SETUP_DATA_CONNECTION_DONE, cp);
         msg.obj = cp;
 
-        int authType = mApnSetting.authType;
-        if (authType == -1) {
-            authType = TextUtils.isEmpty(mApnSetting.user) ? RILConstants.SETUP_DATA_AUTH_NONE
-                    : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
-        }
+        DataProfile dp = new DataProfile(mApnSetting, cp.mProfileId);
 
-        String protocol;
-        if (mPhone.getServiceState().getDataRoamingFromRegistration()) {
-            protocol = mApnSetting.roamingProtocol;
-        } else {
-            protocol = mApnSetting.protocol;
-        }
+        // We need to use the actual modem roaming state instead of the framework roaming state
+        // here. This flag is only passed down to ril_service for picking the correct protocol (for
+        // old modem backward compatibility).
+        boolean isModemRoaming = mPhone.getServiceState().getDataRoamingFromRegistration();
 
-        mPhone.mCi.setupDataCall(
-                cp.mRilRat,
-                cp.mProfileId,
-                mApnSetting.apn, mApnSetting.user, mApnSetting.password,
-                authType,
-                protocol, msg);
+        // Set this flag to true if the user turns on data roaming. Or if we override the roaming
+        // state in framework, we should set this flag to true as well so the modem will not reject
+        // the data call setup (because the modem actually thinks the device is roaming).
+        boolean allowRoaming = mPhone.getDataRoamingEnabled()
+                || (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
+
+        mPhone.mCi.setupDataCall(cp.mRilRat, dp, isModemRoaming, allowRoaming, msg);
     }
 
     /**
@@ -513,21 +500,12 @@
                 discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
             }
         }
-        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 {
-            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));
-        }
+
+        String str = "tearDownData. mCid=" + mCid + ", reason=" + discReason;
+        if (DBG) log(str);
+        if (apnContext != null) apnContext.requestLog(str);
+        mPhone.mCi.deactivateDataCall(mCid, discReason,
+                obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
     }
 
     private void notifyAllWithEvent(ApnContext alreadySent, int event, String reason) {
@@ -708,8 +686,6 @@
                     == CommandException.Error.RADIO_NOT_AVAILABLE) {
                 result = DataCallResponse.SetupResult.ERR_BadCommand;
                 result.mFailCause = DcFailCause.RADIO_NOT_AVAILABLE;
-            } else if ((response == null) || (response.version < 4)) {
-                result = DataCallResponse.SetupResult.ERR_GetLastErrorFromRil;
             } else {
                 result = DataCallResponse.SetupResult.ERR_RilError;
                 result.mFailCause = DcFailCause.fromInt(response.status);
@@ -841,7 +817,7 @@
      *
      * This gets set once per connection setup and is based on conditions at that time.
      * We could theoretically have dynamic capabilities but now is not a good time to
-     * experiement with that.
+     * experiment with that.
      *
      * This flag overrides the APN-based restriction capability, restricting the network
      * based on both having a NetworkRequest with restricted AND needing a restricted
@@ -876,21 +852,27 @@
 
         // Do we need a restricted network to satisfy the request?
         // Is this network metered?  If not, then don't add restricted
-        if (!mApnSetting.isMetered(mPhone.getContext(), mPhone.getSubId(),
-                mPhone.getServiceState().getDataRoaming())) {
+        if (!mApnSetting.isMetered(mPhone)) {
             return;
         }
 
         // Is data disabled?
-        mRestrictedNetworkOverride = (mDct.isDataEnabled(true) == false);
+        mRestrictedNetworkOverride = !mDct.isDataEnabled();
     }
 
-    private NetworkCapabilities makeNetworkCapabilities() {
+    NetworkCapabilities getNetworkCapabilities() {
         NetworkCapabilities result = new NetworkCapabilities();
         result.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
 
         if (mApnSetting != null) {
+            ApnSetting securedDunApn = mDct.fetchDunApn();
             for (String type : mApnSetting.types) {
+                if (!mRestrictedNetworkOverride
+                        && (mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
+                        && ApnSetting.isMeteredApnType(type, mPhone)) {
+                    log("Dropped the metered " + type + " for the unmetered data call.");
+                    continue;
+                }
                 switch (type) {
                     case PhoneConstants.APN_TYPE_ALL: {
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
@@ -900,6 +882,11 @@
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS);
                         result.addCapability(NetworkCapabilities.NET_CAPABILITY_IA);
+                        // check if this is the DUN apn as well as returned by fetchDunApn().
+                        // If yes, add DUN capability too.
+                        if (mApnSetting.equals(securedDunApn)) {
+                            result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
+                        }
                         break;
                     }
                     case PhoneConstants.APN_TYPE_DEFAULT: {
@@ -915,7 +902,6 @@
                         break;
                     }
                     case PhoneConstants.APN_TYPE_DUN: {
-                        ApnSetting securedDunApn = mDct.fetchDunApn();
                         if (securedDunApn == null || securedDunApn.equals(mApnSetting)) {
                             result.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN);
                         }
@@ -945,10 +931,12 @@
                 }
             }
 
-            // If none of the APN types associated with this APN setting is metered,
-            // then we apply NOT_METERED capability to the network.
-            if (!mApnSetting.isMetered(mPhone.getContext(), mPhone.getSubId(),
-                    mPhone.getServiceState().getDataRoaming())) {
+            // Mark NOT_METERED in the following cases,
+            // 1. All APNs in APN settings are unmetered.
+            // 2. The non-restricted data and is intended for unmetered use only.
+            if (((mConnectionParams != null && mConnectionParams.mUnmeteredUseOnly)
+                    && !mRestrictedNetworkOverride)
+                    || !mApnSetting.isMetered(mPhone)) {
                 result.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
                 mNetworkInfo.setMetered(false);
             } else {
@@ -988,15 +976,19 @@
         result.setLinkUpstreamBandwidthKbps(up);
         result.setLinkDownstreamBandwidthKbps(down);
 
-        result.setNetworkSpecifier(Integer.toString(mPhone.getSubId()));
+        result.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(mPhone.getSubId())));
 
         return result;
     }
 
-    private boolean isIpAddress(String address) {
+    /**
+     * @return {@code} true iff. {@code address} is a literal IPv4 or IPv6 address.
+     */
+    @VisibleForTesting
+    public static boolean isIpAddress(String address) {
         if (address == null) return false;
 
-        return Patterns.IP_ADDRESS.matcher(address).matches();
+        return InetAddress.isNumeric(address);
     }
 
     private DataCallResponse.SetupResult setLinkProperties(DataCallResponse response,
@@ -1066,7 +1058,7 @@
             mPhone.getServiceStateTracker().registerForDataRoamingOn(getHandler(),
                     DataConnection.EVENT_DATA_CONNECTION_ROAM_ON, null);
             mPhone.getServiceStateTracker().registerForDataRoamingOff(getHandler(),
-                    DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null);
+                    DataConnection.EVENT_DATA_CONNECTION_ROAM_OFF, null, true);
 
             // Add ourselves to the list of data connections
             mDcController.addDc(DataConnection.this);
@@ -1167,7 +1159,7 @@
                     break;
                 }
                 case DcAsyncChannel.REQ_GET_NETWORK_CAPABILITIES: {
-                    NetworkCapabilities nc = getCopyNetworkCapabilities();
+                    NetworkCapabilities nc = getNetworkCapabilities();
                     if (VDBG) log("REQ_GET_NETWORK_CAPABILITIES networkCapabilities" + nc);
                     mAc.replyToMessage(msg, DcAsyncChannel.RSP_GET_NETWORK_CAPABILITIES, nc);
                     break;
@@ -1229,7 +1221,7 @@
                             TelephonyManager.getNetworkTypeName(networkType));
                     if (mNetworkAgent != null) {
                         updateNetworkInfoSuspendState();
-                        mNetworkAgent.sendNetworkCapabilities(makeNetworkCapabilities());
+                        mNetworkAgent.sendNetworkCapabilities(getNetworkCapabilities());
                         mNetworkAgent.sendNetworkInfo(mNetworkInfo);
                         mNetworkAgent.sendLinkProperties(mLinkProperties);
                     }
@@ -1457,11 +1449,6 @@
                             tearDownData(cp);
                             transitionTo(mDisconnectingErrorCreatingConnection);
                             break;
-                        case ERR_GetLastErrorFromRil:
-                            // Request failed and this is an old RIL
-                            mPhone.mCi.getLastDataCallFailCause(
-                                    obtainMessage(EVENT_GET_LAST_FAIL_DONE, cp));
-                            break;
                         case ERR_RilError:
 
                             // Retrieve the suggested retry delay from the modem and save it.
@@ -1477,8 +1464,8 @@
                                     + " result.isRestartRadioFail=" +
                                     result.mFailCause.isRestartRadioFail(mPhone.getContext(),
                                             mPhone.getSubId())
-                                    + " result.isPermanentFail=" +
-                                    mDct.isPermanentFail(result.mFailCause);
+                                    + " isPermanentFailure=" +
+                                    mDct.isPermanentFailure(result.mFailCause);
                             if (DBG) log(str);
                             if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
 
@@ -1559,34 +1546,23 @@
 
             // verify and get updated information in case these things
             // are obsolete
-            {
-                ServiceState ss = mPhone.getServiceState();
-                final int networkType = ss.getDataNetworkType();
-                if (mNetworkInfo.getSubtype() != networkType) {
-                    log("DcActiveState with incorrect subtype (" + mNetworkInfo.getSubtype() +
-                            ", " + networkType + "), updating.");
-                }
-                mNetworkInfo.setSubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
-                final boolean roaming = ss.getDataRoaming();
-                if (roaming != mNetworkInfo.isRoaming()) {
-                    log("DcActiveState with incorrect roaming (" + mNetworkInfo.isRoaming() +
-                            ", " + roaming +"), updating.");
-                }
-                mNetworkInfo.setRoaming(roaming);
+            ServiceState ss = mPhone.getServiceState();
+            final int networkType = ss.getDataNetworkType();
+            if (mNetworkInfo.getSubtype() != networkType) {
+                log("DcActiveState with incorrect subtype (" + mNetworkInfo.getSubtype()
+                        + ", " + networkType + "), updating.");
+            }
+            mNetworkInfo.setSubtype(networkType, TelephonyManager.getNetworkTypeName(networkType));
+            final boolean roaming = ss.getDataRoaming();
+            if (roaming != mNetworkInfo.isRoaming()) {
+                log("DcActiveState with incorrect roaming (" + mNetworkInfo.isRoaming()
+                        + ", " + roaming + "), updating.");
             }
 
-            boolean createNetworkAgent = true;
-            // If a disconnect is already pending, avoid notifying all of connected
-            if (hasMessages(EVENT_DISCONNECT) ||
-                    hasMessages(EVENT_DISCONNECT_ALL) ||
-                    hasDeferredMessages(EVENT_DISCONNECT) ||
-                    hasDeferredMessages(EVENT_DISCONNECT_ALL)) {
-                log("DcActiveState: skipping notifyAllOfConnected()");
-                createNetworkAgent = false;
-            } else {
-                // If we were retrying there maybe more than one, otherwise they'll only be one.
-                notifyAllOfConnected(Phone.REASON_CONNECTED);
-            }
+            mNetworkInfo.setRoaming(roaming);
+
+            // 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);
@@ -1604,18 +1580,17 @@
 
             final NetworkMisc misc = new NetworkMisc();
             final CarrierSignalAgent carrierSignalAgent = mPhone.getCarrierSignalAgent();
-            if(carrierSignalAgent.hasRegisteredCarrierSignalReceivers()) {
+            if (carrierSignalAgent.hasRegisteredReceivers(TelephonyIntents
+                    .ACTION_CARRIER_SIGNAL_REDIRECTED)) {
                 // carrierSignal Receivers will place the carrier-specific provisioning notification
                 misc.provisioningNotificationDisabled = true;
             }
             misc.subscriberId = mPhone.getSubscriberId();
 
-            if (createNetworkAgent) {
-                setNetworkRestriction();
-                mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
-                        "DcNetworkAgent", mNetworkInfo, makeNetworkCapabilities(), mLinkProperties,
-                        50, misc);
-            }
+            setNetworkRestriction();
+            mNetworkAgent = new DcNetworkAgent(getHandler().getLooper(), mPhone.getContext(),
+                    "DcNetworkAgent", mNetworkInfo, getNetworkCapabilities(), mLinkProperties,
+                    50, misc);
         }
 
         @Override
@@ -1735,7 +1710,7 @@
                     } else {
                         final ArrayList<Integer> capInfo = (ArrayList<Integer>)ar.result;
                         final int lceBwDownKbps = capInfo.get(0);
-                        NetworkCapabilities nc = makeNetworkCapabilities();
+                        NetworkCapabilities nc = getNetworkCapabilities();
                         if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
                             nc.setLinkDownstreamBandwidthKbps(lceBwDownKbps);
                             if (mNetworkAgent != null) {
@@ -2088,7 +2063,7 @@
                 + " mLastFailCause=" + mLastFailCause
                 + " mTag=" + mTag
                 + " mLinkProperties=" + mLinkProperties
-                + " linkCapabilities=" + makeNetworkCapabilities()
+                + " linkCapabilities=" + getNetworkCapabilities()
                 + " mRestrictedNetworkOverride=" + mRestrictedNetworkOverride;
     }
 
@@ -2138,7 +2113,7 @@
         pw.flush();
         pw.println(" mDataRegState=" + mDataRegState);
         pw.println(" mRilRat=" + mRilRat);
-        pw.println(" mNetworkCapabilities=" + makeNetworkCapabilities());
+        pw.println(" mNetworkCapabilities=" + getNetworkCapabilities());
         pw.println(" mCreateTime=" + TimeUtils.logTimeOfDay(mCreateTime));
         pw.println(" mLastFailTime=" + TimeUtils.logTimeOfDay(mLastFailTime));
         pw.println(" mLastFailCause=" + mLastFailCause);
@@ -2149,4 +2124,3 @@
         pw.flush();
     }
 }
-
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
new file mode 100644
index 0000000..e7afdff
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnectionReasons.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import java.util.HashSet;
+
+/**
+ * The class to describe the reasons of allowing or disallowing to establish a data connection.
+ */
+public class DataConnectionReasons {
+    private HashSet<DataDisallowedReasonType> mDataDisallowedReasonSet = new HashSet<>();
+    private DataAllowedReasonType mDataAllowedReason = DataAllowedReasonType.NONE;
+
+    public DataConnectionReasons() {}
+
+    void add(DataDisallowedReasonType reason) {
+        // Adding a disallowed reason will clean up the allowed reason because they are
+        // mutual exclusive.
+        mDataAllowedReason = DataAllowedReasonType.NONE;
+        mDataDisallowedReasonSet.add(reason);
+    }
+
+    void add(DataAllowedReasonType reason) {
+        // Adding an allowed reason will clean up the disallowed reasons because they are
+        // mutual exclusive.
+        mDataDisallowedReasonSet.clear();
+
+        // Only higher priority allowed reason can overwrite the old one. See
+        // DataAllowedReasonType for the oder.
+        if (reason.ordinal() > mDataAllowedReason.ordinal()) {
+            mDataAllowedReason = reason;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder reasonStr = new StringBuilder();
+        if (mDataDisallowedReasonSet.size() > 0) {
+            reasonStr.append("Data disallowed, reasons:");
+            for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
+                reasonStr.append(" ").append(reason);
+            }
+        } else {
+            reasonStr.append("Data allowed, reason:");
+            reasonStr.append(" ").append(mDataAllowedReason);
+        }
+        return reasonStr.toString();
+    }
+
+    void copyFrom(DataConnectionReasons reasons) {
+        this.mDataDisallowedReasonSet = reasons.mDataDisallowedReasonSet;
+        this.mDataAllowedReason = reasons.mDataAllowedReason;
+    }
+
+    boolean allowed() {
+        return mDataDisallowedReasonSet.size() == 0;
+    }
+
+    boolean contains(DataDisallowedReasonType reason) {
+        return mDataDisallowedReasonSet.contains(reason);
+    }
+
+    /**
+     * Check if only one disallowed reason prevent data connection.
+     *
+     * @param reason The given reason to check
+     * @return True if the given reason is the only one that prevents data connection
+     */
+    public boolean containsOnly(DataDisallowedReasonType reason) {
+        return mDataDisallowedReasonSet.size() == 1 && contains(reason);
+    }
+
+    boolean contains(DataAllowedReasonType reason) {
+        return reason == mDataAllowedReason;
+    }
+
+    boolean containsHardDisallowedReasons() {
+        for (DataDisallowedReasonType reason : mDataDisallowedReasonSet) {
+            if (reason.isHardReason()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Disallowed reasons. There could be multiple reasons if data connection is not allowed.
+    public enum DataDisallowedReasonType {
+        // Soft failure reasons. Normally the reasons from users or policy settings.
+        DATA_DISABLED(false),                   // Data is disabled by the user or policy.
+        ROAMING_DISABLED(false),                // Data roaming is disabled by the user.
+
+        // Belows are all hard failure reasons.
+        NOT_ATTACHED(true),
+        RECORD_NOT_LOADED(true),
+        INVALID_PHONE_STATE(true),
+        CONCURRENT_VOICE_DATA_NOT_ALLOWED(true),
+        PS_RESTRICTED(true),
+        UNDESIRED_POWER_STATE(true),
+        INTERNAL_DATA_DISABLED(true),
+        DEFAULT_DATA_UNSELECTED(true),
+        RADIO_DISABLED_BY_CARRIER(true),
+        APN_NOT_CONNECTABLE(true),
+        ON_IWLAN(true),
+        IN_ECBM(true);
+
+        private boolean mIsHardReason;
+
+        boolean isHardReason() {
+            return mIsHardReason;
+        }
+
+        DataDisallowedReasonType(boolean isHardReason) {
+            mIsHardReason = isHardReason;
+        }
+    }
+
+    // Data allowed reasons. There will be only one reason if data is allowed.
+    enum DataAllowedReasonType {
+        // Note that unlike disallowed reasons, we only have one allowed reason every time
+        // when we check data is allowed or not. The order of these allowed reasons is very
+        // important. The lower ones take precedence over the upper ones.
+        NONE,
+        NORMAL,
+        UNMETERED_APN,
+        RESTRICTED_REQUEST,
+        EMERGENCY_APN,
+    }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
index a052f6a..a8bffd3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataEnabledSettings.java
@@ -63,10 +63,17 @@
 
     private final RegistrantList mDataEnabledChangedRegistrants = new RegistrantList();
 
+    @Override
+    public String toString() {
+        return "[mInternalDataEnabled=" + mInternalDataEnabled + ", mUserDataEnabled="
+                + mUserDataEnabled + ", mPolicyDataEnabled=" + mPolicyDataEnabled
+                + ", mCarrierDataEnabled=" + mCarrierDataEnabled + "]";
+    }
+
     public synchronized void setInternalDataEnabled(boolean enabled) {
-        boolean prevDataEnabled = isDataEnabled(true);
+        boolean prevDataEnabled = isDataEnabled();
         mInternalDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled(true)) {
+        if (prevDataEnabled != isDataEnabled()) {
             notifyDataEnabledChanged(!prevDataEnabled, REASON_INTERNAL_DATA_ENABLED);
         }
     }
@@ -75,9 +82,9 @@
     }
 
     public synchronized void setUserDataEnabled(boolean enabled) {
-        boolean prevDataEnabled = isDataEnabled(true);
+        boolean prevDataEnabled = isDataEnabled();
         mUserDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled(true)) {
+        if (prevDataEnabled != isDataEnabled()) {
             notifyDataEnabledChanged(!prevDataEnabled, REASON_USER_DATA_ENABLED);
         }
     }
@@ -86,9 +93,9 @@
     }
 
     public synchronized void setPolicyDataEnabled(boolean enabled) {
-        boolean prevDataEnabled = isDataEnabled(true);
+        boolean prevDataEnabled = isDataEnabled();
         mPolicyDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled(true)) {
+        if (prevDataEnabled != isDataEnabled()) {
             notifyDataEnabledChanged(!prevDataEnabled, REASON_POLICY_DATA_ENABLED);
         }
     }
@@ -97,9 +104,9 @@
     }
 
     public synchronized void setCarrierDataEnabled(boolean enabled) {
-        boolean prevDataEnabled = isDataEnabled(true);
+        boolean prevDataEnabled = isDataEnabled();
         mCarrierDataEnabled = enabled;
-        if (prevDataEnabled != isDataEnabled(true)) {
+        if (prevDataEnabled != isDataEnabled()) {
             notifyDataEnabledChanged(!prevDataEnabled, REASON_DATA_ENABLED_BY_CARRIER);
         }
     }
@@ -107,11 +114,9 @@
         return mCarrierDataEnabled;
     }
 
-    public synchronized boolean isDataEnabled(boolean checkUserDataEnabled) {
-        return (mInternalDataEnabled
-                && (!checkUserDataEnabled || mUserDataEnabled)
-                && (!checkUserDataEnabled || mPolicyDataEnabled)
-                && (!checkUserDataEnabled || mCarrierDataEnabled));
+    public synchronized boolean isDataEnabled() {
+        return (mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled
+                && mCarrierDataEnabled);
     }
 
     private void notifyDataEnabledChanged(boolean enabled, int reason) {
@@ -120,7 +125,7 @@
 
     public void registerForDataEnabledChanged(Handler h, int what, Object obj) {
         mDataEnabledChangedRegistrants.addUnique(h, what, obj);
-        notifyDataEnabledChanged(isDataEnabled(true), REASON_REGISTERED);
+        notifyDataEnabledChanged(isDataEnabled(), REASON_REGISTERED);
     }
 
     public void unregisterForDataEnabledChanged(Handler h) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
index 86beaad..48a8107 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
@@ -16,8 +16,10 @@
 
 package com.android.internal.telephony.dataconnection;
 
-import android.os.Parcel;
 import android.telephony.ServiceState;
+import android.text.TextUtils;
+
+import com.android.internal.telephony.RILConstants;
 
 public class DataProfile {
 
@@ -51,15 +53,37 @@
     public final int waitTime;
     //true to enable the profile, false to disable
     public final boolean enabled;
-
+    //supported APN types bitmap. See RIL_ApnTypes for the value of each bit.
+    public final int supportedApnTypesBitmap;
+    //one of the PDP_type values in TS 27.007 section 10.1.1 used on roaming network.
+    //For example, "IP", "IPV6", "IPV4V6", or "PPP".
+    public final String roamingProtocol;
+    //The bearer bitmap. See RIL_RadioAccessFamily for the value of each bit.
+    public final int bearerBitmap;
+    //maximum transmission unit (MTU) size in bytes
+    public final int mtu;
+    //the MVNO type: possible values are "imsi", "gid", "spn"
+    public final String mvnoType;
+    //MVNO match data. For example, SPN: A MOBILE, BEN NL, ...
+    //IMSI: 302720x94, 2060188, ...
+    //GID: 4E, 33, ...
+    public final String mvnoMatchData;
+    //indicating the data profile was sent to the modem through setDataProfile earlier.
+    public final boolean modemCognitive;
 
     DataProfile(int profileId, String apn, String protocol, int authType,
-            String user, String password, int type, int maxConnsTime, int maxConns,
-            int waitTime, boolean enabled) {
+                String user, String password, int type, int maxConnsTime, int maxConns,
+                int waitTime, boolean enabled, int supportedApnTypesBitmap, String roamingProtocol,
+                int bearerBitmap, int mtu, String mvnoType, String mvnoMatchData,
+                boolean modemCognitive) {
 
         this.profileId = profileId;
         this.apn = apn;
         this.protocol = protocol;
+        if (authType == -1) {
+            authType = TextUtils.isEmpty(user) ? RILConstants.SETUP_DATA_AUTH_NONE
+                    : RILConstants.SETUP_DATA_AUTH_PAP_CHAP;
+        }
         this.authType = authType;
         this.user = user;
         this.password = password;
@@ -68,49 +92,42 @@
         this.maxConns = maxConns;
         this.waitTime = waitTime;
         this.enabled = enabled;
+
+        this.supportedApnTypesBitmap = supportedApnTypesBitmap;
+        this.roamingProtocol = roamingProtocol;
+        this.bearerBitmap = bearerBitmap;
+        this.mtu = mtu;
+        this.mvnoType = mvnoType;
+        this.mvnoMatchData = mvnoMatchData;
+        this.modemCognitive = modemCognitive;
     }
 
-    public DataProfile(ApnSetting apn, boolean isRoaming) {
-        this(apn.profileId, apn.apn, isRoaming? apn.roamingProtocol : apn.protocol,
+    public DataProfile(ApnSetting apn) {
+        this(apn, apn.profileId);
+    }
+
+    public DataProfile(ApnSetting apn, int profileId) {
+        this(profileId, apn.apn, apn.protocol,
                 apn.authType, apn.user, apn.password, apn.bearerBitmask == 0
                         ? TYPE_COMMON : (ServiceState.bearerBitmapHasCdma(apn.bearerBitmask)
                         ? TYPE_3GPP2 : TYPE_3GPP),
-                apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled);
-    }
-
-    public static Parcel toParcel(Parcel pc, DataProfile[] dps) {
-
-        if(pc == null) {
-            return null;
-        }
-
-        pc.writeInt(dps.length);
-        for(int i = 0; i < dps.length; i++) {
-            pc.writeInt(dps[i].profileId);
-            pc.writeString(dps[i].apn);
-            pc.writeString(dps[i].protocol);
-            pc.writeInt(dps[i].authType);
-            pc.writeString(dps[i].user);
-            pc.writeString(dps[i].password);
-            pc.writeInt(dps[i].type);
-            pc.writeInt(dps[i].maxConnsTime);
-            pc.writeInt(dps[i].maxConns);
-            pc.writeInt(dps[i].waitTime);
-            pc.writeInt(dps[i].enabled ? 1 : 0);
-        }
-        return pc;
+                apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled, apn.typesBitmap,
+                apn.roamingProtocol, apn.bearerBitmask, apn.mtu, apn.mvnoType, apn.mvnoMatchData,
+                apn.modemCognitive);
     }
 
     @Override
     public String toString() {
-        return "DataProfile " + profileId + "/" + apn + "/" + protocol + "/" + authType
+        return "DataProfile=" + profileId + "/" + apn + "/" + protocol + "/" + authType
                 + "/" + user + "/" + password + "/" + type + "/" + maxConnsTime
-                + "/" + maxConns + "/" + waitTime + "/" + enabled;
+                + "/" + maxConns + "/" + waitTime + "/" + enabled + "/" + supportedApnTypesBitmap
+                + "/" + roamingProtocol + "/" + bearerBitmap + "/" + mtu + "/" + mvnoType + "/"
+                + mvnoMatchData + "/" + modemCognitive;
     }
 
     @Override
     public boolean equals(Object o) {
         if (o instanceof DataProfile == false) return false;
-        return (toString().equals(o.toString()));
+        return (o == this || toString().equals(o.toString()));
     }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
index 13ab30b..67d91d3 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcAsyncChannel.java
@@ -16,16 +16,16 @@
 
 package com.android.internal.telephony.dataconnection;
 
-import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
-import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
-
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.net.ProxyInfo;
 import android.os.Message;
 
+import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
+import com.android.internal.telephony.dataconnection.DataConnection.DisconnectParams;
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
 /**
  * AsyncChannel to a DataConnection
  */
@@ -319,7 +319,7 @@
      * Evaluate RSP_GET_NETWORK_CAPABILITIES
      *
      * @param response
-     * @return NetworkCapabilites, maybe null.
+     * @return NetworkCapabilities, maybe null.
      */
     public NetworkCapabilities rspNetworkCapabilities(Message response) {
         NetworkCapabilities retVal = (NetworkCapabilities) response.obj;
@@ -342,7 +342,7 @@
                 value = null;
             }
         } else {
-            value = mDc.getCopyNetworkCapabilities();
+            value = mDc.getNetworkCapabilities();
         }
         return value;
     }
@@ -357,23 +357,29 @@
 
     /**
      * Bring up a connection to the apn and return an AsyncResult in onCompletedMsg.
-     * Used for cellular networks that use Acesss Point Names (APN) such
+     * Used for cellular networks that use Access Point Names (APN) such
      * as GSM networks.
      *
      * @param apnContext is the Access Point Name to bring up a connection to
-     * @param profileId for the conneciton
+     * @param profileId for the connection
+     * @param rilRadioTechnology Radio technology for the data connection
+     * @param unmeteredUseOnly Indicates the data connection can only used for unmetered purposes
      * @param onCompletedMsg is sent with its msg.obj as an AsyncResult object.
-     *        With AsyncResult.userObj set to the original msg.obj,
-     *        AsyncResult.result = FailCause and AsyncResult.exception = Exception().
+     *                       With AsyncResult.userObj set to the original msg.obj,
+     *                       AsyncResult.result = FailCause and AsyncResult.exception = Exception().
+     * @param connectionGeneration used to track a single connection request so disconnects can get
+     *                             ignored if obsolete.
      */
     public void bringUp(ApnContext apnContext, int profileId, int rilRadioTechnology,
-                        Message onCompletedMsg, int connectionGeneration) {
+                        boolean unmeteredUseOnly, Message onCompletedMsg,
+                        int connectionGeneration) {
         if (DBG) {
-            log("bringUp: apnContext=" + apnContext + " onCompletedMsg=" + onCompletedMsg);
+            log("bringUp: apnContext=" + apnContext + "unmeteredUseOnly=" + unmeteredUseOnly
+                    + " onCompletedMsg=" + onCompletedMsg);
         }
         sendMessage(DataConnection.EVENT_CONNECT,
-                new ConnectionParams(apnContext, profileId, rilRadioTechnology, onCompletedMsg,
-                        connectionGeneration));
+                new ConnectionParams(apnContext, profileId, rilRadioTechnology, unmeteredUseOnly,
+                        onCompletedMsg, connectionGeneration));
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index 02cd47b..291d6f5 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -18,15 +18,15 @@
 
 import android.content.Context;
 import android.net.LinkAddress;
-import android.net.NetworkUtils;
 import android.net.LinkProperties.CompareResult;
+import android.net.NetworkUtils;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
-import android.telephony.TelephonyManager;
 import android.telephony.PhoneStateListener;
 import android.telephony.Rlog;
+import android.telephony.TelephonyManager;
 
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.Phone;
@@ -159,7 +159,7 @@
         public void enter() {
             mPhone.mCi.registerForRilConnected(getHandler(),
                     DataConnection.EVENT_RIL_CONNECTED, null);
-            mPhone.mCi.registerForDataNetworkStateChanged(getHandler(),
+            mPhone.mCi.registerForDataCallListChanged(getHandler(),
                     DataConnection.EVENT_DATA_STATE_CHANGED, null);
             if (Build.IS_DEBUGGABLE) {
                 mDcTesterDeactivateAll =
@@ -171,7 +171,7 @@
         public void exit() {
             if (mPhone != null) {
                 mPhone.mCi.unregisterForRilConnected(getHandler());
-                mPhone.mCi.unregisterForDataNetworkStateChanged(getHandler());
+                mPhone.mCi.unregisterForDataCallListChanged(getHandler());
             }
             if (mDcTesterDeactivateAll != null) {
                 mDcTesterDeactivateAll.dispose();
@@ -275,7 +275,7 @@
                                             + failCause);
                                 }
                                 mDct.sendRestartRadio();
-                            } else if (mDct.isPermanentFail(failCause)) {
+                            } else if (mDct.isPermanentFailure(failCause)) {
                                 if (DBG) {
                                     log("onDataStateChanged: inactive, add to cleanup list. "
                                             + "failCause=" + failCause);
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java b/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java
index f34076a..b3762e5 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcFailCause.java
@@ -19,7 +19,9 @@
 import android.content.res.Resources;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+
 import java.util.HashMap;
+import java.util.HashSet;
 
 /**
  * Returned as the reason for a connection failure as defined
@@ -105,8 +107,8 @@
     // specified in ril.h
     REGISTRATION_FAIL(-1),
     GPRS_REGISTRATION_FAIL(-2),
-    SIGNAL_LOST(-3),
-    PREF_RADIO_TECH_CHANGED(-4),            /* no retry */
+    SIGNAL_LOST(-3),                        /* no retry */
+    PREF_RADIO_TECH_CHANGED(-4),
     RADIO_POWER_OFF(-5),                    /* no retry */
     TETHERED_CALL_ACTIVE(-6),               /* no retry */
     ERROR_UNSPECIFIED(0xFFFF),
@@ -129,6 +131,12 @@
         }
     }
 
+    /**
+     * Map of subId -> set of data call setup permanent failure for the carrier.
+     */
+    private static final HashMap<Integer, HashSet<DcFailCause>> sPermanentFailureCache =
+            new HashMap<>();
+
     DcFailCause(int errorCode) {
         mErrorCode = errorCode;
     }
@@ -160,16 +168,62 @@
         return false;
     }
 
-    public boolean isPermanentFail() {
-        return (this == OPERATOR_BARRED) || (this == MISSING_UNKNOWN_APN) ||
-                (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
-                (this == ACTIVATION_REJECT_GGSN) || (this == SERVICE_OPTION_NOT_SUPPORTED) ||
-                (this == SERVICE_OPTION_NOT_SUBSCRIBED) || (this == NSAPI_IN_USE) ||
-                (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
-                (this == PROTOCOL_ERRORS) ||
-                (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
-                (this == RADIO_NOT_AVAILABLE) || (this == UNACCEPTABLE_NETWORK_PARAMETER) ||
-                (this == SIGNAL_LOST);
+    public boolean isPermanentFailure(Context context, int subId) {
+
+        synchronized (sPermanentFailureCache) {
+
+            HashSet<DcFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+
+            // In case of cache miss, we need to look up the settings from carrier config.
+            if (permanentFailureSet == null) {
+                // Retrieve the permanent failure from carrier config
+                CarrierConfigManager configManager = (CarrierConfigManager)
+                        context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                if (configManager != null) {
+                    PersistableBundle b = configManager.getConfigForSubId(subId);
+                    if (b != null) {
+                        String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
+                                KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
+
+                        if (permanentFailureStrings != null) {
+                            permanentFailureSet = new HashSet<>();
+                            for (String failure : permanentFailureStrings) {
+                                permanentFailureSet.add(DcFailCause.valueOf(failure));
+                            }
+                        }
+                    }
+                }
+
+                // If we are not able to find the configuration from carrier config, use the default
+                // ones.
+                if (permanentFailureSet == null) {
+                    permanentFailureSet = new HashSet<DcFailCause>() {
+                        {
+                            add(OPERATOR_BARRED);
+                            add(MISSING_UNKNOWN_APN);
+                            add(UNKNOWN_PDP_ADDRESS_TYPE);
+                            add(USER_AUTHENTICATION);
+                            add(ACTIVATION_REJECT_GGSN);
+                            add(SERVICE_OPTION_NOT_SUPPORTED);
+                            add(SERVICE_OPTION_NOT_SUBSCRIBED);
+                            add(NSAPI_IN_USE);
+                            add(ONLY_IPV4_ALLOWED);
+                            add(ONLY_IPV6_ALLOWED);
+                            add(PROTOCOL_ERRORS);
+                            add(RADIO_POWER_OFF);
+                            add(TETHERED_CALL_ACTIVE);
+                            add(RADIO_NOT_AVAILABLE);
+                            add(UNACCEPTABLE_NETWORK_PARAMETER);
+                            add(SIGNAL_LOST);
+                        }
+                    };
+                }
+
+                sPermanentFailureCache.put(subId, permanentFailureSet);
+            }
+
+            return permanentFailureSet.contains(this);
+        }
     }
 
     public boolean isEventLoggable() {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 2cd7370..dec6486 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -75,15 +75,19 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.CarrierActionAgent;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RILConstants;
-import com.android.internal.telephony.ServiceStateTracker;
+import com.android.internal.telephony.SettingsObserver;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataAllowedReasonType;
+import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.UiccController;
@@ -96,13 +100,9 @@
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map.Entry;
-import java.util.Objects;
 import java.util.PriorityQueue;
 import java.util.Set;
-
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -149,8 +149,6 @@
     private static final boolean DATA_STALL_SUSPECTED = true;
     private static final boolean DATA_STALL_NOT_SUSPECTED = false;
 
-    private String RADIO_RESET_PROPERTY = "gsm.radioreset";
-
     private static final String INTENT_RECONNECT_ALARM =
             "com.android.internal.telephony.data-reconnect";
     private static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
@@ -160,57 +158,6 @@
     private static final String INTENT_DATA_STALL_ALARM =
             "com.android.internal.telephony.data-stall";
 
-    @VisibleForTesting
-    public static class DataAllowFailReason {
-        private HashSet<DataAllowFailReasonType> mDataAllowFailReasonSet = new HashSet<>();
-
-        public void addDataAllowFailReason(DataAllowFailReasonType type) {
-            mDataAllowFailReasonSet.add(type);
-        }
-
-        public String getDataAllowFailReason() {
-            StringBuilder failureReason = new StringBuilder();
-            failureReason.append("isDataAllowed: No");
-            for(DataAllowFailReasonType reason : mDataAllowFailReasonSet) {
-                failureReason.append(reason.mFailReasonStr);
-            }
-            return failureReason.toString();
-        }
-
-        public boolean isFailForSingleReason(DataAllowFailReasonType failReasonType) {
-            return (mDataAllowFailReasonSet.size() == 1) &&
-                    (mDataAllowFailReasonSet.contains(failReasonType));
-        }
-
-        public void clearAllReasons() {
-            mDataAllowFailReasonSet.clear();
-        }
-
-        public boolean isFailed() {
-            return mDataAllowFailReasonSet.size() > 0;
-        }
-    }
-
-    @VisibleForTesting
-    public enum DataAllowFailReasonType {
-        NOT_ATTACHED(" - Not attached"),
-        RECORD_NOT_LOADED(" - SIM not loaded"),
-        ROAMING_DISABLED(" - Roaming and data roaming not enabled"),
-        INVALID_PHONE_STATE(" - PhoneState is not idle"),
-        CONCURRENT_VOICE_DATA_NOT_ALLOWED(" - Concurrent voice and data not allowed"),
-        PS_RESTRICTED(" - mIsPsRestricted= true"),
-        UNDESIRED_POWER_STATE(" - desiredPowerState= false"),
-        INTERNAL_DATA_DISABLED(" - mInternalDataEnabled= false"),
-        DEFAULT_DATA_UNSELECTED(" - defaultDataSelected= false"),
-        RADIO_DISABLED_BY_CARRIER(" - powerStateFromCarrier= false");
-
-        public String mFailReasonStr;
-
-        DataAllowFailReasonType(String reason) {
-            mFailReasonStr = reason;
-        }
-    }
-
     private DcTesterFailBringUpAll mDcTesterFailBringUpAll;
     private DcController mDcc;
 
@@ -274,6 +221,7 @@
             String action = intent.getAction();
 
             if (action.equals(Intent.ACTION_SCREEN_ON)) {
+                // TODO: Evaluate hooking this up with DeviceStateMonitor
                 if (DBG) log("screen on");
                 mIsScreenOn = true;
                 stopNetStatPoll();
@@ -312,6 +260,10 @@
                     log("WIFI_STATE_CHANGED_ACTION: enabled=" + enabled
                             + " mIsWifiConnected=" + mIsWifiConnected);
                 }
+            } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+                if (mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded()) {
+                    setDefaultDataRoamingEnabled();
+                }
             } else {
                 if (DBG) log("onReceive: Unknown action=" + action);
             }
@@ -363,46 +315,6 @@
                 }
             };
 
-    private static class SettingsObserver extends ContentObserver {
-        final private HashMap<Uri, Integer> mUriEventMap;
-        final private Context mContext;
-        final private Handler mHandler;
-        final private static String TAG = "DcTracker.SettingsObserver";
-
-        SettingsObserver(Context context, Handler handler) {
-            super(null);
-            mUriEventMap = new HashMap<Uri, Integer>();
-            mContext = context;
-            mHandler = handler;
-        }
-
-        void observe(Uri uri, int what) {
-            mUriEventMap.put(uri, what);
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(uri, false, this);
-        }
-
-        void unobserve() {
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.unregisterContentObserver(this);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            Rlog.e(TAG, "Should never be reached.");
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            final Integer what = mUriEventMap.get(uri);
-            if (what != null) {
-                mHandler.obtainMessage(what.intValue()).sendToTarget();
-            } else {
-                Rlog.e(TAG, "No matching event to send for URI=" + uri);
-            }
-        }
-    }
-
     private final SettingsObserver mSettingsObserver;
 
     private void registerSettingsObserver() {
@@ -464,13 +376,19 @@
     }
 
     private void onActionIntentReconnectAlarm(Intent intent) {
-        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
-        String apnType = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
+        Message msg = obtainMessage(DctConstants.EVENT_DATA_RECONNECT);
+        msg.setData(intent.getExtras());
+        sendMessage(msg);
+    }
+
+    private void onDataReconnect(Bundle bundle) {
+        String reason = bundle.getString(INTENT_RECONNECT_ALARM_EXTRA_REASON);
+        String apnType = bundle.getString(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
 
         int phoneSubId = mPhone.getSubId();
-        int currSubId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+        int currSubId = bundle.getInt(PhoneConstants.SUBSCRIPTION_KEY,
                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        log("onActionIntentReconnectAlarm: currSubId = " + currSubId + " phoneSubId=" + phoneSubId);
+        log("onDataReconnect: currSubId = " + currSubId + " phoneSubId=" + phoneSubId);
 
         // Stop reconnect if not current subId is not correct.
         // FIXME STOPSHIP - phoneSubId is coming up as -1 way after boot and failing this?
@@ -482,33 +400,33 @@
         ApnContext apnContext = mApnContexts.get(apnType);
 
         if (DBG) {
-            log("onActionIntentReconnectAlarm: mState=" + mState + " reason=" + reason +
-                    " apnType=" + apnType + " apnContext=" + apnContext +
-                    " mDataConnectionAsyncChannels=" + mDataConnectionAcHashMap);
+            log("onDataReconnect: mState=" + mState + " reason=" + reason + " apnType=" + apnType
+                    + " apnContext=" + apnContext + " mDataConnectionAsyncChannels="
+                    + mDataConnectionAcHashMap);
         }
 
         if ((apnContext != null) && (apnContext.isEnabled())) {
             apnContext.setReason(reason);
             DctConstants.State apnContextState = apnContext.getState();
             if (DBG) {
-                log("onActionIntentReconnectAlarm: apnContext state=" + apnContextState);
+                log("onDataReconnect: apnContext state=" + apnContextState);
             }
             if ((apnContextState == DctConstants.State.FAILED)
                     || (apnContextState == DctConstants.State.IDLE)) {
                 if (DBG) {
-                    log("onActionIntentReconnectAlarm: state is FAILED|IDLE, disassociate");
+                    log("onDataReconnect: state is FAILED|IDLE, disassociate");
                 }
                 DcAsyncChannel dcac = apnContext.getDcAc();
                 if (dcac != null) {
                     if (DBG) {
-                        log("onActionIntentReconnectAlarm: tearDown apnContext=" + apnContext);
+                        log("onDataReconnect: tearDown apnContext=" + apnContext);
                     }
                     dcac.tearDown(apnContext, "", null);
                 }
                 apnContext.setDataConnectionAc(null);
                 apnContext.setState(DctConstants.State.IDLE);
             } else {
-                if (DBG) log("onActionIntentReconnectAlarm: keep associated");
+                if (DBG) log("onDataReconnect: keep associated");
             }
             // TODO: IF already associated should we send the EVENT_TRY_SETUP_DATA???
             sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
@@ -614,6 +532,15 @@
     private boolean mMeteredApnDisabled = false;
 
     /**
+     * int to remember whether has setDataProfiles and with roaming or not.
+     * 0: default, has never set data profile
+     * 1: has set data profile with home protocol
+     * 2: has set data profile with roaming protocol
+     * This is not needed once RIL command is updated to support both home and roaming protocol.
+     */
+    private int mSetDataProfileStatus = 0;
+
+    /**
      * Handles changes to the APN db.
      */
     private class ApnChangeObserver extends ContentObserver {
@@ -744,7 +671,7 @@
         mPhone.getServiceStateTracker().registerForDataRoamingOn(this,
                 DctConstants.EVENT_ROAMING_ON, null);
         mPhone.getServiceStateTracker().registerForDataRoamingOff(this,
-                DctConstants.EVENT_ROAMING_OFF, null);
+                DctConstants.EVENT_ROAMING_OFF, null, true);
         mPhone.getServiceStateTracker().registerForPsRestrictedEnabled(this,
                 DctConstants.EVENT_PS_RESTRICT_ENABLED, null);
         mPhone.getServiceStateTracker().registerForPsRestrictedDisabled(this,
@@ -767,7 +694,7 @@
         mPhone.mCi.registerForAvailable(this, DctConstants.EVENT_RADIO_AVAILABLE, null);
         mPhone.mCi.registerForOffOrNotAvailable(this,
                 DctConstants.EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
-        mPhone.mCi.registerForDataNetworkStateChanged(this,
+        mPhone.mCi.registerForDataCallListChanged(this,
                 DctConstants.EVENT_DATA_STATE_CHANGED, null);
         // Note, this is fragile - the Phone is now presenting a merged picture
         // of PS (volte) & CS and by diving into its internals you're just seeing
@@ -782,6 +709,9 @@
      //   SubscriptionManager.registerForDdsSwitch(this,
      //          DctConstants.EVENT_CLEAN_UP_ALL_CONNECTIONS, null);
         mPhone.mCi.registerForPcoData(this, DctConstants.EVENT_PCO_DATA_RECEIVED, null);
+        mPhone.getCarrierActionAgent().registerForCarrierAction(
+                CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, this,
+                DctConstants.EVENT_SET_CARRIER_DATA_ENABLED, null, false);
     }
 
     public void dispose() {
@@ -830,12 +760,14 @@
             r.unregisterForRecordsLoaded(this);
             mIccRecords.set(null);
         }
-        mPhone.mCi.unregisterForDataNetworkStateChanged(this);
+        mPhone.mCi.unregisterForDataCallListChanged(this);
         mPhone.getCallTracker().unregisterForVoiceCallEnded(this);
         mPhone.getCallTracker().unregisterForVoiceCallStarted(this);
         unregisterServiceStateTrackerEvents();
         //SubscriptionManager.unregisterForDdsSwitch(this);
         mPhone.mCi.unregisterForPcoData(this);
+        mPhone.getCarrierActionAgent().unregisterForCarrierAction(this,
+                CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED);
     }
 
     /**
@@ -878,8 +810,7 @@
                     Settings.Global.putInt(mResolver, Settings.Global.MOBILE_DATA + phoneSubId,
                             enabled ? 1 : 0);
                 }
-                if (getDataOnRoamingEnabled() == false &&
-                        mPhone.getServiceState().getDataRoaming() == true) {
+                if (!getDataRoamingEnabled() && mPhone.getServiceState().getDataRoaming()) {
                     if (enabled) {
                         notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
                     } else {
@@ -890,7 +821,7 @@
                 // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
                 // handle the rest from there.
                 if (enabled) {
-                    teardownRestrictedMeteredConnections();
+                    reevaluateDataConnections();
                     onTrySetupData(Phone.REASON_DATA_ENABLED);
                 } else {
                     onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
@@ -900,33 +831,49 @@
     }
 
     /**
-     * Handle reverting restricted networks back to unrestricted.
-     * If we're changing user data to enabled and this makes data
-     * truely enabled (not disabled by other factors) we need to
-     * tear down any metered apn type that was enabled anyway by
-     * a privileged request.  This allows us to reconnect
-     * to it in an unrestricted way.
+     * Reevaluate existing data connections when conditions change.
+     *
+     * For example, handle reverting restricted networks back to unrestricted. If we're changing
+     * user data to enabled and this makes data truly enabled (not disabled by other factors) we
+     * need to tear down any metered apn type that was enabled anyway by a privileged request.
+     * This allows us to reconnect to it in an unrestricted way.
+     *
+     * Or when we brought up a unmetered data connection while data is off, we only limit this
+     * data connection for unmetered use only. When data is turned back on, we need to tear that
+     * down so a full capable data connection can be re-established.
      */
-    private void teardownRestrictedMeteredConnections() {
-        if (mDataEnabledSettings.isDataEnabled(true)) {
+    private void reevaluateDataConnections() {
+        if (mDataEnabledSettings.isDataEnabled()) {
             for (ApnContext apnContext : mApnContexts.values()) {
-                if (apnContext.isConnectedOrConnecting() &&
-                        apnContext.getApnSetting().isMetered(mPhone.getContext(),
-                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())) {
-
-                    final DcAsyncChannel dataConnectionAc = apnContext.getDcAc();
-                    if (dataConnectionAc != null) {
-                        final NetworkCapabilities nc =
-                                dataConnectionAc.getNetworkCapabilitiesSync();
-                        if (nc != null && nc.hasCapability(NetworkCapabilities.
-                              NET_CAPABILITY_NOT_RESTRICTED)) {
-                            if (DBG) log("not tearing down unrestricted metered net:" + apnContext);
-                            continue;
+                if (apnContext.isConnectedOrConnecting()) {
+                    final DcAsyncChannel dcac = apnContext.getDcAc();
+                    if (dcac != null) {
+                        final NetworkCapabilities netCaps = dcac.getNetworkCapabilitiesSync();
+                        if (netCaps != null && !netCaps.hasCapability(NetworkCapabilities
+                                .NET_CAPABILITY_NOT_RESTRICTED) && !netCaps.hasCapability(
+                                NetworkCapabilities.NET_CAPABILITY_NOT_METERED)) {
+                            if (DBG) {
+                                log("Tearing down restricted metered net:" + apnContext);
+                            }
+                            // Tearing down the restricted metered data call when
+                            // conditions change. This will allow reestablishing a new unrestricted
+                            // data connection.
+                            apnContext.setReason(Phone.REASON_DATA_ENABLED);
+                            cleanUpConnection(true, apnContext);
+                        } else if (apnContext.getApnSetting().isMetered(mPhone)
+                                && (netCaps != null && netCaps.hasCapability(
+                                        NetworkCapabilities.NET_CAPABILITY_NOT_METERED))) {
+                            if (DBG) {
+                                log("Tearing down unmetered net:" + apnContext);
+                            }
+                            // The APN settings is metered, but the data was still marked as
+                            // unmetered data, must be the unmetered data connection brought up when
+                            // data is off. We need to tear that down when data is enabled again.
+                            // This will allow reestablishing a new full capability data connection.
+                            apnContext.setReason(Phone.REASON_DATA_ENABLED);
+                            cleanUpConnection(true, apnContext);
                         }
                     }
-                    if (DBG) log("tearing down restricted metered net: " + apnContext);
-                    apnContext.setReason(Phone.REASON_DATA_ENABLED);
-                    cleanUpConnection(true, apnContext);
                 }
             }
         }
@@ -935,7 +882,7 @@
     private void onDeviceProvisionedChange() {
         if (getDataEnabled()) {
             mDataEnabledSettings.setUserDataEnabled(true);
-            teardownRestrictedMeteredConnections();
+            reevaluateDataConnections();
             onTrySetupData(Phone.REASON_DATA_ENABLED);
         } else {
             mDataEnabledSettings.setUserDataEnabled(false);
@@ -1031,6 +978,7 @@
         public void onReceive(Context context, Intent intent) {
             // Turning back on the radio can take time on the order of a minute, so show user a
             // spinner so they know something is going on.
+            log("onReceive : ProvisionNotificationBroadcastReceiver");
             mProvisioningSpinner = new ProgressDialog(context);
             mProvisioningSpinner.setTitle(mNetworkOperator);
             mProvisioningSpinner.setMessage(
@@ -1054,38 +1002,6 @@
         }
     }
 
-    public boolean isDataPossible(String apnType) {
-        ApnContext apnContext = mApnContexts.get(apnType);
-        if (apnContext == null) {
-            return false;
-        }
-        boolean apnContextIsEnabled = apnContext.isEnabled();
-        DctConstants.State apnContextState = apnContext.getState();
-        boolean apnTypePossible = !(apnContextIsEnabled &&
-                (apnContextState == DctConstants.State.FAILED));
-        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
-        // Set the emergency APN availability status as TRUE irrespective of conditions checked in
-        // isDataAllowed() like IN_SERVICE, MOBILE DATA status etc.
-        boolean dataAllowed = isEmergencyApn || isDataAllowed(null);
-        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",
-                    apnType, possible, dataAllowed, apnTypePossible,
-                    apnContextIsEnabled, apnContextState));
-        }
-        return possible;
-    }
-
     @Override
     protected void finalize() {
         if(DBG && mPhone != null) log("finalize");
@@ -1269,45 +1185,9 @@
         }
     }
 
-    /**
-     * Report on whether data connectivity is enabled for any APN.
-     * @return {@code false} if data connectivity has been explicitly disabled,
-     * {@code true} otherwise.
-     */
-    public boolean getAnyDataEnabled() {
-        if (!mDataEnabledSettings.isDataEnabled(true)) return false;
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        if (!isDataAllowed(failureReason)) {
-            if (DBG) log(failureReason.getDataAllowFailReason());
-            return false;
-        }
-        for (ApnContext apnContext : mApnContexts.values()) {
-            // Make sure we don't have a context that is going down
-            // and is explicitly disabled.
-            if (isDataAllowedForApn(apnContext)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     @VisibleForTesting
-    public boolean isDataEnabled(boolean checkUserDataEnabled) {
-        return mDataEnabledSettings.isDataEnabled(checkUserDataEnabled);
-    }
-
-    private boolean isDataAllowedForApn(ApnContext apnContext) {
-        //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;
-        }
-
-        return apnContext.isReady();
+    public boolean isDataEnabled() {
+        return mDataEnabledSettings.isDataEnabled();
     }
 
     //****** Called from ServiceStateTracker
@@ -1345,30 +1225,58 @@
         setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
     }
 
-    private boolean isDataAllowed(DataAllowFailReason failureReason) {
-        final boolean internalDataEnabled;
-        internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
+    /**
+     * Check if it is allowed to make a data connection (without checking APN context specific
+     * conditions).
+     *
+     * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
+     *                              param. It's okay to pass null here and no reasons will be
+     *                              provided.
+     * @return True if data connection is allowed, otherwise false.
+     */
+    public boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
+        return isDataAllowed(null, dataConnectionReasons);
+    }
 
+    /**
+     * Check if it is allowed to make a data connection for a given APN type.
+     *
+     * @param apnContext APN context. If passing null, then will only check general but not APN
+     *                   specific conditions (e.g. APN state, metered/unmetered APN).
+     * @param dataConnectionReasons Data connection allowed or disallowed reasons as the output
+     *                              param. It's okay to pass null here and no reasons will be
+     *                              provided.
+     * @return True if data connection is allowed, otherwise false.
+     */
+    boolean isDataAllowed(ApnContext apnContext, DataConnectionReasons dataConnectionReasons) {
+        // Step 1: Get all environment conditions.
+        // Step 2: Special handling for emergency APN.
+        // Step 3. Build disallowed reasons.
+        // Step 4: Determine if data should be allowed in some special conditions.
+
+        DataConnectionReasons reasons = new DataConnectionReasons();
+
+        // Step 1: Get all environment conditions.
+        final boolean internalDataEnabled = mDataEnabledSettings.isInternalDataEnabled();
         boolean attachedState = mAttached.get();
         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
         boolean radioStateFromCarrier = mPhone.getServiceStateTracker().getPowerStateFromCarrier();
+        // TODO: Remove this hack added by ag/641832.
         int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
         if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
             desiredPowerState = true;
             radioStateFromCarrier = true;
         }
 
-        IccRecords r = mIccRecords.get();
-        boolean recordsLoaded = false;
-        if (r != null) {
-            recordsLoaded = r.getRecordsLoaded();
-            if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
-        }
+        boolean recordsLoaded = mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded();
 
-        int dataSub = SubscriptionManager.getDefaultDataSubscriptionId();
-        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(dataSub);
+        boolean defaultDataSelected = SubscriptionManager.isValidSubscriptionId(
+                SubscriptionManager.getDefaultDataSubscriptionId());
 
-        PhoneConstants.State state = PhoneConstants.State.IDLE;
+        boolean isMeteredApnType = apnContext == null
+                || ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone);
+
+        PhoneConstants.State phoneState = PhoneConstants.State.IDLE;
         // Note this is explicitly not using mPhone.getState.  See b/19090488.
         // mPhone.getState reports the merge of CS and PS (volte) voice call state
         // but we only care about CS calls here for data/voice concurrency issues.
@@ -1377,52 +1285,110 @@
         // This should be redesigned to ask explicitly what we want:
         // voiceCallStateAllowDataCall, or dataCallAllowed or something similar.
         if (mPhone.getCallTracker() != null) {
-            state = mPhone.getCallTracker().getState();
+            phoneState = mPhone.getCallTracker().getState();
         }
 
-        if (failureReason != null) failureReason.clearAllReasons();
+        // Step 2: Special handling for emergency APN.
+        if (apnContext != null
+                && apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY)
+                && apnContext.isConnectable()) {
+            // If this is an emergency APN, as long as the APN is connectable, we
+            // should allow it.
+            if (dataConnectionReasons != null) {
+                dataConnectionReasons.add(DataAllowedReasonType.EMERGENCY_APN);
+            }
+            // Bail out without further checks.
+            return true;
+        }
+
+        // Step 3. Build disallowed reasons.
+        if (apnContext != null && !apnContext.isConnectable()) {
+            reasons.add(DataDisallowedReasonType.APN_NOT_CONNECTABLE);
+        }
+
+        // If RAT is IWLAN then don't allow default/IA PDP at all.
+        // Rest of APN types can be evaluated for remaining conditions.
+        if ((apnContext != null && (apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
+                || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA)))
+                && (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+            reasons.add(DataDisallowedReasonType.ON_IWLAN);
+        }
+
+        if (isEmergency()) {
+            reasons.add(DataDisallowedReasonType.IN_ECBM);
+        }
+
         if (!(attachedState || mAutoAttachOnCreation.get())) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.NOT_ATTACHED);
+            reasons.add(DataDisallowedReasonType.NOT_ATTACHED);
         }
         if (!recordsLoaded) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RECORD_NOT_LOADED);
+            reasons.add(DataDisallowedReasonType.RECORD_NOT_LOADED);
         }
-        if (state != PhoneConstants.State.IDLE &&
-                !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INVALID_PHONE_STATE);
-            failureReason.addDataAllowFailReason(
-                    DataAllowFailReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
+        if (phoneState != PhoneConstants.State.IDLE
+                && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
+            reasons.add(DataDisallowedReasonType.INVALID_PHONE_STATE);
+            reasons.add(DataDisallowedReasonType.CONCURRENT_VOICE_DATA_NOT_ALLOWED);
         }
         if (!internalDataEnabled) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.INTERNAL_DATA_DISABLED);
+            reasons.add(DataDisallowedReasonType.INTERNAL_DATA_DISABLED);
         }
         if (!defaultDataSelected) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(
-                    DataAllowFailReasonType.DEFAULT_DATA_UNSELECTED);
+            reasons.add(DataDisallowedReasonType.DEFAULT_DATA_UNSELECTED);
         }
-        if (mPhone.getServiceState().getDataRoaming() && !getDataOnRoamingEnabled()) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.ROAMING_DISABLED);
+        if (mPhone.getServiceState().getDataRoaming() && !getDataRoamingEnabled()) {
+            reasons.add(DataDisallowedReasonType.ROAMING_DISABLED);
         }
         if (mIsPsRestricted) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.PS_RESTRICTED);
+            reasons.add(DataDisallowedReasonType.PS_RESTRICTED);
         }
         if (!desiredPowerState) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.UNDESIRED_POWER_STATE);
+            reasons.add(DataDisallowedReasonType.UNDESIRED_POWER_STATE);
         }
         if (!radioStateFromCarrier) {
-            if(failureReason == null) return false;
-            failureReason.addDataAllowFailReason(DataAllowFailReasonType.RADIO_DISABLED_BY_CARRIER);
+            reasons.add(DataDisallowedReasonType.RADIO_DISABLED_BY_CARRIER);
+        }
+        if (!mDataEnabledSettings.isDataEnabled()) {
+            reasons.add(DataDisallowedReasonType.DATA_DISABLED);
         }
 
-        return failureReason == null || !failureReason.isFailed();
+        // If there are hard disallowed reasons, we should not allow data connection no matter what.
+        if (reasons.containsHardDisallowedReasons()) {
+            if (dataConnectionReasons != null) {
+                dataConnectionReasons.copyFrom(reasons);
+            }
+            return false;
+        }
+
+        // Step 4: Determine if data should be allowed in some special conditions.
+
+        // At this point, if data is not allowed, it must be because of the soft reasons. We
+        // should start to check some special conditions that data will be allowed.
+
+        // If the request APN type is unmetered and there are soft disallowed reasons (e.g. data
+        // disabled, data roaming disabled) existing, we should allow the data because the user
+        // won't be charged anyway.
+        if (!isMeteredApnType && !reasons.allowed()) {
+            reasons.add(DataAllowedReasonType.UNMETERED_APN);
+        }
+
+        // If the request is restricted and there are only soft disallowed reasons (e.g. data
+        // disabled, data roaming disabled) existing, we should allow the data.
+        if (apnContext != null
+                && !apnContext.hasNoRestrictedRequests(true)
+                && !reasons.allowed()) {
+            reasons.add(DataAllowedReasonType.RESTRICTED_REQUEST);
+        }
+
+        // If at this point, we still haven't built any disallowed reasons, we should allow data.
+        if (reasons.allowed()) {
+            reasons.add(DataAllowedReasonType.NORMAL);
+        }
+
+        if (dataConnectionReasons != null) {
+            dataConnectionReasons.copyFrom(reasons);
+        }
+
+        return reasons.allowed();
     }
 
     // arg for setupDataOnConnectableApns
@@ -1456,8 +1422,6 @@
         }
 
         for (ApnContext apnContext : mPrioritySortedApnContexts) {
-            ArrayList<ApnSetting> waitingApns = null;
-
             if (VDBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
 
             if (apnContext.getState() == DctConstants.State.FAILED
@@ -1468,27 +1432,12 @@
                         mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                     // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
                     apnContext.releaseDataConnection(reason);
-                } else {
-                    // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
-                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
-                    ArrayList<ApnSetting> originalApns = apnContext.getWaitingApns();
-                    if (originalApns != null && originalApns.isEmpty() == false) {
-                        waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
-                        if (originalApns.size() != waitingApns.size() ||
-                                originalApns.containsAll(waitingApns) == false) {
-                            apnContext.releaseDataConnection(reason);
-                        } else {
-                            continue;
-                        }
-                    } else {
-                        continue;
-                    }
                 }
             }
             if (apnContext.isConnectable()) {
                 log("isConnectable() call trySetupData");
                 apnContext.setReason(reason);
-                trySetupData(apnContext, waitingApns);
+                trySetupData(apnContext);
             }
         }
     }
@@ -1500,15 +1449,6 @@
     }
 
     private boolean trySetupData(ApnContext apnContext) {
-        return trySetupData(apnContext, null);
-    }
-
-    private boolean trySetupData(ApnContext apnContext, ArrayList<ApnSetting> waitingApns) {
-        if (DBG) {
-            log("trySetupData for type:" + apnContext.getApnType() +
-                    " due to " + apnContext.getReason() + ", mIsPsRestricted=" + mIsPsRestricted);
-        }
-        apnContext.requestLog("trySetupData due to " + apnContext.getReason());
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -1520,34 +1460,13 @@
             return true;
         }
 
-        // Allow SETUP_DATA request for E-APN to be completed during emergency call
-        // and MOBILE DATA On/Off cases as well.
-        boolean isEmergencyApn = apnContext.getApnType().equals(PhoneConstants.APN_TYPE_EMERGENCY);
-        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
-
-        // set to false if apn type is non-metered or if we have a restricted (priveleged)
-        // request for the network.
-        // TODO - may want restricted requests to only apply to carrier-limited data access
-        //        rather than applying to user limited as well.
-        // Exclude DUN for the purposes of the override until we get finer grained
-        // intention in NetworkRequests
-        boolean checkUserDataEnabled =
-                ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
-                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming()) &&
-                apnContext.hasNoRestrictedRequests(true /*exclude DUN */);
-
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-
-        // allow data if currently in roaming service, roaming setting disabled
-        // and requested apn type is non-metered for roaming.
-        boolean isDataAllowed = isDataAllowed(failureReason) ||
-                (failureReason.isFailForSingleReason(DataAllowFailReasonType.ROAMING_DISABLED) &&
-                !(ApnSetting.isMeteredApnType(apnContext.getApnType(), mPhone.getContext(),
-                mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())));
-
-        if (apnContext.isConnectable() && (isEmergencyApn ||
-                (isDataAllowed && isDataAllowedForApn(apnContext) &&
-                        mDataEnabledSettings.isDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean isDataAllowed = isDataAllowed(apnContext, dataConnectionReasons);
+        String logStr = "trySetupData for APN type " + apnContext.getApnType() + ", reason: "
+                + apnContext.getReason() + ". " + dataConnectionReasons.toString();
+        if (DBG) log(logStr);
+        apnContext.requestLog(logStr);
+        if (isDataAllowed) {
             if (apnContext.getState() == DctConstants.State.FAILED) {
                 String str = "trySetupData: make a FAILED ApnContext IDLE so its reusable";
                 if (DBG) log(str);
@@ -1555,11 +1474,11 @@
                 apnContext.setState(DctConstants.State.IDLE);
             }
             int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
-            apnContext.setConcurrentVoiceAndDataAllowed(sst.isConcurrentVoiceAndDataAllowed());
+            apnContext.setConcurrentVoiceAndDataAllowed(mPhone.getServiceStateTracker()
+                    .isConcurrentVoiceAndDataAllowed());
             if (apnContext.getState() == DctConstants.State.IDLE) {
-                if (waitingApns == null) {
-                    waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
-                }
+                ArrayList<ApnSetting> waitingApns =
+                        buildWaitingApns(apnContext.getApnType(), radioTech);
                 if (waitingApns.isEmpty()) {
                     notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
                     notifyOffApnsOfAvailability(apnContext.getReason());
@@ -1576,7 +1495,8 @@
                 }
             }
 
-            boolean retValue = setupData(apnContext, radioTech);
+            boolean retValue = setupData(apnContext, radioTech, dataConnectionReasons.contains(
+                    DataAllowedReasonType.UNMETERED_APN));
             notifyOffApnsOfAvailability(apnContext.getReason());
 
             if (DBG) log("trySetupData: X retValue=" + retValue);
@@ -1590,48 +1510,30 @@
 
             StringBuilder str = new StringBuilder();
 
-            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType() +
-                    ", mState=" + apnContext.getState() + ", mDataEnabled=" +
-                    apnContext.isEnabled() + ", mDependencyMet=" +
-                    apnContext.getDependencyMet() + "] ");
+            str.append("trySetupData failed. apnContext = [type=" + apnContext.getApnType()
+                    + ", mState=" + apnContext.getState() + ", apnEnabled="
+                    + apnContext.isEnabled() + ", mDependencyMet="
+                    + apnContext.getDependencyMet() + "] ");
 
-            if (!apnContext.isConnectable()) {
-                str.append("isConnectable = false. ");
+            if (!mDataEnabledSettings.isDataEnabled()) {
+                str.append("isDataEnabled() = false. " + mDataEnabledSettings);
             }
-            if (!isDataAllowed) {
-                str.append("data not allowed: " + failureReason.getDataAllowFailReason() + ". ");
-            }
-            if (!isDataAllowedForApn(apnContext)) {
-                str.append("isDataAllowedForApn = false. RAT = " +
-                        mPhone.getServiceState().getRilDataRadioTechnology());
-            }
-            if (!mDataEnabledSettings.isDataEnabled(checkUserDataEnabled)) {
-                str.append("isDataEnabled(" + checkUserDataEnabled + ") = false. " +
-                        "isInternalDataEnabled = " + mDataEnabledSettings.isInternalDataEnabled() +
-                        ", userDataEnabled = " + mDataEnabledSettings.isUserDataEnabled() +
-                        ", isPolicyDataEnabled = " + mDataEnabledSettings.isPolicyDataEnabled() +
-                        ", isCarrierDataEnabled = " +
-                        mDataEnabledSettings.isCarrierDataEnabled());
-            }
-            if (isEmergency()) {
-                str.append("emergency = true");
+
+            // If this is a data retry, we should set the APN state to FAILED so it won't stay
+            // in SCANNING forever.
+            if (apnContext.getState() == DctConstants.State.SCANNING) {
+                apnContext.setState(DctConstants.State.FAILED);
+                str.append(" Stop retrying.");
             }
 
             if (DBG) log(str.toString());
             apnContext.requestLog(str.toString());
-
             return false;
         }
     }
 
     // Disabled apn's still need avail/unavail notifications - send them out
     private void notifyOffApnsOfAvailability(String reason) {
-        if (DBG) {
-            DataAllowFailReason failureReason = new DataAllowFailReason();
-            if (!isDataAllowed(failureReason)) {
-                log(failureReason.getDataAllowFailReason());
-            }
-        }
         for (ApnContext apnContext : mApnContexts.values()) {
             if (!mAttached.get() || !apnContext.isReady()) {
                 if (VDBG) log("notifyOffApnOfAvailability type:" + apnContext.getApnType());
@@ -1676,8 +1578,7 @@
                 // Use ApnSetting to decide metered or non-metered.
                 // Tear down all metered data connections.
                 ApnSetting apnSetting = apnContext.getApnSetting();
-                if (apnSetting != null && apnSetting.isMetered(mPhone.getContext(),
-                        mPhone.getSubId(), mPhone.getServiceState().getDataRoaming())) {
+                if (apnSetting != null && apnSetting.isMetered(mPhone)) {
                     if (DBG) log("clean up metered ApnContext Type: " + apnContext.getApnType());
                     apnContext.setReason(reason);
                     cleanUpConnection(tearDown, apnContext);
@@ -1814,56 +1715,74 @@
         apnContext.requestLog(str);
     }
 
-    ApnSetting fetchDunApn() {
+    /**
+     * Fetch dun apn
+     * @return ApnSetting to be used for dun
+     */
+    @VisibleForTesting
+    public ApnSetting fetchDunApn() {
         if (SystemProperties.getBoolean("net.tethering.noprovisioning", false)) {
             log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
             return null;
         }
         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() : "";
+        String operator = (r != null) ? r.getOperatorNumeric() : "";
+        ArrayList<ApnSetting> dunCandidates = new ArrayList<ApnSetting>();
+        ApnSetting retDunSetting = null;
+
+        // Places to look for tether APN in order: TETHER_DUN_APN setting (to be deprecated soon),
+        // APN database, and config_tether_apndata resource (to be deprecated soon).
+        String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
+        if (!TextUtils.isEmpty(apnData)) {
+            dunCandidates.addAll(ApnSetting.arrayFromString(apnData));
+            if (VDBG) log("fetchDunApn: dunCandidates from Setting: " + dunCandidates);
+        }
+
+        // todo: remove this and config_tether_apndata after APNs are moved from overlay to apns xml
+        // If TETHER_DUN_APN isn't set or APN database doesn't have dun APN,
+        // try the resource as last resort.
+        if (dunCandidates.isEmpty()) {
+            String[] apnArrayData = mPhone.getContext().getResources()
+                .getStringArray(R.array.config_tether_apndata);
+            if (!ArrayUtils.isEmpty(apnArrayData)) {
+                for (String apnString : apnArrayData) {
+                    ApnSetting apn = ApnSetting.fromString(apnString);
+                    // apn may be null if apnString isn't valid or has error parsing
+                    if (apn != null) dunCandidates.add(apn);
+                }
+                if (VDBG) log("fetchDunApn: dunCandidates from resource: " + dunCandidates);
+            }
+        }
+
+        if (dunCandidates.isEmpty()) {
+            if (!ArrayUtils.isEmpty(mAllApnSettings)) {
+                for (ApnSetting apn : mAllApnSettings) {
+                    if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
+                        dunCandidates.add(apn);
+                    }
+                }
+                if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
+            }
+        }
+
+        for (ApnSetting dunSetting : dunCandidates) {
             if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
             if (dunSetting.numeric.equals(operator)) {
                 if (dunSetting.hasMvnoParams()) {
                     if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
                             dunSetting.mvnoMatchData)) {
-                        if (VDBG) {
-                            log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
-                        }
-                        return dunSetting;
-                    }
-                } else if (mMvnoMatched == false) {
-                    if (VDBG) log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
-                    return dunSetting;
-                }
-            }
-        }
-
-        Context c = mPhone.getContext();
-        String[] apnArrayData = c.getResources().getStringArray(R.array.config_tether_apndata);
-        for (String apn : apnArrayData) {
-            ApnSetting dunSetting = ApnSetting.fromString(apn);
-            if (dunSetting != null) {
-                if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
-                if (dunSetting.hasMvnoParams()) {
-                    if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
-                            dunSetting.mvnoMatchData)) {
-                        if (VDBG) {
-                            log("fetchDunApn: config_tether_apndata mvno dunSetting=" + dunSetting);
-                        }
-                        return dunSetting;
+                        retDunSetting = dunSetting;
+                        break;
                     }
                 } else if (mMvnoMatched == false) {
                     retDunSetting = dunSetting;
+                    break;
                 }
             }
         }
 
-        if (VDBG) log("fetchDunApn: config_tether_apndata dunSetting=" + retDunSetting);
+        if (VDBG) log("fetchDunApn: dunSetting=" + retDunSetting);
         return retDunSetting;
     }
 
@@ -1918,8 +1837,8 @@
         return result;
     }
 
-    boolean isPermanentFail(DcFailCause dcFailCause) {
-        return (dcFailCause.isPermanentFail() &&
+    boolean isPermanentFailure(DcFailCause dcFailCause) {
+        return (dcFailCause.isPermanentFailure(mPhone.getContext(), mPhone.getSubId()) &&
                 (mAttached.get() == false || dcFailCause != DcFailCause.SIGNAL_LOST));
     }
 
@@ -2031,7 +1950,16 @@
         return null;
     }
 
-    private boolean setupData(ApnContext apnContext, int radioTech) {
+    /**
+     * Setup a data connection based on given APN type.
+     *
+     * @param apnContext APN context
+     * @param radioTech RAT of the data connection
+     * @param unmeteredUseOnly True if this data connection should be only used for unmetered
+     *                         purposes only.
+     * @return True if successful, otherwise false.
+     */
+    private boolean setupData(ApnContext apnContext, int radioTech, boolean unmeteredUseOnly) {
         if (DBG) log("setupData: apnContext=" + apnContext);
         apnContext.requestLog("setupData");
         ApnSetting apnSetting;
@@ -2113,7 +2041,7 @@
         Message msg = obtainMessage();
         msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
         msg.obj = new Pair<ApnContext, Integer>(apnContext, generation);
-        dcac.bringUp(apnContext, profileId, radioTech, msg, generation);
+        dcac.bringUp(apnContext, profileId, radioTech, unmeteredUseOnly, msg, generation);
 
         if (DBG) log("setupData: initing!");
         return true;
@@ -2126,15 +2054,15 @@
 
         log("setInitialApn: E mPreferredApn=" + mPreferredApn);
 
-        if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
+        if (mPreferredApn != null && mPreferredApn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
+              iaApnSetting = mPreferredApn;
+        } else if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
             firstApnSetting = mAllApnSettings.get(0);
             log("setInitialApn: firstApnSetting=" + firstApnSetting);
 
             // Search for Initial APN setting and the first apn that can handle default
             for (ApnSetting apn : mAllApnSettings) {
-                // Can't use apn.canHandleType(), as that returns true for APNs that have no type.
-                if (ArrayUtils.contains(apn.types, PhoneConstants.APN_TYPE_IA) &&
-                        apn.carrierEnabled) {
+                if (apn.canHandleType(PhoneConstants.APN_TYPE_IA)) {
                     // The Initial Attach APN is highest priority so use it if there is one
                     log("setInitialApn: iaApnSetting=" + apn);
                     iaApnSetting = apn;
@@ -2174,9 +2102,8 @@
         } else {
             if (DBG) log("setInitialAttachApn: X selected Apn=" + initialAttachApnSetting);
 
-            mPhone.mCi.setInitialAttachApn(initialAttachApnSetting.apn,
-                    initialAttachApnSetting.protocol, initialAttachApnSetting.authType,
-                    initialAttachApnSetting.user, initialAttachApnSetting.password, null);
+            mPhone.mCi.setInitialAttachApn(new DataProfile(initialAttachApnSetting),
+                    mPhone.getServiceState().getDataRoamingFromRegistration(), null);
         }
     }
 
@@ -2198,7 +2125,7 @@
         if (DBG) log("onApnChanged: createAllApnList and cleanUpAllConnections");
         createAllApnList();
         setInitialAttachApn();
-        cleanUpConnectionsOnUpdatedApns(!isDisconnected);
+        cleanUpConnectionsOnUpdatedApns(!isDisconnected, Phone.REASON_APN_CHANGED);
 
         // FIXME: See bug 17426028 maybe no conditional is needed.
         if (mPhone.getSubId() == SubscriptionManager.getDefaultDataSubscriptionId()) {
@@ -2249,7 +2176,7 @@
     private boolean isOnlySingleDcAllowed(int rilRadioTech) {
         // Default single dc rats with no knowledge of carrier
         int[] singleDcRats = null;
-        // get the carrier specific value, if it exists, from CarrierConfigManager
+        // get the carrier specific value, if it exists, from CarrierConfigManager.
         // generally configManager and bundle should not be null, but if they are it should be okay
         // to leave singleDcRats null as well
         CarrierConfigManager configManager = (CarrierConfigManager)
@@ -2326,7 +2253,7 @@
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
 
         // Get current sub id.
-        int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+        int subId = mPhone.getSubId();
         intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
 
         if (DBG) {
@@ -2349,7 +2276,7 @@
     private void notifyNoData(DcFailCause lastFailCauseCode,
                               ApnContext apnContext) {
         if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
-        if (isPermanentFail(lastFailCauseCode)
+        if (isPermanentFailure(lastFailCauseCode)
             && (!apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT))) {
             mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
         }
@@ -2373,17 +2300,16 @@
         setupDataOnConnectableApns(Phone.REASON_SIM_LOADED);
     }
 
-    public void setApnsEnabledByCarrier(boolean enabled) {
-        Message msg = obtainMessage(DctConstants.EVENT_SET_CARRIER_DATA_ENABLED);
-        msg.arg1 = (enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
-        sendMessage(msg);
-    }
-
     /**
      * Action set from carrier signalling broadcast receivers to enable/disable metered apns.
      */
-    private void onSetCarrierDataEnabled(boolean enabled) {
+    private void onSetCarrierDataEnabled(AsyncResult ar) {
+        if (ar.exception != null) {
+            Rlog.e(LOG_TAG, "CarrierDataEnable exception: " + ar.exception);
+            return;
+        }
         synchronized (mDataEnabledSettings) {
+            boolean enabled = (boolean) ar.result;
             if (enabled != mDataEnabledSettings.isCarrierDataEnabled()) {
                 if (DBG) {
                     log("carrier Action: set metered apns enabled: " + enabled);
@@ -2394,34 +2320,29 @@
 
                 if (!enabled) {
                     // Send otasp_sim_unprovisioned so that SuW is able to proceed and notify users
-                    mPhone.notifyOtaspChanged(ServiceStateTracker.OTASP_SIM_UNPROVISIONED);
+                    mPhone.notifyOtaspChanged(TelephonyManager.OTASP_SIM_UNPROVISIONED);
                     // Tear down all metered apns
                     cleanUpAllConnections(true, Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
                 } else {
-                    teardownRestrictedMeteredConnections();
+                    // Re-evaluate Otasp state
+                    int otaspState = mPhone.getServiceStateTracker().getOtasp();
+                    mPhone.notifyOtaspChanged(otaspState);
+
+                    reevaluateDataConnections();
                     setupDataOnConnectableApns(Phone.REASON_DATA_ENABLED);
                 }
             }
         }
     }
 
-    /**
-     * Action set from carrier signalling broadcast receivers to enable/disable radio
-     */
-    public void carrierActionSetRadioEnabled(boolean enabled) {
-        if (DBG) {
-            log("carrier Action: set radio enabled: " + enabled);
-        }
-        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
-        sst.setRadioPowerFromCarrier(enabled);
-    }
-
     private void onSimNotReady() {
         if (DBG) log("onSimNotReady");
 
         cleanUpAllConnections(true, Phone.REASON_SIM_NOT_READY);
         mAllApnSettings = null;
         mAutoAttachOnCreationConfig = false;
+        // Clear auto attach as modem is expected to do a new attach once SIM is ready
+        mAutoAttachOnCreation.set(false);
     }
 
     private void onSetDependencyMet(String apnType, boolean met) {
@@ -2451,14 +2372,14 @@
 
     private void onSetPolicyDataEnabled(boolean enabled) {
         synchronized (mDataEnabledSettings) {
-            final boolean prevEnabled = getAnyDataEnabled();
+            final boolean prevEnabled = isDataEnabled();
             if (mDataEnabledSettings.isPolicyDataEnabled() != enabled) {
                 mDataEnabledSettings.setPolicyDataEnabled(enabled);
                 // TODO: We should register for DataEnabledSetting's data enabled/disabled event and
                 // handle the rest from there.
-                if (prevEnabled != getAnyDataEnabled()) {
+                if (prevEnabled != isDataEnabled()) {
                     if (!prevEnabled) {
-                        teardownRestrictedMeteredConnections();
+                        reevaluateDataConnections();
                         onTrySetupData(Phone.REASON_DATA_ENABLED);
                     } else {
                         onCleanUpAllConnections(Phone.REASON_DATA_SPECIFIC_DISABLED);
@@ -2483,7 +2404,6 @@
                 DctConstants.State state = apnContext.getState();
                 switch(state) {
                     case CONNECTING:
-                    case SCANNING:
                     case CONNECTED:
                     case DISCONNECTING:
                         // We're "READY" and active so just return
@@ -2493,6 +2413,7 @@
                     case IDLE:
                         // fall through: this is unexpected but if it happens cleanup and try setup
                     case FAILED:
+                    case SCANNING:
                     case RETRYING: {
                         // We're "READY" but not active so disconnect (cleanup = true) and
                         // connect (trySetup = true) to be sure we retry the connection.
@@ -2512,7 +2433,12 @@
                 // request goes away.  This applies to both CDMA and GSM because they both
                 // can declare the DUN APN sharable by default traffic, thus still satisfying
                 // those requests and not torn down organically.
-                if (apnContext.getApnType() == PhoneConstants.APN_TYPE_DUN && teardownForDun()) {
+                if ((apnContext.getApnType() == PhoneConstants.APN_TYPE_DUN && teardownForDun())
+                        || apnContext.getState() != DctConstants.State.CONNECTED) {
+                    str = "Clean up the connection. Apn type = " + apnContext.getApnType()
+                            + ", state = " + apnContext.getState();
+                    if (DBG) log(str);
+                    apnContext.requestLog(str);
                     cleanup = true;
                 } else {
                     cleanup = false;
@@ -2631,6 +2557,16 @@
         // TODO change our retry manager to use the appropriate numbers for the new APN
         if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState");
         applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet());
+
+        if ((enabled == DctConstants.DISABLED) &&
+            isOnlySingleDcAllowed(mPhone.getServiceState().getRilDataRadioTechnology()) &&
+            !isHigherPriorityApnContextActive(apnContext)) {
+
+            if(DBG) log("onEnableApn: isOnlySingleDcAllowed true & higher priority APN disabled");
+            // If the highest priority APN is disabled and only single
+            // data call is allowed, try to setup data call on other connectable APN.
+            setupDataOnConnectableApns(Phone.REASON_SINGLE_PDN_ARBITRATION);
+        }
     }
 
     // TODO: We shouldnt need this.
@@ -2690,16 +2626,17 @@
     }
 
     /**
-     * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value.
+     * Modify {@link android.provider.Settings.Global#DATA_ROAMING} value for user modification only
      */
-    public void setDataOnRoamingEnabled(boolean enabled) {
+    public void setDataRoamingEnabledByUser(boolean enabled) {
         final int phoneSubId = mPhone.getSubId();
-        if (getDataOnRoamingEnabled() != enabled) {
+        if (getDataRoamingEnabled() != enabled) {
             int roaming = enabled ? 1 : 0;
 
             // For single SIM phones, this is a per phone property.
             if (TelephonyManager.getDefault().getSimCount() == 1) {
                 Settings.Global.putInt(mResolver, Settings.Global.DATA_ROAMING, roaming);
+                setDataRoamingFromUserAction(true);
             } else {
                 Settings.Global.putInt(mResolver, Settings.Global.DATA_ROAMING +
                          phoneSubId, roaming);
@@ -2708,12 +2645,12 @@
             mSubscriptionManager.setDataRoaming(roaming, phoneSubId);
             // will trigger handleDataOnRoamingChange() through observer
             if (DBG) {
-               log("setDataOnRoamingEnabled: set phoneSubId=" + phoneSubId
-                       + " isRoaming=" + enabled);
+                log("setDataRoamingEnabledByUser: set phoneSubId=" + phoneSubId
+                        + " isRoaming=" + enabled);
             }
         } else {
             if (DBG) {
-                log("setDataOnRoamingEnabled: unchanged phoneSubId=" + phoneSubId
+                log("setDataRoamingEnabledByUser: unchanged phoneSubId=" + phoneSubId
                         + " isRoaming=" + enabled);
              }
         }
@@ -2722,36 +2659,109 @@
     /**
      * Return current {@link android.provider.Settings.Global#DATA_ROAMING} value.
      */
-    public boolean getDataOnRoamingEnabled() {
-        boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
-                "ro.com.android.dataroaming", "false"));
+    public boolean getDataRoamingEnabled() {
+        boolean isDataRoamingEnabled;
         final int phoneSubId = mPhone.getSubId();
 
         try {
             // For single SIM phones, this is a per phone property.
             if (TelephonyManager.getDefault().getSimCount() == 1) {
                 isDataRoamingEnabled = Settings.Global.getInt(mResolver,
-                        Settings.Global.DATA_ROAMING, isDataRoamingEnabled ? 1 : 0) != 0;
+                        Settings.Global.DATA_ROAMING, getDefaultDataRoamingEnabled() ? 1 : 0) != 0;
             } else {
                 isDataRoamingEnabled = TelephonyManager.getIntWithSubId(mResolver,
                         Settings.Global.DATA_ROAMING, phoneSubId) != 0;
             }
         } catch (SettingNotFoundException snfe) {
-            if (DBG) log("getDataOnRoamingEnabled: SettingNofFoundException snfe=" + snfe);
+            if (DBG) log("getDataRoamingEnabled: SettingNofFoundException snfe=" + snfe);
+            isDataRoamingEnabled = getDefaultDataRoamingEnabled();
         }
         if (VDBG) {
-            log("getDataOnRoamingEnabled: phoneSubId=" + phoneSubId +
-                    " isDataRoamingEnabled=" + isDataRoamingEnabled);
+            log("getDataRoamingEnabled: phoneSubId=" + phoneSubId
+                    + " isDataRoamingEnabled=" + isDataRoamingEnabled);
         }
         return isDataRoamingEnabled;
     }
 
-    private void onRoamingOff() {
-        if (DBG) log("onRoamingOff");
+    /**
+     * get default values for {@link Settings.Global#DATA_ROAMING}
+     * return {@code true} if either
+     * {@link CarrierConfigManager#KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL} or
+     * system property ro.com.android.dataroaming is set to true. otherwise return {@code false}
+     */
+    private boolean getDefaultDataRoamingEnabled() {
+        final CarrierConfigManager configMgr = (CarrierConfigManager)
+                mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        boolean isDataRoamingEnabled = "true".equalsIgnoreCase(SystemProperties.get(
+                "ro.com.android.dataroaming", "false"));
+        isDataRoamingEnabled |= configMgr.getConfigForSubId(mPhone.getSubId()).getBoolean(
+                CarrierConfigManager.KEY_CARRIER_DEFAULT_DATA_ROAMING_ENABLED_BOOL);
+        return isDataRoamingEnabled;
+    }
 
-        if (!mDataEnabledSettings.isUserDataEnabled()) return;
+    /**
+     * Set default value for {@link android.provider.Settings.Global#DATA_ROAMING}
+     * if the setting is not from user actions. default value is based on carrier config and system
+     * properties.
+     */
+    private void setDefaultDataRoamingEnabled() {
+        // For single SIM phones, this is a per phone property.
+        String setting = Settings.Global.DATA_ROAMING;
+        boolean useCarrierSpecificDefault = false;
+        if (TelephonyManager.getDefault().getSimCount() != 1) {
+            setting = setting + mPhone.getSubId();
+            try {
+                Settings.Global.getInt(mResolver, setting);
+            } catch (SettingNotFoundException ex) {
+                // For msim, update to carrier default if uninitialized.
+                useCarrierSpecificDefault = true;
+            }
+        } else if (!isDataRoamingFromUserAction()) {
+            // for single sim device, update to carrier default if user action is not set
+            useCarrierSpecificDefault = true;
+        }
+        if (useCarrierSpecificDefault) {
+            boolean defaultVal = getDefaultDataRoamingEnabled();
+            log("setDefaultDataRoamingEnabled: " + setting + "default value: " + defaultVal);
+            Settings.Global.putInt(mResolver, setting, defaultVal ? 1 : 0);
+            mSubscriptionManager.setDataRoaming(defaultVal ? 1 : 0, mPhone.getSubId());
+        }
+    }
 
-        if (getDataOnRoamingEnabled() == false) {
+    private boolean isDataRoamingFromUserAction() {
+        final SharedPreferences sp = PreferenceManager
+                .getDefaultSharedPreferences(mPhone.getContext());
+        // since we don't want to unset user preference from system update, pass true as the default
+        // value if shared pref does not exist and set shared pref to false explicitly from factory
+        // reset.
+        if (!sp.contains(Phone.DATA_ROAMING_IS_USER_SETTING_KEY)
+                && Settings.Global.getInt(mResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0) {
+            sp.edit().putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, false).commit();
+        }
+        return sp.getBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, true);
+    }
+
+    private void setDataRoamingFromUserAction(boolean isUserAction) {
+        final SharedPreferences.Editor sp = PreferenceManager
+                .getDefaultSharedPreferences(mPhone.getContext()).edit();
+        sp.putBoolean(Phone.DATA_ROAMING_IS_USER_SETTING_KEY, isUserAction).commit();
+    }
+
+    // When the data roaming status changes from roaming to non-roaming.
+    private void onDataRoamingOff() {
+        if (DBG) log("onDataRoamingOff");
+
+        if (!getDataRoamingEnabled()) {
+            // TODO: Remove this once all old vendor RILs are gone. We don't need to set initial apn
+            // attach and send the data profile again as the modem should have both roaming and
+            // non-roaming protocol in place. Modem should choose the right protocol based on the
+            // roaming condition.
+            setInitialAttachApn();
+            setDataProfilesAsNeeded();
+
+            // If the user did not enable data roaming, now when we transit from roaming to
+            // non-roaming, we should try to reestablish the data connection.
+
             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
             setupDataOnConnectableApns(Phone.REASON_ROAMING_OFF);
         } else {
@@ -2759,13 +2769,11 @@
         }
     }
 
-    private void onRoamingOn() {
-        if (DBG) log("onRoamingOn");
-
-        if (!mDataEnabledSettings.isUserDataEnabled()) {
-            if (DBG) log("data not enabled by user");
-            return;
-        }
+    // This method is called
+    // 1. When the data roaming status changes from non-roaming to roaming.
+    // 2. When allowed data roaming settings is changed by the user.
+    private void onDataRoamingOnOrSettingsChanged() {
+        if (DBG) log("onDataRoamingOnOrSettingsChanged");
 
         // Check if the device is actually data roaming
         if (!mPhone.getServiceState().getDataRoaming()) {
@@ -2773,12 +2781,16 @@
             return;
         }
 
-        if (getDataOnRoamingEnabled()) {
-            if (DBG) log("onRoamingOn: setup data on roaming");
+        if (getDataRoamingEnabled()) {
+            if (DBG) log("onDataRoamingOnOrSettingsChanged: setup data on roaming");
+
             setupDataOnConnectableApns(Phone.REASON_ROAMING_ON);
             notifyDataConnection(Phone.REASON_ROAMING_ON);
         } else {
-            if (DBG) log("onRoamingOn: Tear down data connection on roaming.");
+            // If the user does not turn on data roaming, when we transit from non-roaming to
+            // roaming, we need to tear down the data connection otherwise the user might be
+            // charged for data roaming usage.
+            if (DBG) log("onDataRoamingOnOrSettingsChanged: Tear down data connection on roaming.");
             cleanUpAllConnections(true, Phone.REASON_ROAMING_ON);
             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_ON);
         }
@@ -2811,6 +2823,9 @@
 
         mReregisterOnReconnectFailure = false;
 
+        // Clear auto attach as modem is expected to do a new attach
+        mAutoAttachOnCreation.set(false);
+
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
             // FIXME  this can be improved
@@ -3028,7 +3043,7 @@
 
             // If the data call failure cause is a permanent failure, we mark the APN as permanent
             // failed.
-            if (isPermanentFail(cause)) {
+            if (isPermanentFailure(cause)) {
                 log("cause = " + cause + ", mark apn as permanent failed. apn = " + apn);
                 apnContext.markApnPermanentFailed(apn);
             }
@@ -3111,9 +3126,8 @@
         if (!TextUtils.isEmpty(redirectUrl)) {
             Intent intent = new Intent(TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED);
             intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, redirectUrl);
-            if(mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent)) {
-                log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
-            }
+            mPhone.getCarrierSignalAgent().notifyCarrierSignalReceivers(intent);
+            log("Notify carrier signal receivers with redirectUrl: " + redirectUrl);
         }
     }
 
@@ -3162,7 +3176,7 @@
             // we're not tying up the RIL command channel.
             // This also helps in any external dependency to turn off the context.
             if (DBG) log("onDisconnectDone: attached, ready and retry after disconnect");
-            long delay = apnContext.getInterApnDelay(mFailFast);
+            long delay = apnContext.getRetryAfterDisconnectDelay();
             if (delay > 0) {
                 // Data connection is in IDLE state, so when we reconnect later, we'll rebuild
                 // the waiting APN list, which will also reset/reconfigure the retry manager.
@@ -3289,22 +3303,15 @@
             ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
             for (ApnSetting apn : mAllApnSettings) {
                 if (apn.modemCognitive) {
-                    DataProfile dp = new DataProfile(apn,
-                            mPhone.getServiceState().getDataRoaming());
-                    boolean isDup = false;
-                    for(DataProfile dpIn : dps) {
-                        if (dp.equals(dpIn)) {
-                            isDup = true;
-                            break;
-                        }
-                    }
-                    if (!isDup) {
+                    DataProfile dp = new DataProfile(apn);
+                    if (!dps.contains(dp)) {
                         dps.add(dp);
                     }
                 }
             }
-            if(dps.size() > 0) {
-                mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]), null);
+            if (dps.size() > 0) {
+                mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]),
+                        mPhone.getServiceState().getDataRoamingFromRegistration(), null);
             }
         }
     }
@@ -3315,7 +3322,7 @@
      */
     private void createAllApnList() {
         mMvnoMatched = false;
-        mAllApnSettings = new ArrayList<ApnSetting>();
+        mAllApnSettings = new ArrayList<>();
         IccRecords r = mIccRecords.get();
         String operator = (r != null) ? r.getOperatorNumeric() : "";
         if (operator != null) {
@@ -3371,7 +3378,7 @@
             int j = i + 1;
             while (j < mAllApnSettings.size()) {
                 second = mAllApnSettings.get(j);
-                if (apnsSimilar(first, second)) {
+                if (first.similar(second)) {
                     ApnSetting newApn = mergeApns(first, second);
                     mAllApnSettings.set(i, newApn);
                     first = newApn;
@@ -3384,64 +3391,6 @@
         }
     }
 
-    //check whether the types of two APN same (even only one type of each APN is same)
-    private boolean apnTypeSameAny(ApnSetting first, ApnSetting second) {
-        if(VDBG) {
-            StringBuilder apnType1 = new StringBuilder(first.apn + ": ");
-            for(int index1 = 0; index1 < first.types.length; index1++) {
-                apnType1.append(first.types[index1]);
-                apnType1.append(",");
-            }
-
-            StringBuilder apnType2 = new StringBuilder(second.apn + ": ");
-            for(int index1 = 0; index1 < second.types.length; index1++) {
-                apnType2.append(second.types[index1]);
-                apnType2.append(",");
-            }
-            log("APN1: is " + apnType1);
-            log("APN2: is " + apnType2);
-        }
-
-        for(int index1 = 0; index1 < first.types.length; index1++) {
-            for(int index2 = 0; index2 < second.types.length; index2++) {
-                if(first.types[index1].equals(PhoneConstants.APN_TYPE_ALL) ||
-                        second.types[index2].equals(PhoneConstants.APN_TYPE_ALL) ||
-                        first.types[index1].equals(second.types[index2])) {
-                    if(VDBG)log("apnTypeSameAny: return true");
-                    return true;
-                }
-            }
-        }
-
-        if(VDBG)log("apnTypeSameAny: return false");
-        return false;
-    }
-
-    // Check if neither mention DUN and are substantially similar
-    private boolean apnsSimilar(ApnSetting first, ApnSetting second) {
-        return (first.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
-                second.canHandleType(PhoneConstants.APN_TYPE_DUN) == false &&
-                Objects.equals(first.apn, second.apn) &&
-                !apnTypeSameAny(first, second) &&
-                xorEquals(first.proxy, second.proxy) &&
-                xorEquals(first.port, second.port) &&
-                first.carrierEnabled == second.carrierEnabled &&
-                first.bearerBitmask == second.bearerBitmask &&
-                first.profileId == second.profileId &&
-                Objects.equals(first.mvnoType, second.mvnoType) &&
-                Objects.equals(first.mvnoMatchData, second.mvnoMatchData) &&
-                xorEquals(first.mmsc, second.mmsc) &&
-                xorEquals(first.mmsProxy, second.mmsProxy) &&
-                xorEquals(first.mmsPort, second.mmsPort));
-    }
-
-    // equal or one is not specified
-    private boolean xorEquals(String first, String second) {
-        return (Objects.equals(first, second) ||
-                TextUtils.isEmpty(first) ||
-                TextUtils.isEmpty(second));
-    }
-
     private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
         int id = dest.id;
         ArrayList<String> resultTypes = new ArrayList<String>();
@@ -3770,6 +3719,15 @@
                 break;
 
             case DctConstants.EVENT_DATA_RAT_CHANGED:
+                if (mPhone.getServiceState().getRilDataRadioTechnology()
+                        == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                    // unknown rat is an exception for data rat change. It's only received when out
+                    // of service and is not applicable for apn bearer bitmask. We should bypass the
+                    // check of waiting apn list and keep the data connection on, and no need to
+                    // setup a new one.
+                    break;
+                }
+                cleanUpConnectionsOnUpdatedApns(false, Phone.REASON_NW_TYPE_CHANGED);
                 //May new Network allow setupData, so try it here
                 setupDataOnConnectableApns(Phone.REASON_NW_TYPE_CHANGED,
                         RetryFailures.ONLY_ON_CHANGE);
@@ -3798,11 +3756,11 @@
                 break;
 
             case DctConstants.EVENT_ROAMING_OFF:
-                onRoamingOff();
+                onDataRoamingOff();
                 break;
 
             case DctConstants.EVENT_ROAMING_ON:
-                onRoamingOn();
+                onDataRoamingOnOrSettingsChanged();
                 break;
 
             case DctConstants.EVENT_DEVICE_PROVISIONED_CHANGE:
@@ -4007,7 +3965,10 @@
                 break;
             }
             case DctConstants.EVENT_SET_CARRIER_DATA_ENABLED:
-                onSetCarrierDataEnabled(msg.arg1 == DctConstants.ENABLED);
+                onSetCarrierDataEnabled((AsyncResult) msg.obj);
+                break;
+            case DctConstants.EVENT_DATA_RECONNECT:
+                onDataReconnect(msg.getData());
                 break;
             default:
                 Rlog.e("DcTracker", "Unhandled event=" + msg);
@@ -4071,11 +4032,6 @@
                     mIccRecords.set(newIccRecords);
                     newIccRecords.registerForRecordsLoaded(
                             this, DctConstants.EVENT_RECORDS_LOADED, null);
-                    // reset carrier actions on sim loaded
-                    final ServiceStateTracker sst = mPhone.getServiceStateTracker();
-                    sst.setRadioPowerFromCarrier(true);
-                    mDataEnabledSettings.setCarrierDataEnabled(true);
-                    mPhone.getCarrierSignalAgent().reset();
                 }
             } else {
                 onSimNotReady();
@@ -4196,9 +4152,8 @@
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("DcTracker:");
         pw.println(" RADIO_TESTS=" + RADIO_TESTS);
-        pw.println(" isInternalDataEnabled=" + mDataEnabledSettings.isInternalDataEnabled());
-        pw.println(" isUserDataEnabled=" + mDataEnabledSettings.isUserDataEnabled());
-        pw.println(" isPolicyDataEnabled=" + mDataEnabledSettings.isPolicyDataEnabled());
+        pw.println(" mDataEnabledSettings=" + mDataEnabledSettings);
+        pw.println(" isDataAllowed=" + isDataAllowed(null));
         pw.flush();
         pw.println(" mRequestedApnType=" + mRequestedApnType);
         pw.println(" mPhone=" + mPhone.getPhoneName());
@@ -4374,39 +4329,48 @@
         }
     }
 
-    private void cleanUpConnectionsOnUpdatedApns(boolean tearDown) {
+    private boolean containsAllApns(ArrayList<ApnSetting> oldApnList,
+                                    ArrayList<ApnSetting> newApnList) {
+        for (ApnSetting newApnSetting : newApnList) {
+            boolean canHandle = false;
+            for (ApnSetting oldApnSetting : oldApnList) {
+                // Make sure at least one of the APN from old list can cover the new APN
+                if (oldApnSetting.equals(newApnSetting,
+                        mPhone.getServiceState().getDataRoamingFromRegistration())) {
+                    canHandle = true;
+                    break;
+                }
+            }
+            if (!canHandle) return false;
+        }
+        return true;
+    }
+
+    private void cleanUpConnectionsOnUpdatedApns(boolean tearDown, String reason) {
         if (DBG) log("cleanUpConnectionsOnUpdatedApns: tearDown=" + tearDown);
-        if (mAllApnSettings.isEmpty()) {
+        if (mAllApnSettings != null && mAllApnSettings.isEmpty()) {
             cleanUpAllConnections(tearDown, Phone.REASON_APN_CHANGED);
         } else {
             for (ApnContext apnContext : mApnContexts.values()) {
-                if (VDBG) log("cleanUpConnectionsOnUpdatedApns for "+ apnContext);
-
-                boolean cleanUpApn = true;
                 ArrayList<ApnSetting> currentWaitingApns = apnContext.getWaitingApns();
-
-                if ((currentWaitingApns != null) && (!apnContext.isDisconnected())) {
-                    int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
-                    ArrayList<ApnSetting> waitingApns = buildWaitingApns(
-                            apnContext.getApnType(), radioTech);
-                    if (VDBG) log("new waitingApns:" + waitingApns);
-                    if (waitingApns.size() == currentWaitingApns.size()) {
-                        cleanUpApn = false;
-                        for (int i = 0; i < waitingApns.size(); i++) {
-                            if (!currentWaitingApns.get(i).equals(waitingApns.get(i))) {
-                                if (VDBG) log("new waiting apn is different at " + i);
-                                cleanUpApn = true;
-                                apnContext.setWaitingApns(waitingApns);
-                                break;
-                            }
-                        }
+                ArrayList<ApnSetting> waitingApns = buildWaitingApns(
+                        apnContext.getApnType(),
+                        mPhone.getServiceState().getRilDataRadioTechnology());
+                if (VDBG) log("new waitingApns:" + waitingApns);
+                if ((currentWaitingApns != null)
+                        && ((waitingApns.size() != currentWaitingApns.size())
+                        // Check if the existing waiting APN list can cover the newly built APN
+                        // list. If yes, then we don't need to tear down the existing data call.
+                        // TODO: We probably need to rebuild APN list when roaming status changes.
+                        || !containsAllApns(currentWaitingApns, waitingApns))) {
+                    if (VDBG) log("new waiting apn is different for " + apnContext);
+                    apnContext.setWaitingApns(waitingApns);
+                    if (!apnContext.isDisconnected()) {
+                        if (VDBG) log("cleanUpConnectionsOnUpdatedApns for " + apnContext);
+                        apnContext.setReason(reason);
+                        cleanUpConnection(true, apnContext);
                     }
                 }
-
-                if (cleanUpApn) {
-                    apnContext.setReason(Phone.REASON_APN_CHANGED);
-                    cleanUpConnection(true, apnContext);
-                }
             }
         }
 
@@ -4592,13 +4556,11 @@
         public static final int CLEANUP                 = 1;
         public static final int REREGISTER              = 2;
         public static final int RADIO_RESTART           = 3;
-        public static final int RADIO_RESTART_WITH_PROP = 4;
 
         private static boolean isAggressiveRecovery(int value) {
             return ((value == RecoveryAction.CLEANUP) ||
                     (value == RecoveryAction.REREGISTER) ||
-                    (value == RecoveryAction.RADIO_RESTART) ||
-                    (value == RecoveryAction.RADIO_RESTART_WITH_PROP));
+                    (value == RecoveryAction.RADIO_RESTART));
         }
     }
 
@@ -4644,23 +4606,6 @@
                 EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART,
                         mSentSinceLastRecv);
                 if (DBG) log("restarting radio");
-                putRecoveryAction(RecoveryAction.RADIO_RESTART_WITH_PROP);
-                restartRadio();
-                break;
-            case RecoveryAction.RADIO_RESTART_WITH_PROP:
-                // This is in case radio restart has not recovered the data.
-                // It will set an additional "gsm.radioreset" property to tell
-                // RIL or system to take further action.
-                // The implementation of hard reset recovery action is up to OEM product.
-                // Once RADIO_RESET property is consumed, it is expected to set back
-                // to false by RIL.
-                EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_RADIO_RESTART_WITH_PROP, -1);
-                if (DBG) log("restarting radio with gsm.radioreset to true");
-                SystemProperties.set(RADIO_RESET_PROPERTY, "true");
-                // give 1 sec so property change can be notified.
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException e) {}
                 restartRadio();
                 putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
                 break;
@@ -4697,7 +4642,7 @@
             mSentSinceLastRecv = 0;
             putRecoveryAction(RecoveryAction.GET_DATA_CALL_LIST);
         } else if (sent > 0 && received == 0) {
-            if (mPhone.getState() == PhoneConstants.State.IDLE) {
+            if (isPhoneStateIdle()) {
                 mSentSinceLastRecv += sent;
             } else {
                 mSentSinceLastRecv = 0;
@@ -4715,6 +4660,17 @@
         }
     }
 
+    private boolean isPhoneStateIdle() {
+        for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
+            Phone phone = PhoneFactory.getPhone(i);
+            if (phone != null && phone.getState() != PhoneConstants.State.IDLE) {
+                log("isPhoneStateIdle false: Voice call active on phone " + i);
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void onDataStallAlarm(int tag) {
         if (mDataStallAlarmTag != tag) {
             if (DBG) {
@@ -4770,7 +4726,7 @@
             intent.putExtra(DATA_STALL_ALARM_TAG_EXTRA, mDataStallAlarmTag);
             mDataStallAlarmIntent = PendingIntent.getBroadcast(mPhone.getContext(), 0, intent,
                     PendingIntent.FLAG_UPDATE_CURRENT);
-            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+            mAlarmManager.set(AlarmManager.ELAPSED_REALTIME,
                     SystemClock.elapsedRealtime() + delayInMs, mDataStallAlarmIntent);
         } else {
             if (VDBG_STALL) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
index 5010ba2..114a4b4 100644
--- a/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
+++ b/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java
@@ -22,18 +22,17 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
+import android.net.StringNetworkSpecifier;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.telephony.Rlog;
-import android.text.TextUtils;
 import android.util.LocalLog;
 
 import com.android.internal.telephony.PhoneSwitcher;
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.SubscriptionMonitor;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.internal.util.Protocol;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -119,7 +118,7 @@
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
         nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
-        nc.setNetworkSpecifier(String.valueOf(subscriptionId));
+        nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subscriptionId)));
         return nc;
     }
 
@@ -223,7 +222,7 @@
         NetworkRequest networkRequest = (NetworkRequest)msg.obj;
         boolean isApplicable = false;
         LocalLog localLog = null;
-        if (TextUtils.isEmpty(networkRequest.networkCapabilities.getNetworkSpecifier())) {
+        if (networkRequest.networkCapabilities.getNetworkSpecifier() == null) {
             // request only for the default network
             localLog = mDefaultRequests.get(networkRequest);
             if (localLog == null) {
@@ -263,7 +262,7 @@
         NetworkRequest networkRequest = (NetworkRequest)msg.obj;
         LocalLog localLog = null;
         boolean isApplicable = false;
-        if (TextUtils.isEmpty(networkRequest.networkCapabilities.getNetworkSpecifier())) {
+        if (networkRequest.networkCapabilities.getNetworkSpecifier() == null) {
             // request only for the default network
             localLog = mDefaultRequests.remove(networkRequest);
             isApplicable = (localLog != null) && mIsDefault;
diff --git a/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java b/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
index d9335b9..dec9805 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
@@ -22,8 +22,8 @@
 import android.telephony.CellLocation;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
-import android.telephony.gsm.GsmCellLocation;
 import android.telephony.TelephonyManager;
+import android.telephony.gsm.GsmCellLocation;
 
 import com.android.internal.telephony.CellBroadcastHandler;
 import com.android.internal.telephony.Phone;
@@ -185,7 +185,7 @@
                 }
             }
 
-            return GsmSmsCbMessage.createSmsCbMessage(header, location, pdus);
+            return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus);
 
         } catch (RuntimeException e) {
             loge("Error in decoding SMS CB pdu", e);
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 425a469..ad325ea 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -32,7 +32,6 @@
 import android.telephony.Rlog;
 
 import static com.android.internal.telephony.CommandsInterface.*;
-import com.android.internal.telephony.gsm.SsData;
 
 import java.util.regex.Pattern;
 import java.util.regex.Matcher;
@@ -136,6 +135,7 @@
     State mState = State.PENDING;
     CharSequence mMessage;
     private boolean mIsSsInfo = false;
+    private ResultReceiver mCallbackReceiver;
 
 
     //***** Class Variables
@@ -183,9 +183,13 @@
      *
      * Please see flow chart in TS 22.030 6.5.3.2
      */
+    public static GsmMmiCode newFromDialString(String dialString, GsmCdmaPhone phone,
+            UiccCardApplication app) {
+        return newFromDialString(dialString, phone, app, null);
+    }
 
-    public static GsmMmiCode
-    newFromDialString(String dialString, GsmCdmaPhone phone, UiccCardApplication app) {
+    public static GsmMmiCode newFromDialString(String dialString, GsmCdmaPhone phone,
+            UiccCardApplication app, ResultReceiver wrappedCallback) {
         Matcher m;
         GsmMmiCode ret = null;
 
@@ -202,6 +206,7 @@
             ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
             ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
             ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
+            ret.mCallbackReceiver = wrappedCallback;
             // According to TS 22.030 6.5.2 "Structure of the MMI",
             // the dialing number should not ending with #.
             // The dialing number ending # is treated as unique USSD,
@@ -624,6 +629,11 @@
 
     }
 
+    @Override
+    public String getDialString() {
+        return mPoundString;
+    }
+
     static private boolean
     isTwoDigitShortCode(Context context, String dialString) {
         Rlog.d(LOG_TAG, "isTwoDigitShortCode");
@@ -781,14 +791,14 @@
     processCode() throws CallStateException {
         try {
             if (isShortCode()) {
-                Rlog.d(LOG_TAG, "isShortCode");
+                Rlog.d(LOG_TAG, "processCode: isShortCode");
                 // These just get treated as USSD.
                 sendUssd(mDialingNumber);
             } else if (mDialingNumber != null) {
                 // We should have no dialing numbers here
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             } else if (mSc != null && mSc.equals(SC_CLIP)) {
-                Rlog.d(LOG_TAG, "is CLIP");
+                Rlog.d(LOG_TAG, "processCode: is CLIP");
                 if (isInterrogate()) {
                     mPhone.mCi.queryCLIP(
                             obtainMessage(EVENT_QUERY_COMPLETE, this));
@@ -796,7 +806,7 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (mSc != null && mSc.equals(SC_CLIR)) {
-                Rlog.d(LOG_TAG, "is CLIR");
+                Rlog.d(LOG_TAG, "processCode: is CLIR");
                 if (isActivate()) {
                     mPhone.mCi.setCLIR(CommandsInterface.CLIR_INVOCATION,
                         obtainMessage(EVENT_SET_COMPLETE, this));
@@ -810,7 +820,7 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (isServiceCodeCallForwarding(mSc)) {
-                Rlog.d(LOG_TAG, "is CF");
+                Rlog.d(LOG_TAG, "processCode: is CF");
 
                 String dialingNumber = mSia;
                 int serviceClass = siToServiceClass(mSib);
@@ -856,7 +866,7 @@
                         ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
                                 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
 
-                    Rlog.d(LOG_TAG, "is CF setCallForward");
+                    Rlog.d(LOG_TAG, "processCode: is CF setCallForward");
                     mPhone.mCi.setCallForward(cfAction, reason, serviceClass,
                             dialingNumber, time, obtainMessage(
                                     EVENT_SET_CFF_COMPLETE,
@@ -947,7 +957,8 @@
                         // Sim is puk-locked
                         handlePasswordError(com.android.internal.R.string.needPuk);
                     } else if (mUiccApplication != null) {
-                        Rlog.d(LOG_TAG, "process mmi service code using UiccApp sc=" + mSc);
+                        Rlog.d(LOG_TAG,
+                                "processCode: process mmi service code using UiccApp sc=" + mSc);
 
                         // We have an app and the pre-checks are OK
                         if (mSc.equals(SC_PIN)) {
@@ -974,11 +985,13 @@
             } else if (mPoundString != null) {
                 sendUssd(mPoundString);
             } else {
+                Rlog.d(LOG_TAG, "processCode: Invalid or Unsupported MMI Code");
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             }
         } catch (RuntimeException exc) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+            Rlog.d(LOG_TAG, "processCode: RuntimeException=" + exc);
             mPhone.onMMIDone(this);
         }
     }
@@ -1004,7 +1017,8 @@
     public void
     onUssdFinished(String ussdMessage, boolean isUssdRequest) {
         if (mState == State.PENDING) {
-            if (ussdMessage == null) {
+            if (TextUtils.isEmpty(ussdMessage)) {
+                Rlog.d(LOG_TAG, "onUssdFinished: no network provided message; using default.");
                 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
             } else {
                 mMessage = ussdMessage;
@@ -1014,7 +1028,7 @@
             if (!isUssdRequest) {
                 mState = State.COMPLETE;
             }
-
+            Rlog.d(LOG_TAG, "onUssdFinished: ussdMessage=" + ussdMessage);
             mPhone.onMMIDone(this);
         }
     }
@@ -1030,7 +1044,7 @@
         if (mState == State.PENDING) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
-
+            Rlog.d(LOG_TAG, "onUssdFinishedError");
             mPhone.onMMIDone(this);
         }
     }
@@ -1049,7 +1063,7 @@
         if (mState == State.PENDING) {
             mState = State.COMPLETE;
             mMessage = null;
-
+            Rlog.d(LOG_TAG, "onUssdRelease");
             mPhone.onMMIDone(this);
         }
     }
@@ -1062,7 +1076,6 @@
         // response does not complete this MMI code...we wait for
         // an unsolicited USSD "Notify" or "Request".
         // The matching up of this is done in GsmCdmaPhone.
-
         mPhone.mCi.sendUSSD(ussdMessage,
             obtainMessage(EVENT_USSD_COMPLETE, this));
     }
@@ -1238,6 +1251,17 @@
                 } else if (err == CommandException.Error.FDN_CHECK_FAILURE) {
                     Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE");
                     sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError));
+                } else if (err == CommandException.Error.MODEM_ERR) {
+                    // Some carriers do not allow changing call forwarding settings while roaming
+                    // and will return an error from the modem.
+                    if (isServiceCodeCallForwarding(mSc)
+                            && mPhone.getServiceState().getVoiceRoaming()
+                            && !mPhone.supports3gppCallForwardingWhileRoaming()) {
+                        sb.append(mContext.getText(
+                                com.android.internal.R.string.mmiErrorWhileRoaming));
+                    } else {
+                        sb.append(getErrorMessage(ar));
+                    }
                 } else {
                     sb.append(getErrorMessage(ar));
                 }
@@ -1281,6 +1305,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onSetComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1362,6 +1387,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onGetClirComplete: mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1514,6 +1540,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this);
         mPhone.onMMIDone(this);
 
     }
@@ -1551,6 +1578,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryComplete: mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1587,6 +1615,10 @@
         return sb;
     }
 
+    public ResultReceiver getUssdCallbackReceiver() {
+        return this.mCallbackReceiver;
+    }
+
     /***
      * TODO: It would be nice to have a method here that can take in a dialstring and
      * figure out if there is an MMI code embedded within it.  This code would replace
@@ -1601,12 +1633,15 @@
         sb.append("State=" + getState());
         if (mAction != null) sb.append(" action=" + mAction);
         if (mSc != null) sb.append(" sc=" + mSc);
-        if (mSia != null) sb.append(" sia=" + mSia);
-        if (mSib != null) sb.append(" sib=" + mSib);
-        if (mSic != null) sb.append(" sic=" + mSic);
-        if (mPoundString != null) sb.append(" poundString=" + mPoundString);
-        if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
-        if (mPwd != null) sb.append(" pwd=" + mPwd);
+        if (mSia != null) sb.append(" sia=" + Rlog.pii(LOG_TAG, mSia));
+        if (mSib != null) sb.append(" sib=" + Rlog.pii(LOG_TAG, mSib));
+        if (mSic != null) sb.append(" sic=" + Rlog.pii(LOG_TAG, mSic));
+        if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString));
+        if (mDialingNumber != null) {
+            sb.append(" dialingNumber=" + Rlog.pii(LOG_TAG, mDialingNumber));
+        }
+        if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd));
+        if (mCallbackReceiver != null) sb.append(" hasReceiver");
         sb.append("}");
         return sb.toString();
     }
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 15981aa..8f18c61 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -117,8 +117,8 @@
      *           be a String representing the status report PDU, as ASCII hex.
      */
     private void handleStatusReport(AsyncResult ar) {
-        String pduString = (String) ar.result;
-        SmsMessage sms = SmsMessage.newFromCDS(pduString);
+        byte[] pdu = (byte[]) ar.result;
+        SmsMessage sms = SmsMessage.newFromCDS(pdu);
 
         if (sms != null) {
             int tpStatus = sms.getStatus();
@@ -134,7 +134,7 @@
                     }
                     PendingIntent intent = tracker.mDeliveryIntent;
                     Intent fillIn = new Intent();
-                    fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
+                    fillIn.putExtra("pdu", pdu);
                     fillIn.putExtra("format", getFormat());
                     try {
                         intent.send(mContext, Activity.RESULT_OK, fillIn);
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/src/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
deleted file mode 100644
index 2fbf7ed..0000000
--- a/src/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ /dev/null
@@ -1,153 +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.gsm;
-
-import android.telephony.PhoneNumberUtils;
-import java.text.ParseException;
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.SmsAddress;
-
-public class GsmSmsAddress extends SmsAddress {
-
-    static final int OFFSET_ADDRESS_LENGTH = 0;
-
-    static final int OFFSET_TOA = 1;
-
-    static final int OFFSET_ADDRESS_VALUE = 2;
-
-    /**
-     * New GsmSmsAddress from TS 23.040 9.1.2.5 Address Field
-     *
-     * @param offset the offset of the Address-Length byte
-     * @param length the length in bytes rounded up, e.g. "2 +
-     *        (addressLength + 1) / 2"
-     * @throws ParseException
-     */
-
-    public GsmSmsAddress(byte[] data, int offset, int length) throws ParseException {
-        origBytes = new byte[length];
-        System.arraycopy(data, offset, origBytes, 0, length);
-
-        // addressLength is the count of semi-octets, not bytes
-        int addressLength = origBytes[OFFSET_ADDRESS_LENGTH] & 0xff;
-
-        int toa = origBytes[OFFSET_TOA] & 0xff;
-        ton = 0x7 & (toa >> 4);
-
-        // TOA must have its high bit set
-        if ((toa & 0x80) != 0x80) {
-            throw new ParseException("Invalid TOA - high bit must be set. toa = " + toa,
-                    offset + OFFSET_TOA);
-        }
-
-        if (isAlphanumeric()) {
-            // An alphanumeric address
-            int countSeptets = addressLength * 4 / 7;
-
-            address = GsmAlphabet.gsm7BitPackedToString(origBytes,
-                    OFFSET_ADDRESS_VALUE, countSeptets);
-        } else {
-            // TS 23.040 9.1.2.5 says
-            // that "the MS shall interpret reserved values as 'Unknown'
-            // but shall store them exactly as received"
-
-            byte lastByte = origBytes[length - 1];
-
-            if ((addressLength & 1) == 1) {
-                // Make sure the final unused BCD digit is 0xf
-                origBytes[length - 1] |= 0xf0;
-            }
-            address = PhoneNumberUtils.calledPartyBCDToString(origBytes,
-                    OFFSET_TOA, length - OFFSET_TOA);
-
-            // And restore origBytes
-            origBytes[length - 1] = lastByte;
-        }
-    }
-
-    @Override
-    public String getAddressString() {
-        return address;
-    }
-
-    /**
-     * Returns true if this is an alphanumeric address
-     */
-    @Override
-    public boolean isAlphanumeric() {
-        return ton == TON_ALPHANUMERIC;
-    }
-
-    @Override
-    public boolean isNetworkSpecific() {
-        return ton == TON_NETWORK;
-    }
-
-    /**
-     * Returns true of this is a valid CPHS voice message waiting indicator
-     * address
-     */
-    public boolean isCphsVoiceMessageIndicatorAddress() {
-        // CPHS-style MWI message
-        // See CPHS 4.7 B.4.2.1
-        //
-        // Basically:
-        //
-        // - Originating address should be 4 bytes long and alphanumeric
-        // - Decode will result with two chars:
-        // - Char 1
-        // 76543210
-        // ^ set/clear indicator (0 = clear)
-        // ^^^ type of indicator (000 = voice)
-        // ^^^^ must be equal to 0001
-        // - Char 2:
-        // 76543210
-        // ^ line number (0 = line 1)
-        // ^^^^^^^ set to 0
-        //
-        // Remember, since the alpha address is stored in 7-bit compact form,
-        // the "line number" is really the top bit of the first address value
-        // byte
-
-        return (origBytes[OFFSET_ADDRESS_LENGTH] & 0xff) == 4
-                && isAlphanumeric() && (origBytes[OFFSET_TOA] & 0x0f) == 0;
-    }
-
-    /**
-     * Returns true if this is a valid CPHS voice message waiting indicator
-     * address indicating a "set" of "indicator 1" of type "voice message
-     * waiting"
-     */
-    public boolean isCphsVoiceMessageSet() {
-        // 0x11 means "set" "voice message waiting" "indicator 1"
-        return isCphsVoiceMessageIndicatorAddress()
-                && (origBytes[OFFSET_ADDRESS_VALUE] & 0xff) == 0x11;
-
-    }
-
-    /**
-     * Returns true if this is a valid CPHS voice message waiting indicator
-     * address indicating a "clear" of "indicator 1" of type "voice message
-     * waiting"
-     */
-    public boolean isCphsVoiceMessageClear() {
-        // 0x10 means "clear" "voice message waiting" "indicator 1"
-        return isCphsVoiceMessageIndicatorAddress()
-                && (origBytes[OFFSET_ADDRESS_VALUE] & 0xff) == 0x10;
-
-    }
-}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/src/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
deleted file mode 100644
index 15ed810..0000000
--- a/src/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2012 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.gsm;
-
-import android.telephony.SmsCbLocation;
-import android.telephony.SmsCbMessage;
-import android.util.Pair;
-
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.SmsConstants;
-
-import java.io.UnsupportedEncodingException;
-
-/**
- * Parses a GSM or UMTS format SMS-CB message into an {@link SmsCbMessage} object. The class is
- * public because {@link #createSmsCbMessage(SmsCbLocation, byte[][])} is used by some test cases.
- */
-public class GsmSmsCbMessage {
-
-    /**
-     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
-     */
-    private static final String[] LANGUAGE_CODES_GROUP_0 = {
-            "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
-            "pl", null
-    };
-
-    /**
-     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
-     */
-    private static final String[] LANGUAGE_CODES_GROUP_2 = {
-            "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
-            null, null
-    };
-
-    private static final char CARRIAGE_RETURN = 0x0d;
-
-    private static final int PDU_BODY_PAGE_LENGTH = 82;
-
-    /** Utility class with only static methods. */
-    private GsmSmsCbMessage() { }
-
-    /**
-     * Create a new SmsCbMessage object from a header object plus one or more received PDUs.
-     *
-     * @param pdus PDU bytes
-     */
-    public static SmsCbMessage createSmsCbMessage(SmsCbHeader header, SmsCbLocation location,
-            byte[][] pdus) throws IllegalArgumentException {
-        if (header.isEtwsPrimaryNotification()) {
-            // ETSI TS 23.041 ETWS Primary Notification message
-            // ETWS primary message only contains 4 fields including serial number,
-            // message identifier, warning type, and warning security information.
-            // There is no field for the content/text. We hardcode "ETWS" in the
-            // text body so the user won't see an empty dialog without any text.
-            return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
-                    header.getGeographicalScope(), header.getSerialNumber(),
-                    location, header.getServiceCategory(),
-                    null, "ETWS", SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY,
-                    header.getEtwsInfo(), header.getCmasInfo());
-        } else {
-            String language = null;
-            StringBuilder sb = new StringBuilder();
-            for (byte[] pdu : pdus) {
-                Pair<String, String> p = parseBody(header, pdu);
-                language = p.first;
-                sb.append(p.second);
-            }
-            int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY
-                    : SmsCbMessage.MESSAGE_PRIORITY_NORMAL;
-
-            return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
-                    header.getGeographicalScope(), header.getSerialNumber(), location,
-                    header.getServiceCategory(), language, sb.toString(), priority,
-                    header.getEtwsInfo(), header.getCmasInfo());
-        }
-    }
-
-    /**
-     * Create a new SmsCbMessage object from one or more received PDUs. This is used by some
-     * CellBroadcastReceiver test cases, because SmsCbHeader is now package local.
-     *
-     * @param location the location (geographical scope) for the message
-     * @param pdus PDU bytes
-     */
-    public static SmsCbMessage createSmsCbMessage(SmsCbLocation location, byte[][] pdus)
-            throws IllegalArgumentException {
-        SmsCbHeader header = new SmsCbHeader(pdus[0]);
-        return createSmsCbMessage(header, location, pdus);
-    }
-
-    /**
-     * Parse and unpack the body text according to the encoding in the DCS.
-     * After completing successfully this method will have assigned the body
-     * text into mBody, and optionally the language code into mLanguage
-     *
-     * @param header the message header to use
-     * @param pdu the PDU to decode
-     * @return a Pair of Strings containing the language and body of the message
-     */
-    private static Pair<String, String> parseBody(SmsCbHeader header, byte[] pdu) {
-        int encoding;
-        String language = null;
-        boolean hasLanguageIndicator = false;
-        int dataCodingScheme = header.getDataCodingScheme();
-
-        // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
-        // section 5.
-        switch ((dataCodingScheme & 0xf0) >> 4) {
-            case 0x00:
-                encoding = SmsConstants.ENCODING_7BIT;
-                language = LANGUAGE_CODES_GROUP_0[dataCodingScheme & 0x0f];
-                break;
-
-            case 0x01:
-                hasLanguageIndicator = true;
-                if ((dataCodingScheme & 0x0f) == 0x01) {
-                    encoding = SmsConstants.ENCODING_16BIT;
-                } else {
-                    encoding = SmsConstants.ENCODING_7BIT;
-                }
-                break;
-
-            case 0x02:
-                encoding = SmsConstants.ENCODING_7BIT;
-                language = LANGUAGE_CODES_GROUP_2[dataCodingScheme & 0x0f];
-                break;
-
-            case 0x03:
-                encoding = SmsConstants.ENCODING_7BIT;
-                break;
-
-            case 0x04:
-            case 0x05:
-                switch ((dataCodingScheme & 0x0c) >> 2) {
-                    case 0x01:
-                        encoding = SmsConstants.ENCODING_8BIT;
-                        break;
-
-                    case 0x02:
-                        encoding = SmsConstants.ENCODING_16BIT;
-                        break;
-
-                    case 0x00:
-                    default:
-                        encoding = SmsConstants.ENCODING_7BIT;
-                        break;
-                }
-                break;
-
-            case 0x06:
-            case 0x07:
-                // Compression not supported
-            case 0x09:
-                // UDH structure not supported
-            case 0x0e:
-                // Defined by the WAP forum not supported
-                throw new IllegalArgumentException("Unsupported GSM dataCodingScheme "
-                        + dataCodingScheme);
-
-            case 0x0f:
-                if (((dataCodingScheme & 0x04) >> 2) == 0x01) {
-                    encoding = SmsConstants.ENCODING_8BIT;
-                } else {
-                    encoding = SmsConstants.ENCODING_7BIT;
-                }
-                break;
-
-            default:
-                // Reserved values are to be treated as 7-bit
-                encoding = SmsConstants.ENCODING_7BIT;
-                break;
-        }
-
-        if (header.isUmtsFormat()) {
-            // Payload may contain multiple pages
-            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
-
-            if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
-                    * nrPages) {
-                throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
-                        + nrPages + " pages");
-            }
-
-            StringBuilder sb = new StringBuilder();
-
-            for (int i = 0; i < nrPages; i++) {
-                // Each page is 82 bytes followed by a length octet indicating
-                // the number of useful octets within those 82
-                int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
-                int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
-
-                if (length > PDU_BODY_PAGE_LENGTH) {
-                    throw new IllegalArgumentException("Page length " + length
-                            + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
-                }
-
-                Pair<String, String> p = unpackBody(pdu, encoding, offset, length,
-                        hasLanguageIndicator, language);
-                language = p.first;
-                sb.append(p.second);
-            }
-            return new Pair<String, String>(language, sb.toString());
-        } else {
-            // Payload is one single page
-            int offset = SmsCbHeader.PDU_HEADER_LENGTH;
-            int length = pdu.length - offset;
-
-            return unpackBody(pdu, encoding, offset, length, hasLanguageIndicator, language);
-        }
-    }
-
-    /**
-     * Unpack body text from the pdu using the given encoding, position and
-     * length within the pdu
-     *
-     * @param pdu The pdu
-     * @param encoding The encoding, as derived from the DCS
-     * @param offset Position of the first byte to unpack
-     * @param length Number of bytes to unpack
-     * @param hasLanguageIndicator true if the body text is preceded by a
-     *            language indicator. If so, this method will as a side-effect
-     *            assign the extracted language code into mLanguage
-     * @param language the language to return if hasLanguageIndicator is false
-     * @return a Pair of Strings containing the language and body of the message
-     */
-    private static Pair<String, String> unpackBody(byte[] pdu, int encoding, int offset, int length,
-            boolean hasLanguageIndicator, String language) {
-        String body = null;
-
-        switch (encoding) {
-            case SmsConstants.ENCODING_7BIT:
-                body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
-
-                if (hasLanguageIndicator && body != null && body.length() > 2) {
-                    // Language is two GSM characters followed by a CR.
-                    // The actual body text is offset by 3 characters.
-                    language = body.substring(0, 2);
-                    body = body.substring(3);
-                }
-                break;
-
-            case SmsConstants.ENCODING_16BIT:
-                if (hasLanguageIndicator && pdu.length >= offset + 2) {
-                    // Language is two GSM characters.
-                    // The actual body text is offset by 2 bytes.
-                    language = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
-                    offset += 2;
-                    length -= 2;
-                }
-
-                try {
-                    body = new String(pdu, offset, (length & 0xfffe), "utf-16");
-                } catch (UnsupportedEncodingException e) {
-                    // Apparently it wasn't valid UTF-16.
-                    throw new IllegalArgumentException("Error decoding UTF-16 message", e);
-                }
-                break;
-
-            default:
-                break;
-        }
-
-        if (body != null) {
-            // Remove trailing carriage return
-            for (int i = body.length() - 1; i >= 0; i--) {
-                if (body.charAt(i) != CARRIAGE_RETURN) {
-                    body = body.substring(0, i + 1);
-                    break;
-                }
-            }
-        } else {
-            body = "";
-        }
-
-        return new Pair<String, String>(language, body);
-    }
-}
diff --git a/src/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java b/src/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
deleted file mode 100644
index f4f4036..0000000
--- a/src/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2009 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.gsm;
-
-/**
- * SmsBroadcastConfigInfo defines one configuration of Cell Broadcast
- * Message (CBM) to be received by the ME
- *
- * fromServiceId - toServiceId defines a range of CBM message identifiers
- * whose value is 0x0000 - 0xFFFF as defined in TS 23.041 9.4.1.2.2 for GMS
- * and 9.4.4.2.2 for UMTS. All other values can be treated as empty
- * CBM message ID.
- *
- * fromCodeScheme - toCodeScheme defines a range of CBM data coding schemes
- * whose value is 0x00 - 0xFF as defined in TS 23.041 9.4.1.2.3 for GMS
- * and 9.4.4.2.3 for UMTS.
- * All other values can be treated as empty CBM data coding scheme.
- *
- * selected false means message types specified in {@code <fromServiceId, toServiceId>}
- * and {@code <fromCodeScheme, toCodeScheme>} are not accepted, while true means accepted.
- *
- */
-public final class SmsBroadcastConfigInfo {
-    private int mFromServiceId;
-    private int mToServiceId;
-    private int mFromCodeScheme;
-    private int mToCodeScheme;
-    private boolean mSelected;
-
-    /**
-     * Initialize the object from rssi and cid.
-     */
-    public SmsBroadcastConfigInfo(int fromId, int toId, int fromScheme,
-            int toScheme, boolean selected) {
-        mFromServiceId = fromId;
-        mToServiceId = toId;
-        mFromCodeScheme = fromScheme;
-        mToCodeScheme = toScheme;
-        mSelected = selected;
-    }
-
-    /**
-     * @param fromServiceId the fromServiceId to set
-     */
-    public void setFromServiceId(int fromServiceId) {
-        mFromServiceId = fromServiceId;
-    }
-
-    /**
-     * @return the fromServiceId
-     */
-    public int getFromServiceId() {
-        return mFromServiceId;
-    }
-
-    /**
-     * @param toServiceId the toServiceId to set
-     */
-    public void setToServiceId(int toServiceId) {
-        mToServiceId = toServiceId;
-    }
-
-    /**
-     * @return the toServiceId
-     */
-    public int getToServiceId() {
-        return mToServiceId;
-    }
-
-    /**
-     * @param fromCodeScheme the fromCodeScheme to set
-     */
-    public void setFromCodeScheme(int fromCodeScheme) {
-        mFromCodeScheme = fromCodeScheme;
-    }
-
-    /**
-     * @return the fromCodeScheme
-     */
-    public int getFromCodeScheme() {
-        return mFromCodeScheme;
-    }
-
-    /**
-     * @param toCodeScheme the toCodeScheme to set
-     */
-    public void setToCodeScheme(int toCodeScheme) {
-        mToCodeScheme = toCodeScheme;
-    }
-
-    /**
-     * @return the toCodeScheme
-     */
-    public int getToCodeScheme() {
-        return mToCodeScheme;
-    }
-
-    /**
-     * @param selected the selected to set
-     */
-    public void setSelected(boolean selected) {
-        mSelected = selected;
-    }
-
-    /**
-     * @return the selected
-     */
-    public boolean isSelected() {
-        return mSelected;
-    }
-
-    @Override
-    public String toString() {
-        return "SmsBroadcastConfigInfo: Id [" +
-                mFromServiceId + ',' + mToServiceId + "] Code [" +
-                mFromCodeScheme + ',' + mToCodeScheme + "] " +
-            (mSelected ? "ENABLED" : "DISABLED");
-    }
-}
diff --git a/src/java/com/android/internal/telephony/gsm/SmsCbConstants.java b/src/java/com/android/internal/telephony/gsm/SmsCbConstants.java
deleted file mode 100644
index bce5680..0000000
--- a/src/java/com/android/internal/telephony/gsm/SmsCbConstants.java
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2012 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.gsm;
-
-/**
- * Constants used in SMS Cell Broadcast messages (see 3GPP TS 23.041). This class is used by the
- * boot-time broadcast channel enable and database upgrade code in CellBroadcastReceiver, so it
- * is public, but should be avoided in favor of the radio technology independent constants in
- * {@link android.telephony.SmsCbMessage}, {@link android.telephony.SmsCbEtwsInfo}, and
- * {@link android.telephony.SmsCbCmasInfo} classes.
- *
- * {@hide}
- */
-public class SmsCbConstants {
-
-    /** Private constructor for utility class. */
-    private SmsCbConstants() { }
-
-    /** Channel 50 required by Brazil. ID 0~999 is allocated by GSMA */
-    public static final int MESSAGE_ID_GSMA_ALLOCATED_CHANNEL_50
-            = 0x0032;
-
-    /** Channel 911 required by Taiwan NCC. ID 0~999 is allocated by GSMA */
-    public static final int MESSAGE_ID_GSMA_ALLOCATED_CHANNEL_911
-            = 0x038F; // 911
-
-    /** Channel 919 required by Taiwan NCC and Israel. ID 0~999 is allocated by GSMA */
-    public static final int MESSAGE_ID_GSMA_ALLOCATED_CHANNEL_919
-            = 0x0397; // 919
-
-    /** Channel 928 required by Israel. ID 0~999 is allocated by GSMA */
-    public static final int MESSAGE_ID_GSMA_ALLOCATED_CHANNEL_928
-            = 0x03A0; // 928
-
-    /** Start of PWS Message Identifier range (includes ETWS and CMAS). */
-    public static final int MESSAGE_ID_PWS_FIRST_IDENTIFIER
-            = 0x1100; // 4352
-
-    /** Bitmask for messages of ETWS type (including future extensions). */
-    public static final int MESSAGE_ID_ETWS_TYPE_MASK
-            = 0xFFF8;
-
-    /** Value for messages of ETWS type after applying {@link #MESSAGE_ID_ETWS_TYPE_MASK}. */
-    public static final int MESSAGE_ID_ETWS_TYPE
-            = 0x1100; // 4352
-
-    /** ETWS Message Identifier for earthquake warning message. */
-    public static final int MESSAGE_ID_ETWS_EARTHQUAKE_WARNING
-            = 0x1100; // 4352
-
-    /** ETWS Message Identifier for tsunami warning message. */
-    public static final int MESSAGE_ID_ETWS_TSUNAMI_WARNING
-            = 0x1101; // 4353
-
-    /** ETWS Message Identifier for earthquake and tsunami combined warning message. */
-    public static final int MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING
-            = 0x1102; // 4354
-
-    /** ETWS Message Identifier for test message. */
-    public static final int MESSAGE_ID_ETWS_TEST_MESSAGE
-            = 0x1103; // 4355
-
-    /** ETWS Message Identifier for messages related to other emergency types. */
-    public static final int MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE
-            = 0x1104; // 4356
-
-    /** Start of CMAS Message Identifier range. */
-    public static final int MESSAGE_ID_CMAS_FIRST_IDENTIFIER
-            = 0x1112; // 4370
-
-    /** CMAS Message Identifier for Presidential Level alerts. */
-    public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL
-            = 0x1112; // 4370
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED
-            = 0x1113; // 4371
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY
-            = 0x1114; // 4372
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED
-            = 0x1115; // 4373
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY
-            = 0x1116; // 4374
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed. */
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED
-            = 0x1117; // 4375
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely. */
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY
-            = 0x1118; // 4376
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed. */
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED
-            = 0x1119; // 4377
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely. */
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY
-            = 0x111A; // 4378
-
-    /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert). */
-    public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY
-            = 0x111B; // 4379
-
-    /** CMAS Message Identifier for the Required Monthly Test. */
-    public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST
-            = 0x111C; // 4380
-
-    /** CMAS Message Identifier for CMAS Exercise. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE
-            = 0x111D; // 4381
-
-    /** CMAS Message Identifier for operator defined use. */
-    public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE
-            = 0x111E; // 4382
-
-    /** CMAS Message Identifier for Presidential Level alerts for additional languages
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE
-            = 0x111F; // 4383
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE
-            = 0x1120; // 4384
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE
-            = 0x1121; // 4385
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE
-            = 0x1122; // 4386
-
-    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE
-            = 0x1123; // 4387
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE
-            = 0x1124; // 4388
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely
-     *  for additional languages.*/
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE
-            = 0x1125; // 4389
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE
-            = 0x1126; // 4390
-
-    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely
-     *  for additional languages.*/
-    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE
-            = 0x1127; // 4391
-
-    /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert)
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE
-            = 0x1128; // 4392
-
-    /** CMAS Message Identifier for the Required Monthly Test
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE
-            = 0x1129; // 4393
-
-    /** CMAS Message Identifier for CMAS Exercise
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE
-            = 0x112A; // 4394
-
-    /** CMAS Message Identifier for operator defined use
-     *  for additional languages. */
-    public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE
-            = 0x112B; // 4395
-
-    /** End of CMAS Message Identifier range (including future extensions). */
-    public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER
-            = 0x112F; // 4399
-
-    /** End of PWS Message Identifier range (includes ETWS, CMAS, and future extensions). */
-    public static final int MESSAGE_ID_PWS_LAST_IDENTIFIER
-            = 0x18FF; // 6399
-
-    /** ETWS serial number flag to activate the popup display. */
-    public static final int SERIAL_NUMBER_ETWS_ACTIVATE_POPUP
-            = 0x1000; // 4096
-
-    /** ETWS serial number flag to activate the emergency user alert. */
-    public static final int SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT
-            = 0x2000; // 8192
-}
diff --git a/src/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/src/java/com/android/internal/telephony/gsm/SmsCbHeader.java
deleted file mode 100644
index d267ad2..0000000
--- a/src/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ /dev/null
@@ -1,450 +0,0 @@
-/*
- * Copyright (C) 2010 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.gsm;
-
-import android.telephony.SmsCbCmasInfo;
-import android.telephony.SmsCbEtwsInfo;
-
-import java.util.Arrays;
-
-/**
- * Parses a 3GPP TS 23.041 cell broadcast message header. This class is public for use by
- * CellBroadcastReceiver test cases, but should not be used by applications.
- *
- * All relevant header information is now sent as a Parcelable
- * {@link android.telephony.SmsCbMessage} object in the "message" extra of the
- * {@link android.provider.Telephony.Sms.Intents#SMS_CB_RECEIVED_ACTION} or
- * {@link android.provider.Telephony.Sms.Intents#SMS_EMERGENCY_CB_RECEIVED_ACTION} intent.
- * The raw PDU is no longer sent to SMS CB applications.
- */
-public class SmsCbHeader {
-
-    /**
-     * Length of SMS-CB header
-     */
-    static final int PDU_HEADER_LENGTH = 6;
-
-    /**
-     * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
-     */
-    static final int FORMAT_GSM = 1;
-
-    /**
-     * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
-     */
-    static final int FORMAT_UMTS = 2;
-
-    /**
-     * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
-     */
-    static final int FORMAT_ETWS_PRIMARY = 3;
-
-    /**
-     * Message type value as defined in 3gpp TS 25.324, section 11.1.
-     */
-    private static final int MESSAGE_TYPE_CBS_MESSAGE = 1;
-
-    /**
-     * Length of GSM pdus
-     */
-    private static final int PDU_LENGTH_GSM = 88;
-
-    /**
-     * Maximum length of ETWS primary message GSM pdus
-     */
-    private static final int PDU_LENGTH_ETWS = 56;
-
-    private final int mGeographicalScope;
-
-    /** The serial number combines geographical scope, message code, and update number. */
-    private final int mSerialNumber;
-
-    /** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */
-    private final int mMessageIdentifier;
-
-    private final int mDataCodingScheme;
-
-    private final int mPageIndex;
-
-    private final int mNrOfPages;
-
-    private final int mFormat;
-
-    /** ETWS warning notification info. */
-    private final SmsCbEtwsInfo mEtwsInfo;
-
-    /** CMAS warning notification info. */
-    private final SmsCbCmasInfo mCmasInfo;
-
-    public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
-        if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
-            throw new IllegalArgumentException("Illegal PDU");
-        }
-
-        if (pdu.length <= PDU_LENGTH_GSM) {
-            // can be ETWS or GSM format.
-            // Per TS23.041 9.4.1.2 and 9.4.1.3.2, GSM and ETWS format both
-            // contain serial number which contains GS, Message Code, and Update Number
-            // per 9.4.1.2.1, and message identifier in same octets
-            mGeographicalScope = (pdu[0] & 0xc0) >>> 6;
-            mSerialNumber = ((pdu[0] & 0xff) << 8) | (pdu[1] & 0xff);
-            mMessageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
-            if (isEtwsMessage() && pdu.length <= PDU_LENGTH_ETWS) {
-                mFormat = FORMAT_ETWS_PRIMARY;
-                mDataCodingScheme = -1;
-                mPageIndex = -1;
-                mNrOfPages = -1;
-                boolean emergencyUserAlert = (pdu[4] & 0x1) != 0;
-                boolean activatePopup = (pdu[5] & 0x80) != 0;
-                int warningType = (pdu[4] & 0xfe) >>> 1;
-                byte[] warningSecurityInfo;
-                // copy the Warning-Security-Information, if present
-                if (pdu.length > PDU_HEADER_LENGTH) {
-                    warningSecurityInfo = Arrays.copyOfRange(pdu, 6, pdu.length);
-                } else {
-                    warningSecurityInfo = null;
-                }
-                mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup,
-                        true, warningSecurityInfo);
-                mCmasInfo = null;
-                return;     // skip the ETWS/CMAS initialization code for regular notifications
-            } else {
-                // GSM pdus are no more than 88 bytes
-                mFormat = FORMAT_GSM;
-                mDataCodingScheme = pdu[4] & 0xff;
-
-                // Check for invalid page parameter
-                int pageIndex = (pdu[5] & 0xf0) >>> 4;
-                int nrOfPages = pdu[5] & 0x0f;
-
-                if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
-                    pageIndex = 1;
-                    nrOfPages = 1;
-                }
-
-                mPageIndex = pageIndex;
-                mNrOfPages = nrOfPages;
-            }
-        } else {
-            // UMTS pdus are always at least 90 bytes since the payload includes
-            // a number-of-pages octet and also one length octet per page
-            mFormat = FORMAT_UMTS;
-
-            int messageType = pdu[0];
-
-            if (messageType != MESSAGE_TYPE_CBS_MESSAGE) {
-                throw new IllegalArgumentException("Unsupported message type " + messageType);
-            }
-
-            mMessageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff;
-            mGeographicalScope = (pdu[3] & 0xc0) >>> 6;
-            mSerialNumber = ((pdu[3] & 0xff) << 8) | (pdu[4] & 0xff);
-            mDataCodingScheme = pdu[5] & 0xff;
-
-            // We will always consider a UMTS message as having one single page
-            // since there's only one instance of the header, even though the
-            // actual payload may contain several pages.
-            mPageIndex = 1;
-            mNrOfPages = 1;
-        }
-
-        if (isEtwsMessage()) {
-            boolean emergencyUserAlert = isEtwsEmergencyUserAlert();
-            boolean activatePopup = isEtwsPopupAlert();
-            int warningType = getEtwsWarningType();
-            mEtwsInfo = new SmsCbEtwsInfo(warningType, emergencyUserAlert, activatePopup,
-                    false, null);
-            mCmasInfo = null;
-        } else if (isCmasMessage()) {
-            int messageClass = getCmasMessageClass();
-            int severity = getCmasSeverity();
-            int urgency = getCmasUrgency();
-            int certainty = getCmasCertainty();
-            mEtwsInfo = null;
-            mCmasInfo = new SmsCbCmasInfo(messageClass, SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN,
-                    SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN, severity, urgency, certainty);
-        } else {
-            mEtwsInfo = null;
-            mCmasInfo = null;
-        }
-    }
-
-    int getGeographicalScope() {
-        return mGeographicalScope;
-    }
-
-    int getSerialNumber() {
-        return mSerialNumber;
-    }
-
-    int getServiceCategory() {
-        return mMessageIdentifier;
-    }
-
-    int getDataCodingScheme() {
-        return mDataCodingScheme;
-    }
-
-    int getPageIndex() {
-        return mPageIndex;
-    }
-
-    int getNumberOfPages() {
-        return mNrOfPages;
-    }
-
-    SmsCbEtwsInfo getEtwsInfo() {
-        return mEtwsInfo;
-    }
-
-    SmsCbCmasInfo getCmasInfo() {
-        return mCmasInfo;
-    }
-
-    /**
-     * Return whether this broadcast is an emergency (PWS) message type.
-     * @return true if this message is emergency type; false otherwise
-     */
-    boolean isEmergencyMessage() {
-        return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER
-                && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER;
-    }
-
-    /**
-     * Return whether this broadcast is an ETWS emergency message type.
-     * @return true if this message is ETWS emergency type; false otherwise
-     */
-    private boolean isEtwsMessage() {
-        return (mMessageIdentifier & SmsCbConstants.MESSAGE_ID_ETWS_TYPE_MASK)
-                == SmsCbConstants.MESSAGE_ID_ETWS_TYPE;
-    }
-
-    /**
-     * Return whether this broadcast is an ETWS primary notification.
-     * @return true if this message is an ETWS primary notification; false otherwise
-     */
-    boolean isEtwsPrimaryNotification() {
-        return mFormat == FORMAT_ETWS_PRIMARY;
-    }
-
-    /**
-     * Return whether this broadcast is in UMTS format.
-     * @return true if this message is in UMTS format; false otherwise
-     */
-    boolean isUmtsFormat() {
-        return mFormat == FORMAT_UMTS;
-    }
-
-    /**
-     * Return whether this message is a CMAS emergency message type.
-     * @return true if this message is CMAS emergency type; false otherwise
-     */
-    private boolean isCmasMessage() {
-        return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_CMAS_FIRST_IDENTIFIER
-                && mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_CMAS_LAST_IDENTIFIER;
-    }
-
-    /**
-     * Return whether the popup alert flag is set for an ETWS warning notification.
-     * This method assumes that the message ID has already been checked for ETWS type.
-     *
-     * @return true if the message code indicates a popup alert should be displayed
-     */
-    private boolean isEtwsPopupAlert() {
-        return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_ACTIVATE_POPUP) != 0;
-    }
-
-    /**
-     * Return whether the emergency user alert flag is set for an ETWS warning notification.
-     * This method assumes that the message ID has already been checked for ETWS type.
-     *
-     * @return true if the message code indicates an emergency user alert
-     */
-    private boolean isEtwsEmergencyUserAlert() {
-        return (mSerialNumber & SmsCbConstants.SERIAL_NUMBER_ETWS_EMERGENCY_USER_ALERT) != 0;
-    }
-
-    /**
-     * Returns the warning type for an ETWS warning notification.
-     * This method assumes that the message ID has already been checked for ETWS type.
-     *
-     * @return the ETWS warning type defined in 3GPP TS 23.041 section 9.3.24
-     */
-    private int getEtwsWarningType() {
-        return mMessageIdentifier - SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING;
-    }
-
-    /**
-     * Returns the message class for a CMAS warning notification.
-     * This method assumes that the message ID has already been checked for CMAS type.
-     * @return the CMAS message class as defined in {@link SmsCbCmasInfo}
-     */
-    private int getCmasMessageClass() {
-        switch (mMessageIdentifier) {
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_EXTREME_THREAT;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_SEVERE_THREAT;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_REQUIRED_MONTHLY_TEST;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_CMAS_EXERCISE;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CLASS_OPERATOR_DEFINED_USE;
-
-            default:
-                return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN;
-        }
-    }
-
-    /**
-     * Returns the severity for a CMAS warning notification. This is only available for extreme
-     * and severe alerts, not for other types such as Presidential Level and AMBER alerts.
-     * This method assumes that the message ID has already been checked for CMAS type.
-     * @return the CMAS severity as defined in {@link SmsCbCmasInfo}
-     */
-    private int getCmasSeverity() {
-        switch (mMessageIdentifier) {
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_SEVERITY_EXTREME;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_SEVERITY_SEVERE;
-
-            default:
-                return SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN;
-        }
-    }
-
-    /**
-     * Returns the urgency for a CMAS warning notification. This is only available for extreme
-     * and severe alerts, not for other types such as Presidential Level and AMBER alerts.
-     * This method assumes that the message ID has already been checked for CMAS type.
-     * @return the CMAS urgency as defined in {@link SmsCbCmasInfo}
-     */
-    private int getCmasUrgency() {
-        switch (mMessageIdentifier) {
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_URGENCY_IMMEDIATE;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_URGENCY_EXPECTED;
-
-            default:
-                return SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN;
-        }
-    }
-
-    /**
-     * Returns the certainty for a CMAS warning notification. This is only available for extreme
-     * and severe alerts, not for other types such as Presidential Level and AMBER alerts.
-     * This method assumes that the message ID has already been checked for CMAS type.
-     * @return the CMAS certainty as defined in {@link SmsCbCmasInfo}
-     */
-    private int getCmasCertainty() {
-        switch (mMessageIdentifier) {
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CERTAINTY_OBSERVED;
-
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY:
-            case SmsCbConstants.MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE:
-                return SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY;
-
-            default:
-                return SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN;
-        }
-    }
-
-    @Override
-    public String toString() {
-        return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" +
-                Integer.toHexString(mSerialNumber) +
-                ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) +
-                ", DCS=0x" + Integer.toHexString(mDataCodingScheme) +
-                ", page " + mPageIndex + " of " + mNrOfPages + '}';
-    }
-}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/gsm/SmsMessage.java b/src/java/com/android/internal/telephony/gsm/SmsMessage.java
deleted file mode 100644
index 0d9bed4..0000000
--- a/src/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ /dev/null
@@ -1,1369 +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.gsm;
-
-import android.telephony.PhoneNumberUtils;
-import android.text.format.Time;
-import android.telephony.Rlog;
-import android.content.res.Resources;
-import android.text.TextUtils;
-
-import com.android.internal.telephony.EncodeException;
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.uicc.IccUtils;
-import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
-
-import java.io.ByteArrayOutputStream;
-import java.io.UnsupportedEncodingException;
-import java.text.ParseException;
-
-import static com.android.internal.telephony.SmsConstants.MessageClass;
-import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
-import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
-
-/**
- * A Short Message Service message.
- *
- */
-public class SmsMessage extends SmsMessageBase {
-    static final String LOG_TAG = "SmsMessage";
-    private static final boolean VDBG = false;
-
-    private MessageClass messageClass;
-
-    /**
-     * TP-Message-Type-Indicator
-     * 9.2.3
-     */
-    private int mMti;
-
-    /** TP-Protocol-Identifier (TP-PID) */
-    private int mProtocolIdentifier;
-
-    // TP-Data-Coding-Scheme
-    // see TS 23.038
-    private int mDataCodingScheme;
-
-    // TP-Reply-Path
-    // e.g. 23.040 9.2.2.1
-    private boolean mReplyPathPresent = false;
-
-    /** The address of the receiver. */
-    private GsmSmsAddress mRecipientAddress;
-
-    /**
-     *  TP-Status - status of a previously submitted SMS.
-     *  This field applies to SMS-STATUS-REPORT messages.  0 indicates success;
-     *  see TS 23.040, 9.2.3.15 for description of other possible values.
-     */
-    private int mStatus;
-
-    /**
-     *  TP-Status - status of a previously submitted SMS.
-     *  This field is true iff the message is a SMS-STATUS-REPORT message.
-     */
-    private boolean mIsStatusReportMessage = false;
-
-    private int mVoiceMailCount = 0;
-
-    public static class SubmitPdu extends SubmitPduBase {
-    }
-
-    /**
-     * Create an SmsMessage from a raw PDU.
-     */
-    public static SmsMessage createFromPdu(byte[] pdu) {
-        try {
-            SmsMessage msg = new SmsMessage();
-            msg.parsePdu(pdu);
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
-            return null;
-        } catch (OutOfMemoryError e) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e);
-            return null;
-        }
-    }
-
-    /**
-     * 3GPP TS 23.040 9.2.3.9 specifies that Type Zero messages are indicated
-     * by TP_PID field set to value 0x40
-     */
-    public boolean isTypeZero() {
-        return (mProtocolIdentifier == 0x40);
-    }
-
-    /**
-     * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the
-     * +CMT unsolicited response (PDU mode, of course)
-     *  +CMT: [&lt;alpha>],<length><CR><LF><pdu>
-     *
-     * Only public for debugging
-     *
-     * {@hide}
-     */
-    public static SmsMessage newFromCMT(String[] lines) {
-        try {
-            SmsMessage msg = new SmsMessage();
-            msg.parsePdu(IccUtils.hexStringToBytes(lines[1]));
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
-            return null;
-        }
-    }
-
-    /** @hide */
-    public static SmsMessage newFromCDS(String line) {
-        try {
-            SmsMessage msg = new SmsMessage();
-            msg.parsePdu(IccUtils.hexStringToBytes(line));
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "CDS SMS PDU parsing failed: ", ex);
-            return null;
-        }
-    }
-
-    /**
-     * Create an SmsMessage from an SMS EF record.
-     *
-     * @param index Index of SMS record. This should be index in ArrayList
-     *              returned by SmsManager.getAllMessagesFromSim + 1.
-     * @param data Record data.
-     * @return An SmsMessage representing the record.
-     *
-     * @hide
-     */
-    public static SmsMessage createFromEfRecord(int index, byte[] data) {
-        try {
-            SmsMessage msg = new SmsMessage();
-
-            msg.mIndexOnIcc = index;
-
-            // First byte is status: RECEIVED_READ, RECEIVED_UNREAD, STORED_SENT,
-            // or STORED_UNSENT
-            // See TS 51.011 10.5.3
-            if ((data[0] & 1) == 0) {
-                Rlog.w(LOG_TAG,
-                        "SMS parsing failed: Trying to parse a free record");
-                return null;
-            } else {
-                msg.mStatusOnIcc = data[0] & 0x07;
-            }
-
-            int size = data.length - 1;
-
-            // Note: Data may include trailing FF's.  That's OK; message
-            // should still parse correctly.
-            byte[] pdu = new byte[size];
-            System.arraycopy(data, 1, pdu, 0, size);
-            msg.parsePdu(pdu);
-            return msg;
-        } catch (RuntimeException ex) {
-            Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
-            return null;
-        }
-    }
-
-    /**
-     * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the
-     * length in bytes (not hex chars) less the SMSC header
-     */
-    public static int getTPLayerLengthForPDU(String pdu) {
-        int len = pdu.length() / 2;
-        int smscLen = Integer.parseInt(pdu.substring(0, 2), 16);
-
-        return len - smscLen - 1;
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
-     *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     * @hide
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, String message,
-            boolean statusReportRequested, byte[] header) {
-        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
-                ENCODING_UNKNOWN, 0, 0);
-    }
-
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message using the
-     * specified encoding.
-     *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @param encoding Encoding defined by constants in
-     *        com.android.internal.telephony.SmsConstants.ENCODING_*
-     * @param languageTable
-     * @param languageShiftTable
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     * @hide
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, String message,
-            boolean statusReportRequested, byte[] header, int encoding,
-            int languageTable, int languageShiftTable) {
-
-        // Perform null parameter checks.
-        if (message == null || destinationAddress == null) {
-            return null;
-        }
-
-        if (encoding == ENCODING_UNKNOWN) {
-            // Find the best encoding to use
-            TextEncodingDetails ted = calculateLength(message, false);
-            encoding = ted.codeUnitSize;
-            languageTable = ted.languageTable;
-            languageShiftTable = ted.languageShiftTable;
-
-            if (encoding == ENCODING_7BIT &&
-                    (languageTable != 0 || languageShiftTable != 0)) {
-                if (header != null) {
-                    SmsHeader smsHeader = SmsHeader.fromByteArray(header);
-                    if (smsHeader.languageTable != languageTable
-                            || smsHeader.languageShiftTable != languageShiftTable) {
-                        Rlog.w(LOG_TAG, "Updating language table in SMS header: "
-                                + smsHeader.languageTable + " -> " + languageTable + ", "
-                                + smsHeader.languageShiftTable + " -> " + languageShiftTable);
-                        smsHeader.languageTable = languageTable;
-                        smsHeader.languageShiftTable = languageShiftTable;
-                        header = SmsHeader.toByteArray(smsHeader);
-                    }
-                } else {
-                    SmsHeader smsHeader = new SmsHeader();
-                    smsHeader.languageTable = languageTable;
-                    smsHeader.languageShiftTable = languageShiftTable;
-                    header = SmsHeader.toByteArray(smsHeader);
-                }
-            }
-        }
-
-        SubmitPdu ret = new SubmitPdu();
-        // MTI = SMS-SUBMIT, UDHI = header != null
-        byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00));
-        ByteArrayOutputStream bo = getSubmitPduHead(
-                scAddress, destinationAddress, mtiByte,
-                statusReportRequested, ret);
-
-        // User Data (and length)
-        byte[] userData;
-        try {
-            if (encoding == ENCODING_7BIT) {
-                userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
-                        languageTable, languageShiftTable);
-            } else { //assume UCS-2
-                try {
-                    userData = encodeUCS2(message, header);
-                } catch(UnsupportedEncodingException uex) {
-                    Rlog.e(LOG_TAG,
-                            "Implausible UnsupportedEncodingException ",
-                            uex);
-                    return null;
-                }
-            }
-        } catch (EncodeException ex) {
-            // Encoding to the 7-bit alphabet failed. Let's see if we can
-            // send it as a UCS-2 encoded message
-            try {
-                userData = encodeUCS2(message, header);
-                encoding = ENCODING_16BIT;
-            } catch(UnsupportedEncodingException uex) {
-                Rlog.e(LOG_TAG,
-                        "Implausible UnsupportedEncodingException ",
-                        uex);
-                return null;
-            }
-        }
-
-        if (encoding == ENCODING_7BIT) {
-            if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
-                // Message too long
-                Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
-                return null;
-            }
-            // TP-Data-Coding-Scheme
-            // Default encoding, uncompressed
-            // To test writing messages to the SIM card, change this value 0x00
-            // to 0x12, which means "bits 1 and 0 contain message class, and the
-            // class is 2". Note that this takes effect for the sender. In other
-            // words, messages sent by the phone with this change will end up on
-            // the receiver's SIM card. You can then send messages to yourself
-            // (on a phone with this change) and they'll end up on the SIM card.
-            bo.write(0x00);
-        } else { // assume UCS-2
-            if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
-                // Message too long
-                Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
-                return null;
-            }
-            // TP-Data-Coding-Scheme
-            // UCS-2 encoding, uncompressed
-            bo.write(0x08);
-        }
-
-        // (no TP-Validity-Period)
-        bo.write(userData, 0, userData.length);
-        ret.encodedMessage = bo.toByteArray();
-        return ret;
-    }
-
-    /**
-     * Packs header and UCS-2 encoded message. Includes TP-UDL & TP-UDHL if necessary
-     *
-     * @return encoded message as UCS2
-     * @throws UnsupportedEncodingException
-     */
-    private static byte[] encodeUCS2(String message, byte[] header)
-        throws UnsupportedEncodingException {
-        byte[] userData, textPart;
-        textPart = message.getBytes("utf-16be");
-
-        if (header != null) {
-            // Need 1 byte for UDHL
-            userData = new byte[header.length + textPart.length + 1];
-
-            userData[0] = (byte)header.length;
-            System.arraycopy(header, 0, userData, 1, header.length);
-            System.arraycopy(textPart, 0, userData, header.length + 1, textPart.length);
-        }
-        else {
-            userData = textPart;
-        }
-        byte[] ret = new byte[userData.length+1];
-        ret[0] = (byte) (userData.length & 0xff );
-        System.arraycopy(userData, 0, ret, 1, userData.length);
-        return ret;
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a destination address and a message
-     *
-     * @param scAddress Service Centre address.  Null means use default.
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, String message,
-            boolean statusReportRequested) {
-
-        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, null);
-    }
-
-    /**
-     * Get an SMS-SUBMIT PDU for a data message to a destination address &amp; port
-     *
-     * @param scAddress Service Centre address. null == use default
-     * @param destinationAddress the address of the destination for the message
-     * @param destinationPort the port to deliver the message to at the
-     *        destination
-     * @param data the data for the message
-     * @return a <code>SubmitPdu</code> containing the encoded SC
-     *         address, if applicable, and the encoded message.
-     *         Returns null on encode error.
-     */
-    public static SubmitPdu getSubmitPdu(String scAddress,
-            String destinationAddress, int destinationPort, byte[] data,
-            boolean statusReportRequested) {
-
-        SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
-        portAddrs.destPort = destinationPort;
-        portAddrs.origPort = 0;
-        portAddrs.areEightBits = false;
-
-        SmsHeader smsHeader = new SmsHeader();
-        smsHeader.portAddrs = portAddrs;
-
-        byte[] smsHeaderData = SmsHeader.toByteArray(smsHeader);
-
-        if ((data.length + smsHeaderData.length + 1) > MAX_USER_DATA_BYTES) {
-            Rlog.e(LOG_TAG, "SMS data message may only contain "
-                    + (MAX_USER_DATA_BYTES - smsHeaderData.length - 1) + " bytes");
-            return null;
-        }
-
-        SubmitPdu ret = new SubmitPdu();
-        ByteArrayOutputStream bo = getSubmitPduHead(
-                scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
-                                                            // TP-UDHI = true
-                statusReportRequested, ret);
-
-        // TP-Data-Coding-Scheme
-        // No class, 8 bit data
-        bo.write(0x04);
-
-        // (no TP-Validity-Period)
-
-        // Total size
-        bo.write(data.length + smsHeaderData.length + 1);
-
-        // User data header
-        bo.write(smsHeaderData.length);
-        bo.write(smsHeaderData, 0, smsHeaderData.length);
-
-        // User data
-        bo.write(data, 0, data.length);
-
-        ret.encodedMessage = bo.toByteArray();
-        return ret;
-    }
-
-    /**
-     * Create the beginning of a SUBMIT PDU.  This is the part of the
-     * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
-     * one of which takes a byte array and the other of which takes a
-     * <code>String</code>.
-     *
-     * @param scAddress Service Centre address. null == use default
-     * @param destinationAddress the address of the destination for the message
-     * @param mtiByte
-     * @param ret <code>SubmitPdu</code> containing the encoded SC
-     *        address, if applicable, and the encoded message
-     */
-    private static ByteArrayOutputStream getSubmitPduHead(
-            String scAddress, String destinationAddress, byte mtiByte,
-            boolean statusReportRequested, SubmitPdu ret) {
-        ByteArrayOutputStream bo = new ByteArrayOutputStream(
-                MAX_USER_DATA_BYTES + 40);
-
-        // SMSC address with length octet, or 0
-        if (scAddress == null) {
-            ret.encodedScAddress = null;
-        } else {
-            ret.encodedScAddress = PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
-                    scAddress);
-        }
-
-        // TP-Message-Type-Indicator (and friends)
-        if (statusReportRequested) {
-            // Set TP-Status-Report-Request bit.
-            mtiByte |= 0x20;
-            if (VDBG) Rlog.d(LOG_TAG, "SMS status report requested");
-        }
-        bo.write(mtiByte);
-
-        // space for TP-Message-Reference
-        bo.write(0);
-
-        byte[] daBytes;
-
-        daBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(destinationAddress);
-
-        // destination address length in BCD digits, ignoring TON byte and pad
-        // TODO Should be better.
-        bo.write((daBytes.length - 1) * 2
-                - ((daBytes[daBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
-
-        // destination address
-        bo.write(daBytes, 0, daBytes.length);
-
-        // TP-Protocol-Identifier
-        bo.write(0);
-        return bo;
-    }
-
-    private static class PduParser {
-        byte mPdu[];
-        int mCur;
-        SmsHeader mUserDataHeader;
-        byte[] mUserData;
-        int mUserDataSeptetPadding;
-
-        PduParser(byte[] pdu) {
-            mPdu = pdu;
-            mCur = 0;
-            mUserDataSeptetPadding = 0;
-        }
-
-        /**
-         * Parse and return the SC address prepended to SMS messages coming via
-         * the TS 27.005 / AT interface.  Returns null on invalid address
-         */
-        String getSCAddress() {
-            int len;
-            String ret;
-
-            // length of SC Address
-            len = getByte();
-
-            if (len == 0) {
-                // no SC address
-                ret = null;
-            } else {
-                // SC address
-                try {
-                    ret = PhoneNumberUtils
-                            .calledPartyBCDToString(mPdu, mCur, len);
-                } catch (RuntimeException tr) {
-                    Rlog.d(LOG_TAG, "invalid SC address: ", tr);
-                    ret = null;
-                }
-            }
-
-            mCur += len;
-
-            return ret;
-        }
-
-        /**
-         * returns non-sign-extended byte value
-         */
-        int getByte() {
-            return mPdu[mCur++] & 0xff;
-        }
-
-        /**
-         * Any address except the SC address (eg, originating address) See TS
-         * 23.040 9.1.2.5
-         */
-        GsmSmsAddress getAddress() {
-            GsmSmsAddress ret;
-
-            // "The Address-Length field is an integer representation of
-            // the number field, i.e. excludes any semi-octet containing only
-            // fill bits."
-            // The TOA field is not included as part of this
-            int addressLength = mPdu[mCur] & 0xff;
-            int lengthBytes = 2 + (addressLength + 1) / 2;
-
-            try {
-                ret = new GsmSmsAddress(mPdu, mCur, lengthBytes);
-            } catch (ParseException e) {
-                ret = null;
-                //This is caught by createFromPdu(byte[] pdu)
-                throw new RuntimeException(e.getMessage());
-            }
-
-            mCur += lengthBytes;
-
-            return ret;
-        }
-
-        /**
-         * Parses an SC timestamp and returns a currentTimeMillis()-style
-         * timestamp
-         */
-
-        long getSCTimestampMillis() {
-            // TP-Service-Centre-Time-Stamp
-            int year = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
-            int month = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
-            int day = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
-            int hour = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
-            int minute = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
-            int second = IccUtils.gsmBcdByteToInt(mPdu[mCur++]);
-
-            // For the timezone, the most significant bit of the
-            // least significant nibble is the sign byte
-            // (meaning the max range of this field is 79 quarter-hours,
-            // which is more than enough)
-
-            byte tzByte = mPdu[mCur++];
-
-            // Mask out sign bit.
-            int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
-
-            timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
-
-            Time time = new Time(Time.TIMEZONE_UTC);
-
-            // It's 2006.  Should I really support years < 2000?
-            time.year = year >= 90 ? year + 1900 : year + 2000;
-            time.month = month - 1;
-            time.monthDay = day;
-            time.hour = hour;
-            time.minute = minute;
-            time.second = second;
-
-            // Timezone offset is in quarter hours.
-            return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000);
-        }
-
-        /**
-         * Pulls the user data out of the PDU, and separates the payload from
-         * the header if there is one.
-         *
-         * @param hasUserDataHeader true if there is a user data header
-         * @param dataInSeptets true if the data payload is in septets instead
-         *  of octets
-         * @return the number of septets or octets in the user data payload
-         */
-        int constructUserData(boolean hasUserDataHeader, boolean dataInSeptets) {
-            int offset = mCur;
-            int userDataLength = mPdu[offset++] & 0xff;
-            int headerSeptets = 0;
-            int userDataHeaderLength = 0;
-
-            if (hasUserDataHeader) {
-                userDataHeaderLength = mPdu[offset++] & 0xff;
-
-                byte[] udh = new byte[userDataHeaderLength];
-                System.arraycopy(mPdu, offset, udh, 0, userDataHeaderLength);
-                mUserDataHeader = SmsHeader.fromByteArray(udh);
-                offset += userDataHeaderLength;
-
-                int headerBits = (userDataHeaderLength + 1) * 8;
-                headerSeptets = headerBits / 7;
-                headerSeptets += (headerBits % 7) > 0 ? 1 : 0;
-                mUserDataSeptetPadding = (headerSeptets * 7) - headerBits;
-            }
-
-            int bufferLen;
-            if (dataInSeptets) {
-                /*
-                 * Here we just create the user data length to be the remainder of
-                 * the pdu minus the user data header, since userDataLength means
-                 * the number of uncompressed septets.
-                 */
-                bufferLen = mPdu.length - offset;
-            } else {
-                /*
-                 * userDataLength is the count of octets, so just subtract the
-                 * user data header.
-                 */
-                bufferLen = userDataLength - (hasUserDataHeader ? (userDataHeaderLength + 1) : 0);
-                if (bufferLen < 0) {
-                    bufferLen = 0;
-                }
-            }
-
-            mUserData = new byte[bufferLen];
-            System.arraycopy(mPdu, offset, mUserData, 0, mUserData.length);
-            mCur = offset;
-
-            if (dataInSeptets) {
-                // Return the number of septets
-                int count = userDataLength - headerSeptets;
-                // If count < 0, return 0 (means UDL was probably incorrect)
-                return count < 0 ? 0 : count;
-            } else {
-                // Return the number of octets
-                return mUserData.length;
-            }
-        }
-
-        /**
-         * Returns the user data payload, not including the headers
-         *
-         * @return the user data payload, not including the headers
-         */
-        byte[] getUserData() {
-            return mUserData;
-        }
-
-        /**
-         * Returns an object representing the user data headers
-         *
-         * {@hide}
-         */
-        SmsHeader getUserDataHeader() {
-            return mUserDataHeader;
-        }
-
-        /**
-         * Interprets the user data payload as packed GSM 7bit characters, and
-         * decodes them into a String.
-         *
-         * @param septetCount the number of septets in the user data payload
-         * @return a String with the decoded characters
-         */
-        String getUserDataGSM7Bit(int septetCount, int languageTable,
-                int languageShiftTable) {
-            String ret;
-
-            ret = GsmAlphabet.gsm7BitPackedToString(mPdu, mCur, septetCount,
-                    mUserDataSeptetPadding, languageTable, languageShiftTable);
-
-            mCur += (septetCount * 7) / 8;
-
-            return ret;
-        }
-
-        /**
-         * Interprets the user data payload as pack GSM 8-bit (a GSM alphabet string that's
-         * stored in 8-bit unpacked format) characters, and decodes them into a String.
-         *
-         * @param byteCount the number of byest in the user data payload
-         * @return a String with the decoded characters
-         */
-        String getUserDataGSM8bit(int byteCount) {
-            String ret;
-
-            ret = GsmAlphabet.gsm8BitUnpackedToString(mPdu, mCur, byteCount);
-
-            mCur += byteCount;
-
-            return ret;
-        }
-
-        /**
-         * Interprets the user data payload as UCS2 characters, and
-         * decodes them into a String.
-         *
-         * @param byteCount the number of bytes in the user data payload
-         * @return a String with the decoded characters
-         */
-        String getUserDataUCS2(int byteCount) {
-            String ret;
-
-            try {
-                ret = new String(mPdu, mCur, byteCount, "utf-16");
-            } catch (UnsupportedEncodingException ex) {
-                ret = "";
-                Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
-            }
-
-            mCur += byteCount;
-            return ret;
-        }
-
-        /**
-         * Interprets the user data payload as KSC-5601 characters, and
-         * decodes them into a String.
-         *
-         * @param byteCount the number of bytes in the user data payload
-         * @return a String with the decoded characters
-         */
-        String getUserDataKSC5601(int byteCount) {
-            String ret;
-
-            try {
-                ret = new String(mPdu, mCur, byteCount, "KSC5601");
-            } catch (UnsupportedEncodingException ex) {
-                ret = "";
-                Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException", ex);
-            }
-
-            mCur += byteCount;
-            return ret;
-        }
-
-        boolean moreDataPresent() {
-            return (mPdu.length > mCur);
-        }
-    }
-
-    /**
-     * 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
-     * @return TextEncodingDetails
-     */
-    public static TextEncodingDetails calculateLength(CharSequence msgBody,
-            boolean use7bitOnly) {
-        CharSequence newMsgBody = null;
-        Resources r = Resources.getSystem();
-        if (r.getBoolean(com.android.internal.R.bool.config_sms_force_7bit_encoding)) {
-            newMsgBody  = Sms7BitEncodingTranslator.translate(msgBody);
-        }
-        if (TextUtils.isEmpty(newMsgBody)) {
-            newMsgBody = msgBody;
-        }
-        TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(newMsgBody, use7bitOnly);
-        if (ted == null) {
-            return SmsMessageBase.calcUnicodeEncodingDetails(newMsgBody);
-        }
-        return ted;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int getProtocolIdentifier() {
-        return mProtocolIdentifier;
-    }
-
-    /**
-     * Returns the TP-Data-Coding-Scheme byte, for acknowledgement of SMS-PP download messages.
-     * @return the TP-DCS field of the SMS header
-     */
-    int getDataCodingScheme() {
-        return mDataCodingScheme;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isReplace() {
-        return (mProtocolIdentifier & 0xc0) == 0x40
-                && (mProtocolIdentifier & 0x3f) > 0
-                && (mProtocolIdentifier & 0x3f) < 8;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isCphsMwiMessage() {
-        return ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageClear()
-                || ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isMWIClearMessage() {
-        if (mIsMwi && !mMwiSense) {
-            return true;
-        }
-
-        return mOriginatingAddress != null
-                && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageClear();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isMWISetMessage() {
-        if (mIsMwi && mMwiSense) {
-            return true;
-        }
-
-        return mOriginatingAddress != null
-                && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isMwiDontStore() {
-        if (mIsMwi && mMwiDontStore) {
-            return true;
-        }
-
-        if (isCphsMwiMessage()) {
-            // See CPHS 4.2 Section B.4.2.1
-            // If the user data is a single space char, do not store
-            // the message. Otherwise, store and display as usual
-            if (" ".equals(getMessageBody())) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public int getStatus() {
-        return mStatus;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isStatusReportMessage() {
-        return mIsStatusReportMessage;
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public boolean isReplyPathPresent() {
-        return mReplyPathPresent;
-    }
-
-    /**
-     * TS 27.005 3.1, &lt;pdu&gt; definition "In the case of SMS: 3GPP TS 24.011 [6]
-     * SC address followed by 3GPP TS 23.040 [3] TPDU in hexadecimal format:
-     * ME/TA converts each octet of TP data unit into two IRA character long
-     * hex number (e.g. octet with integer value 42 is presented to TE as two
-     * characters 2A (IRA 50 and 65))" ...in the case of cell broadcast,
-     * something else...
-     */
-    private void parsePdu(byte[] pdu) {
-        mPdu = pdu;
-        // Rlog.d(LOG_TAG, "raw sms message:");
-        // Rlog.d(LOG_TAG, s);
-
-        PduParser p = new PduParser(pdu);
-
-        mScAddress = p.getSCAddress();
-
-        if (mScAddress != null) {
-            if (VDBG) Rlog.d(LOG_TAG, "SMS SC address: " + mScAddress);
-        }
-
-        // TODO(mkf) support reply path, user data header indicator
-
-        // TP-Message-Type-Indicator
-        // 9.2.3
-        int firstByte = p.getByte();
-
-        mMti = firstByte & 0x3;
-        switch (mMti) {
-        // TP-Message-Type-Indicator
-        // 9.2.3
-        case 0:
-        case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved.
-                //This should be processed in the same way as MTI == 0 (Deliver)
-            parseSmsDeliver(p, firstByte);
-            break;
-        case 1:
-            parseSmsSubmit(p, firstByte);
-            break;
-        case 2:
-            parseSmsStatusReport(p, firstByte);
-            break;
-        default:
-            // TODO(mkf) the rest of these
-            throw new RuntimeException("Unsupported message type");
-        }
-    }
-
-    /**
-     * Parses a SMS-STATUS-REPORT message.
-     *
-     * @param p A PduParser, cued past the first byte.
-     * @param firstByte The first byte of the PDU, which contains MTI, etc.
-     */
-    private void parseSmsStatusReport(PduParser p, int firstByte) {
-        mIsStatusReportMessage = true;
-
-        // TP-Message-Reference
-        mMessageRef = p.getByte();
-        // TP-Recipient-Address
-        mRecipientAddress = p.getAddress();
-        // TP-Service-Centre-Time-Stamp
-        mScTimeMillis = p.getSCTimestampMillis();
-        p.getSCTimestampMillis();
-        // TP-Status
-        mStatus = p.getByte();
-
-        // The following are optional fields that may or may not be present.
-        if (p.moreDataPresent()) {
-            // TP-Parameter-Indicator
-            int extraParams = p.getByte();
-            int moreExtraParams = extraParams;
-            while ((moreExtraParams & 0x80) != 0) {
-                // We only know how to parse a few extra parameters, all
-                // indicated in the first TP-PI octet, so skip over any
-                // additional TP-PI octets.
-                moreExtraParams = p.getByte();
-            }
-            // As per 3GPP 23.040 section 9.2.3.27 TP-Parameter-Indicator,
-            // only process the byte if the reserved bits (bits3 to 6) are zero.
-            if ((extraParams & 0x78) == 0) {
-                // TP-Protocol-Identifier
-                if ((extraParams & 0x01) != 0) {
-                    mProtocolIdentifier = p.getByte();
-                }
-                // TP-Data-Coding-Scheme
-                if ((extraParams & 0x02) != 0) {
-                    mDataCodingScheme = p.getByte();
-                }
-                // TP-User-Data-Length (implies existence of TP-User-Data)
-                if ((extraParams & 0x04) != 0) {
-                    boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
-                    parseUserData(p, hasUserDataHeader);
-                }
-            }
-        }
-    }
-
-    private void parseSmsDeliver(PduParser p, int firstByte) {
-        mReplyPathPresent = (firstByte & 0x80) == 0x80;
-
-        mOriginatingAddress = p.getAddress();
-
-        if (mOriginatingAddress != null) {
-            if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
-                    + mOriginatingAddress.address);
-        }
-
-        // TP-Protocol-Identifier (TP-PID)
-        // TS 23.040 9.2.3.9
-        mProtocolIdentifier = p.getByte();
-
-        // TP-Data-Coding-Scheme
-        // see TS 23.038
-        mDataCodingScheme = p.getByte();
-
-        if (VDBG) {
-            Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier
-                    + " data coding scheme: " + mDataCodingScheme);
-        }
-
-        mScTimeMillis = p.getSCTimestampMillis();
-
-        if (VDBG) Rlog.d(LOG_TAG, "SMS SC timestamp: " + mScTimeMillis);
-
-        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
-
-        parseUserData(p, hasUserDataHeader);
-    }
-
-    /**
-     * Parses a SMS-SUBMIT message.
-     *
-     * @param p A PduParser, cued past the first byte.
-     * @param firstByte The first byte of the PDU, which contains MTI, etc.
-     */
-    private void parseSmsSubmit(PduParser p, int firstByte) {
-        mReplyPathPresent = (firstByte & 0x80) == 0x80;
-
-        // TP-MR (TP-Message Reference)
-        mMessageRef = p.getByte();
-
-        mRecipientAddress = p.getAddress();
-
-        if (mRecipientAddress != null) {
-            if (VDBG) Rlog.v(LOG_TAG, "SMS recipient address: " + mRecipientAddress.address);
-        }
-
-        // TP-Protocol-Identifier (TP-PID)
-        // TS 23.040 9.2.3.9
-        mProtocolIdentifier = p.getByte();
-
-        // TP-Data-Coding-Scheme
-        // see TS 23.038
-        mDataCodingScheme = p.getByte();
-
-        if (VDBG) {
-            Rlog.v(LOG_TAG, "SMS TP-PID:" + mProtocolIdentifier
-                    + " data coding scheme: " + mDataCodingScheme);
-        }
-
-        // TP-Validity-Period-Format
-        int validityPeriodLength = 0;
-        int validityPeriodFormat = ((firstByte>>3) & 0x3);
-        if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/
-        {
-            validityPeriodLength = 0;
-        }
-        else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/
-        {
-            validityPeriodLength = 1;
-        }
-        else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/
-        {
-            validityPeriodLength = 7;
-        }
-
-        // TP-Validity-Period is not used on phone, so just ignore it for now.
-        while (validityPeriodLength-- > 0)
-        {
-            p.getByte();
-        }
-
-        boolean hasUserDataHeader = (firstByte & 0x40) == 0x40;
-
-        parseUserData(p, hasUserDataHeader);
-    }
-
-    /**
-     * Parses the User Data of an SMS.
-     *
-     * @param p The current PduParser.
-     * @param hasUserDataHeader Indicates whether a header is present in the
-     *                          User Data.
-     */
-    private void parseUserData(PduParser p, boolean hasUserDataHeader) {
-        boolean hasMessageClass = false;
-        boolean userDataCompressed = false;
-
-        int encodingType = ENCODING_UNKNOWN;
-
-        // Look up the data encoding scheme
-        if ((mDataCodingScheme & 0x80) == 0) {
-            userDataCompressed = (0 != (mDataCodingScheme & 0x20));
-            hasMessageClass = (0 != (mDataCodingScheme & 0x10));
-
-            if (userDataCompressed) {
-                Rlog.w(LOG_TAG, "4 - Unsupported SMS data coding scheme "
-                        + "(compression) " + (mDataCodingScheme & 0xff));
-            } else {
-                switch ((mDataCodingScheme >> 2) & 0x3) {
-                case 0: // GSM 7 bit default alphabet
-                    encodingType = ENCODING_7BIT;
-                    break;
-
-                case 2: // UCS 2 (16bit)
-                    encodingType = ENCODING_16BIT;
-                    break;
-
-                case 1: // 8 bit data
-                    //Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
-                    //that's stored in 8-bit unpacked format) characters.
-                    Resources r = Resources.getSystem();
-                    if (r.getBoolean(com.android.internal.
-                            R.bool.config_sms_decode_gsm_8bit_data)) {
-                        encodingType = ENCODING_8BIT;
-                        break;
-                    }
-
-                case 3: // reserved
-                    Rlog.w(LOG_TAG, "1 - Unsupported SMS data coding scheme "
-                            + (mDataCodingScheme & 0xff));
-                    encodingType = ENCODING_8BIT;
-                    break;
-                }
-            }
-        } else if ((mDataCodingScheme & 0xf0) == 0xf0) {
-            hasMessageClass = true;
-            userDataCompressed = false;
-
-            if (0 == (mDataCodingScheme & 0x04)) {
-                // GSM 7 bit default alphabet
-                encodingType = ENCODING_7BIT;
-            } else {
-                // 8 bit data
-                encodingType = ENCODING_8BIT;
-            }
-        } else if ((mDataCodingScheme & 0xF0) == 0xC0
-                || (mDataCodingScheme & 0xF0) == 0xD0
-                || (mDataCodingScheme & 0xF0) == 0xE0) {
-            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
-
-            // 0xC0 == 7 bit, don't store
-            // 0xD0 == 7 bit, store
-            // 0xE0 == UCS-2, store
-
-            if ((mDataCodingScheme & 0xF0) == 0xE0) {
-                encodingType = ENCODING_16BIT;
-            } else {
-                encodingType = ENCODING_7BIT;
-            }
-
-            userDataCompressed = false;
-            boolean active = ((mDataCodingScheme & 0x08) == 0x08);
-            // bit 0x04 reserved
-
-            // VM - If TP-UDH is present, these values will be overwritten
-            if ((mDataCodingScheme & 0x03) == 0x00) {
-                mIsMwi = true; /* Indicates vmail */
-                mMwiSense = active;/* Indicates vmail notification set/clear */
-                mMwiDontStore = ((mDataCodingScheme & 0xF0) == 0xC0);
-
-                /* Set voice mail count based on notification bit */
-                if (active == true) {
-                    mVoiceMailCount = -1; // unknown number of messages waiting
-                } else {
-                    mVoiceMailCount = 0; // no unread messages
-                }
-
-                Rlog.w(LOG_TAG, "MWI in DCS for Vmail. DCS = "
-                        + (mDataCodingScheme & 0xff) + " Dont store = "
-                        + mMwiDontStore + " vmail count = " + mVoiceMailCount);
-
-            } else {
-                mIsMwi = false;
-                Rlog.w(LOG_TAG, "MWI in DCS for fax/email/other: "
-                        + (mDataCodingScheme & 0xff));
-            }
-        } else if ((mDataCodingScheme & 0xC0) == 0x80) {
-            // 3GPP TS 23.038 V7.0.0 (2006-03) section 4
-            // 0x80..0xBF == Reserved coding groups
-            if (mDataCodingScheme == 0x84) {
-                // This value used for KSC5601 by carriers in Korea.
-                encodingType = ENCODING_KSC5601;
-            } else {
-                Rlog.w(LOG_TAG, "5 - Unsupported SMS data coding scheme "
-                        + (mDataCodingScheme & 0xff));
-            }
-        } else {
-            Rlog.w(LOG_TAG, "3 - Unsupported SMS data coding scheme "
-                    + (mDataCodingScheme & 0xff));
-        }
-
-        // set both the user data and the user data header.
-        int count = p.constructUserData(hasUserDataHeader,
-                encodingType == ENCODING_7BIT);
-        this.mUserData = p.getUserData();
-        this.mUserDataHeader = p.getUserDataHeader();
-
-        /*
-         * Look for voice mail indication in TP_UDH TS23.040 9.2.3.24
-         * ieid = 1 (0x1) (SPECIAL_SMS_MSG_IND)
-         * ieidl =2 octets
-         * ieda msg_ind_type = 0x00 (voice mail; discard sms )or
-         *                   = 0x80 (voice mail; store sms)
-         * msg_count = 0x00 ..0xFF
-         */
-        if (hasUserDataHeader && (mUserDataHeader.specialSmsMsgList.size() != 0)) {
-            for (SmsHeader.SpecialSmsMsg msg : mUserDataHeader.specialSmsMsgList) {
-                int msgInd = msg.msgIndType & 0xff;
-                /*
-                 * TS 23.040 V6.8.1 Sec 9.2.3.24.2
-                 * bits 1 0 : basic message indication type
-                 * bits 4 3 2 : extended message indication type
-                 * bits 6 5 : Profile id bit 7 storage type
-                 */
-                if ((msgInd == 0) || (msgInd == 0x80)) {
-                    mIsMwi = true;
-                    if (msgInd == 0x80) {
-                        /* Store message because TP_UDH indicates so*/
-                        mMwiDontStore = false;
-                    } else if (mMwiDontStore == false) {
-                        /* Storage bit is not set by TP_UDH
-                         * Check for conflict
-                         * between message storage bit in TP_UDH
-                         * & DCS. The message shall be stored if either of
-                         * the one indicates so.
-                         * TS 23.040 V6.8.1 Sec 9.2.3.24.2
-                         */
-                        if (!((((mDataCodingScheme & 0xF0) == 0xD0)
-                               || ((mDataCodingScheme & 0xF0) == 0xE0))
-                               && ((mDataCodingScheme & 0x03) == 0x00))) {
-                            /* Even DCS did not have voice mail with Storage bit
-                             * 3GPP TS 23.038 V7.0.0 section 4
-                             * So clear this flag*/
-                            mMwiDontStore = true;
-                        }
-                    }
-
-                    mVoiceMailCount = msg.msgCount & 0xff;
-
-                    /*
-                     * In the event of a conflict between message count setting
-                     * and DCS then the Message Count in the TP-UDH shall
-                     * override the indication in the TP-DCS. Set voice mail
-                     * notification based on count in TP-UDH
-                     */
-                    if (mVoiceMailCount > 0)
-                        mMwiSense = true;
-                    else
-                        mMwiSense = false;
-
-                    Rlog.w(LOG_TAG, "MWI in TP-UDH for Vmail. Msg Ind = " + msgInd
-                            + " Dont store = " + mMwiDontStore + " Vmail count = "
-                            + mVoiceMailCount);
-
-                    /*
-                     * There can be only one IE for each type of message
-                     * indication in TP_UDH. In the event they are duplicated
-                     * last occurence will be used. Hence the for loop
-                     */
-                } else {
-                    Rlog.w(LOG_TAG, "TP_UDH fax/email/"
-                            + "extended msg/multisubscriber profile. Msg Ind = " + msgInd);
-                }
-            } // end of for
-        } // end of if UDH
-
-        switch (encodingType) {
-        case ENCODING_UNKNOWN:
-            mMessageBody = null;
-            break;
-
-        case ENCODING_8BIT:
-            //Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet string
-            //that's stored in 8-bit unpacked format) characters.
-            Resources r = Resources.getSystem();
-            if (r.getBoolean(com.android.internal.
-                    R.bool.config_sms_decode_gsm_8bit_data)) {
-                mMessageBody = p.getUserDataGSM8bit(count);
-            } else {
-                mMessageBody = null;
-            }
-            break;
-
-        case ENCODING_7BIT:
-            mMessageBody = p.getUserDataGSM7Bit(count,
-                    hasUserDataHeader ? mUserDataHeader.languageTable : 0,
-                    hasUserDataHeader ? mUserDataHeader.languageShiftTable : 0);
-            break;
-
-        case ENCODING_16BIT:
-            mMessageBody = p.getUserDataUCS2(count);
-            break;
-
-        case ENCODING_KSC5601:
-            mMessageBody = p.getUserDataKSC5601(count);
-            break;
-        }
-
-        if (VDBG) Rlog.v(LOG_TAG, "SMS message body (raw): '" + mMessageBody + "'");
-
-        if (mMessageBody != null) {
-            parseMessageBody();
-        }
-
-        if (!hasMessageClass) {
-            messageClass = MessageClass.UNKNOWN;
-        } else {
-            switch (mDataCodingScheme & 0x3) {
-            case 0:
-                messageClass = MessageClass.CLASS_0;
-                break;
-            case 1:
-                messageClass = MessageClass.CLASS_1;
-                break;
-            case 2:
-                messageClass = MessageClass.CLASS_2;
-                break;
-            case 3:
-                messageClass = MessageClass.CLASS_3;
-                break;
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public MessageClass getMessageClass() {
-        return messageClass;
-    }
-
-    /**
-     * Returns true if this is a (U)SIM data download type SM.
-     * See 3GPP TS 31.111 section 9.1 and TS 23.040 section 9.2.3.9.
-     *
-     * @return true if this is a USIM data download message; false otherwise
-     */
-    boolean isUsimDataDownload() {
-        return messageClass == MessageClass.CLASS_2 &&
-                (mProtocolIdentifier == 0x7f || mProtocolIdentifier == 0x7c);
-    }
-
-    public int getNumOfVoicemails() {
-        /*
-         * Order of priority if multiple indications are present is 1.UDH,
-         *      2.DCS, 3.CPHS.
-         * Voice mail count if voice mail present indication is
-         * received
-         *  1. UDH (or both UDH & DCS): mVoiceMailCount = 0 to 0xff. Ref[TS 23. 040]
-         *  2. DCS only: count is unknown mVoiceMailCount= -1
-         *  3. CPHS only: count is unknown mVoiceMailCount = 0xff. Ref[GSM-BTR-1-4700]
-         * Voice mail clear, mVoiceMailCount = 0.
-         */
-        if ((!mIsMwi) && isCphsMwiMessage()) {
-            if (mOriginatingAddress != null
-                    && ((GsmSmsAddress) mOriginatingAddress).isCphsVoiceMessageSet()) {
-                mVoiceMailCount = 0xff;
-            } else {
-                mVoiceMailCount = 0;
-            }
-            Rlog.v(LOG_TAG, "CPHS voice mail message");
-        }
-        return mVoiceMailCount;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
new file mode 100644
index 0000000..2f790b7
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import android.Manifest;
+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.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Creates a list of ImsServices that are available to bind to based on the Device configuration
+ * overlay value "config_ims_package" and Carrier Configuration value
+ * "config_ims_package_override_string".
+ * These ImsServices are then bound to in the following order:
+ *
+ * 1. Carrier Config defined override value per SIM.
+ * 2. Device overlay default value (including no SIM case).
+ *
+ * ImsManager can then retrieve the binding to the correct ImsService using
+ * {@link #getImsServiceControllerAndListen} on a per-slot and per feature basis.
+ */
+
+public class ImsResolver implements ImsServiceController.ImsServiceControllerCallbacks {
+
+    private static final String TAG = "ImsResolver";
+
+    public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+    public static final String METADATA_EMERGENCY_MMTEL_FEATURE =
+            "android.telephony.ims.EMERGENCY_MMTEL_FEATURE";
+    public static final String METADATA_MMTEL_FEATURE = "android.telephony.ims.MMTEL_FEATURE";
+    public static final String METADATA_RCS_FEATURE = "android.telephony.ims.RCS_FEATURE";
+
+    // Based on updates from PackageManager
+    private static final int HANDLER_ADD_PACKAGE = 0;
+    // Based on updates from PackageManager
+    private static final int HANDLER_REMOVE_PACKAGE = 1;
+    // Based on updates from CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED
+    private static final int HANDLER_CONFIG_CHANGED = 2;
+
+    /**
+     * Stores information about an ImsService, including the package name, class name, and features
+     * that the service supports.
+     */
+    @VisibleForTesting
+    public static class ImsServiceInfo {
+        public ComponentName name;
+        public Set<Integer> supportedFeatures;
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            ImsServiceInfo that = (ImsServiceInfo) o;
+
+            if (name != null ? !name.equals(that.name) : that.name != null) return false;
+            return supportedFeatures != null ? supportedFeatures.equals(that.supportedFeatures)
+                    : that.supportedFeatures == null;
+
+        }
+
+        @Override
+        public int hashCode() {
+            int result = name != null ? name.hashCode() : 0;
+            result = 31 * result + (supportedFeatures != null ? supportedFeatures.hashCode() : 0);
+            return result;
+        }
+    }
+
+    // Receives broadcasts from the system involving changes to the installed applications. If
+    // an ImsService that we are configured to use is installed, we must bind to it.
+    private BroadcastReceiver mAppChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final String packageName = intent.getData().getSchemeSpecificPart();
+            switch (action) {
+                case Intent.ACTION_PACKAGE_ADDED:
+                    // intentional fall-through
+                case Intent.ACTION_PACKAGE_CHANGED:
+                    mHandler.obtainMessage(HANDLER_ADD_PACKAGE, packageName).sendToTarget();
+                    break;
+                case Intent.ACTION_PACKAGE_REMOVED:
+                    mHandler.obtainMessage(HANDLER_REMOVE_PACKAGE, packageName).sendToTarget();
+                    break;
+                default:
+                    return;
+            }
+        }
+    };
+
+    // Receives the broadcast that a new Carrier Config has been loaded in order to possibly
+    // unbind from one service and bind to another.
+    private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+
+            int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+            if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                Log.i(TAG, "Received SIM change for invalid sub id.");
+                return;
+            }
+
+            Log.i(TAG, "Received Carrier Config Changed for SubId: " + subId);
+
+            mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, subId).sendToTarget();
+        }
+    };
+
+    /**
+     * Testing interface used to mock SubscriptionManager in testing
+     */
+    @VisibleForTesting
+    public interface SubscriptionManagerProxy {
+        /**
+         * Mock-able interface for {@link SubscriptionManager#getSubId(int)} used for testing.
+         */
+        int getSubId(int slotId);
+        /**
+         * Mock-able interface for {@link SubscriptionManager#getSlotIndex(int)} used for testing.
+         */
+        int getSlotIndex(int subId);
+    }
+
+    private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
+        @Override
+        public int getSubId(int slotId) {
+            int[] subIds = SubscriptionManager.getSubId(slotId);
+            if (subIds != null) {
+                // This is done in all other places getSubId is used.
+                return subIds[0];
+            }
+            return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        }
+
+        @Override
+        public int getSlotIndex(int subId) {
+            return SubscriptionManager.getSlotIndex(subId);
+        }
+    };
+
+    /**
+     * Testing interface for injecting mock ImsServiceControllers.
+     */
+    @VisibleForTesting
+    public interface ImsServiceControllerFactory {
+        /**
+         * Returns the ImsServiceController created usiing the context and componentName supplied.
+         * Used for DI when testing.
+         */
+        ImsServiceController get(Context context, ComponentName componentName);
+    }
+
+    private ImsServiceControllerFactory mImsServiceControllerFactory = (context, componentName) ->
+            new ImsServiceController(context, componentName, this);
+
+    private final CarrierConfigManager mCarrierConfigManager;
+    private final Context mContext;
+    // Locks mBoundImsServicesByFeature only. Be careful to avoid deadlocks from
+    // ImsServiceController callbacks.
+    private final Object mBoundServicesLock = new Object();
+    private final int mNumSlots;
+
+    // Synchronize all messages on a handler to ensure that the cache includes the most recent
+    // version of the installed ImsServices.
+    private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
+        switch (msg.what) {
+            case HANDLER_ADD_PACKAGE: {
+                String packageName = (String) msg.obj;
+                maybeAddedImsService(packageName);
+                break;
+            }
+            case HANDLER_REMOVE_PACKAGE: {
+                String packageName = (String) msg.obj;
+                maybeRemovedImsService(packageName);
+                break;
+            }
+            case HANDLER_CONFIG_CHANGED: {
+                int subId = (Integer) msg.obj;
+                maybeRebindService(subId);
+                break;
+            }
+            default:
+                return false;
+        }
+        return true;
+    });
+
+    // Package name of the default device service.
+    private String mDeviceService;
+    // Array index corresponds to slot Id associated with the service package name.
+    private String[] mCarrierServices;
+    // List index corresponds to Slot Id, Maps ImsFeature.FEATURE->bound ImsServiceController
+    // Locked on mBoundServicesLock
+    private List<SparseArray<ImsServiceController>> mBoundImsServicesByFeature;
+    // not locked, only accessed on a handler thread.
+    private Set<ImsServiceInfo> mInstalledServicesCache = new ArraySet<>();
+    // not locked, only accessed on a handler thread.
+    private Set<ImsServiceController> mActiveControllers = new ArraySet<>();
+
+    public ImsResolver(Context context, String defaultImsPackageName, int numSlots) {
+        mContext = context;
+        mDeviceService = defaultImsPackageName;
+        mNumSlots = numSlots;
+        mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService(
+                Context.CARRIER_CONFIG_SERVICE);
+        mCarrierServices = new String[numSlots];
+        mBoundImsServicesByFeature = Stream.generate(SparseArray<ImsServiceController>::new)
+                .limit(mNumSlots).collect(Collectors.toList());
+
+        IntentFilter appChangedFilter = new IntentFilter();
+        appChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        appChangedFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        appChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        appChangedFilter.addDataScheme("package");
+        context.registerReceiverAsUser(mAppChangedReceiver, UserHandle.ALL, appChangedFilter, null,
+                null);
+
+        context.registerReceiver(mConfigChangedReceiver, new IntentFilter(
+                CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+    }
+
+    @VisibleForTesting
+    public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
+        mSubscriptionManagerProxy = proxy;
+    }
+
+    @VisibleForTesting
+    public void setImsServiceControllerFactory(ImsServiceControllerFactory factory) {
+        mImsServiceControllerFactory = factory;
+    }
+
+    @VisibleForTesting
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    /**
+     * Needs to be called after the constructor to first populate the cache and possibly bind to
+     * ImsServices.
+     */
+    public void populateCacheAndStartBind() {
+        Log.i(TAG, "Initializing cache and binding.");
+        // Populates the CarrierConfig override package names for each slot
+        mHandler.obtainMessage(HANDLER_CONFIG_CHANGED, -1).sendToTarget();
+        // Starts first bind to the system.
+        mHandler.obtainMessage(HANDLER_ADD_PACKAGE, null).sendToTarget();
+    }
+
+    /**
+     * Returns the {@link IImsServiceController} that corresponds to the given slot Id and IMS
+     * feature or {@link null} if the service is not available. If an ImsServiceController is
+     * available, the {@link IImsServiceFeatureListener} callback is registered as a listener for
+     * feature updates.
+     * @param slotId The SIM slot that we are requesting the {@link IImsServiceController} for.
+     * @param feature The IMS Feature we are requesting.
+     * @param callback Listener that will send updates to ImsManager when there are updates to
+     * ImsServiceController.
+     * @return {@link IImsServiceController} interface for the feature specified or {@link null} if
+     * it is unavailable.
+     */
+    public IImsServiceController getImsServiceControllerAndListen(int slotId, int feature,
+            IImsServiceFeatureListener callback) {
+        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
+                || feature >= ImsFeature.MAX) {
+            return null;
+        }
+        ImsServiceController controller;
+        synchronized (mBoundServicesLock) {
+            SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
+            if (services == null) {
+                return null;
+            }
+            controller = services.get(feature);
+        }
+        if (controller != null) {
+            controller.addImsServiceFeatureListener(callback);
+            return controller.getImsServiceController();
+        }
+        return null;
+    }
+
+    private void putImsController(int slotId, int feature, ImsServiceController controller) {
+        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
+                || feature >= ImsFeature.MAX) {
+            Log.w(TAG, "putImsController received invalid parameters - slot: " + slotId
+                    + ", feature: " + feature);
+            return;
+        }
+        synchronized (mBoundServicesLock) {
+            SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
+            if (services == null) {
+                services = new SparseArray<>();
+                mBoundImsServicesByFeature.add(slotId, services);
+            }
+            Log.i(TAG, "ImsServiceController added on slot: " + slotId + " with feature: "
+                    + feature + " using package: " + controller.getComponentName());
+            services.put(feature, controller);
+        }
+    }
+
+    private ImsServiceController removeImsController(int slotId, int feature) {
+        if (slotId < 0 || slotId >= mNumSlots || feature <= ImsFeature.INVALID
+                || feature >= ImsFeature.MAX) {
+            Log.w(TAG, "removeImsController received invalid parameters - slot: " + slotId
+                    + ", feature: " + feature);
+            return null;
+        }
+        synchronized (mBoundServicesLock) {
+            SparseArray<ImsServiceController> services = mBoundImsServicesByFeature.get(slotId);
+            if (services == null) {
+                return null;
+            }
+            ImsServiceController c = services.get(feature, null);
+            if (c != null) {
+                Log.i(TAG, "ImsServiceController removed on slot: " + slotId + " with feature: "
+                        + feature + " using package: " + c.getComponentName());
+                services.remove(feature);
+            }
+            return c;
+        }
+    }
+
+
+    // Update the current cache with the new ImsService(s) if it has been added or update the
+    // supported IMS features if they have changed.
+    // Called from the handler ONLY
+    private void maybeAddedImsService(String packageName) {
+        Log.d(TAG, "maybeAddedImsService, packageName: " + packageName);
+        List<ImsServiceInfo> infos = getImsServiceInfo(packageName);
+        List<ImsServiceInfo> newlyAddedInfos = new ArrayList<>();
+        for (ImsServiceInfo info : infos) {
+            // Checking to see if the ComponentName is the same, so we can update the supported
+            // features. Will only be one (if it exists), since it is a set.
+            Optional<ImsServiceInfo> match = getInfoByComponentName(mInstalledServicesCache,
+                    info.name);
+            if (match.isPresent()) {
+                // update features in the cache
+                Log.i(TAG, "Updating features in cached ImsService: " + info.name);
+                Log.d(TAG, "Updating features - Old features: " + match.get().supportedFeatures
+                        + " new features: " + info.supportedFeatures);
+                match.get().supportedFeatures = info.supportedFeatures;
+                updateImsServiceFeatures(info);
+            } else {
+                Log.i(TAG, "Adding newly added ImsService to cache: " + info.name);
+                mInstalledServicesCache.add(info);
+                newlyAddedInfos.add(info);
+            }
+        }
+        // Loop through the newly created ServiceInfos in a separate loop to make sure the cache
+        // is fully updated.
+        for (ImsServiceInfo info : newlyAddedInfos) {
+            if (isActiveCarrierService(info)) {
+                // New ImsService is registered to active carrier services and must be newly
+                // bound.
+                bindNewImsService(info);
+                // Update existing device service features
+                updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+            } else if (isDeviceService(info)) {
+                // New ImsService is registered as device default and must be newly bound.
+                bindNewImsService(info);
+            }
+        }
+    }
+
+    // Remove the ImsService from the cache. At this point, the ImsService will have already been
+    // killed.
+    // Called from the handler ONLY
+    private boolean maybeRemovedImsService(String packageName) {
+        Optional<ImsServiceInfo> match = getInfoByPackageName(mInstalledServicesCache, packageName);
+        if (match.isPresent()) {
+            mInstalledServicesCache.remove(match.get());
+            Log.i(TAG, "Removing ImsService: " + match.get().name);
+            unbindImsService(match.get());
+            updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+            return true;
+        }
+        return false;
+    }
+
+    // Returns true if the CarrierConfig that has been loaded includes this ImsServiceInfo
+    // package name.
+    // Called from Handler ONLY
+    private boolean isActiveCarrierService(ImsServiceInfo info) {
+        for (int i = 0; i < mNumSlots; i++) {
+            if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isDeviceService(ImsServiceInfo info) {
+        return TextUtils.equals(mDeviceService, info.name.getPackageName());
+    }
+
+    private int getSlotForActiveCarrierService(ImsServiceInfo info) {
+        for (int i = 0; i < mNumSlots; i++) {
+            if (TextUtils.equals(mCarrierServices[i], info.name.getPackageName())) {
+                return i;
+            }
+        }
+        return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    }
+
+    private Optional<ImsServiceController> getControllerByServiceInfo(
+            Set<ImsServiceController> searchSet, ImsServiceInfo matchValue) {
+        return searchSet.stream()
+                .filter(c -> Objects.equals(c.getComponentName(), matchValue.name)).findFirst();
+    }
+
+    private Optional<ImsServiceInfo> getInfoByPackageName(Set<ImsServiceInfo> searchSet,
+            String matchValue) {
+        return searchSet.stream()
+                .filter((i) -> Objects.equals(i.name.getPackageName(), matchValue)).findFirst();
+    }
+
+    private Optional<ImsServiceInfo> getInfoByComponentName(Set<ImsServiceInfo> searchSet,
+            ComponentName matchValue) {
+        return searchSet.stream()
+                .filter((i) -> Objects.equals(i.name, matchValue)).findFirst();
+    }
+
+    // Creates new features in active ImsServices and removes obsolete cached features. If
+    // cachedInfo == null, then newInfo is assumed to be a new ImsService and will have all features
+    // created.
+    private void updateImsServiceFeatures(ImsServiceInfo newInfo) {
+        if (newInfo == null) {
+            return;
+        }
+        Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers,
+                newInfo);
+        if (o.isPresent()) {
+            Log.i(TAG, "Updating features for ImsService: " + o.get().getComponentName());
+            HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(newInfo);
+            try {
+                if (features.size() > 0) {
+                    Log.d(TAG, "Updating Features - New Features: " + features);
+                    o.get().changeImsServiceFeatures(features);
+
+                    // If the carrier service features have changed, the device features will also
+                    // need to be recalculated.
+                    if (isActiveCarrierService(newInfo)
+                            // Prevent infinite recursion from bad behavior
+                            && !TextUtils.equals(newInfo.name.getPackageName(), mDeviceService)) {
+                        Log.i(TAG, "Updating device default");
+                        updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+                    }
+                } else {
+                    Log.i(TAG, "Unbinding: features = 0 for ImsService: "
+                            + o.get().getComponentName());
+                    o.get().unbind();
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "updateImsServiceFeatures: Remote Exception: " + e.getMessage());
+            }
+        }
+    }
+
+    // Bind to a new ImsService and wait for the service to be connected to create ImsFeatures.
+    private void bindNewImsService(ImsServiceInfo info) {
+        if (info == null) {
+            return;
+        }
+        ImsServiceController controller = mImsServiceControllerFactory.get(mContext, info.name);
+        HashSet<Pair<Integer, Integer>> features = calculateFeaturesToCreate(info);
+        // Only bind if there are features that will be created by the service.
+        if (features.size() > 0) {
+            Log.i(TAG, "Binding ImsService: " + controller.getComponentName() + " with features: "
+                    + features);
+            controller.bind(features);
+            mActiveControllers.add(controller);
+        }
+    }
+
+    // Clean up and unbind from an ImsService
+    private void unbindImsService(ImsServiceInfo info) {
+        if (info == null) {
+            return;
+        }
+        Optional<ImsServiceController> o = getControllerByServiceInfo(mActiveControllers, info);
+        if (o.isPresent()) {
+            // Calls imsServiceFeatureRemoved on all features in the controller
+            try {
+                Log.i(TAG, "Unbinding ImsService: " + o.get().getComponentName());
+                o.get().unbind();
+            } catch (RemoteException e) {
+                Log.e(TAG, "unbindImsService: Remote Exception: " + e.getMessage());
+            }
+            mActiveControllers.remove(o.get());
+        }
+    }
+
+    // Calculate which features an ImsServiceController will need. If it is the carrier specific
+    // ImsServiceController, it will be granted all of the features it requests on the associated
+    // slot. If it is the device ImsService, it will get all of the features not covered by the
+    // carrier implementation.
+    private HashSet<Pair<Integer, Integer>> calculateFeaturesToCreate(ImsServiceInfo info) {
+        HashSet<Pair<Integer, Integer>> imsFeaturesBySlot = new HashSet<>();
+        // Check if the info is a carrier service
+        int slotId = getSlotForActiveCarrierService(info);
+        if (slotId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
+                    feature -> new Pair<>(slotId, feature)).collect(Collectors.toList()));
+        } else if (isDeviceService(info)) {
+            // For all slots that are not currently using a carrier ImsService, enable all features
+            // for the device default.
+            for (int i = 0; i < mNumSlots; i++) {
+                final int currSlotId = i;
+                ImsServiceInfo carrierImsInfo = getImsServiceInfoFromCache(mCarrierServices[i]);
+                if (carrierImsInfo == null) {
+                    // No Carrier override, add all features
+                    imsFeaturesBySlot.addAll(info.supportedFeatures.stream().map(
+                            feature -> new Pair<>(currSlotId, feature)).collect(
+                            Collectors.toList()));
+                } else {
+                    // Add all features to the device service that are not currently covered by
+                    // the carrier ImsService.
+                    Set<Integer> deviceFeatures = new HashSet<>(info.supportedFeatures);
+                    deviceFeatures.removeAll(carrierImsInfo.supportedFeatures);
+                    imsFeaturesBySlot.addAll(deviceFeatures.stream().map(
+                            feature -> new Pair<>(currSlotId, feature)).collect(
+                            Collectors.toList()));
+                }
+            }
+        }
+        return imsFeaturesBySlot;
+    }
+
+    /**
+     * Implementation of
+     * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureCreated}, which
+     * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
+     */
+    public void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller) {
+        putImsController(slotId, feature, controller);
+    }
+
+    /**
+     * Implementation of
+     * {@link ImsServiceController.ImsServiceControllerCallbacks#imsServiceFeatureRemoved}, which
+     * removes the ImsServiceController from the mBoundImsServicesByFeature structure.
+     */
+    public void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller) {
+        removeImsController(slotId, feature);
+    }
+
+    // Possibly rebind to another ImsService if currently installed ImsServices were changed or if
+    // the SIM card has changed.
+    // Called from the handler ONLY
+    private void maybeRebindService(int subId) {
+        if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            // not specified, replace package on all slots.
+            for (int i = 0; i < mNumSlots; i++) {
+                // get Sub id from Slot Id
+                subId = mSubscriptionManagerProxy.getSubId(i);
+                updateBoundCarrierServices(subId);
+            }
+        } else {
+            updateBoundCarrierServices(subId);
+        }
+
+    }
+
+    private void updateBoundCarrierServices(int subId) {
+        int slotId = mSubscriptionManagerProxy.getSlotIndex(subId);
+        String newPackageName = mCarrierConfigManager.getConfigForSubId(subId).getString(
+                CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null);
+        if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX && slotId < mNumSlots) {
+            String oldPackageName = mCarrierServices[slotId];
+            mCarrierServices[slotId] = newPackageName;
+            if (!TextUtils.equals(newPackageName, oldPackageName)) {
+                Log.i(TAG, "Carrier Config updated, binding new ImsService");
+                // Unbind old ImsService, not needed anymore
+                // ImsService is retrieved from the cache. If the cache hasn't been populated yet,
+                // the calls to unbind/bind will fail (intended during initial start up).
+                unbindImsService(getImsServiceInfoFromCache(oldPackageName));
+                bindNewImsService(getImsServiceInfoFromCache(newPackageName));
+                // Recalculate the device ImsService features to reflect changes.
+                updateImsServiceFeatures(getImsServiceInfoFromCache(mDeviceService));
+            }
+        }
+    }
+
+    /**
+     * Returns the ImsServiceInfo that matches the provided packageName. Visible for testing
+     * the ImsService caching functionality.
+     */
+    @VisibleForTesting
+    public ImsServiceInfo getImsServiceInfoFromCache(String packageName) {
+        if (TextUtils.isEmpty(packageName)) {
+            return null;
+        }
+        Optional<ImsServiceInfo> infoFilter = getInfoByPackageName(mInstalledServicesCache,
+                packageName);
+        if (infoFilter.isPresent()) {
+            return infoFilter.get();
+        } else {
+            return null;
+        }
+    }
+
+    // Return the ImsServiceInfo specified for the package name. If the package name is null,
+    // get all packages that support ImsServices.
+    private List<ImsServiceInfo> getImsServiceInfo(String packageName) {
+        List<ImsServiceInfo> infos = new ArrayList<>();
+
+        Intent serviceIntent = new Intent(SERVICE_INTERFACE);
+        serviceIntent.setPackage(packageName);
+
+        PackageManager packageManager = mContext.getPackageManager();
+        for (ResolveInfo entry : packageManager.queryIntentServicesAsUser(
+                serviceIntent,
+                PackageManager.GET_META_DATA,
+                mContext.getUserId())) {
+            ServiceInfo serviceInfo = entry.serviceInfo;
+
+            if (serviceInfo != null) {
+                ImsServiceInfo info = new ImsServiceInfo();
+                info.name = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+                info.supportedFeatures = new HashSet<>(ImsFeature.MAX);
+                // Add all supported features
+                if (serviceInfo.metaData != null) {
+                    if (serviceInfo.metaData.getBoolean(METADATA_EMERGENCY_MMTEL_FEATURE, false)) {
+                        info.supportedFeatures.add(ImsFeature.EMERGENCY_MMTEL);
+                    }
+                    if (serviceInfo.metaData.getBoolean(METADATA_MMTEL_FEATURE, false)) {
+                        info.supportedFeatures.add(ImsFeature.MMTEL);
+                    }
+                    if (serviceInfo.metaData.getBoolean(METADATA_RCS_FEATURE, false)) {
+                        info.supportedFeatures.add(ImsFeature.RCS);
+                    }
+                }
+                // Check manifest permission to be sure that the service declares the correct
+                // permissions.
+                if (TextUtils.equals(serviceInfo.permission,
+                        Manifest.permission.BIND_IMS_SERVICE)) {
+                    Log.d(TAG, "ImsService added to cache: " + info.name + " with features: "
+                            + info.supportedFeatures);
+                    infos.add(info);
+                } else {
+                    Log.w(TAG, "ImsService does not have BIND_IMS_SERVICE permission: "
+                            + info.name);
+                }
+            }
+        }
+        return infos;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceController.java b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
new file mode 100644
index 0000000..5257dad
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceController.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.IPackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
+ * ImsService will support.
+ *
+ * When the ImsService is first bound, {@link IImsServiceController#createImsFeature} will be
+ * called
+ * on each feature that the service supports. For each ImsFeature that is created,
+ * {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the
+ * listener that the ImsService now supports that feature.
+ *
+ * When {@link #changeImsServiceFeatures} is called with a set of features that is different from
+ * the original set, {@link IImsServiceController#createImsFeature} and
+ * {@link IImsServiceController#removeImsFeature} will be called for each feature that is
+ * created/removed.
+ */
+public class ImsServiceController {
+
+    class ImsDeathRecipient implements IBinder.DeathRecipient {
+
+        private ComponentName mComponentName;
+
+        ImsDeathRecipient(ComponentName name) {
+            mComponentName = name;
+        }
+
+        @Override
+        public void binderDied() {
+            Log.e(LOG_TAG, "ImsService(" + mComponentName + ") died. Restarting...");
+            notifyAllFeaturesRemoved();
+            cleanUpService();
+            startDelayedRebindToService();
+        }
+    }
+
+    class ImsServiceConnection implements ServiceConnection {
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                mIsBound = true;
+                mIsBinding = false;
+                grantPermissionsToService();
+                Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: "
+                        + service);
+                if (service != null) {
+                    mImsDeathRecipient = new ImsDeathRecipient(name);
+                    try {
+                        service.linkToDeath(mImsDeathRecipient, 0);
+                        mImsServiceControllerBinder = service;
+                        mIImsServiceController = IImsServiceController.Stub.asInterface(service);
+                        // create all associated features in the ImsService
+                        for (Pair<Integer, Integer> i : mImsFeatures) {
+                            addImsServiceFeature(i);
+                        }
+                    } catch (RemoteException e) {
+                        mIsBound = false;
+                        mIsBinding = false;
+                        // Remote exception means that the binder already died.
+                        if (mImsDeathRecipient != null) {
+                            mImsDeathRecipient.binderDied();
+                        }
+                        Log.e(LOG_TAG, "ImsService(" + name + ") RemoteException:"
+                                + e.getMessage());
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized (mLock) {
+                mIsBinding = false;
+            }
+            if (mIImsServiceController != null) {
+                mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
+            }
+            notifyAllFeaturesRemoved();
+            cleanUpService();
+            Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Rebinding...");
+            startDelayedRebindToService();
+        }
+    }
+
+    /**
+     * Defines callbacks that are used by the ImsServiceController to notify when an ImsService
+     * has created or removed a new feature as well as the associated ImsServiceController.
+     */
+    public interface ImsServiceControllerCallbacks {
+        /**
+         * Called by ImsServiceController when a new feature has been created.
+         */
+        void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
+        /**
+         * Called by ImsServiceController when a new feature has been removed.
+         */
+        void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller);
+    }
+
+    /**
+     * Returns the currently defined rebind retry timeout. Used for testing.
+     */
+    @VisibleForTesting
+    public interface RebindRetry {
+        /**
+         * Return a long in ms indiciating how long the ImsServiceController should wait before
+         * rebinding.
+         */
+        long getRetryTimeout();
+    }
+
+    private static final String LOG_TAG = "ImsServiceController";
+    private static final int REBIND_RETRY_TIME = 5000;
+    private final Context mContext;
+    private final ComponentName mComponentName;
+    private final Object mLock = new Object();
+    private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
+    private final IPackageManager mPackageManager;
+    private ImsServiceControllerCallbacks mCallbacks;
+    private Handler mHandler;
+
+    private boolean mIsBound = false;
+    private boolean mIsBinding = false;
+    // Set of a pair of slotId->feature
+    private HashSet<Pair<Integer, Integer>> mImsFeatures;
+    private IImsServiceController mIImsServiceController;
+    // Easier for testing.
+    private IBinder mImsServiceControllerBinder;
+    private ImsServiceConnection mImsServiceConnection;
+    private ImsDeathRecipient mImsDeathRecipient;
+    private Set<IImsServiceFeatureListener> mImsStatusCallbacks = new HashSet<>();
+    // Only added or removed, never accessed on purpose.
+    private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
+
+    /**
+     * Container class for the IImsFeatureStatusCallback callback implementation. This class is
+     * never used directly, but we need to keep track of the IImsFeatureStatusCallback
+     * implementations explicitly.
+     */
+    private class ImsFeatureStatusCallback {
+        private int mSlotId;
+        private int mFeatureType;
+
+        private final IImsFeatureStatusCallback mCallback = new IImsFeatureStatusCallback.Stub() {
+
+            @Override
+            public void notifyImsFeatureStatus(int featureStatus) throws RemoteException {
+                Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature="
+                        + mFeatureType + ", status=" + featureStatus);
+                sendImsFeatureStatusChanged(mSlotId, mFeatureType, featureStatus);
+            }
+        };
+
+        ImsFeatureStatusCallback(int slotId, int featureType) {
+            mSlotId = slotId;
+            mFeatureType = featureType;
+        }
+
+        public IImsFeatureStatusCallback getCallback() {
+            return mCallback;
+        }
+    }
+
+    // Retry the bind to the ImsService that has died after mRebindRetry timeout.
+    private Runnable mRestartImsServiceRunnable = new Runnable() {
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                if (mIsBound) {
+                    return;
+                }
+                bind(mImsFeatures);
+            }
+        }
+    };
+
+    private RebindRetry mRebindRetry = () -> REBIND_RETRY_TIME;
+
+    @VisibleForTesting
+    public void setRebindRetryTime(RebindRetry retry) {
+        mRebindRetry = retry;
+    }
+
+    @VisibleForTesting
+    public Handler getHandler() {
+        return mHandler;
+    }
+
+    public ImsServiceController(Context context, ComponentName componentName,
+            ImsServiceControllerCallbacks callbacks) {
+        mContext = context;
+        mComponentName = componentName;
+        mCallbacks = callbacks;
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+    }
+
+    @VisibleForTesting
+    // Creating a new HandlerThread and background handler for each test causes a segfault, so for
+    // testing, use a handler supplied by the testing system.
+    public ImsServiceController(Context context, ComponentName componentName,
+            ImsServiceControllerCallbacks callbacks, Handler testHandler) {
+        mContext = context;
+        mComponentName = componentName;
+        mCallbacks = callbacks;
+        mHandler = testHandler;
+        mPackageManager = null;
+    }
+
+    /**
+     * Sends request to bind to ImsService designated by the {@ComponentName} with the feature set
+     * imsFeatureSet
+     *
+     * @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be
+     *                      created once the service is bound.
+     * @return {@link true} if the service is in the process of being bound, {@link false} if it
+     * has failed.
+     */
+    public boolean bind(HashSet<Pair<Integer, Integer>> imsFeatureSet) {
+        synchronized (mLock) {
+            // Remove pending rebind retry
+            mHandler.removeCallbacks(mRestartImsServiceRunnable);
+            if (!mIsBound && !mIsBinding) {
+                mIsBinding = true;
+                mImsFeatures = imsFeatureSet;
+                Intent imsServiceIntent = new Intent(ImsResolver.SERVICE_INTERFACE).setComponent(
+                        mComponentName);
+                mImsServiceConnection = new ImsServiceConnection();
+                int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+                        | Context.BIND_IMPORTANT;
+                Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
+                try {
+                    return mContext.bindService(imsServiceIntent, mImsServiceConnection,
+                            serviceFlags);
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, "Error binding (" + mComponentName + ") with exception: "
+                            + e.getMessage());
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Calls {@link IImsServiceController#removeImsFeature} on all features that the
+     * ImsService supports and then unbinds the service.
+     */
+    public void unbind() throws RemoteException {
+        synchronized (mLock) {
+            // Remove pending rebind retry
+            mHandler.removeCallbacks(mRestartImsServiceRunnable);
+            if (mImsServiceConnection == null || mImsDeathRecipient == null) {
+                return;
+            }
+            // Clean up all features
+            changeImsServiceFeatures(new HashSet<>());
+            removeImsServiceFeatureListener();
+            mImsServiceControllerBinder.unlinkToDeath(mImsDeathRecipient, 0);
+            Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
+            mContext.unbindService(mImsServiceConnection);
+            cleanUpService();
+        }
+    }
+
+    /**
+     * Finds the difference between the set of features that the ImsService has active and the new
+     * set defined in newImsFeatures. For every feature that is added,
+     * {@link IImsServiceController#createImsFeature} is called on the service. For every ImsFeature
+     * that is removed, {@link IImsServiceController#removeImsFeature} is called.
+     */
+    public void changeImsServiceFeatures(HashSet<Pair<Integer, Integer>> newImsFeatures)
+            throws RemoteException {
+        synchronized (mLock) {
+            if (mIsBound) {
+                // add features to service.
+                HashSet<Pair<Integer, Integer>> newFeatures = new HashSet<>(newImsFeatures);
+                newFeatures.removeAll(mImsFeatures);
+                for (Pair<Integer, Integer> i : newFeatures) {
+                    addImsServiceFeature(i);
+                }
+                // remove old features
+                HashSet<Pair<Integer, Integer>> oldFeatures = new HashSet<>(mImsFeatures);
+                oldFeatures.removeAll(newImsFeatures);
+                for (Pair<Integer, Integer> i : oldFeatures) {
+                    removeImsServiceFeature(i);
+                }
+            }
+            Log.i(LOG_TAG, "Features changed (" + mImsFeatures + "->" + newImsFeatures + ") for "
+                    + "ImsService: " + mComponentName);
+            mImsFeatures = newImsFeatures;
+        }
+    }
+
+    @VisibleForTesting
+    public IImsServiceController getImsServiceController() {
+        return mIImsServiceController;
+    }
+
+    @VisibleForTesting
+    public IBinder getImsServiceControllerBinder() {
+        return mImsServiceControllerBinder;
+    }
+
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * Add a callback to ImsManager that signals a new feature that the ImsServiceProxy can handle.
+     */
+    public void addImsServiceFeatureListener(IImsServiceFeatureListener callback) {
+        synchronized (mLock) {
+            mImsStatusCallbacks.add(callback);
+        }
+    }
+
+    private void removeImsServiceFeatureListener() {
+        synchronized (mLock) {
+            mImsStatusCallbacks.clear();
+        }
+    }
+
+    // Only add a new rebind if there are no pending rebinds waiting.
+    private void startDelayedRebindToService() {
+        if (!mHandler.hasCallbacks(mRestartImsServiceRunnable)) {
+            mHandler.postDelayed(mRestartImsServiceRunnable, mRebindRetry.getRetryTimeout());
+        }
+    }
+
+    // Grant runtime permissions to ImsService. PackageManager ensures that the ImsService is
+    // system/signed before granting permissions.
+    private void grantPermissionsToService() {
+        Log.i(LOG_TAG, "Granting Runtime permissions to:" + getComponentName());
+        String[] pkgToGrant = {mComponentName.getPackageName()};
+        try {
+            if (mPackageManager != null) {
+                mPackageManager.grantDefaultPermissionsToEnabledImsServices(pkgToGrant,
+                        mContext.getUserId());
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Unable to grant permissions, binder died.");
+        }
+    }
+
+    private void sendImsFeatureCreatedCallback(int slot, int feature) {
+        synchronized (mLock) {
+            for (Iterator<IImsServiceFeatureListener> i = mImsStatusCallbacks.iterator();
+                    i.hasNext(); ) {
+                IImsServiceFeatureListener callbacks = i.next();
+                try {
+                    callbacks.imsFeatureCreated(slot, feature);
+                } catch (RemoteException e) {
+                    // binder died, remove callback.
+                    Log.w(LOG_TAG, "sendImsFeatureCreatedCallback: Binder died, removing "
+                            + "callback. Exception:" + e.getMessage());
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    private void sendImsFeatureRemovedCallback(int slot, int feature) {
+        synchronized (mLock) {
+            for (Iterator<IImsServiceFeatureListener> i = mImsStatusCallbacks.iterator();
+                    i.hasNext(); ) {
+                IImsServiceFeatureListener callbacks = i.next();
+                try {
+                    callbacks.imsFeatureRemoved(slot, feature);
+                } catch (RemoteException e) {
+                    // binder died, remove callback.
+                    Log.w(LOG_TAG, "sendImsFeatureRemovedCallback: Binder died, removing "
+                            + "callback. Exception:" + e.getMessage());
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    private void sendImsFeatureStatusChanged(int slot, int feature, int status) {
+        synchronized (mLock) {
+            for (Iterator<IImsServiceFeatureListener> i = mImsStatusCallbacks.iterator();
+                    i.hasNext(); ) {
+                IImsServiceFeatureListener callbacks = i.next();
+                try {
+                    callbacks.imsStatusChanged(slot, feature, status);
+                } catch (RemoteException e) {
+                    // binder died, remove callback.
+                    Log.w(LOG_TAG, "sendImsFeatureStatusChanged: Binder died, removing "
+                            + "callback. Exception:" + e.getMessage());
+                    i.remove();
+                }
+            }
+        }
+    }
+
+    // This method should only be called when synchronized on mLock
+    private void addImsServiceFeature(Pair<Integer, Integer> featurePair) throws RemoteException {
+        if (mIImsServiceController == null || mCallbacks == null) {
+            Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
+            return;
+        }
+        ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(featurePair.first,
+                featurePair.second);
+        mFeatureStatusCallbacks.add(c);
+        mIImsServiceController.createImsFeature(featurePair.first, featurePair.second,
+                c.getCallback());
+        // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+        mCallbacks.imsServiceFeatureCreated(featurePair.first, featurePair.second, this);
+        // Send callback to ImsServiceProxy to change supported ImsFeatures
+        sendImsFeatureCreatedCallback(featurePair.first, featurePair.second);
+    }
+
+    // This method should only be called when synchronized on mLock
+    private void removeImsServiceFeature(Pair<Integer, Integer> featurePair)
+            throws RemoteException {
+        if (mIImsServiceController == null || mCallbacks == null) {
+            Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
+            return;
+        }
+        ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
+                c.mSlotId == featurePair.first && c.mFeatureType == featurePair.second)
+                .findFirst().orElse(null);
+        // Remove status callbacks from list.
+        if (callbackToRemove != null) {
+            mFeatureStatusCallbacks.remove(callbackToRemove);
+        }
+        mIImsServiceController.removeImsFeature(featurePair.first, featurePair.second,
+                (callbackToRemove != null ? callbackToRemove.getCallback() : null));
+        // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
+        mCallbacks.imsServiceFeatureRemoved(featurePair.first, featurePair.second, this);
+        // Send callback to ImsServiceProxy to change supported ImsFeatures
+        // Ensure that ImsServiceProxy callback occurs after ImsResolver callback. If an
+        // ImsManager requests the ImsService while it is being removed in ImsResolver, this
+        // callback will clean it up after.
+        sendImsFeatureRemovedCallback(featurePair.first, featurePair.second);
+    }
+
+    private void notifyAllFeaturesRemoved() {
+        if (mCallbacks == null) {
+            Log.w(LOG_TAG, "notifyAllFeaturesRemoved called with invalid callbacks.");
+            return;
+        }
+        synchronized (mLock) {
+            for (Pair<Integer, Integer> feature : mImsFeatures) {
+                mCallbacks.imsServiceFeatureRemoved(feature.first, feature.second, this);
+                sendImsFeatureRemovedCallback(feature.first, feature.second);
+            }
+        }
+    }
+
+    private void cleanUpService() {
+        synchronized (mLock) {
+            mImsDeathRecipient = null;
+            mImsServiceConnection = null;
+            mImsServiceControllerBinder = null;
+            mIImsServiceController = null;
+            mIsBound = false;
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index c4df6e0..03843a7 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -16,14 +16,36 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
+import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
+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;
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
 import android.app.Activity;
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 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.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
@@ -31,19 +53,20 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.os.PowerManager.WakeLock;
+import android.os.ResultReceiver;
 import android.os.SystemProperties;
 import android.os.UserHandle;
-
-import android.provider.Telephony;
+import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.UssdResponse;
 import android.text.TextUtils;
 
 import com.android.ims.ImsCallForwardInfo;
@@ -52,33 +75,9 @@
 import com.android.ims.ImsEcbmStateListener;
 import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
-import com.android.ims.ImsMultiEndpoint;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
-
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
-import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
-
-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;
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallForwardInfo;
@@ -88,6 +87,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.GsmCdmaPhone;
+import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
@@ -97,6 +97,7 @@
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.util.NotificationChannelController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -132,7 +133,6 @@
     ImsPhoneCallTracker mCT;
     ImsExternalCallTracker mExternalCallTracker;
     private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
-
     private ServiceState mSS = new ServiceState();
 
     // To redial silently through GSM or CDMA when dialing through IMS fails
@@ -213,10 +213,6 @@
 
         mPhoneId = mDefaultPhone.getPhoneId();
 
-        // This is needed to handle phone process crashes
-        // Same property is used for both CDMA & IMS phone.
-        mIsPhoneInEcmState = getInEcmMode();
-
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
         mWakeLock.setReferenceCounted(false);
@@ -226,7 +222,10 @@
                     .registerForDataRegStateOrRatChanged(this,
                             EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
         }
-        updateDataServiceState();
+        // Sets the Voice reg state to STATE_OUT_OF_SERVICE and also queries the data service
+        // state. We don't ever need the voice reg state to be anything other than in or out of
+        // service.
+        setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
 
         mDefaultPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
         // Force initial roaming state update later, on EVENT_CARRIER_CONFIG_CHANGED.
@@ -344,6 +343,11 @@
         return mCT.mRingingCall;
     }
 
+    @Override
+    public boolean isImsAvailable() {
+        return mCT.isImsServiceReady();
+    }
+
     private boolean handleCallDeflectionIncallSupplementaryService(
             String dialString) {
         if (dialString.length() > 1) {
@@ -370,6 +374,44 @@
         return true;
     }
 
+    private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode,
+                                   ResultReceiver wrappedCallback) {
+        UssdResponse response = new UssdResponse(ussdRequest, message);
+        Bundle returnData = new Bundle();
+        returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
+        wrappedCallback.send(returnCode, returnData);
+
+    }
+
+    @Override
+    public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)
+            throws CallStateException {
+        if (mPendingMMIs.size() > 0) {
+            // There are MMI codes in progress; fail attempt now.
+            Rlog.i(LOG_TAG, "handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest));
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback );
+            return true;
+        }
+        try {
+            dialInternal(ussdRequest, VideoProfile.STATE_AUDIO_ONLY, null, wrappedCallback);
+        } catch (CallStateException cse) {
+            if (CS_FALLBACK.equals(cse.getMessage())) {
+                throw cse;
+            } else {
+                Rlog.w(LOG_TAG, "Could not execute USSD " + cse);
+                sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                        wrappedCallback);
+            }
+        } catch (Exception e) {
+            Rlog.w(LOG_TAG, "Could not execute USSD " + e);
+            sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
+                    wrappedCallback);
+            return false;
+        }
+        return true;
+    }
+
     private boolean handleCallWaitingIncallSupplementaryService(
             String dialString) {
         int len = dialString.length();
@@ -521,6 +563,16 @@
                ringingCallState.isAlive());
     }
 
+    @Override
+    public boolean isInEcm() {
+        return mDefaultPhone.isInEcm();
+    }
+
+    @Override
+    public void setIsInEcm(boolean isInEcm){
+        mDefaultPhone.setIsInEcm(isInEcm);
+    }
+
     public void notifyNewRingingConnection(Connection c) {
         mDefaultPhone.notifyNewRingingConnectionP(c);
     }
@@ -538,7 +590,7 @@
     @Override
     public Connection
     dial(String dialString, int videoState) throws CallStateException {
-        return dialInternal(dialString, videoState, null);
+        return dialInternal(dialString, videoState, null, null);
     }
 
     @Override
@@ -546,10 +598,16 @@
     dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
             throws CallStateException {
         // ignore UUSInfo
-        return dialInternal (dialString, videoState, intentExtras);
+        return dialInternal (dialString, videoState, intentExtras, null);
     }
 
-    private Connection dialInternal(String dialString, int videoState, Bundle intentExtras)
+    protected Connection dialInternal(String dialString, int videoState, Bundle intentExtras)
+            throws CallStateException {
+        return dialInternal(dialString, videoState, intentExtras, null);
+    }
+
+    private Connection dialInternal(String dialString, int videoState,
+                                    Bundle intentExtras, ResultReceiver wrappedCallback)
             throws CallStateException {
         // Need to make sure dialString gets parsed properly
         String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -566,9 +624,9 @@
         // Only look at the Network portion for mmi
         String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
         ImsPhoneMmiCode mmi =
-                ImsPhoneMmiCode.newFromDialString(networkPortion, this);
+                ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback);
         if (DBG) Rlog.d(LOG_TAG,
-                "dialing w/ mmi '" + mmi + "'...");
+                "dialInternal: dialing w/ mmi '" + mmi + "'...");
 
         if (mmi == null) {
             return mCT.dial(dialString, videoState, intentExtras);
@@ -577,11 +635,26 @@
         } else if (!mmi.isSupportedOverImsPhone()) {
             // If the mmi is not supported by IMS service,
             // try to initiate dialing with default phone
+            // Note: This code is never reached; there is a bug in isSupportedOverImsPhone which
+            // causes it to return true even though the "processCode" method ultimately throws the
+            // exception.
+            Rlog.i(LOG_TAG, "dialInternal: USSD not supported by IMS; fallback to CS.");
             throw new CallStateException(CS_FALLBACK);
         } else {
             mPendingMMIs.add(mmi);
             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
-            mmi.processCode();
+
+            try {
+                mmi.processCode();
+            } catch (CallStateException cse) {
+                if (CS_FALLBACK.equals(cse.getMessage())) {
+                    Rlog.i(LOG_TAG, "dialInternal: fallback to GSM required.");
+                    // Make sure we remove from the list of pending MMIs since it will handover to
+                    // GSM.
+                    mPendingMMIs.remove(mmi);
+                    throw cse;
+                }
+            }
 
             return null;
         }
@@ -629,6 +702,11 @@
     }
 
     @Override
+    public void setTTYMode(int ttyMode, Message onComplete) {
+        mCT.setTtyMode(ttyMode);
+    }
+
+    @Override
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
         mCT.setUiTTYMode(uiTtyMode, onComplete);
     }
@@ -800,7 +878,7 @@
                         dialingNumber,
                         serviceClass,
                         timerSeconds,
-                        onComplete);
+                        resp);
             } catch (ImsException e) {
                 sendErrorResponse(onComplete, e);
             }
@@ -927,8 +1005,8 @@
         }
     }
 
-    /* package */
-    void sendErrorResponse(Message onComplete, Throwable e) {
+    @VisibleForTesting
+    public void sendErrorResponse(Message onComplete, Throwable e) {
         Rlog.d(LOG_TAG, "sendErrorResponse");
         if (onComplete != null) {
             AsyncResult.forMessage(onComplete, null, getCommandException(e));
@@ -950,6 +1028,9 @@
                 break;
             case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
                 error = CommandException.Error.RADIO_NOT_AVAILABLE;
+                break;
+            case ImsReasonInfo.CODE_FDN_BLOCKED:
+                error = CommandException.Error.FDN_CHECK_FAILURE;
             default:
                 break;
         }
@@ -1005,18 +1086,17 @@
             } else {
                 found.onUssdFinished(ussdMessage, isUssdRequest);
             }
-        } else { // pending USSD not found
-            // The network may initiate its own USSD request
+        } else if (!isUssdError && ussdMessage != null) {
+                // pending USSD not found
+                // The network may initiate its own USSD request
 
-            // ignore everything that isnt a Notify or a Request
-            // also, discard if there is no message to present
-            if (!isUssdError && ussdMessage != null) {
+                // ignore everything that isnt a Notify or a Request
+                // also, discard if there is no message to present
                 ImsPhoneMmiCode mmi;
                 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
                         isUssdRequest,
                         this);
                 onNetworkInitiatedUssd(mmi);
-            }
         }
     }
 
@@ -1031,9 +1111,19 @@
          * The exception is cancellation of an incoming USSD-REQUEST, which is
          * not on the list.
          */
+        Rlog.d(LOG_TAG, "onMMIDone: mmi=" + mmi);
         if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest()) {
-            mMmiCompleteRegistrants.notifyRegistrants(
+            ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
+            if (receiverCallback != null) {
+                int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
+                        TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
+                sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
+                        receiverCallback );
+            } else {
+                Rlog.v(LOG_TAG, "onMMIDone: notifyRegistrants");
+                mMmiCompleteRegistrants.notifyRegistrants(
                     new AsyncResult(null, mmi, null));
+            }
         }
     }
 
@@ -1299,10 +1389,10 @@
     private void sendEmergencyCallbackModeChange() {
         // Send an Intent
         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
-        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, mIsPhoneInEcmState);
+        intent.putExtra(PhoneConstants.PHONE_IN_ECM_STATE, isInEcm());
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
-        ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
-        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange");
+        ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
+        if (DBG) Rlog.d(LOG_TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
     }
 
     @Override
@@ -1325,12 +1415,11 @@
     private void handleEnterEmergencyCallbackMode() {
         if (DBG) {
             Rlog.d(LOG_TAG, "handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= "
-                    + mIsPhoneInEcmState);
+                    + isInEcm());
         }
         // if phone is not in Ecm mode, and it's changed to Ecm mode
-        if (mIsPhoneInEcmState == false) {
-            setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "true");
-            mIsPhoneInEcmState = true;
+        if (!isInEcm()) {
+            setIsInEcm(true);
             // notify change
             sendEmergencyCallbackModeChange();
 
@@ -1347,12 +1436,11 @@
     private void handleExitEmergencyCallbackMode() {
         if (DBG) {
             Rlog.d(LOG_TAG, "handleExitEmergencyCallbackMode: mIsPhoneInEcmState = "
-                    + mIsPhoneInEcmState);
+                    + isInEcm());
         }
 
-        if (mIsPhoneInEcmState) {
-            setSystemProperty(TelephonyProperties.PROPERTY_INECM_MODE, "false");
-            mIsPhoneInEcmState = false;
+        if (isInEcm()) {
+            setIsInEcm(false);
         }
 
         // Remove pending exit Ecm runnable, if any
@@ -1467,14 +1555,15 @@
                                 PendingIntent.FLAG_UPDATE_CURRENT
                         );
 
-                final Notification notification =
-                        new Notification.Builder(mContext)
+                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))
+                                .setStyle(new Notification.BigTextStyle()
+                                .bigText(messageNotification))
+                                .setChannelId(NotificationChannelController.CHANNEL_ID_WFC)
                                 .build();
                 final String notificationTag = "wifi_calling";
                 final int notificationId = 1;
@@ -1494,95 +1583,103 @@
     public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
         if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
                 && imsReasonInfo.mExtraMessage != null) {
+            // Suppress WFC Registration notifications if WFC is not enabled by the user.
+            if (ImsManager.isWfcEnabledByUser(mContext)) {
+                processWfcDisconnectForNotification(imsReasonInfo);
+            }
+        }
+    }
 
-            CarrierConfigManager configManager =
-                    (CarrierConfigManager)mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-            if (configManager == null) {
-                Rlog.e(LOG_TAG, "processDisconnectReason: CarrierConfigManager is not ready");
-                return;
-            }
-            PersistableBundle pb = configManager.getConfigForSubId(getSubId());
-            if (pb == null) {
-                Rlog.e(LOG_TAG, "processDisconnectReason: no config for subId " + getSubId());
-                return;
-            }
-            final String[] wfcOperatorErrorCodes =
-                    pb.getStringArray(
-                            CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
-            if (wfcOperatorErrorCodes == null) {
-                // no operator-specific error codes
-                return;
+    // Processes an IMS disconnect cause for possible WFC registration errors and optionally
+    // disable WFC.
+    private void processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo) {
+        CarrierConfigManager configManager =
+                (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            Rlog.e(LOG_TAG, "processDisconnectReason: CarrierConfigManager is not ready");
+            return;
+        }
+        PersistableBundle pb = configManager.getConfigForSubId(getSubId());
+        if (pb == null) {
+            Rlog.e(LOG_TAG, "processDisconnectReason: no config for subId " + getSubId());
+            return;
+        }
+        final String[] wfcOperatorErrorCodes =
+                pb.getStringArray(
+                        CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
+        if (wfcOperatorErrorCodes == null) {
+            // no operator-specific error codes
+            return;
+        }
+
+        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++) {
+            String[] codes = wfcOperatorErrorCodes[i].split("\\|");
+            if (codes.length != 2) {
+                Rlog.e(LOG_TAG, "Invalid carrier config: " + wfcOperatorErrorCodes[i]);
+                continue;
             }
 
-            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++) {
-                String[] codes = wfcOperatorErrorCodes[i].split("\\|");
-                if (codes.length != 2) {
-                    Rlog.e(LOG_TAG, "Invalid carrier config: " + wfcOperatorErrorCodes[i]);
-                    continue;
-                }
-
-                // Match error code.
-                if (!imsReasonInfo.mExtraMessage.startsWith(
-                        codes[0])) {
-                    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 = codes[0].length();
-                char lastChar = codes[0].charAt(codeStringLength - 1);
-                if (Character.isLetterOrDigit(lastChar)) {
-                    if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
-                        char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
-                        if (Character.isLetterOrDigit(nextChar)) {
-                            continue;
-                        }
+            // Match error code.
+            if (!imsReasonInfo.mExtraMessage.startsWith(
+                    codes[0])) {
+                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 = codes[0].length();
+            char lastChar = codes[0].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);
-
-                int idx = Integer.parseInt(codes[1]);
-                if (idx < 0 ||
-                        idx >= wfcOperatorErrorAlertMessages.length ||
-                        idx >= wfcOperatorErrorNotificationMessages.length) {
-                    Rlog.e(LOG_TAG, "Invalid index: " + wfcOperatorErrorCodes[i]);
-                    continue;
-                }
-                CharSequence messageAlert = imsReasonInfo.mExtraMessage;
-                CharSequence messageNotification = imsReasonInfo.mExtraMessage;
-                if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
-                    messageAlert = wfcOperatorErrorAlertMessages[idx];
-                }
-                if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
-                    messageNotification = wfcOperatorErrorNotificationMessages[idx];
-                }
-
-                // 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;
             }
+
+            final CharSequence title = mContext.getText(
+                    com.android.internal.R.string.wfcRegErrorTitle);
+
+            int idx = Integer.parseInt(codes[1]);
+            if (idx < 0
+                    || idx >= wfcOperatorErrorAlertMessages.length
+                    || idx >= wfcOperatorErrorNotificationMessages.length) {
+                Rlog.e(LOG_TAG, "Invalid index: " + wfcOperatorErrorCodes[i]);
+                continue;
+            }
+            CharSequence messageAlert = imsReasonInfo.mExtraMessage;
+            CharSequence messageNotification = imsReasonInfo.mExtraMessage;
+            if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
+                messageAlert = wfcOperatorErrorAlertMessages[idx];
+            }
+            if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
+                messageNotification = wfcOperatorErrorNotificationMessages[idx];
+            }
+
+            // 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;
         }
     }
 
@@ -1607,8 +1704,8 @@
     }
 
     @Override
-    public long getVtDataUsage() {
-        return mCT.getVtDataUsage();
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
+        return mCT.getVtDataUsage(perUidStats);
     }
 
     private void updateRoamingState(boolean newRoaming) {
@@ -1642,7 +1739,7 @@
         pw.println("  mPostDialHandler = " + mPostDialHandler);
         pw.println("  mSS = " + mSS);
         pw.println("  mWakeLock = " + mWakeLock);
-        pw.println("  mIsPhoneInEcmState = " + mIsPhoneInEcmState);
+        pw.println("  mIsPhoneInEcmState = " + isInEcm());
         pw.println("  mEcmExitRespRegistrant = " + mEcmExitRespRegistrant);
         pw.println("  mSilentRedialRegistrants = " + mSilentRedialRegistrants);
         pw.println("  mImsRegistered = " + mImsRegistered);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 3899785..ed24d22 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -23,16 +23,17 @@
 import android.os.Message;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
+import android.telephony.NetworkScanRequest;
+import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
-import android.telephony.Rlog;
 import android.util.Pair;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.MmiCode;
@@ -41,6 +42,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.uicc.IccFileHandler;
 
 import java.util.ArrayList;
@@ -145,12 +147,12 @@
      * @return all available cell information or null if none.
      */
     @Override
-    public List<CellInfo> getAllCellInfo() {
-        return getServiceStateTracker().getAllCellInfo();
+    public List<CellInfo> getAllCellInfo(WorkSource workSource) {
+        return getServiceStateTracker().getAllCellInfo(workSource);
     }
 
     @Override
-    public CellLocation getCellLocation() {
+    public CellLocation getCellLocation(WorkSource workSource) {
         return null;
     }
 
@@ -219,6 +221,8 @@
 
     public void notifyDisconnect(Connection cn) {
         mDisconnectRegistrants.notifyResult(cn);
+
+        mNotifier.notifyDisconnectCause(cn.getDisconnectCause(), cn.getPreciseDisconnectCause());
     }
 
     void notifyUnknownConnection() {
@@ -424,6 +428,14 @@
     }
 
     @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+    }
+
+    @Override
+    public void stopNetworkScan(Message response) {
+    }
+
+    @Override
     public void setNetworkSelectionModeAutomatic(Message response) {
     }
 
@@ -433,10 +445,6 @@
     }
 
     @Override
-    public void getNeighboringCids(Message response) {
-    }
-
-    @Override
     public void getDataCallList(Message response) {
     }
 
@@ -484,7 +492,7 @@
     }
 
     @Override
-    public boolean isDataConnectivityPossible() {
+    public boolean isDataAllowed() {
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index a011f757..52636f5 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -267,6 +267,7 @@
             long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime();
             if (conferenceConnectTime > 0) {
                 imsPhoneConnection.setConnectTime(conferenceConnectTime);
+                imsPhoneConnection.setConnectTimeReal(imsPhoneConnection.getConnectTimeReal());
             } else {
                 if (DBG) {
                     Rlog.d(LOG_TAG, "merge: conference connect time is 0");
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index c5378d1..9b4aced 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -22,8 +22,10 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
@@ -33,22 +35,28 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telecom.ConferenceParticipant;
+import android.telecom.TelecomManager;
 import android.telecom.VideoProfile;
 import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.PreciseDisconnectCause;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsServiceProxy;
+import android.telephony.ims.feature.ImsFeature;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Pair;
+import android.util.SparseIntArray;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsCallProfile;
@@ -65,6 +73,9 @@
 import com.android.ims.ImsUtInterface;
 import com.android.ims.internal.IImsVideoCallProvider;
 import com.android.ims.internal.ImsVideoCallProviderWrapper;
+import com.android.ims.internal.VideoPauseTracker;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.CallTracker;
@@ -74,12 +85,13 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 import com.android.internal.telephony.metrics.TelephonyMetrics;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
+import com.android.server.net.NetworkStatsService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -87,6 +99,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.regex.Pattern;
 
 /**
@@ -100,6 +113,10 @@
         void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState);
     }
 
+    public interface SharedPreferenceProxy {
+        SharedPreferences getDefaultSharedPreferences(Context context);
+    }
+
     private static final boolean DBG = true;
 
     // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
@@ -115,6 +132,7 @@
             "UTLTE", "UTWiFi"};
 
     private TelephonyMetrics mMetrics;
+    private boolean mCarrierConfigLoaded = false;
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -155,9 +173,14 @@
                     // If there is an active call.
                     if (mForegroundCall.hasConnections()) {
                         ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
-                        boolean answeringWillDisconnect =
-                                shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
-                        conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
+                        if (activeCall != null && imsCall != null) {
+                            // activeCall could be null if the foreground call is in a disconnected
+                            // state.  If either of the calls is null there is no need to check if
+                            // one will be disconnected on answer.
+                            boolean answeringWillDisconnect =
+                                    shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
+                            conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
+                        }
                     }
                     conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
                     addConnection(conn);
@@ -194,6 +217,10 @@
                     log("onReceive : Updating mAllowEmergencyVideoCalls = " +
                             mAllowEmergencyVideoCalls);
                 }
+                mCarrierConfigLoaded  = true;
+            } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
+                mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
+                        TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
             }
         }
     };
@@ -211,13 +238,16 @@
     private static final int EVENT_DATA_ENABLED_CHANGED = 23;
     private static final int EVENT_GET_IMS_SERVICE = 24;
     private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25;
+    private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26;
 
     private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
 
-    // The number of times we will try to connect to the ImsService before giving up.
-    private static final int NUM_IMS_SERVICE_RETRIES = 10;
-    // The number of milliseconds in between each try.
-    private static final int TIME_BETWEEN_IMS_SERVICE_RETRIES_MS = 400; // ms
+    // Initial condition for ims connection retry.
+    private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
+    // Ceiling bitshift amount for service query timeout, calculated as:
+    // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
+    // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
+    private static final int CEILING_SERVICE_RETRY_COUNT = 6;
 
     private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms
 
@@ -236,7 +266,11 @@
     // Hold aggregated video call data usage for each video call since boot.
     // The ImsCall's call id is the key of the map.
     private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>();
-    private volatile long mTotalVtDataUsage = 0;
+
+    private volatile NetworkStats mVtDataUsageSnapshot = null;
+    private volatile NetworkStats mVtDataUsageUidSnapshot = null;
+
+    private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL);
 
     private ImsPhoneConnection mPendingMO;
     private int mClirMode = CommandsInterface.CLIR_DEFAULT;
@@ -260,6 +294,7 @@
     private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
 
     private boolean mIsInEmergencyCall = false;
+    private boolean mIsDataEnabled = false;
 
     private int pendingCallClirMode;
     private int mPendingCallVideoState;
@@ -268,6 +303,8 @@
     private boolean mSwitchingFgAndBgCalls = false;
     private ImsCall mCallExpectedToResume = null;
     private boolean mAllowEmergencyVideoCalls = false;
+    private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
+    private boolean mIsViLteDataMetered = false;
 
     /**
      * Listeners to changes in the phone state.  Intended for use by other interested IMS components
@@ -306,6 +343,233 @@
     private boolean mSupportDowngradeVtToAudio = false;
 
     /**
+     * Stores the mapping of {@code ImsReasonInfo#CODE_*} to {@code PreciseDisconnectCause#*}
+     */
+    private static final SparseIntArray PRECISE_CAUSE_MAP = new SparseIntArray();
+    static {
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT,
+                PreciseDisconnectCause.LOCAL_ILLEGAL_ARGUMENT);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE,
+                PreciseDisconnectCause.LOCAL_ILLEGAL_STATE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR,
+                PreciseDisconnectCause.LOCAL_INTERNAL_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN,
+                PreciseDisconnectCause.LOCAL_IMS_SERVICE_DOWN);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL,
+                PreciseDisconnectCause.LOCAL_NO_PENDING_CALL);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+                PreciseDisconnectCause.NORMAL);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_POWER_OFF,
+                PreciseDisconnectCause.LOCAL_POWER_OFF);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY,
+                PreciseDisconnectCause.LOCAL_LOW_BATTERY);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE,
+                PreciseDisconnectCause.LOCAL_NETWORK_NO_SERVICE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+                PreciseDisconnectCause.LOCAL_NETWORK_NO_LTE_COVERAGE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING,
+                PreciseDisconnectCause.LOCAL_NETWORK_ROAMING);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED,
+                PreciseDisconnectCause.LOCAL_NETWORK_IP_CHANGED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE,
+                PreciseDisconnectCause.LOCAL_SERVICE_UNAVAILABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED,
+                PreciseDisconnectCause.LOCAL_NOT_REGISTERED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED,
+                PreciseDisconnectCause.LOCAL_MAX_CALL_EXCEEDED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE,
+                PreciseDisconnectCause.LOCAL_CALL_DECLINE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+                PreciseDisconnectCause.LOCAL_CALL_VCC_ON_PROGRESSING);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+                PreciseDisconnectCause.LOCAL_CALL_RESOURCE_RESERVATION_FAILED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+                PreciseDisconnectCause.LOCAL_CALL_CS_RETRY_REQUIRED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+                PreciseDisconnectCause.LOCAL_CALL_VOLTE_RETRY_REQUIRED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED,
+                PreciseDisconnectCause.LOCAL_CALL_TERMINATED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
+                PreciseDisconnectCause.LOCAL_HO_NOT_FEASIBLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING,
+                PreciseDisconnectCause.TIMEOUT_1XX_WAITING);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER,
+                PreciseDisconnectCause.TIMEOUT_NO_ANSWER);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+                PreciseDisconnectCause.TIMEOUT_NO_ANSWER_CALL_UPDATE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_FDN_BLOCKED,
+                PreciseDisconnectCause.FDN_BLOCKED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REDIRECTED,
+                PreciseDisconnectCause.SIP_REDIRECTED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_REQUEST,
+                PreciseDisconnectCause.SIP_BAD_REQUEST);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_FORBIDDEN,
+                PreciseDisconnectCause.SIP_FORBIDDEN);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_FOUND,
+                PreciseDisconnectCause.SIP_NOT_FOUND);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED,
+                PreciseDisconnectCause.SIP_NOT_SUPPORTED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT,
+                PreciseDisconnectCause.SIP_REQUEST_TIMEOUT);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE,
+                PreciseDisconnectCause.SIP_TEMPRARILY_UNAVAILABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_ADDRESS,
+                PreciseDisconnectCause.SIP_BAD_ADDRESS);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BUSY,
+                PreciseDisconnectCause.SIP_BUSY);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED,
+                PreciseDisconnectCause.SIP_REQUEST_CANCELLED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE,
+                PreciseDisconnectCause.SIP_NOT_ACCEPTABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_REACHABLE,
+                PreciseDisconnectCause.SIP_NOT_REACHABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_CLIENT_ERROR,
+                PreciseDisconnectCause.SIP_CLIENT_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR,
+                PreciseDisconnectCause.SIP_SERVER_INTERNAL_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE,
+                PreciseDisconnectCause.SIP_SERVICE_UNAVAILABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT,
+                PreciseDisconnectCause.SIP_SERVER_TIMEOUT);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_ERROR,
+                PreciseDisconnectCause.SIP_SERVER_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_USER_REJECTED,
+                PreciseDisconnectCause.SIP_USER_REJECTED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR,
+                PreciseDisconnectCause.SIP_GLOBAL_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE,
+                PreciseDisconnectCause.EMERGENCY_TEMP_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE,
+                PreciseDisconnectCause.EMERGENCY_PERM_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_INIT_FAILED,
+                PreciseDisconnectCause.MEDIA_INIT_FAILED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NO_DATA,
+                PreciseDisconnectCause.MEDIA_NO_DATA);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE,
+                PreciseDisconnectCause.MEDIA_NOT_ACCEPTABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED,
+                PreciseDisconnectCause.MEDIA_UNSPECIFIED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED,
+                PreciseDisconnectCause.USER_TERMINATED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_NOANSWER,
+                PreciseDisconnectCause.USER_NOANSWER);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_IGNORE,
+                PreciseDisconnectCause.USER_IGNORE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_DECLINE,
+                PreciseDisconnectCause.USER_DECLINE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOW_BATTERY,
+                PreciseDisconnectCause.LOW_BATTERY);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID,
+                PreciseDisconnectCause.BLACKLISTED_CALL_ID);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE,
+                PreciseDisconnectCause.USER_TERMINATED_BY_REMOTE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NOT_SUPPORTED,
+                PreciseDisconnectCause.UT_NOT_SUPPORTED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE,
+                PreciseDisconnectCause.UT_SERVICE_UNAVAILABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED,
+                PreciseDisconnectCause.UT_OPERATION_NOT_ALLOWED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NETWORK_ERROR,
+                PreciseDisconnectCause.UT_NETWORK_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH,
+                PreciseDisconnectCause.UT_CB_PASSWORD_MISMATCH);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED,
+                PreciseDisconnectCause.ECBM_NOT_SUPPORTED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED,
+                PreciseDisconnectCause.MULTIENDPOINT_NOT_SUPPORTED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+                PreciseDisconnectCause.CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
+                PreciseDisconnectCause.ANSWERED_ELSEWHERE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC,
+                PreciseDisconnectCause.CALL_PULL_OUT_OF_SYNC);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL,
+                PreciseDisconnectCause.CALL_PULLED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_FAILED,
+                PreciseDisconnectCause.SUPP_SVC_FAILED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED,
+                PreciseDisconnectCause.SUPP_SVC_CANCELLED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION,
+                PreciseDisconnectCause.SUPP_SVC_REINVITE_COLLISION);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE,
+                PreciseDisconnectCause.IWLAN_DPD_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+                PreciseDisconnectCause.EPDG_TUNNEL_ESTABLISH_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE,
+                PreciseDisconnectCause.EPDG_TUNNEL_REKEY_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION,
+                PreciseDisconnectCause.EPDG_TUNNEL_LOST_CONNECTION);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+                PreciseDisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE,
+                PreciseDisconnectCause.REMOTE_CALL_DECLINE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_LIMIT_REACHED,
+                PreciseDisconnectCause.DATA_LIMIT_REACHED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_DISABLED,
+                PreciseDisconnectCause.DATA_DISABLED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_WIFI_LOST,
+                PreciseDisconnectCause.WIFI_LOST);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_OFF,
+                PreciseDisconnectCause.RADIO_OFF);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NO_VALID_SIM,
+                PreciseDisconnectCause.NO_VALID_SIM);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR,
+                PreciseDisconnectCause.RADIO_INTERNAL_ERROR);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT,
+                PreciseDisconnectCause.NETWORK_RESP_TIMEOUT);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_REJECT,
+                PreciseDisconnectCause.NETWORK_REJECT);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE,
+                PreciseDisconnectCause.RADIO_ACCESS_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_FAILURE,
+                PreciseDisconnectCause.RADIO_LINK_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_LOST,
+                PreciseDisconnectCause.RADIO_LINK_LOST);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE,
+                PreciseDisconnectCause.RADIO_UPLINK_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE,
+                PreciseDisconnectCause.RADIO_SETUP_FAILURE);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL,
+                PreciseDisconnectCause.RADIO_RELEASE_NORMAL);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL,
+                PreciseDisconnectCause.RADIO_RELEASE_ABNORMAL);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED,
+                PreciseDisconnectCause.ACCESS_CLASS_BLOCKED);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_DETACH,
+                PreciseDisconnectCause.NETWORK_DETACH);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_1,
+                PreciseDisconnectCause.OEM_CAUSE_1);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_2,
+                PreciseDisconnectCause.OEM_CAUSE_2);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_3,
+                PreciseDisconnectCause.OEM_CAUSE_3);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_4,
+                PreciseDisconnectCause.OEM_CAUSE_4);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_5,
+                PreciseDisconnectCause.OEM_CAUSE_5);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_6,
+                PreciseDisconnectCause.OEM_CAUSE_6);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_7,
+                PreciseDisconnectCause.OEM_CAUSE_7);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_8,
+                PreciseDisconnectCause.OEM_CAUSE_8);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_9,
+                PreciseDisconnectCause.OEM_CAUSE_9);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_10,
+                PreciseDisconnectCause.OEM_CAUSE_10);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_11,
+                PreciseDisconnectCause.OEM_CAUSE_11);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_12,
+                PreciseDisconnectCause.OEM_CAUSE_12);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_13,
+                PreciseDisconnectCause.OEM_CAUSE_13);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_14,
+                PreciseDisconnectCause.OEM_CAUSE_14);
+        PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_15,
+                PreciseDisconnectCause.OEM_CAUSE_15);
+    }
+
+    /**
      * Carrier configuration option which determines whether the carrier wants to inform the user
      * when a video call is handed over from WIFI to LTE.
      * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL} for more
@@ -314,6 +578,13 @@
     private boolean mNotifyHandoverVideoFromWifiToLTE = false;
 
     /**
+     * Carrier configuration option which determines whether the carrier supports the
+     * {@link VideoProfile#STATE_PAUSED} signalling.
+     * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information.
+     */
+    private boolean mSupportPauseVideo = false;
+
+    /**
      * Carrier configuration option which defines a mapping from pairs of
      * {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()} values to a new
      * {@code ImsReasonInfo#CODE_*} value.
@@ -322,6 +593,69 @@
      */
     private Map<Pair<Integer, String>, Integer> mImsReasonCodeMap = new ArrayMap<>();
 
+
+    /**
+     * TODO: Remove this code; it is a workaround.
+     * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(Context, int, boolean)} to
+     * be called when an ongoing video call is disconnected.  In some cases, where video pause is
+     * supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data
+     * has been disabled we will pause the video rather than disconnecting the call.  When this
+     * happens we need to prevent the IMS service config from being updated, as this will cause VT
+     * to be disabled mid-call, resulting in an inability to un-pause the video.
+     */
+    private boolean mShouldUpdateImsConfigOnDisconnect = false;
+
+    /**
+     * Default implementation for retrieving shared preferences; uses the actual PreferencesManager.
+     */
+    private SharedPreferenceProxy mSharedPreferenceProxy = (Context context) -> {
+        return PreferenceManager.getDefaultSharedPreferences(context);
+    };
+
+    // Callback fires when ImsManager MMTel Feature changes state
+    private ImsServiceProxy.INotifyStatusChanged mNotifyStatusChangedCallback = () -> {
+        try {
+            int status = mImsManager.getImsServiceStatus();
+            log("Status Changed: " + status);
+            switch(status) {
+                case ImsFeature.STATE_READY: {
+                    startListeningForCalls();
+                    break;
+                }
+                case ImsFeature.STATE_INITIALIZING:
+                    // fall through
+                case ImsFeature.STATE_NOT_AVAILABLE: {
+                    stopListeningForCalls();
+                    break;
+                }
+                default: {
+                    Log.w(LOG_TAG, "Unexpected State!");
+                }
+            }
+        } catch (ImsException e) {
+            // Could not get the ImsService, retry!
+            retryGetImsService();
+        }
+    };
+
+    @VisibleForTesting
+    public interface IRetryTimeout {
+        int get();
+    }
+
+    /**
+     * Default implementation of interface that calculates the ImsService retry timeout.
+     * Override-able for testing.
+     */
+    @VisibleForTesting
+    public IRetryTimeout mRetryTimeout = () -> {
+        int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
+        if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
+            mImsServiceRetryCount++;
+        }
+        return timeout;
+    };
+
     //***** Events
 
 
@@ -335,6 +669,7 @@
         IntentFilter intentfilter = new IntentFilter();
         intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
         intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
         mPhone.getContext().registerReceiver(mReceiver, intentfilter);
         cacheCarrierConfiguration(mPhone.getSubId());
 
@@ -342,11 +677,47 @@
                 this, EVENT_DATA_ENABLED_CHANGED, null);
 
         mImsServiceRetryCount = 0;
+
+        final TelecomManager telecomManager =
+                (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
+        mDefaultDialerUid.set(
+                getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
+
+        long currentTime = SystemClock.elapsedRealtime();
+        mVtDataUsageSnapshot = new NetworkStats(currentTime, 1);
+        mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
+
         // Send a message to connect to the Ims Service and open a connection through
         // getImsService().
         sendEmptyMessage(EVENT_GET_IMS_SERVICE);
     }
 
+    /**
+     * Test-only method used to mock out access to the shared preferences through the
+     * {@link PreferenceManager}.
+     * @param sharedPreferenceProxy
+     */
+    @VisibleForTesting
+    public void setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy) {
+        mSharedPreferenceProxy = sharedPreferenceProxy;
+    }
+
+    private int getPackageUid(Context context, String pkg) {
+        if (pkg == null) {
+            return NetworkStats.UID_ALL;
+        }
+
+        // Initialize to UID_ALL so at least it can be counted to overall data usage if
+        // the dialer's package uid is not available.
+        int uid = NetworkStats.UID_ALL;
+        try {
+            uid = context.getPackageManager().getPackageUid(pkg, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            loge("Cannot find package uid. pkg = " + pkg);
+        }
+        return uid;
+    }
+
     private PendingIntent createIncomingCallPendingIntent() {
         Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -357,6 +728,16 @@
     private void getImsService() throws ImsException {
         if (DBG) log("getImsService");
         mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
+        // Adding to set, will be safe adding multiple times. If the ImsService is not active yet,
+        // this method will throw an ImsException.
+        mImsManager.addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
+        // Wait for ImsService.STATE_READY to start listening for calls.
+        // Call the callback right away for compatibility with older devices that do not use states.
+        mNotifyStatusChangedCallback.notifyStatusChanged();
+    }
+
+    private void startListeningForCalls() throws ImsException {
+        mImsServiceRetryCount = 0;
         mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
                 createIncomingCallPendingIntent(),
                 mImsConnectionStateListener);
@@ -370,16 +751,34 @@
             mPhone.exitEmergencyCallbackMode();
         }
         int mPreferredTtyMode = Settings.Secure.getInt(
-            mPhone.getContext().getContentResolver(),
-            Settings.Secure.PREFERRED_TTY_MODE,
-            Phone.TTY_MODE_OFF);
-        mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, mPreferredTtyMode, null);
+                mPhone.getContext().getContentResolver(),
+                Settings.Secure.PREFERRED_TTY_MODE,
+                Phone.TTY_MODE_OFF);
+        mImsManager.setUiTTYMode(mPhone.getContext(), mPreferredTtyMode, null);
 
         ImsMultiEndpoint multiEndpoint = getMultiEndpointInterface();
         if (multiEndpoint != null) {
             multiEndpoint.setExternalCallStateListener(
                     mPhone.getExternalCallTracker().getExternalCallStateListener());
         }
+
+        if (mCarrierConfigLoaded) {
+            ImsManager.updateImsServiceConfig(mPhone.getContext(),
+                    mPhone.getPhoneId(), true);
+        }
+    }
+
+    private void stopListeningForCalls() {
+        try {
+            resetImsCapabilities();
+            // Only close on valid session.
+            if (mImsManager != null && mServiceId > 0) {
+                mImsManager.close(mServiceId);
+                mServiceId = -1;
+            }
+        } catch (ImsException e) {
+            // If the binder is unavailable, then the ImsService doesn't need to close.
+        }
     }
 
     public void dispose() {
@@ -391,7 +790,7 @@
 
         clearDisconnected();
         mPhone.getContext().unregisterReceiver(mReceiver);
-        mPhone.unregisterForDataEnabledChanged(this);
+        mPhone.getDefaultPhone().unregisterForDataEnabledChanged(this);
         removeMessages(EVENT_GET_IMS_SERVICE);
     }
 
@@ -427,8 +826,16 @@
 
     public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
             CallStateException {
-        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        int oirMode = sp.getInt(Phone.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
+        int oirMode;
+        if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) {
+            SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences(
+                    mPhone.getContext());
+            oirMode = sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(),
+                    CommandsInterface.CLIR_DEFAULT);
+        } else {
+            loge("dial; could not get default CLIR mode.");
+            oirMode = CommandsInterface.CLIR_DEFAULT;
+        }
         return dial(dialString, oirMode, videoState, intentExtras);
     }
 
@@ -535,6 +942,14 @@
         return mPendingMO;
     }
 
+    boolean isImsServiceReady() {
+        if (mImsManager == null) {
+            return false;
+        }
+
+        return mImsManager.isServiceAvailable();
+    }
+
     /**
      * Caches frequently used carrier configuration items locally.
      *
@@ -570,7 +985,13 @@
         mSupportDowngradeVtToAudio = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
         mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
-                CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
+                CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL);
+        mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
+        mIsViLteDataMetered = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL);
+        mSupportPauseVideo = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
 
         String[] mappings = carrierConfig
                 .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
@@ -582,12 +1003,18 @@
                 }
 
                 try {
-                    int fromCode = Integer.parseInt(values[0]);
+                    Integer fromCode;
+                    if (values[0].equals("*")) {
+                        fromCode = null;
+                    } else {
+                        fromCode = Integer.parseInt(values[0]);
+                    }
                     String message = values[1];
                     int toCode = Integer.parseInt(values[2]);
 
-                    mImsReasonCodeMap.put(new Pair<>(fromCode, message), toCode);
-                    log("Loaded ImsReasonInfo mapping : fromCode = " + fromCode + " ; message = " +
+                    addReasonCodeRemapping(fromCode, message, toCode);
+                    log("Loaded ImsReasonInfo mapping : fromCode = " +
+                            fromCode == null ? "any" : fromCode + " ; message = " +
                             message + " ; toCode = " + toCode);
                 } catch (NumberFormatException nfe) {
                     loge("Invalid ImsReasonInfo mapping found: " + mapping);
@@ -680,6 +1107,7 @@
             loge("dialInternal : " + e);
             conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
             sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
+            retryGetImsService();
         } catch (RemoteException e) {
         }
     }
@@ -782,7 +1210,7 @@
 
             // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls.
             // If hold or resume later fails, we will swap them back.
-            boolean switchingWithWaitingCall = mBackgroundCall.getImsCall() == null &&
+            boolean switchingWithWaitingCall = !mBackgroundCall.getState().isAlive() &&
                     mRingingCall != null &&
                     mRingingCall.getState() == ImsPhoneCall.State.WAITING;
 
@@ -851,6 +1279,13 @@
         ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
         if (foregroundConnection != null) {
             foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
+            foregroundConnection.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_START,
+                    null);
+        }
+        ImsPhoneConnection backgroundConnection = findConnection(bgImsCall);
+        if (backgroundConnection != null) {
+            backgroundConnection.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_START,
+                    null);
         }
 
         try {
@@ -883,19 +1318,16 @@
             && !mForegroundCall.isFull();
     }
 
-    public boolean
-    canDial() {
+    public boolean canDial() {
         boolean ret;
-        int serviceState = mPhone.getServiceState().getState();
         String disableCall = SystemProperties.get(
                 TelephonyProperties.PROPERTY_DISABLE_CALL, "false");
 
-        ret = (serviceState != ServiceState.STATE_POWER_OFF)
-            && mPendingMO == null
-            && !mRingingCall.isRinging()
-            && !disableCall.equals("true")
-            && (!mForegroundCall.getState().isAlive()
-                    || !mBackgroundCall.getState().isAlive());
+        ret = mPendingMO == null
+                && !mRingingCall.isRinging()
+                && !disableCall.equals("true")
+                && (!mForegroundCall.getState().isAlive()
+                        || !mBackgroundCall.getState().isAlive());
 
         return ret;
     }
@@ -992,7 +1424,27 @@
     }
 
     //***** Called from ImsPhone
+    /**
+     * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
+     */
+    public void setTtyMode(int ttyMode) {
+        if (mImsManager == null) {
+            Log.w(LOG_TAG, "ImsManager is null when setting TTY mode");
+            return;
+        }
 
+        try {
+            mImsManager.setTtyMode(ttyMode);
+        } catch (ImsException e) {
+            loge("setTtyMode : " + e);
+            retryGetImsService();
+        }
+    }
+
+    /**
+     * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
+     * settings screen.
+     */
     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
         if (mImsManager == null) {
             mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException());
@@ -1000,10 +1452,11 @@
         }
 
         try {
-            mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, uiTtyMode, onComplete);
+            mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete);
         } catch (ImsException e) {
-            loge("setTTYMode : " + e);
+            loge("setUITTYMode : " + e);
             mPhone.sendErrorResponse(onComplete, e);
+            retryGetImsService();
         }
     }
 
@@ -1203,6 +1656,7 @@
         } catch (ImsException e) {
             loge("sendUSSD : " + e);
             mPhone.sendErrorResponse(response, e);
+            retryGetImsService();
         }
     }
 
@@ -1322,6 +1776,18 @@
     }
 
     /**
+     * Adds a reason code remapping, for test purposes.
+     *
+     * @param fromCode The from code, or {@code null} if all.
+     * @param message The message to map.
+     * @param toCode The code to remap to.
+     */
+    @VisibleForTesting
+    public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) {
+        mImsReasonCodeMap.put(new Pair<>(fromCode, message), toCode);
+    }
+
+    /**
      * Returns the {@link ImsReasonInfo#getCode()}, potentially remapping to a new value based on
      * the {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()}.
      *
@@ -1330,25 +1796,41 @@
      * @param reasonInfo The {@link ImsReasonInfo}.
      * @return The remapped code.
      */
-    private int maybeRemapReasonCode(ImsReasonInfo reasonInfo) {
+    @VisibleForTesting
+    public int maybeRemapReasonCode(ImsReasonInfo reasonInfo) {
         int code = reasonInfo.getCode();
 
         Pair<Integer, String> toCheck = new Pair<>(code, reasonInfo.getExtraMessage());
-
+        Pair<Integer, String> wildcardToCheck = new Pair<>(null, reasonInfo.getExtraMessage());
         if (mImsReasonCodeMap.containsKey(toCheck)) {
             int toCode = mImsReasonCodeMap.get(toCheck);
 
             log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
                     + reasonInfo.getExtraMessage() + " ; toCode = " + toCode);
             return toCode;
+        } else if (mImsReasonCodeMap.containsKey(wildcardToCheck)) {
+            // Handle the case where a wildcard is specified for the fromCode; in this case we will
+            // match without caring about the fromCode.
+            int toCode = mImsReasonCodeMap.get(wildcardToCheck);
+
+            log("maybeRemapReasonCode : fromCode(wildcard) = " + reasonInfo.getCode() +
+                    " ; message = " + reasonInfo.getExtraMessage() + " ; toCode = " + toCode);
+            return toCode;
         }
         return code;
     }
 
-    private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
+    /**
+     * Maps an {@link ImsReasonInfo} reason code to a {@link DisconnectCause} cause code.
+     * The {@link Call.State} provided is the state of the call prior to disconnection.
+     * @param reasonInfo the {@link ImsReasonInfo} for the disconnection.
+     * @param callState The {@link Call.State} prior to disconnection.
+     * @return The {@link DisconnectCause} code.
+     */
+    @VisibleForTesting
+    public int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState) {
         int cause = DisconnectCause.ERROR_UNSPECIFIED;
 
-        //int type = reasonInfo.getReasonType();
         int code = maybeRemapReasonCode(reasonInfo);
         switch (code) {
             case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
@@ -1361,6 +1843,9 @@
             case ImsReasonInfo.CODE_USER_TERMINATED:
                 return DisconnectCause.LOCAL;
 
+            case ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE:
+                return DisconnectCause.IMS_MERGED_SUCCESSFULLY;
+
             case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE:
             case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE:
                 // If the call has been declined locally (on this device), or on remotely (on
@@ -1401,13 +1886,24 @@
             case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
                 return DisconnectCause.TIMED_OUT;
 
-            case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
             case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
                 return DisconnectCause.POWER_OFF;
 
+            case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
+            case ImsReasonInfo.CODE_LOW_BATTERY: {
+                if (callState == Call.State.DIALING) {
+                    return DisconnectCause.DIAL_LOW_BATTERY;
+                } else {
+                    return DisconnectCause.LOW_BATTERY;
+                }
+            }
+
             case ImsReasonInfo.CODE_FDN_BLOCKED:
                 return DisconnectCause.FDN_BLOCKED;
 
+            case ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED:
+                return DisconnectCause.IMEI_NOT_ACCEPTED;
+
             case ImsReasonInfo.CODE_ANSWERED_ELSEWHERE:
                 return DisconnectCause.ANSWERED_ELSEWHERE;
 
@@ -1422,17 +1918,35 @@
 
             case ImsReasonInfo.CODE_DATA_LIMIT_REACHED:
                 return DisconnectCause.DATA_LIMIT_REACHED;
+
+            case ImsReasonInfo.CODE_WIFI_LOST:
+                return DisconnectCause.WIFI_LOST;
+
+            case ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED:
+                return DisconnectCause.IMS_ACCESS_BLOCKED;
+
+            case ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE:
+                return DisconnectCause.EMERGENCY_TEMP_FAILURE;
+
+            case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE:
+                return DisconnectCause.EMERGENCY_PERM_FAILURE;
+
             default:
         }
 
         return cause;
     }
 
+    private int getPreciseDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
+        return PRECISE_CAUSE_MAP.get(maybeRemapReasonCode(reasonInfo),
+                PreciseDisconnectCause.ERROR_UNSPECIFIED);
+    }
+
     /**
      * @return true if the phone is in Emergency Callback mode, otherwise false
      */
     private boolean isPhoneInEcbMode() {
-        return SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false);
+        return mPhone.isInEcm();
     }
 
     /**
@@ -1538,7 +2052,24 @@
                     return;
                 } else {
                     mPendingMO = null;
-                    int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
+                    ImsPhoneConnection conn = findConnection(imsCall);
+                    Call.State callState;
+                    if (conn != null) {
+                        callState = conn.getState();
+                    } else {
+                        // Need to fall back in case connection is null; it shouldn't be, but a sane
+                        // fallback is to assume we're dialing.  This state is only used to
+                        // determine which disconnect string to show in the case of a low battery
+                        // disconnect.
+                        callState = Call.State.DIALING;
+                    }
+                    int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
+
+                    if(conn != null) {
+                        conn.setPreciseDisconnectCause(
+                                getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
+                    }
+
                     processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
                 }
                 mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
@@ -1550,8 +2081,18 @@
         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
             if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
 
-            int cause = getDisconnectCauseFromReasonInfo(reasonInfo);
             ImsPhoneConnection conn = findConnection(imsCall);
+            Call.State callState;
+            if (conn != null) {
+                callState = conn.getState();
+            } else {
+                // Connection shouldn't be null, but if it is, we can assume the call was active.
+                // This call state is only used for determining which disconnect message to show in
+                // the case of the device's battery being low resulting in a call drop.
+                callState = Call.State.ACTIVE;
+            }
+            int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
+
             if (DBG) log("cause = " + cause + " conn = " + conn);
 
             if (conn != null) {
@@ -1608,6 +2149,10 @@
             mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
                     reasonInfo);
 
+            if(conn != null) {
+                conn.setPreciseDisconnectCause(getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
+            }
+
             processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
             if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE) {
                 if (mRingingCall.getState().isRinging()) {
@@ -1646,6 +2191,13 @@
                     mCallExpectedToResume = null;
                 }
             }
+
+            if (mShouldUpdateImsConfigOnDisconnect) {
+                // Ensure we update the IMS config when the call is disconnected; we delayed this
+                // because a video call was paused.
+                ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+                mShouldUpdateImsConfigOnDisconnect = false;
+            }
         }
 
         @Override
@@ -1792,10 +2344,21 @@
                     mPhone.stopOnHoldTone(conn);
                     mOnHoldToneStarted = false;
                 }
-
                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
             }
 
+            boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                    com.android.internal.R.bool.config_useVideoPauseWorkaround);
+            if (useVideoPauseWorkaround && mSupportPauseVideo &&
+                    VideoProfile.isVideo(conn.getVideoState())) {
+                // If we are using the video pause workaround, the vendor IMS code has issues
+                // with video pause signalling.  In this case, when a call is remotely
+                // held, the modem does not reliably change the video state of the call to be
+                // paused.
+                // As a workaround, we will turn on that bit now.
+                conn.changeToUnPausedState();
+            }
+
             SuppServiceNotification supp = new SuppServiceNotification();
             // Type of notification: 0 = MO; 1 = MT
             // Refer SuppServiceNotification class documentation.
@@ -1817,8 +2380,19 @@
                     mOnHoldToneStarted = true;
                     mOnHoldToneId = System.identityHashCode(conn);
                 }
-
                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
+
+                boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                        com.android.internal.R.bool.config_useVideoPauseWorkaround);
+                if (useVideoPauseWorkaround && mSupportPauseVideo &&
+                        VideoProfile.isVideo(conn.getVideoState())) {
+                    // If we are using the video pause workaround, the vendor IMS code has issues
+                    // with video pause signalling.  In this case, when a call is remotely
+                    // held, the modem does not reliably change the video state of the call to be
+                    // paused.
+                    // As a workaround, we will turn on that bit now.
+                    conn.changeToPausedState();
+                }
             }
 
             SuppServiceNotification supp = new SuppServiceNotification();
@@ -1905,6 +2479,7 @@
             ImsPhoneConnection conn = findConnection(call);
             if (conn != null) {
                 conn.onConferenceMergeFailed();
+                conn.onConnectionEvent(android.telecom.Connection.EVENT_MERGE_COMPLETE, null);
             }
         }
 
@@ -1935,28 +2510,40 @@
             ImsReasonInfo reasonInfo) {
             if (DBG) {
                 log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
-                    targetAccessTech + ", reasonInfo=" + reasonInfo);
+                        targetAccessTech + ", reasonInfo=" + reasonInfo);
             }
 
-            boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN &&
-                    targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+            // Only consider it a valid handover to WIFI if the source radio tech is known.
+            boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                    && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                    && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
             if (isHandoverToWifi) {
                 // If we handed over to wifi successfully, don't check for failure in the future.
                 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
             }
 
-            boolean isHandoverFromWifi =
-                    srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN &&
-                            targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
-            if (mNotifyHandoverVideoFromWifiToLTE && isHandoverFromWifi && imsCall.isVideoCall()) {
-                log("onCallHandover :: notifying of WIFI to LTE handover.");
-                ImsPhoneConnection conn = findConnection(imsCall);
-                if (conn != null) {
-                    conn.onConnectionEvent(
-                            TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
-                } else {
-                    loge("onCallHandover :: failed to notify of handover; connection is null.");
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                // Only consider it a handover from WIFI if the source and target radio tech is known.
+                boolean isHandoverFromWifi =
+                        srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                                && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                                && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+                if (isHandoverFromWifi && imsCall.isVideoCall()) {
+                    if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
+                        log("onCallHandover :: notifying of WIFI to LTE handover.");
+                        conn.onConnectionEvent(
+                                TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
+                    }
+
+                    if (!mIsDataEnabled && mIsViLteDataMetered) {
+                        // Call was downgraded from WIFI to LTE and data is metered; downgrade the
+                        // call now.
+                        downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn);
+                    }
                 }
+            } else {
+                loge("onCallHandover :: connection null.");
             }
 
             mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
@@ -1991,6 +2578,34 @@
             }
         }
 
+        @Override
+        public void onRttModifyRequestReceived(ImsCall imsCall) {
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                conn.onRttModifyRequestReceived();
+            }
+        }
+
+        @Override
+        public void onRttModifyResponseReceived(ImsCall imsCall, int status) {
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                conn.onRttModifyResponseReceived(status);
+                if (status ==
+                        android.telecom.Connection.RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
+                    conn.startRttTextProcessing();
+                }
+            }
+        }
+
+        @Override
+        public void onRttMessageReceived(ImsCall imsCall, String message) {
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                conn.onRttMessageReceived(message);
+            }
+        }
+
         /**
          * Handles a change to the multiparty state for an {@code ImsCall}.  Notifies the associated
          * {@link ImsPhoneConnection} of the change.
@@ -2080,8 +2695,8 @@
     private ImsConnectionStateListener mImsConnectionStateListener =
         new ImsConnectionStateListener() {
         @Override
-        public void onImsConnected() {
-            if (DBG) log("onImsConnected");
+        public void onImsConnected(int imsRadioTech) {
+            if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech);
             mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
             mPhone.setImsRegistered(true);
             mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
@@ -2091,6 +2706,7 @@
         @Override
         public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
             if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
+            resetImsCapabilities();
             mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
             mPhone.setImsRegistered(false);
             mPhone.processDisconnectReason(imsReasonInfo);
@@ -2099,8 +2715,8 @@
         }
 
         @Override
-        public void onImsProgressing() {
-            if (DBG) log("onImsProgressing");
+        public void onImsProgressing(int imsRadioTech) {
+            if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech);
             mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
             mPhone.setImsRegistered(false);
             mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
@@ -2127,65 +2743,14 @@
         @Override
         public void onFeatureCapabilityChanged(int serviceClass,
                 int[] enabledFeatures, int[] disabledFeatures) {
-            if (serviceClass == ImsServiceClass.MMTEL) {
-                boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
-                // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
-                StringBuilder sb;
-                if (DBG) {
-                    sb = new StringBuilder(120);
-                    sb.append("onFeatureCapabilityChanged: ");
-                }
-                for (int  i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
-                        i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI &&
-                        i < enabledFeatures.length; i++) {
-                    if (enabledFeatures[i] == i) {
-                        // If the feature is set to its own integer value it is enabled.
-                        if (DBG) {
-                            sb.append(mImsFeatureStrings[i]);
-                            sb.append(":true ");
-                        }
-
-                        mImsFeatureEnabled[i] = true;
-                    } else if (enabledFeatures[i]
-                            == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
-                        // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
-                        if (DBG) {
-                            sb.append(mImsFeatureStrings[i]);
-                            sb.append(":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 (DBG) {
-                    log(sb.toString());
-                }
-                if (tmpIsVideoCallEnabled != isVideoCallEnabled()) {
-                    mPhone.notifyForVideoCapabilityChanged(isVideoCallEnabled());
-                }
-
-                // 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: isVolteEnabled=" + isVolteEnabled()
-                            + ", isVideoCallEnabled=" + isVideoCallEnabled()
-                            + ", isVowifiEnabled=" + isVowifiEnabled()
-                            + ", isUtEnabled=" + isUtEnabled());
-                for (ImsPhoneConnection connection : mConnections) {
-                    connection.updateWifiState();
-                }
-
-                mPhone.onFeatureCapabilityChanged();
-
-                mMetrics.writeOnImsCapabilities(
-                        mPhone.getPhoneId(), mImsFeatureEnabled);
-            }
+            if (DBG) log("onFeatureCapabilityChanged");
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = serviceClass;
+            args.arg1 = enabledFeatures;
+            args.arg2 = disabledFeatures;
+            // Remove any pending updates; they're already stale, so no need to process them.
+            removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
+            obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
         }
 
         @Override
@@ -2224,7 +2789,7 @@
             throw getImsManagerIsNullException();
         }
 
-        ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId);
+        ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration();
         return ut;
     }
 
@@ -2334,13 +2899,9 @@
                 ImsCall call = (ImsCall) ar.userObj;
                 Long usage = (long) ar.result;
                 log("VT data usage update. usage = " + usage + ", imsCall = " + call);
-
-                Long oldUsage = 0L;
-                if (mVtDataUsageMap.containsKey(call.uniqueId)) {
-                    oldUsage = mVtDataUsageMap.get(call.uniqueId);
+                if (usage > 0) {
+                    updateVtDataUsage(call, usage);
                 }
-                mTotalVtDataUsage += (usage - oldUsage);
-                mVtDataUsageMap.put(call.uniqueId, usage);
                 break;
             case EVENT_DATA_ENABLED_CHANGED:
                 ar = (AsyncResult) msg.obj;
@@ -2354,20 +2915,7 @@
                     getImsService();
                 } catch (ImsException e) {
                     loge("getImsService: " + e);
-                    //Leave mImsManager as null, then CallStateException will be thrown when dialing
-                    mImsManager = null;
-                    if (mImsServiceRetryCount < NUM_IMS_SERVICE_RETRIES) {
-                        loge("getImsService: Retrying getting ImsService...");
-                        sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE,
-                                TIME_BETWEEN_IMS_SERVICE_RETRIES_MS);
-                        mImsServiceRetryCount++;
-                    } else {
-                        // We have been unable to connect for
-                        // NUM_IMS_SERVICE_RETRIES*TIME_BETWEEN_IMS_SERVICE_RETRIES_MS ms. We will
-                        // probably never be able to connect, so we should just give up.
-                        loge("getImsService: ImsService retrieval timeout... ImsService is " +
-                                "unavailable.");
-                    }
+                    retryGetImsService();
                 }
                 break;
             case EVENT_CHECK_FOR_WIFI_HANDOVER:
@@ -2382,9 +2930,76 @@
                     }
                 }
                 break;
+            case EVENT_ON_FEATURE_CAPABILITY_CHANGED: {
+                SomeArgs args = (SomeArgs) msg.obj;
+                try {
+                    int serviceClass = args.argi1;
+                    int[] enabledFeatures = (int[]) args.arg1;
+                    int[] disabledFeatures = (int[]) args.arg2;
+                    handleFeatureCapabilityChanged(serviceClass, enabledFeatures, disabledFeatures);
+                } finally {
+                    args.recycle();
+                }
+                break;
+            }
         }
     }
 
+    /**
+     * Update video call data usage
+     *
+     * @param call The IMS call
+     * @param dataUsage The aggregated data usage for the call
+     */
+    private void updateVtDataUsage(ImsCall call, long dataUsage) {
+        long oldUsage = 0L;
+        if (mVtDataUsageMap.containsKey(call.uniqueId)) {
+            oldUsage = mVtDataUsageMap.get(call.uniqueId);
+        }
+
+        long delta = dataUsage - oldUsage;
+        mVtDataUsageMap.put(call.uniqueId, dataUsage);
+
+        log("updateVtDataUsage: call=" + call + ", delta=" + delta);
+
+        long currentTime = SystemClock.elapsedRealtime();
+        int isRoaming = mPhone.getServiceState().getDataRoaming() ? 1 : 0;
+
+        // Create the snapshot of total video call data usage.
+        NetworkStats vtDataUsageSnapshot = new NetworkStats(currentTime, 1);
+        vtDataUsageSnapshot.combineAllValues(mVtDataUsageSnapshot);
+        // Since the modem only reports the total vt data usage rather than rx/tx separately,
+        // the only thing we can do here is splitting the usage into half rx and half tx.
+        // Uid -1 indicates this is for the overall device data usage.
+        vtDataUsageSnapshot.combineValues(new NetworkStats.Entry(
+                NetworkStatsService.VT_INTERFACE, -1, NetworkStats.SET_FOREGROUND,
+                NetworkStats.TAG_NONE, 1, isRoaming, delta / 2, 0, delta / 2, 0, 0));
+        mVtDataUsageSnapshot = vtDataUsageSnapshot;
+
+        // Create the snapshot of video call data usage per dialer. combineValues will create
+        // a separate entry if uid is different from the previous snapshot.
+        NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
+        vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot);
+
+        // The dialer uid might not be initialized correctly during boot up due to telecom service
+        // not ready or its default dialer cache not ready. So we double check again here to see if
+        // default dialer uid is really not available.
+        if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) {
+            final TelecomManager telecomManager =
+                    (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
+            mDefaultDialerUid.set(
+                    getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
+        }
+
+        // Since the modem only reports the total vt data usage rather than rx/tx separately,
+        // the only thing we can do here is splitting the usage into half rx and half tx.
+        vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
+                NetworkStatsService.VT_INTERFACE, mDefaultDialerUid.get(),
+                NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, 1, isRoaming, delta / 2,
+                0, delta / 2, 0, 0));
+        mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot;
+    }
+
     @Override
     protected void log(String msg) {
         Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
@@ -2440,10 +3055,9 @@
             pw.println(" " + mImsFeatureStrings[i] + ": "
                     + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled"));
         }
-        pw.println(" mTotalVtDataUsage=" + mTotalVtDataUsage);
-        for (Map.Entry<Integer, Long> entry : mVtDataUsageMap.entrySet()) {
-            pw.println("    id=" + entry.getKey() + " ,usage=" + entry.getValue());
-        }
+        pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
+        pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
+        pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
 
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
@@ -2518,13 +3132,33 @@
         return mState;
     }
 
+    private void retryGetImsService() {
+        // The binder connection is already up. Do not try to get it again.
+        if (mImsManager.isServiceAvailable()) {
+            return;
+        }
+        //Leave mImsManager as null, then CallStateException will be thrown when dialing
+        mImsManager = null;
+        // Exponential backoff during retry, limited to 32 seconds.
+        loge("getImsService: Retrying getting ImsService...");
+        removeMessages(EVENT_GET_IMS_SERVICE);
+        sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get());
+    }
+
     private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
             throws RemoteException {
         IImsVideoCallProvider imsVideoCallProvider =
                 imsCall.getCallSession().getVideoCallProvider();
         if (imsVideoCallProvider != null) {
+            // TODO: Remove this when we can better formalize the format of session modify requests.
+            boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
+                    com.android.internal.R.bool.config_useVideoPauseWorkaround);
+
             ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
                     new ImsVideoCallProviderWrapper(imsVideoCallProvider);
+            if (useVideoPauseWorkaround) {
+                imsVideoCallProviderWrapper.setUseVideoPauseWorkaround(useVideoPauseWorkaround);
+            }
             conn.setVideoProvider(imsVideoCallProviderWrapper);
             imsVideoCallProviderWrapper.registerForDataUsageUpdate
                     (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall);
@@ -2649,6 +3283,10 @@
     private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall,
             ImsCall incomingCall) {
 
+        if (activeCall == null || incomingCall == null) {
+            return false;
+        }
+
         if (!mDropVideoCallWhenAnsweringAudioCall) {
             return false;
         }
@@ -2666,11 +3304,13 @@
         return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio && !isVoWifiEnabled;
     }
 
-    /** Get aggregated video call data usage since boot.
+    /**
+     * Get aggregated video call data usage since boot.
      *
-     * @return data usage in bytes
+     * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
+     * @return Snapshot of video call data usage
      */
-    public long getVtDataUsage() {
+    public NetworkStats getVtDataUsage(boolean perUidStats) {
 
         // If there is an ongoing VT call, request the latest VT usage from the modem. The latest
         // usage will return asynchronously so it won't be counted in this round, but it will be
@@ -2684,7 +3324,7 @@
             }
         }
 
-        return mTotalVtDataUsage;
+        return perUidStats ? mVtDataUsageUidSnapshot : mVtDataUsageSnapshot;
     }
 
     public void registerPhoneStateListener(PhoneStateListener listener) {
@@ -2734,19 +3374,50 @@
     private void onDataEnabledChanged(boolean enabled, int reason) {
 
         log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
+
         ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
+        mIsDataEnabled = enabled;
 
+        if (!mIsViLteDataMetered) {
+            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " - carrier policy "
+                    + "indicates that data is not metered for ViLTE calls.");
+            return;
+        }
+
+        // Inform connections that data has been disabled to ensure we turn off video capability
+        // if this is an LTE call.
+        for (ImsPhoneConnection conn : mConnections) {
+            conn.handleDataEnabledChange(enabled);
+        }
+
+        int reasonCode;
+        if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
+            reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
+        } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
+            reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
+        } else {
+            // Unexpected code, default to data disabled.
+            reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
+        }
+
+        // Potentially send connection events so the InCall UI knows that video calls are being
+        // downgraded due to data being enabled/disabled.
+        maybeNotifyDataDisabled(enabled, reasonCode);
+        // Handle video state changes required as a result of data being enabled/disabled.
+        handleDataEnabledChange(enabled, reasonCode);
+
+        // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
+        // the carrier config has loaded and will deregister IMS.
+        if (!mShouldUpdateImsConfigOnDisconnect
+                && reason != DataEnabledSettings.REASON_REGISTERED) {
+            // This will call into updateVideoCallFeatureValue and eventually all clients will be
+            // asynchronously notified that the availability of VT over LTE has changed.
+            ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+        }
+    }
+
+    private void maybeNotifyDataDisabled(boolean enabled, int reasonCode) {
         if (!enabled) {
-            int reasonCode;
-            if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
-                reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
-            } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
-                reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
-            } else {
-                // Unexpected code, default to data disabled.
-                reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
-            }
-
             // If data is disabled while there are ongoing VT calls which are not taking place over
             // wifi, then they should be disconnected to prevent the user from incurring further
             // data charges.
@@ -2766,23 +3437,89 @@
                             conn.onConnectionEvent(
                                     TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
                         }
-                        modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
-                    } else {
-                        // If the carrier does not support downgrading to voice, the only choice we
-                        // have is to terminate the call.
-                        try {
-                            imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
-                        } catch (ImsException ie) {
-                            loge("Couldn't terminate call " + imsCall);
-                        }
                     }
                 }
             }
         }
+    }
 
-        // This will call into updateVideoCallFeatureValue and eventually all clients will be
-        // asynchronously notified that the availability of VT over LTE has changed.
-        ImsManager.updateImsServiceConfig(mPhone.getContext(), mPhone.getPhoneId(), true);
+    /**
+     * Handles changes to the enabled state of mobile data.
+     * When data is disabled, handles auto-downgrade of video calls over LTE.
+     * When data is enabled, handled resuming of video calls paused when data was disabled.
+     * @param enabled {@code true} if mobile data is enabled, {@code false} if mobile data is
+     *                            disabled.
+     * @param reasonCode The {@link ImsReasonInfo} code for the data enabled state change.
+     */
+    private void handleDataEnabledChange(boolean enabled, int reasonCode) {
+        if (!enabled) {
+            // If data is disabled while there are ongoing VT calls which are not taking place over
+            // wifi, then they should be disconnected to prevent the user from incurring further
+            // data charges.
+            for (ImsPhoneConnection conn : mConnections) {
+                ImsCall imsCall = conn.getImsCall();
+                if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
+                    log("handleDataEnabledChange - downgrading " + conn);
+                    downgradeVideoCall(reasonCode, conn);
+                }
+            }
+        } else if (mSupportPauseVideo) {
+            // Data was re-enabled, so un-pause previously paused video calls.
+            for (ImsPhoneConnection conn : mConnections) {
+                // If video is paused, check to see if there are any pending pauses due to enabled
+                // state of data changing.
+                log("handleDataEnabledChange - resuming " + conn);
+                if (VideoProfile.isPaused(conn.getVideoState()) &&
+                        conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) {
+                    // The data enabled state was a cause of a pending pause, so potentially
+                    // resume the video now.
+                    conn.resumeVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
+                }
+            }
+            mShouldUpdateImsConfigOnDisconnect = false;
+        }
+    }
+
+    /**
+     * Handles downgrading a video call.  The behavior depends on carrier capabilities; we will
+     * attempt to take one of the following actions (in order of precedence):
+     * 1. If supported by the carrier, the call will be downgraded to an audio-only call.
+     * 2. If the carrier supports video pause signalling, the video will be paused.
+     * 3. The call will be disconnected.
+     * @param reasonCode The {@link ImsReasonInfo} reason code for the downgrade.
+     * @param conn The {@link ImsPhoneConnection} to downgrade.
+     */
+    private void downgradeVideoCall(int reasonCode, ImsPhoneConnection conn) {
+        ImsCall imsCall = conn.getImsCall();
+        if (imsCall != null) {
+            if (conn.hasCapabilities(
+                    Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
+                            Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
+
+                // If the carrier supports downgrading to voice, then we can simply issue a
+                // downgrade to voice instead of terminating the call.
+                modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
+            } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) {
+                // The carrier supports video pause signalling, so pause the video if we didn't just
+                // lose wifi; in that case just disconnect.
+                mShouldUpdateImsConfigOnDisconnect = true;
+                conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
+            } else {
+                // At this point the only choice we have is to terminate the call.
+                try {
+                    imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
+                } catch (ImsException ie) {
+                    loge("Couldn't terminate call " + imsCall);
+                }
+            }
+        }
+    }
+
+    private void resetImsCapabilities() {
+        log("Resetting Capabilities...");
+        for (int i = 0; i < mImsFeatureEnabled.length; i++) {
+            mImsFeatureEnabled[i] = false;
+        }
     }
 
     /**
@@ -2806,4 +3543,71 @@
     public boolean isCarrierDowngradeOfVtCallSupported() {
         return mSupportDowngradeVtToAudio;
     }
+
+    private void handleFeatureCapabilityChanged(int serviceClass,
+            int[] enabledFeatures, int[] disabledFeatures) {
+        if (serviceClass == ImsServiceClass.MMTEL) {
+            boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
+            // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
+            StringBuilder sb;
+            if (DBG) {
+                sb = new StringBuilder(120);
+                sb.append("handleFeatureCapabilityChanged: ");
+            }
+            for (int  i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
+                    i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI &&
+                            i < enabledFeatures.length; i++) {
+                if (enabledFeatures[i] == i) {
+                    // If the feature is set to its own integer value it is enabled.
+                    if (DBG) {
+                        sb.append(mImsFeatureStrings[i]);
+                        sb.append(":true ");
+                    }
+
+                    mImsFeatureEnabled[i] = true;
+                } else if (enabledFeatures[i]
+                        == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+                    // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
+                    if (DBG) {
+                        sb.append(mImsFeatureStrings[i]);
+                        sb.append(":false ");
+                    }
+
+                    mImsFeatureEnabled[i] = false;
+                } else {
+                    // Feature has unknown state; it is not its own value or -1.
+                    if (DBG) {
+                        loge("handleFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i]
+                                + "): unexpectedValue=" + enabledFeatures[i]);
+                    }
+                }
+            }
+            boolean isVideoEnabled = isVideoCallEnabled();
+            boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
+            if (DBG) {
+                sb.append(" isVideoEnabledStateChanged=");
+                sb.append(isVideoEnabledStatechanged);
+            }
+
+            if (isVideoEnabledStatechanged) {
+                log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged=" +
+                        isVideoEnabled);
+                mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
+            }
+
+            if (DBG) {
+                log(sb.toString());
+            }
+
+            if (DBG) log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled()
+                    + ", isVideoCallEnabled=" + isVideoCallEnabled()
+                    + ", isVowifiEnabled=" + isVowifiEnabled()
+                    + ", isUtEnabled=" + isUtEnabled());
+
+            mPhone.onFeatureCapabilityChanged();
+
+            mMetrics.writeOnImsCapabilities(
+                    mPhone.getPhoneId(), mImsFeatureEnabled);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 7bfafc0..60a50b9 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -20,14 +20,16 @@
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.RadioCapability;
+import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.dataconnection.DataProfile;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
-import com.android.internal.telephony.RadioCapability;
-import com.android.internal.telephony.UUSInfo;
 
 import java.util.List;
 
@@ -258,9 +260,8 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, int profile,
-            String apn, String user, String password, int authType,
-            String protocol, Message result) {
+    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, Message result) {
     }
 
     @Override
@@ -334,6 +335,14 @@
     }
 
     @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+    }
+
+    @Override
+    public void stopNetworkScan(Message response) {
+    }
+
+    @Override
     public void setCallForward(int action, int cfReason, int serviceClass,
                 String number, int timeSeconds, Message response) {
     }
@@ -425,10 +434,6 @@
     }
 
     @Override
-    public void getNeighboringCids(Message response) {
-    }
-
-    @Override
     public void setLocationUpdates(boolean enable, Message response) {
     }
 
@@ -555,24 +560,15 @@
     }
 
     @Override
-    public void getCellInfoList(Message result) {
+    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
     }
 
     @Override
-    public void setCellInfoListRate(int rateInMillis, Message response) {
+    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
     }
 
     @Override
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-            String password, Message result) {
-    }
-
-    @Override
-    public void setDataProfile(DataProfile[] dps, Message result) {
-    }
-
-    @Override
-    public void iccOpenLogicalChannel(String AID, Message response) {}
+    public void iccOpenLogicalChannel(String AID, int p2, Message response) {}
 
     @Override
     public void iccCloseLogicalChannel(int channel, Message response) {}
@@ -629,10 +625,27 @@
     }
 
     @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+                                                Message result) {
+    }
+
+    @Override
     public void setAllowedCarriers(List<CarrierIdentifier> carriers, Message result) {
     }
 
     @Override
     public void getAllowedCarriers(Message result) {
     }
+
+    @Override
+    public void sendDeviceState(int stateType, boolean state, Message result) {
+    }
+
+    @Override
+    public void setUnsolResponseFilter(int filter, Message result){
+    }
+
+    @Override
+    public void setSimCardPower(int state, 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 2d215af..0ad81d2 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -100,15 +100,20 @@
      */
     private boolean mShouldIgnoreVideoStateChanges = false;
 
+    private ImsVideoCallProviderWrapper mImsVideoCallProviderWrapper;
+
+    private int mPreciseDisconnectCause = 0;
+
+    private ImsRttTextHandler mRttTextHandler;
+    private android.telecom.Connection.RttTextStream mRttTextStream;
+
     /**
-     * Used to indicate whether the wifi state is based on
-     * {@link com.android.ims.ImsConnectionStateListener#
-     *      onFeatureCapabilityChanged(int, int[], int[])} callbacks, or values received via the
-     * {@link ImsCallProfile#EXTRA_CALL_RAT_TYPE} extra.  Util we receive a value via the extras,
-     * we will use the wifi state based on the {@code onFeatureCapabilityChanged}.  Once a value
-     * is received via the extras, we will prefer those values going forward.
+     * Used as an override to determine whether video is locally available for this call.
+     * This allows video availability to be overridden in the case that the modem says video is
+     * currently available, but mobile data is off and the carrier is metering data for video
+     * calls.
      */
-    private boolean mIsWifiStateFromExtras = false;
+    private boolean mIsVideoEnabled = true;
 
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
@@ -179,8 +184,6 @@
         mCreateTime = System.currentTimeMillis();
         mUusInfo = null;
 
-        updateWifiState();
-
         // Ensure any extras set on the ImsCallProfile at the start of the call are cached locally
         // in the ImsPhoneConnection.  This isn't going to inform any listeners (since the original
         // connection is not likely to be associated with a TelephonyConnection yet).
@@ -242,11 +245,20 @@
         return (a == null) ? (b == null) : a.equals (b);
     }
 
-    private static int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
-        Rlog.w(LOG_TAG, "applyLocalCallCapabilities - localProfile = "+localProfile);
+    static boolean
+    equalsBaseDialString (String a, String b) {
+        return (a == null) ? (b == null) : (b != null && a.startsWith (b));
+    }
+
+    private int applyLocalCallCapabilities(ImsCallProfile localProfile, int capabilities) {
+        Rlog.i(LOG_TAG, "applyLocalCallCapabilities - localProfile = " + localProfile);
         capabilities = removeCapability(capabilities,
                 Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
 
+        if (!mIsVideoEnabled) {
+            Rlog.i(LOG_TAG, "applyLocalCallCapabilities - disabling video (overidden)");
+            return capabilities;
+        }
         switch (localProfile.mCallType) {
             case ImsCallProfile.CALL_TYPE_VT:
                 // Fall-through
@@ -418,8 +430,10 @@
             } else {
                 Rlog.d(LOG_TAG, "onDisconnect: no parent");
             }
-            if (mImsCall != null) mImsCall.close();
-            mImsCall = null;
+            synchronized (this) {
+                if (mImsCall != null) mImsCall.close();
+                mImsCall = null;
+            }
         }
         releaseWakeLock();
         return changed;
@@ -581,10 +595,12 @@
 
     void
     releaseWakeLock() {
-        synchronized(mPartialWakeLock) {
-            if (mPartialWakeLock.isHeld()) {
-                Rlog.d(LOG_TAG, "releaseWakeLock");
-                mPartialWakeLock.release();
+        if (mPartialWakeLock != null) {
+            synchronized (mPartialWakeLock) {
+                if (mPartialWakeLock.isHeld()) {
+                    Rlog.d(LOG_TAG, "releaseWakeLock");
+                    mPartialWakeLock.release();
+                }
             }
         }
     }
@@ -614,7 +630,7 @@
     }
 
     @Override
-    public boolean isMultiparty() {
+    public synchronized boolean isMultiparty() {
         return mImsCall != null && mImsCall.isMultiparty();
     }
 
@@ -627,11 +643,8 @@
      *      {@code false} otherwise.
      */
     @Override
-    public boolean isConferenceHost() {
-        if (mImsCall == null) {
-            return false;
-        }
-        return mImsCall.isConferenceHost();
+    public synchronized boolean isConferenceHost() {
+        return mImsCall != null && mImsCall.isConferenceHost();
     }
 
     @Override
@@ -639,11 +652,11 @@
         return !isConferenceHost();
     }
 
-    public ImsCall getImsCall() {
+    public synchronized ImsCall getImsCall() {
         return mImsCall;
     }
 
-    public void setImsCall(ImsCall imsCall) {
+    public synchronized void setImsCall(ImsCall imsCall) {
         mImsCall = imsCall;
     }
 
@@ -684,18 +697,20 @@
         }
 
         boolean updateParent = mParent.update(this, imsCall, state);
-        boolean updateWifiState = updateWifiState();
         boolean updateAddressDisplay = updateAddressDisplay(imsCall);
         boolean updateMediaCapabilities = updateMediaCapabilities(imsCall);
         boolean updateExtras = updateExtras(imsCall);
 
-        return updateParent || updateWifiState || updateAddressDisplay || updateMediaCapabilities
-                || updateExtras;
+        return updateParent || updateAddressDisplay || updateMediaCapabilities || updateExtras;
     }
 
     @Override
     public int getPreciseDisconnectCause() {
-        return 0;
+        return mPreciseDisconnectCause;
+    }
+
+    public void setPreciseDisconnectCause(int cause) {
+        mPreciseDisconnectCause = cause;
     }
 
     /**
@@ -761,7 +776,7 @@
                 Rlog.d(LOG_TAG, "address = " + Rlog.pii(LOG_TAG, address) + " name = " + name +
                         " nump = " + nump + " namep = " + namep);
             }
-            if(equalsHandlesNulls(mAddress, address)) {
+            if(!equalsBaseDialString(mAddress, address)) {
                 mAddress = address;
                 changed = true;
             }
@@ -825,7 +840,7 @@
                     }
 
                     if (!mShouldIgnoreVideoStateChanges) {
-                        setVideoState(newVideoState);
+                        updateVideoState(newVideoState);
                         changed = true;
                     } else {
                         Rlog.d(LOG_TAG, "updateMediaCapabilities - ignoring video state change " +
@@ -888,26 +903,56 @@
         return changed;
     }
 
-    /**
-     * 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() {
-        // If we've received the wifi state via the ImsCallProfile.EXTRA_CALL_RAT_TYPE extra, we
-        // will no longer use state updates which are based on the onFeatureCapabilityChanged
-        // callback.
-        if (mIsWifiStateFromExtras) {
-            return false;
+    private void updateVideoState(int newVideoState) {
+        if (mImsVideoCallProviderWrapper != null) {
+            mImsVideoCallProviderWrapper.onVideoStateChanged(newVideoState);
         }
+        setVideoState(newVideoState);
+    }
 
-        Rlog.d(LOG_TAG, "updateWifiState: " + mOwner.isVowifiEnabled());
-        if (isWifi() != mOwner.isVowifiEnabled()) {
-            setWifi(mOwner.isVowifiEnabled());
-            return true;
+    public void sendRttModifyRequest(android.telecom.Connection.RttTextStream textStream) {
+        getImsCall().sendRttModifyRequest();
+        setCurrentRttTextStream(textStream);
+    }
+
+    /**
+     * Sends the user's response to a remotely-issued RTT upgrade request
+     *
+     * @param textStream A valid {@link android.telecom.Connection.RttTextStream} if the user
+     *                   accepts, {@code null} if not.
+     */
+    public void sendRttModifyResponse(android.telecom.Connection.RttTextStream textStream) {
+        boolean accept = textStream != null;
+        ImsCall imsCall = getImsCall();
+
+        imsCall.sendRttModifyResponse(accept);
+        if (accept) {
+            setCurrentRttTextStream(textStream);
+            startRttTextProcessing();
+        } else {
+            Rlog.e(LOG_TAG, "sendRttModifyResponse: foreground call has no connections");
         }
-        return false;
+    }
+
+    public void onRttMessageReceived(String message) {
+        getOrCreateRttTextHandler().sendToInCall(message);
+    }
+
+    public void setCurrentRttTextStream(android.telecom.Connection.RttTextStream rttTextStream) {
+        mRttTextStream = rttTextStream;
+    }
+
+    public void startRttTextProcessing() {
+        getOrCreateRttTextHandler().initialize(mRttTextStream);
+    }
+
+    private ImsRttTextHandler getOrCreateRttTextHandler() {
+        if (mRttTextHandler != null) {
+            return mRttTextHandler;
+        }
+        mRttTextHandler = new ImsRttTextHandler(Looper.getMainLooper(),
+                (message) -> getImsCall().sendRttMessage(message));
+        return mRttTextHandler;
     }
 
     /**
@@ -921,10 +966,6 @@
         if (extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE) ||
                 extras.containsKey(ImsCallProfile.EXTRA_CALL_RAT_TYPE_ALT)) {
 
-            // We've received the extra indicating the radio technology, so we will continue to
-            // prefer the radio technology received via this extra going forward.
-            mIsWifiStateFromExtras = true;
-
             ImsCall call = getImsCall();
             boolean isWifi = false;
             if (call != null) {
@@ -1037,15 +1078,26 @@
         sb.append(" address: ");
         sb.append(Rlog.pii(LOG_TAG, getAddress()));
         sb.append(" ImsCall: ");
-        if (mImsCall == null) {
-            sb.append("null");
-        } else {
-            sb.append(mImsCall);
+        synchronized (this) {
+            if (mImsCall == null) {
+                sb.append("null");
+            } else {
+                sb.append(mImsCall);
+            }
         }
         sb.append("]");
         return sb.toString();
     }
 
+    @Override
+    public void setVideoProvider(android.telecom.Connection.VideoProvider videoProvider) {
+        super.setVideoProvider(videoProvider);
+
+        if (videoProvider instanceof ImsVideoCallProviderWrapper) {
+            mImsVideoCallProviderWrapper = (ImsVideoCallProviderWrapper) videoProvider;
+        }
+    }
+
     /**
      * Indicates whether current phone connection is emergency or not
      * @return boolean: true if emergency, false otherwise
@@ -1097,4 +1149,73 @@
             setVideoState(currentVideoState);
         }
     }
+
+    /**
+     * Issues a request to pause the video using {@link VideoProfile#STATE_PAUSED} from a source
+     * other than the InCall UI.
+     *
+     * @param source The source of the pause request.
+     */
+    public void pauseVideo(int source) {
+        if (mImsVideoCallProviderWrapper == null) {
+            return;
+        }
+
+        mImsVideoCallProviderWrapper.pauseVideo(getVideoState(), source);
+    }
+
+    /**
+     * Issues a request to resume the video using {@link VideoProfile#STATE_PAUSED} from a source
+     * other than the InCall UI.
+     *
+     * @param source The source of the resume request.
+     */
+    public void resumeVideo(int source) {
+        if (mImsVideoCallProviderWrapper == null) {
+            return;
+        }
+
+        mImsVideoCallProviderWrapper.resumeVideo(getVideoState(), source);
+    }
+
+    /**
+     * Determines if a specified source has issued a pause request.
+     *
+     * @param source The source.
+     * @return {@code true} if the source issued a pause request, {@code false} otherwise.
+     */
+    public boolean wasVideoPausedFromSource(int source) {
+        if (mImsVideoCallProviderWrapper == null) {
+            return false;
+        }
+
+        return mImsVideoCallProviderWrapper.wasVideoPausedFromSource(source);
+    }
+
+    public void changeToPausedState() {
+        int newVideoState = getVideoState() | VideoProfile.STATE_PAUSED;
+        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToPausedState - setting paused bit; "
+                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
+        updateVideoState(newVideoState);
+        mShouldIgnoreVideoStateChanges = true;
+    }
+
+    public void changeToUnPausedState() {
+        int newVideoState = getVideoState() & ~VideoProfile.STATE_PAUSED;
+        Rlog.i(LOG_TAG, "ImsPhoneConnection: changeToUnPausedState - unsetting paused bit; "
+                + "newVideoState=" + VideoProfile.videoStateToString(newVideoState));
+        updateVideoState(newVideoState);
+        mShouldIgnoreVideoStateChanges = false;
+    }
+
+    public void handleDataEnabledChange(boolean isDataEnabled) {
+        mIsVideoEnabled = isDataEnabled;
+        Rlog.i(LOG_TAG, "handleDataEnabledChange: isDataEnabled=" + isDataEnabled
+                + "; updating local video availability.");
+        updateMediaCapabilities(getImsCall());
+        if (mImsVideoCallProviderWrapper != null) {
+            mImsVideoCallProviderWrapper.setIsVideoEnabled(
+                    hasCapabilities(Connection.Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index 9c7e875..b861b8f 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -16,43 +16,43 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
+import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ResultReceiver;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
-import android.telephony.Rlog;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
 import com.android.internal.telephony.CallForwardInfo;
-import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.uicc.IccRecords;
-
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
-import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
-
 import com.android.internal.telephony.MmiCode;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.uicc.IccRecords;
 
-import java.util.regex.Pattern;
 import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * The motto for this file is:
@@ -176,6 +176,7 @@
     private String mPoundString;         // Entire MMI string up to and including #
     private String mDialingNumber;
     private String mPwd;                 // For password registration
+    private ResultReceiver mCallbackReceiver;
 
     private boolean mIsPendingUSSD;
 
@@ -234,8 +235,12 @@
      * Please see flow chart in TS 22.030 6.5.3.2
      */
 
-    static ImsPhoneMmiCode
-    newFromDialString(String dialString, ImsPhone phone) {
+    static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone) {
+       return newFromDialString(dialString, phone, null);
+    }
+
+    static ImsPhoneMmiCode newFromDialString(String dialString,
+                                             ImsPhone phone, ResultReceiver wrappedCallback) {
         Matcher m;
         ImsPhoneMmiCode ret = null;
 
@@ -252,6 +257,7 @@
             ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
             ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
             ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
+            ret.mCallbackReceiver = wrappedCallback;
             // According to TS 22.030 6.5.2 "Structure of the MMI",
             // the dialing number should not ending with #.
             // The dialing number ending # is treated as unique USSD,
@@ -534,6 +540,11 @@
 
     }
 
+    @Override
+    public String getDialString() {
+        return mPoundString;
+    }
+
     static private boolean
     isTwoDigitShortCode(Context context, String dialString) {
         Rlog.d(LOG_TAG, "isTwoDigitShortCode");
@@ -744,14 +755,14 @@
     processCode () throws CallStateException {
         try {
             if (isShortCode()) {
-                Rlog.d(LOG_TAG, "isShortCode");
+                Rlog.d(LOG_TAG, "processCode: isShortCode");
 
                 // These just get treated as USSD.
-                Rlog.d(LOG_TAG, "Sending short code '"
+                Rlog.d(LOG_TAG, "processCode: Sending short code '"
                        + mDialingNumber + "' over CS pipe.");
                 throw new CallStateException(Phone.CS_FALLBACK);
             } else if (isServiceCodeCallForwarding(mSc)) {
-                Rlog.d(LOG_TAG, "is CF");
+                Rlog.d(LOG_TAG, "processCode: is CF");
 
                 String dialingNumber = mSia;
                 int reason = scToCallForwardReason(mSc);
@@ -794,7 +805,7 @@
                         ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
                                 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
 
-                    Rlog.d(LOG_TAG, "is CF setCallForward");
+                    Rlog.d(LOG_TAG, "processCode: is CF setCallForward");
                     mPhone.setCallForwardingOption(cfAction, reason,
                             dialingNumber, serviceClass, time, obtainMessage(
                                     EVENT_SET_CFF_COMPLETE,
@@ -827,21 +838,21 @@
                         mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_INVOCATION,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
                     }
                 } else if (isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_SUPPRESSION,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
                     }
                 } else if (isInterrogate()) {
                     try {
                         mPhone.mCT.getUtInterface()
                             .queryCLIR(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCLIR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIR.");
                     }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -853,14 +864,14 @@
                         mPhone.mCT.getUtInterface()
                             .queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCLIP.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIP.");
                     }
                 } else if (isActivate() || isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCLIP(isActivate(),
                                 obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIP.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIP.");
                     }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -872,14 +883,14 @@
                         mPhone.mCT.getUtInterface()
                             .queryCOLP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCOLP.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLP.");
                     }
                 } else if (isActivate() || isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCOLP(isActivate(),
                                  obtainMessage(EVENT_SET_COMPLETE, this));
                      } catch (ImsException e) {
-                         Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLP.");
+                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLP.");
                      }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -891,21 +902,21 @@
                         mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_RESTRICTED,
                                 obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR.");
                     }
                 } else if (isDeactivate()) {
                     try {
                         mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_ALLOWED,
                                 obtainMessage(EVENT_SET_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR.");
                     }
                 } else if (isInterrogate()) {
                     try {
                         mPhone.mCT.getUtInterface()
                             .queryCOLR(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
                     } catch (ImsException e) {
-                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCOLR.");
+                        Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLR.");
                     }
                 } else {
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
@@ -921,7 +932,7 @@
                     }
                  // TODO: isRegister() case needs to be handled.
                 } catch (ImsException e) {
-                    Rlog.d(LOG_TAG, "Could not get UT handle for ICB.");
+                    Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICB.");
                 }
             } else if (mSc != null && mSc.equals(SC_BAICa)) {
                 int callAction =0;
@@ -944,7 +955,7 @@
                                 null);
                     }
                 } catch (ImsException e) {
-                    Rlog.d(LOG_TAG, "Could not get UT handle for ICBa.");
+                    Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICBa.");
                 }
             } else if (mSc != null && mSc.equals(SC_WAIT)) {
                 // sia = basic service group
@@ -959,15 +970,17 @@
                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
                 }
             } else if (mPoundString != null) {
-                Rlog.d(LOG_TAG, "Sending pound string '"
+                Rlog.d(LOG_TAG, "processCode: Sending pound string '"
                        + mDialingNumber + "' over CS pipe.");
                 throw new CallStateException(Phone.CS_FALLBACK);
             } else {
+                Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI");
                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
             }
         } catch (RuntimeException exc) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
+            Rlog.d(LOG_TAG, "processCode: RuntimeException = " + exc);
             mPhone.onMMIDone(this);
         }
     }
@@ -984,9 +997,11 @@
     void
     onUssdFinished(String ussdMessage, boolean isUssdRequest) {
         if (mState == State.PENDING) {
-            if (ussdMessage == null) {
+            if (TextUtils.isEmpty(ussdMessage)) {
                 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
+                Rlog.v(LOG_TAG, "onUssdFinished: no message; using: " + mMessage);
             } else {
+                Rlog.v(LOG_TAG, "onUssdFinished: message: " + ussdMessage);
                 mMessage = ussdMessage;
             }
             mIsUssdRequest = isUssdRequest;
@@ -994,7 +1009,6 @@
             if (!isUssdRequest) {
                 mState = State.COMPLETE;
             }
-
             mPhone.onMMIDone(this);
         }
     }
@@ -1010,7 +1024,7 @@
         if (mState == State.PENDING) {
             mState = State.FAILED;
             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
-
+            Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this);
             mPhone.onMMIDone(this);
         }
     }
@@ -1128,11 +1142,19 @@
                                obtainMessage(EVENT_SET_COMPLETE,this),
                                icbNum);
         } catch (ImsException e) {
-            Rlog.d(LOG_TAG, "Could not get UT handle for updating ICB.");
+            Rlog.d(LOG_TAG, "processIcbMmiCodeForUpdate:Could not get UT handle for updating ICB.");
         }
     }
 
     private CharSequence getErrorMessage(AsyncResult ar) {
+        if (ar.exception instanceof CommandException) {
+            CommandException.Error err = ((CommandException) (ar.exception)).getCommandError();
+            if (err == CommandException.Error.FDN_CHECK_FAILURE) {
+                Rlog.i(LOG_TAG, "FDN_CHECK_FAILURE");
+                return mContext.getText(com.android.internal.R.string.mmiFdnError);
+            }
+        }
+
         return mContext.getText(com.android.internal.R.string.mmiError);
     }
 
@@ -1177,18 +1199,15 @@
                 if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) {
                     sb.append(mContext.getText(
                             com.android.internal.R.string.passwordIncorrect));
+                } else if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) {
+                    sb.append(mContext.getText(com.android.internal.R.string.mmiFdnError));
                 } else if (err.getMessage() != null) {
                     sb.append(err.getMessage());
                 } else {
                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
                 }
-            } else {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+            } else if (ar.exception instanceof ImsException) {
+                sb.append(getImsErrorMessage(ar));
             }
         } else if (isActivate()) {
             mState = State.COMPLETE;
@@ -1218,6 +1237,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onSetComplete: mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1320,12 +1340,7 @@
             mState = State.FAILED;
 
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             }
             else {
                 sb.append(getErrorMessage(ar));
@@ -1372,6 +1387,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this);
         mPhone.onMMIDone(this);
 
     }
@@ -1380,29 +1396,23 @@
         StringBuilder sb = new StringBuilder(getScString());
         sb.append("\n");
 
+        mState = State.FAILED;
         if (ar.exception != null) {
-            mState = State.FAILED;
-
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             } else {
                 sb.append(getErrorMessage(ar));
             }
         } else {
-            mState = State.FAILED;
             ImsSsInfo ssInfo = null;
             if (ar.result instanceof Bundle) {
-                Rlog.d(LOG_TAG, "Received CLIP/COLP/COLR Response.");
+                Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response.");
                 // Response for CLIP, COLP and COLR queries.
                 Bundle ssInfoResp = (Bundle) ar.result;
                 ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
                 if (ssInfo != null) {
-                    Rlog.d(LOG_TAG, "ImsSsInfo mStatus = " + ssInfo.mStatus);
+                    Rlog.d(LOG_TAG,
+                            "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.mStatus);
                     if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
                         sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
                         mState = State.COMPLETE;
@@ -1417,7 +1427,7 @@
                 }
 
             } else {
-                Rlog.d(LOG_TAG, "Received Call Barring Response.");
+                Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received Call Barring Response.");
                 // Response for Call Barring queries.
                 int[] cbInfos = (int[]) ar.result;
                 if (cbInfos[0] == 1) {
@@ -1431,11 +1441,12 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onSuppSvcQueryComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
     private void onIcbQueryComplete(AsyncResult ar) {
-        Rlog.d(LOG_TAG, "onIcbQueryComplete ");
+        Rlog.d(LOG_TAG, "onIcbQueryComplete mmi=" + this);
         StringBuilder sb = new StringBuilder(getScString());
         sb.append("\n");
 
@@ -1443,12 +1454,7 @@
             mState = State.FAILED;
 
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             } else {
                 sb.append(getErrorMessage(ar));
             }
@@ -1482,21 +1488,15 @@
         mState = State.FAILED;
 
         if (ar.exception != null) {
-
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             }
         } else {
             Bundle ssInfo = (Bundle) ar.result;
             int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR);
             // clirInfo[0] = The 'n' parameter from TS 27.007 7.7
             // clirInfo[1] = The 'm' parameter from TS 27.007 7.7
-            Rlog.d(LOG_TAG, "CLIR param n=" + clirInfo[0]
+            Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0]
                     + " m=" + clirInfo[1]);
 
             // 'm' parameter.
@@ -1567,6 +1567,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryClirComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1579,12 +1580,7 @@
             mState = State.FAILED;
 
             if (ar.exception instanceof ImsException) {
-                ImsException error = (ImsException) ar.exception;
-                if (error.getMessage() != null) {
-                    sb.append(error.getMessage());
-                } else {
-                    sb.append(getErrorMessage(ar));
-                }
+                sb.append(getImsErrorMessage(ar));
             } else {
                 sb.append(getErrorMessage(ar));
             }
@@ -1611,6 +1607,7 @@
         }
 
         mMessage = sb;
+        Rlog.d(LOG_TAG, "onQueryComplete mmi=" + this);
         mPhone.onMMIDone(this);
     }
 
@@ -1631,6 +1628,22 @@
         return sb;
     }
 
+    private CharSequence getImsErrorMessage(AsyncResult ar) {
+        ImsException error = (ImsException) ar.exception;
+        if (error.getCode() == ImsReasonInfo.CODE_FDN_BLOCKED) {
+            return mContext.getText(com.android.internal.R.string.mmiFdnError);
+        } else if (error.getMessage() != null) {
+            return error.getMessage();
+        } else {
+            return getErrorMessage(ar);
+        }
+    }
+
+    @Override
+    public ResultReceiver getUssdCallbackReceiver() {
+        return this.mCallbackReceiver;
+    }
+
     /***
      * TODO: It would be nice to have a method here that can take in a dialstring and
      * figure out if there is an MMI code embedded within it.  This code would replace
@@ -1648,9 +1661,11 @@
         if (mSia != null) sb.append(" sia=" + mSia);
         if (mSib != null) sb.append(" sib=" + mSib);
         if (mSic != null) sb.append(" sic=" + mSic);
-        if (mPoundString != null) sb.append(" poundString=" + mPoundString);
-        if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
-        if (mPwd != null) sb.append(" pwd=" + mPwd);
+        if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString));
+        if (mDialingNumber != null) sb.append(" dialingNumber="
+                + Rlog.pii(LOG_TAG, mDialingNumber));
+        if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd));
+        if (mCallbackReceiver != null) sb.append(" hasReceiver");
         sb.append("}");
         return sb.toString();
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java b/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
new file mode 100644
index 0000000..68a832b
--- /dev/null
+++ b/src/java/com/android/internal/telephony/imsphone/ImsRttTextHandler.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.imsphone;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.telecom.Connection;
+import android.telephony.Rlog;
+
+import java.io.IOException;
+
+public class ImsRttTextHandler extends Handler {
+    public interface NetworkWriter {
+        void write(String s);
+    }
+
+    private static final String LOG_TAG = "ImsRttTextHandler";
+    // RTT buffering and sending tuning constants.
+    // TODO: put this in carrier config?
+
+    // These count Unicode codepoints, not Java char types.
+    public static final int MAX_CODEPOINTS_PER_SECOND = 30;
+    // Assuming that we do not exceed the rate limit, this is the maximum time between when a
+    // piece of text is received and when it is actually sent over the network.
+    public static final int MAX_BUFFERING_DELAY_MILLIS = 200;
+    // Assuming that we do not exceed the rate limit, this is the maximum size we will allow
+    // the buffer to grow to before sending as many as we can.
+    public static final int MAX_BUFFERED_CHARACTER_COUNT = 5;
+    private static final int MILLIS_PER_SECOND = 1000;
+
+    // Messages for the handler.
+    // Initializes the text handler. Should have an RttTextStream set in msg.obj
+    private static final int INITIALIZE = 1;
+    // Appends a string to the buffer to send to the network. Should have the string in msg.obj
+    private static final int APPEND_TO_NETWORK_BUFFER = 2;
+    // Send a string received from the network to the in-call app. Should have the string in
+    // msg.obj.
+    private static final int SEND_TO_INCALL = 3;
+    // Send as many characters as possible, as constrained by the rate limit. No extra data.
+    private static final int ATTEMPT_SEND_TO_NETWORK = 4;
+    // Indicates that N characters were sent a second ago and should be ignored by the rate
+    // limiter. msg.arg1 should be set to N.
+    private static final int EXPIRE_SENT_CODEPOINT_COUNT = 5;
+    // Indicates that the call is over and we should teardown everything we have set up.
+    private static final int TEARDOWN = 6;
+
+    private Connection.RttTextStream mRttTextStream;
+
+    private class InCallReaderThread extends Thread {
+        private final Connection.RttTextStream mReaderThreadRttTextStream;
+
+        public InCallReaderThread(Connection.RttTextStream textStream) {
+            mReaderThreadRttTextStream = textStream;
+        }
+
+        @Override
+        public void run() {
+            while (true) {
+                String charsReceived;
+                try {
+                    charsReceived = mReaderThreadRttTextStream.read();
+                } catch (IOException e) {
+                    Rlog.e(LOG_TAG, "RttReaderThread - IOException encountered " +
+                            "reading from in-call: %s", e);
+                    obtainMessage(TEARDOWN).sendToTarget();
+                    break;
+                }
+                if (charsReceived == null) {
+                    if (Thread.currentThread().isInterrupted()) {
+                        Rlog.i(LOG_TAG, "RttReaderThread - Thread interrupted. Finishing.");
+                        break;
+                    }
+                    Rlog.e(LOG_TAG, "RttReaderThread - Stream closed unexpectedly. Attempt to " +
+                            "reinitialize.");
+                    obtainMessage(TEARDOWN).sendToTarget();
+                    break;
+                }
+                if (charsReceived.length() == 0) {
+                    continue;
+                }
+                obtainMessage(APPEND_TO_NETWORK_BUFFER, charsReceived)
+                        .sendToTarget();
+            }
+        }
+    }
+
+    private int mCodepointsAvailableForTransmission = MAX_CODEPOINTS_PER_SECOND;
+    private StringBuffer mBufferedTextToNetwork = new StringBuffer();
+    private InCallReaderThread mReaderThread;
+    // This is only ever used when the pipes fail and we have to re-setup. Messages received
+    // from the network are buffered here until Telecom gets back to us with the new pipes.
+    private StringBuffer mBufferedTextToIncall = new StringBuffer();
+    private final NetworkWriter mNetworkWriter;
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case INITIALIZE:
+                if (mRttTextStream != null || mReaderThread != null) {
+                    Rlog.e(LOG_TAG, "RTT text stream already initialized. Ignoring.");
+                    return;
+                }
+                mRttTextStream = (Connection.RttTextStream) msg.obj;
+                mReaderThread = new InCallReaderThread(mRttTextStream);
+                mReaderThread.start();
+                break;
+            case SEND_TO_INCALL:
+                String messageToIncall = (String) msg.obj;
+                try {
+                    mRttTextStream.write(messageToIncall);
+                } catch (IOException e) {
+                    Rlog.e(LOG_TAG, "IOException encountered writing to in-call: %s", e);
+                    obtainMessage(TEARDOWN).sendToTarget();
+                    mBufferedTextToIncall.append(messageToIncall);
+                }
+                break;
+            case APPEND_TO_NETWORK_BUFFER:
+                // First, append the text-to-send to the string buffer
+                mBufferedTextToNetwork.append((String) msg.obj);
+                // Check to see how many codepoints we have buffered. If we have more than 5,
+                // send immediately, otherwise, wait until a timeout happens.
+                int numCodepointsBuffered = mBufferedTextToNetwork
+                        .codePointCount(0, mBufferedTextToNetwork.length());
+                if (numCodepointsBuffered >= MAX_BUFFERED_CHARACTER_COUNT) {
+                    sendMessageAtFrontOfQueue(obtainMessage(ATTEMPT_SEND_TO_NETWORK));
+                } else {
+                    sendEmptyMessageDelayed(
+                            ATTEMPT_SEND_TO_NETWORK, MAX_BUFFERING_DELAY_MILLIS);
+                }
+                break;
+            case ATTEMPT_SEND_TO_NETWORK:
+                // Check to see how many codepoints we can send, and send that many.
+                int numCodePointsAvailableInBuffer = mBufferedTextToNetwork.codePointCount(0,
+                        mBufferedTextToNetwork.length());
+                int numCodePointsSent = Math.min(numCodePointsAvailableInBuffer,
+                        mCodepointsAvailableForTransmission);
+                if (numCodePointsSent == 0) {
+                    break;
+                }
+                int endSendIndex = mBufferedTextToNetwork.offsetByCodePoints(0,
+                        numCodePointsSent);
+
+                String stringToSend = mBufferedTextToNetwork.substring(0, endSendIndex);
+
+                mBufferedTextToNetwork.delete(0, endSendIndex);
+                mNetworkWriter.write(stringToSend);
+                mCodepointsAvailableForTransmission -= numCodePointsSent;
+                sendMessageDelayed(
+                        obtainMessage(EXPIRE_SENT_CODEPOINT_COUNT, numCodePointsSent, 0),
+                        MILLIS_PER_SECOND);
+                break;
+            case EXPIRE_SENT_CODEPOINT_COUNT:
+                mCodepointsAvailableForTransmission += msg.arg1;
+                if (mCodepointsAvailableForTransmission > 0) {
+                    sendMessageAtFrontOfQueue(obtainMessage(ATTEMPT_SEND_TO_NETWORK));
+                }
+                break;
+            case TEARDOWN:
+                try {
+                    if (mReaderThread != null) {
+                        mReaderThread.join(1000);
+                    }
+                } catch (InterruptedException e) {
+                    // Ignore and assume it'll finish on its own.
+                }
+                mReaderThread = null;
+                mRttTextStream = null;
+                break;
+        }
+    }
+
+    public ImsRttTextHandler(Looper looper, NetworkWriter networkWriter) {
+        super(looper);
+        mNetworkWriter = networkWriter;
+    }
+
+    public void sendToInCall(String msg) {
+        obtainMessage(SEND_TO_INCALL, msg).sendToTarget();
+    }
+
+    public void initialize(Connection.RttTextStream rttTextStream) {
+        obtainMessage(INITIALIZE, rttTextStream).sendToTarget();
+    }
+
+    public void tearDown() {
+        obtainMessage(TEARDOWN).sendToTarget();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
index 1cf0825..a8221b4 100644
--- a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
@@ -16,13 +16,14 @@
 
 package com.android.internal.telephony.metrics;
 
-import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.ImsReasonInfo;
-import com.android.internal.telephony.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
+import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.ImsReasonInfo;
+import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 
 public class CallSessionEventBuilder {
     private final TelephonyCallSession.Event mEvent = new TelephonyCallSession.Event();
@@ -32,46 +33,46 @@
     }
 
     public CallSessionEventBuilder(int type) {
-        mEvent.setType(type);
+        mEvent.type = type;
     }
 
     public CallSessionEventBuilder setDelay(int delay) {
-        mEvent.setDelay(delay);
+        mEvent.delay = delay;
         return this;
     }
 
     public CallSessionEventBuilder setRilRequest(int rilRequestType) {
-        mEvent.setRilRequest(rilRequestType);
+        mEvent.rilRequest = rilRequestType;
         return this;
     }
 
     public CallSessionEventBuilder setRilRequestId(int rilRequestId) {
-        mEvent.setRilRequestId(rilRequestId);
+        mEvent.rilRequestId = rilRequestId;
         return this;
     }
 
     public CallSessionEventBuilder setRilError(int rilError) {
-        mEvent.setError(rilError);
+        mEvent.error = rilError;
         return this;
     }
 
     public CallSessionEventBuilder setCallIndex(int callIndex) {
-        mEvent.setCallIndex(callIndex);
+        mEvent.callIndex = callIndex;
         return this;
     }
 
     public CallSessionEventBuilder setCallState(int state) {
-        mEvent.setCallState(state);
+        mEvent.callState = state;
         return this;
     }
 
     public CallSessionEventBuilder setSrvccState(int srvccState) {
-        mEvent.setSrvccState(srvccState);
+        mEvent.srvccState = srvccState;
         return this;
     }
 
     public CallSessionEventBuilder setImsCommand(int imsCommand) {
-        mEvent.setImsCommand(imsCommand);
+        mEvent.imsCommand = imsCommand;
         return this;
     }
 
@@ -81,12 +82,12 @@
     }
 
     public CallSessionEventBuilder setSrcAccessTech(int tech) {
-        mEvent.setSrcAccessTech(tech);
+        mEvent.srcAccessTech = tech;
         return this;
     }
 
     public CallSessionEventBuilder setTargetAccessTech(int tech) {
-        mEvent.setTargetAccessTech(tech);
+        mEvent.targetAccessTech = tech;
         return this;
     }
 
@@ -116,12 +117,17 @@
     }
 
     public CallSessionEventBuilder setPhoneState(int phoneState) {
-        mEvent.setPhoneState(phoneState);
+        mEvent.phoneState = phoneState;
         return this;
     }
 
     public CallSessionEventBuilder setNITZ(long timestamp) {
-        mEvent.setNitzTimestampMillis(timestamp);
+        mEvent.nitzTimestampMillis = timestamp;
+        return this;
+    }
+
+    public CallSessionEventBuilder setRilCalls(RilCall[] rilCalls) {
+        mEvent.calls = rilCalls;
         return this;
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java b/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java
index 49a7ee4..748fe7d 100644
--- a/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java
+++ b/src/java/com/android/internal/telephony/metrics/InProgressCallSession.java
@@ -18,7 +18,7 @@
 
 import android.os.SystemClock;
 
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
 
 import java.util.ArrayDeque;
 import java.util.Deque;
@@ -47,6 +47,9 @@
     /** Indicating events dropped */
     private boolean mEventsDropped = false;
 
+    /** Last known phone state */
+    private int mLastKnownPhoneState;
+
     /** Check if events dropped */
     public boolean isEventsDropped() { return mEventsDropped; }
 
@@ -91,4 +94,34 @@
         events.add(builder.build());
         mLastElapsedTimeMs = timestamp;
     }
-}
\ No newline at end of file
+
+    /**
+     * Check if the Call Session contains CS calls
+     * @return true if there are CS calls in the call list
+     */
+    public boolean containsCsCalls() {
+        for (TelephonyCallSession.Event event : events) {
+            if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Set Phone State
+     * @param state
+     */
+    public void setLastKnownPhoneState(int state) {
+        mLastKnownPhoneState = state;
+    }
+
+    /**
+     * Checks if Phone is in Idle state
+     * @return true if device is in Phone is idle state.
+     *
+     */
+    public boolean isPhoneIdle() {
+        return (mLastKnownPhoneState == TelephonyCallSession.Event.PhoneState.STATE_IDLE);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java b/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java
index 4a01161..9da6536 100644
--- a/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java
+++ b/src/java/com/android/internal/telephony/metrics/InProgressSmsSession.java
@@ -18,7 +18,7 @@
 
 import android.os.SystemClock;
 
-import com.android.internal.telephony.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
 
 import java.util.ArrayDeque;
 import java.util.Deque;
diff --git a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
index aeaac19..530be1d 100644
--- a/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/SmsSessionEventBuilder.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.telephony.metrics;
 
-import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
+import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 
 public class SmsSessionEventBuilder {
     SmsSession.Event mEvent = new SmsSession.Event();
@@ -31,26 +31,26 @@
     }
 
     public SmsSessionEventBuilder(int type) {
-        mEvent.setType(type);
+        mEvent.type = type;
     }
 
     public SmsSessionEventBuilder setDelay(int delay) {
-        mEvent.setDelay(delay);
+        mEvent.delay = delay;
         return this;
     }
 
     public SmsSessionEventBuilder setTech(int tech) {
-        mEvent.setTech(tech);
+        mEvent.tech = tech;
         return this;
     }
 
     public SmsSessionEventBuilder setErrorCode(int code) {
-        mEvent.setErrorCode(code);
+        mEvent.errorCode = code;
         return this;
     }
 
     public SmsSessionEventBuilder setRilErrno(int errno) {
-        mEvent.setError(errno);
+        mEvent.error = errno;
         return this;
     }
 
@@ -80,12 +80,12 @@
     }
 
     public SmsSessionEventBuilder setRilRequestId(int id) {
-        mEvent.setRilRequestId(id);
+        mEvent.rilRequestId = id;
         return this;
     }
 
     public SmsSessionEventBuilder setFormat(int format) {
-        mEvent.setFormat(format);
+        mEvent.format = format;
         return this;
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
index 16dbb83..6530802 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
@@ -16,17 +16,18 @@
 
 package com.android.internal.telephony.metrics;
 
-import android.os.SystemClock;
+import static com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import static com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import static com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 
-import static com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import static com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import static com.android.internal.telephony.TelephonyProto.RilDataCall;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCall;
-import static com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
-import static com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import static com.android.internal.telephony.TelephonyProto.TelephonySettings;
+import android.os.SystemClock;
 
 public class TelephonyEventBuilder {
     private final TelephonyEvent mEvent = new TelephonyEvent();
@@ -40,73 +41,79 @@
     }
 
     public TelephonyEventBuilder(long timestamp, int phoneId) {
-        mEvent.setTimestampMillis(timestamp);
-        mEvent.setPhoneId(phoneId);
+        mEvent.timestampMillis = timestamp;
+        mEvent.phoneId = phoneId;
     }
 
     public TelephonyEventBuilder setSettings(TelephonySettings settings) {
-        mEvent.setType(TelephonyEvent.Type.SETTINGS_CHANGED);
+        mEvent.type = TelephonyEvent.Type.SETTINGS_CHANGED;
         mEvent.settings = settings;
         return this;
     }
 
     public TelephonyEventBuilder setServiceState(TelephonyServiceState state) {
-        mEvent.setType(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED);
+        mEvent.type = TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED;
         mEvent.serviceState = state;
         return this;
     }
 
     public TelephonyEventBuilder setImsConnectionState(ImsConnectionState state) {
-        mEvent.setType(TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED);
+        mEvent.type = TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED;
         mEvent.imsConnectionState = state;
         return this;
     }
 
     public TelephonyEventBuilder setImsCapabilities(ImsCapabilities capabilities) {
-        mEvent.setType(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED);
+        mEvent.type = TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED;
         mEvent.imsCapabilities = capabilities;
         return this;
     }
 
     public TelephonyEventBuilder setDataStallRecoveryAction(int action) {
-        mEvent.setType(TelephonyEvent.Type.DATA_STALL_ACTION);
-        mEvent.setDataStallAction(action);
+        mEvent.type = TelephonyEvent.Type.DATA_STALL_ACTION;
+        mEvent.dataStallAction = action;
         return this;
     }
 
     public TelephonyEventBuilder setSetupDataCall(RilSetupDataCall request) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_SETUP);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_SETUP;
         mEvent.setupDataCall = request;
         return this;
     }
 
     public TelephonyEventBuilder setSetupDataCallResponse(RilSetupDataCallResponse rsp) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_SETUP_RESPONSE;
         mEvent.setupDataCallResponse = rsp;
         return this;
     }
 
     public TelephonyEventBuilder setDeactivateDataCall(RilDeactivateDataCall request) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_DEACTIVATE);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_DEACTIVATE;
         mEvent.deactivateDataCall = request;
         return this;
     }
 
     public TelephonyEventBuilder setDeactivateDataCallResponse(int errno) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE);
-        mEvent.setError(errno);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE;
+        mEvent.error = errno;
         return this;
     }
 
     public TelephonyEventBuilder setDataCalls(RilDataCall[] dataCalls) {
-        mEvent.setType(TelephonyEvent.Type.DATA_CALL_LIST_CHANGED);
+        mEvent.type = TelephonyEvent.Type.DATA_CALL_LIST_CHANGED;
         mEvent.dataCalls = dataCalls;
         return this;
     }
 
     public TelephonyEventBuilder setNITZ(long timestamp) {
-        mEvent.setType(TelephonyEvent.Type.NITZ_TIME);
-        mEvent.setNitzTimestampMillis(timestamp);
+        mEvent.type = TelephonyEvent.Type.NITZ_TIME;
+        mEvent.nitzTimestampMillis = timestamp;
+        return this;
+    }
+
+    public TelephonyEventBuilder setModemRestart(ModemRestart modemRestart) {
+        mEvent.type = TelephonyEvent.Type.MODEM_RESTART;
+        mEvent.modemRestart = modemRestart;
         return this;
     }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index b4615ab..4c642c0 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -16,65 +16,76 @@
 
 package com.android.internal.telephony.metrics;
 
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
+import static com.android.internal.telephony.RILConstants
+        .RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IP;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV6;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_PPP;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_UNKNOWN;
+
+import android.os.Build;
 import android.os.SystemClock;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyHistogram;
+import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
 
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.internal.ImsCallSession;
+import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.TelephonyProto;
-import com.android.internal.telephony.TelephonyProto.ImsCapabilities;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.RilDataCall;
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCall;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
-import com.android.internal.telephony.TelephonyProto.TelephonyLog;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonySettings;
-import com.android.internal.telephony.TelephonyProto.TimeInterval;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.imsphone.ImsPhoneCall;
+import com.android.internal.telephony.nano.TelephonyProto;
+import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall.Type;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse
+        .RilDataCallFailCause;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
+import com.android.internal.telephony.nano.TelephonyProto.TimeInterval;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Deque;
 import java.util.List;
 
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DIAL;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_IMS_SEND_SMS;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS_EXPECT_MORE;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IP;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV6;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_PPP;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_UNKNOWN;
-
 /**
  * Telephony metrics holds all metrics events and convert it into telephony proto buf.
  * @hide
@@ -135,6 +146,11 @@
      */
     private final SparseArray<ImsConnectionState> mLastImsConnectionState = new SparseArray<>();
 
+    /**
+     * Last settings state. This is for deduping same settings event logged.
+     */
+    private final SparseArray<TelephonySettings> mLastSettings = new SparseArray<>();
+
     /** The start system time of the TelephonyLog in milliseconds*/
     private long mStartSystemTimeMs;
 
@@ -269,6 +285,8 @@
                 return "IMS_CALL_HANDOVER_FAILED";
             case TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED:
                 return "PHONE_STATE_CHANGED";
+            case TelephonyCallSession.Event.Type.NITZ_TIME:
+                return "NITZ_TIME";
             default:
                 return Integer.toString(event);
         }
@@ -317,18 +335,20 @@
         pw.println("Telephony events:");
         pw.increaseIndent();
         for (TelephonyEvent event : mTelephonyEvents) {
-            if (event.hasTimestampMillis()) {
-                pw.print(event.getTimestampMillis());
-                pw.print(" [");
-                if (event.hasPhoneId()) pw.print(event.getPhoneId());
-                pw.print("] ");
+            pw.print(event.timestampMillis);
+            pw.print(" [");
+            pw.print(event.phoneId);
+            pw.print("] ");
 
-                if (event.hasType()) {
-                    pw.print("T=");
-                    pw.print(telephonyEventToString(event.getType()));
-                }
-                pw.println("");
+            pw.print("T=");
+            if (event.type == TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED) {
+                pw.print(telephonyEventToString(event.type)
+                        + "(" + event.serviceState.dataRat + ")");
+            } else {
+                pw.print(telephonyEventToString(event.type));
             }
+
+            pw.println("");
         }
 
         pw.decreaseIndent();
@@ -336,18 +356,29 @@
         pw.increaseIndent();
 
         for (TelephonyCallSession callSession : mCompletedCallSessions) {
-            if (callSession.hasStartTimeMinutes()) {
-                pw.println("Start time in minutes: " + callSession.getStartTimeMinutes());
-            }
-            if (callSession.hasEventsDropped()) {
-                pw.println("Events dropped: " + callSession.getEventsDropped());
-            }
+            pw.println("Start time in minutes: " + callSession.startTimeMinutes);
+            pw.println("Events dropped: " + callSession.eventsDropped);
+
             pw.println("Events: ");
             pw.increaseIndent();
             for (TelephonyCallSession.Event event : callSession.events) {
-                pw.print(event.getDelay());
+                pw.print(event.delay);
                 pw.print(" T=");
-                pw.println(callSessionEventToString(event.getType()));
+                if (event.type == TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED) {
+                    pw.println(callSessionEventToString(event.type)
+                            + "(" + event.serviceState.dataRat + ")");
+                } else if (event.type == TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED) {
+                    pw.println(callSessionEventToString(event.type));
+                    pw.increaseIndent();
+                    for (RilCall call : event.calls) {
+                        pw.println(call.index + ". Type = " + call.type + " State = "
+                                + call.state + " End Reason " + call.callEndReason
+                                + " isMultiparty = " + call.isMultiparty);
+                    }
+                    pw.decreaseIndent();
+                } else {
+                    pw.println(callSessionEventToString(event.type));
+                }
             }
             pw.decreaseIndent();
         }
@@ -359,19 +390,18 @@
         int count = 0;
         for (SmsSession smsSession : mCompletedSmsSessions) {
             count++;
-            if (smsSession.hasStartTimeMinutes()) {
-                pw.print("[" + count + "] Start time in minutes: "
-                        + smsSession.getStartTimeMinutes());
-            }
-            if (smsSession.hasEventsDropped()) {
-                pw.println(", events dropped: " + smsSession.getEventsDropped());
+            pw.print("[" + count + "] Start time in minutes: "
+                    + smsSession.startTimeMinutes);
+
+            if (smsSession.eventsDropped) {
+                pw.println(", events dropped: " + smsSession.eventsDropped);
             }
             pw.println("Events: ");
             pw.increaseIndent();
             for (SmsSession.Event event : smsSession.events) {
-                pw.print(event.getDelay());
+                pw.print(event.delay);
                 pw.print(" T=");
-                pw.println(smsSessionEventToString(event.getType()));
+                pw.println(smsSessionEventToString(event.type));
             }
             pw.decreaseIndent();
         }
@@ -441,7 +471,7 @@
         // Build telephony events
         log.events = new TelephonyEvent[mTelephonyEvents.size()];
         mTelephonyEvents.toArray(log.events);
-        log.setEventsDropped(mTelephonyEventsDropped);
+        log.eventsDropped = mTelephonyEventsDropped;
 
         // Build call sessions
         log.callSessions = new TelephonyCallSession[mCompletedCallSessions.size()];
@@ -459,25 +489,25 @@
             TelephonyHistogram rilHistogram = rilHistograms.get(i);
             TelephonyProto.TelephonyHistogram histogramProto = log.histograms[i];
 
-            histogramProto.setCategory(rilHistogram.getCategory());
-            histogramProto.setId(rilHistogram.getId());
-            histogramProto.setMinTimeMillis(rilHistogram.getMinTime());
-            histogramProto.setMaxTimeMillis(rilHistogram.getMaxTime());
-            histogramProto.setAvgTimeMillis(rilHistogram.getAverageTime());
-            histogramProto.setCount(rilHistogram.getSampleCount());
-            histogramProto.setBucketCount(rilHistogram.getBucketCount());
+            histogramProto.category = rilHistogram.getCategory();
+            histogramProto.id = rilHistogram.getId();
+            histogramProto.minTimeMillis = rilHistogram.getMinTime();
+            histogramProto.maxTimeMillis = rilHistogram.getMaxTime();
+            histogramProto.avgTimeMillis = rilHistogram.getAverageTime();
+            histogramProto.count = rilHistogram.getSampleCount();
+            histogramProto.bucketCount = rilHistogram.getBucketCount();
             histogramProto.bucketEndPoints = rilHistogram.getBucketEndPoints();
             histogramProto.bucketCounters = rilHistogram.getBucketCounters();
         }
 
         // Log the starting system time
         log.startTime = new TelephonyProto.Time();
-        log.startTime.setSystemTimestampMillis(mStartSystemTimeMs);
-        log.startTime.setElapsedTimestampMillis(mStartElapsedTimeMs);
+        log.startTime.systemTimestampMillis = mStartSystemTimeMs;
+        log.startTime.elapsedTimestampMillis = mStartElapsedTimeMs;
 
         log.endTime = new TelephonyProto.Time();
-        log.endTime.setSystemTimestampMillis(System.currentTimeMillis());
-        log.endTime.setElapsedTimestampMillis(SystemClock.elapsedRealtime());
+        log.endTime.systemTimestampMillis = System.currentTimeMillis();
+        log.endTime.elapsedTimestampMillis = SystemClock.elapsedRealtime();
 
         return log;
     }
@@ -554,39 +584,39 @@
     private TelephonyServiceState toServiceStateProto(ServiceState serviceState) {
         TelephonyServiceState ssProto = new TelephonyServiceState();
 
-        ssProto.setVoiceRoamingType(serviceState.getVoiceRoamingType());
-        ssProto.setDataRoamingType(serviceState.getDataRoamingType());
+        ssProto.voiceRoamingType = serviceState.getVoiceRoamingType();
+        ssProto.dataRoamingType = serviceState.getDataRoamingType();
 
         ssProto.voiceOperator = new TelephonyServiceState.TelephonyOperator();
 
         if (serviceState.getVoiceOperatorAlphaLong() != null) {
-            ssProto.voiceOperator.setAlphaLong(serviceState.getVoiceOperatorAlphaLong());
+            ssProto.voiceOperator.alphaLong = serviceState.getVoiceOperatorAlphaLong();
         }
 
         if (serviceState.getVoiceOperatorAlphaShort() != null) {
-            ssProto.voiceOperator.setAlphaShort(serviceState.getVoiceOperatorAlphaShort());
+            ssProto.voiceOperator.alphaShort = serviceState.getVoiceOperatorAlphaShort();
         }
 
         if (serviceState.getVoiceOperatorNumeric() != null) {
-            ssProto.voiceOperator.setNumeric(serviceState.getVoiceOperatorNumeric());
+            ssProto.voiceOperator.numeric = serviceState.getVoiceOperatorNumeric();
         }
 
         ssProto.dataOperator = new TelephonyServiceState.TelephonyOperator();
 
         if (serviceState.getDataOperatorAlphaLong() != null) {
-            ssProto.dataOperator.setAlphaLong(serviceState.getDataOperatorAlphaLong());
+            ssProto.dataOperator.alphaLong = serviceState.getDataOperatorAlphaLong();
         }
 
         if (serviceState.getDataOperatorAlphaShort() != null) {
-            ssProto.dataOperator.setAlphaShort(serviceState.getDataOperatorAlphaShort());
+            ssProto.dataOperator.alphaShort = serviceState.getDataOperatorAlphaShort();
         }
 
         if (serviceState.getDataOperatorNumeric() != null) {
-            ssProto.dataOperator.setNumeric(serviceState.getDataOperatorNumeric());
+            ssProto.dataOperator.numeric = serviceState.getDataOperatorNumeric();
         }
 
-        ssProto.setVoiceRat(serviceState.getRilVoiceRadioTechnology());
-        ssProto.setDataRat(serviceState.getRilDataRadioTechnology());
+        ssProto.voiceRat = serviceState.getRilVoiceRadioTechnology();
+        ssProto.dataRat = serviceState.getRilDataRadioTechnology();
         return ssProto;
     }
 
@@ -707,9 +737,9 @@
         TelephonyCallSession callSession = new TelephonyCallSession();
         callSession.events = new TelephonyCallSession.Event[inProgressCallSession.events.size()];
         inProgressCallSession.events.toArray(callSession.events);
-        callSession.setStartTimeMinutes(inProgressCallSession.startSystemTimeMin);
-        callSession.setPhoneId(inProgressCallSession.phoneId);
-        callSession.setEventsDropped(inProgressCallSession.isEventsDropped());
+        callSession.startTimeMinutes = inProgressCallSession.startSystemTimeMin;
+        callSession.phoneId = inProgressCallSession.phoneId;
+        callSession.eventsDropped = inProgressCallSession.isEventsDropped();
         if (mCompletedCallSessions.size() >= MAX_COMPLETED_CALL_SESSIONS) {
             mCompletedCallSessions.removeFirst();
         }
@@ -728,9 +758,9 @@
             SmsSession smsSession = new SmsSession();
             smsSession.events = new SmsSession.Event[inProgressSmsSession.events.size()];
             inProgressSmsSession.events.toArray(smsSession.events);
-            smsSession.setStartTimeMinutes(inProgressSmsSession.startSystemTimeMin);
-            smsSession.setPhoneId(inProgressSmsSession.phoneId);
-            smsSession.setEventsDropped(inProgressSmsSession.isEventsDropped());
+            smsSession.startTimeMinutes = inProgressSmsSession.startSystemTimeMin;
+            smsSession.phoneId = inProgressSmsSession.phoneId;
+            smsSession.eventsDropped = inProgressSmsSession.isEventsDropped();
             if (mCompletedSmsSessions.size() >= MAX_COMPLETED_SMS_SESSIONS) {
                 mCompletedSmsSessions.removeFirst();
             }
@@ -764,14 +794,21 @@
         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
                 .setServiceState(toServiceStateProto(serviceState)).build();
 
+        // If service state doesn't change, we don't log the event.
+        if (mLastServiceState.get(phoneId) != null &&
+                Arrays.equals(TelephonyServiceState.toByteArray(mLastServiceState.get(phoneId)),
+                        TelephonyServiceState.toByteArray(event.serviceState))) {
+            return;
+        }
+
         mLastServiceState.put(phoneId, event.serviceState);
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
                         .setServiceState(event.serviceState));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(
                         SmsSession.Event.Type.RIL_SERVICE_STATE_CHANGED)
                         .setServiceState(event.serviceState));
@@ -802,26 +839,35 @@
         TelephonySettings s = new TelephonySettings();
         switch (feature) {
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE:
-                s.setIsEnhanced4GLteModeEnabled(value != 0);
+                s.isEnhanced4GLteModeEnabled = (value != 0);
                 break;
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI:
-                s.setIsWifiCallingEnabled(value != 0);
+                s.isWifiCallingEnabled = (value != 0);
                 break;
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE:
-                s.setIsVtOverLteEnabled(value != 0);
+                s.isVtOverLteEnabled = (value != 0);
                 break;
             case ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI:
-                s.setIsVtOverWifiEnabled(value != 0);
+                s.isVtOverWifiEnabled = (value != 0);
                 break;
         }
 
+        // If the settings don't change, we don't log the event.
+        if (mLastSettings.get(phoneId) != null &&
+                Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
+                        TelephonySettings.toByteArray(s))) {
+            return;
+        }
+
+        mLastSettings.put(phoneId, s);
+
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setSettings(s).build();
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(TelephonyCallSession.Event.Type.SETTINGS_CHANGED)
                         .setSettings(s));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(SmsSession.Event.Type.SETTINGS_CHANGED)
                         .setSettings(s));
     }
@@ -834,7 +880,17 @@
      */
     public void writeSetPreferredNetworkType(int phoneId, int networkType) {
         TelephonySettings s = new TelephonySettings();
-        s.setPreferredNetworkMode(networkType);
+        s.preferredNetworkMode = networkType + 1;
+
+        // If the settings don't change, we don't log the event.
+        if (mLastSettings.get(phoneId) != null &&
+                Arrays.equals(TelephonySettings.toByteArray(mLastSettings.get(phoneId)),
+                        TelephonySettings.toByteArray(s))) {
+            return;
+        }
+
+        mLastSettings.put(phoneId, s);
+
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSettings(s).build());
     }
 
@@ -848,31 +904,39 @@
     public synchronized void writeOnImsConnectionState(int phoneId, int state,
                                                        ImsReasonInfo reasonInfo) {
         ImsConnectionState imsState = new ImsConnectionState();
-        imsState.setState(state);
-        mLastImsConnectionState.put(phoneId, imsState);
+        imsState.state = state;
 
         if (reasonInfo != null) {
             TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
 
-            ri.setReasonCode(reasonInfo.getCode());
-            ri.setExtraCode(reasonInfo.getExtraCode());
+            ri.reasonCode = reasonInfo.getCode();
+            ri.extraCode = reasonInfo.getExtraCode();
             String extraMessage = reasonInfo.getExtraMessage();
             if (extraMessage != null) {
-                ri.setExtraMessage(extraMessage);
+                ri.extraMessage = extraMessage;
             }
 
             imsState.reasonInfo = ri;
         }
 
+        // If the connection state does not change, do not log it.
+        if (mLastImsConnectionState.get(phoneId) != null &&
+                Arrays.equals(ImsConnectionState.toByteArray(mLastImsConnectionState.get(phoneId)),
+                        ImsConnectionState.toByteArray(imsState))) {
+            return;
+        }
+
+        mLastImsConnectionState.put(phoneId, imsState);
+
         TelephonyEvent event = new TelephonyEventBuilder(phoneId)
                 .setImsConnectionState(imsState).build();
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
                         .setImsConnectionState(event.imsConnectionState));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(
                         SmsSession.Event.Type.IMS_CONNECTION_STATE_CHANGED)
                         .setImsConnectionState(event.imsConnectionState));
@@ -887,22 +951,30 @@
     public synchronized void writeOnImsCapabilities(int phoneId, boolean[] capabilities) {
         ImsCapabilities cap = new ImsCapabilities();
 
-        cap.setVoiceOverLte(capabilities[0]);
-        cap.setVideoOverLte(capabilities[1]);
-        cap.setVoiceOverWifi(capabilities[2]);
-        cap.setVideoOverWifi(capabilities[3]);
-        cap.setUtOverLte(capabilities[4]);
-        cap.setUtOverWifi(capabilities[5]);
+        cap.voiceOverLte = capabilities[0];
+        cap.videoOverLte = capabilities[1];
+        cap.voiceOverWifi = capabilities[2];
+        cap.videoOverWifi = capabilities[3];
+        cap.utOverLte = capabilities[4];
+        cap.utOverWifi = capabilities[5];
 
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setImsCapabilities(cap).build();
+
+        // If the capabilities don't change, we don't log the event.
+        if (mLastImsCapabilities.get(phoneId) != null &&
+                Arrays.equals(ImsCapabilities.toByteArray(mLastImsCapabilities.get(phoneId)),
+                ImsCapabilities.toByteArray(cap))) {
+            return;
+        }
+
         mLastImsCapabilities.put(phoneId, cap);
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.IMS_CAPABILITIES_CHANGED)
                         .setImsCapabilities(event.imsCapabilities));
-        annotateInProgressSmsSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressSmsSession(event.timestampMillis, phoneId,
                 new SmsSessionEventBuilder(
                         SmsSession.Event.Type.IMS_CAPABILITIES_CHANGED)
                         .setImsCapabilities(event.imsCapabilities));
@@ -944,13 +1016,13 @@
                                       String apn, int authType, String protocol) {
 
         RilSetupDataCall setupDataCall = new RilSetupDataCall();
-        setupDataCall.setRat(radioTechnology);
-        setupDataCall.setDataProfile(profile + 1);  // off by 1 between proto and RIL constants.
+        setupDataCall.rat = radioTechnology;
+        setupDataCall.dataProfile = profile + 1;  // off by 1 between proto and RIL constants.
         if (apn != null) {
-            setupDataCall.setApn(apn);
+            setupDataCall.apn = apn;
         }
         if (protocol != null) {
-            setupDataCall.setType(toPdpType(protocol));
+            setupDataCall.type = toPdpType(protocol);
         }
 
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setSetupDataCall(
@@ -968,8 +1040,8 @@
     public void writeRilDeactivateDataCall(int phoneId, int rilSerial, int cid, int reason) {
 
         RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall();
-        deactivateDataCall.setCid(cid);
-        deactivateDataCall.setReason(reason + 1);
+        deactivateDataCall.cid = cid;
+        deactivateDataCall.reason = reason + 1;
 
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall(
                 deactivateDataCall).build());
@@ -987,12 +1059,12 @@
 
         for (int i = 0; i < dcsList.size(); i++) {
             dataCalls[i] = new RilDataCall();
-            dataCalls[i].setCid(dcsList.get(i).cid);
-            if (dcsList.get(i).ifname != null) {
-                dataCalls[i].setIframe(dcsList.get(i).ifname);
+            dataCalls[i].cid = dcsList.get(i).cid;
+            if (!TextUtils.isEmpty(dcsList.get(i).ifname)) {
+                dataCalls[i].iframe = dcsList.get(i).ifname;
             }
-            if (dcsList.get(i).type != null) {
-                dataCalls[i].setType(toPdpType(dcsList.get(i).type));
+            if (!TextUtils.isEmpty(dcsList.get(i).type)) {
+                dataCalls[i].type = toPdpType(dcsList.get(i).type);
             }
         }
 
@@ -1000,22 +1072,116 @@
     }
 
     /**
+     * Write CS call list event
+     *
+     * @param phoneId    Phone id
+     * @param connections Array of GsmCdmaConnection objects
+     */
+    public void writeRilCallList(int phoneId, ArrayList<GsmCdmaConnection> connections) {
+        if (VDBG) {
+            Rlog.v(TAG, "Logging CallList Changed Connections Size = " + connections.size());
+        }
+        InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
+        if (callSession == null) {
+            Rlog.e(TAG, "writeRilCallList: Call session is missing");
+        } else {
+            RilCall[] calls = convertConnectionsToRilCalls(connections);
+            callSession.addEvent(
+                    new CallSessionEventBuilder(
+                            TelephonyCallSession.Event.Type.RIL_CALL_LIST_CHANGED)
+                            .setRilCalls(calls)
+            );
+            if (VDBG) Rlog.v(TAG, "Logged Call list changed");
+            if (callSession.isPhoneIdle() && disconnectReasonsKnown(calls)) {
+                finishCallSession(callSession);
+            }
+        }
+    }
+
+    private boolean disconnectReasonsKnown(RilCall[] calls) {
+        for (RilCall call : calls) {
+            if (call.callEndReason == 0) return false;
+        }
+        return true;
+    }
+
+    private RilCall[] convertConnectionsToRilCalls(ArrayList<GsmCdmaConnection> mConnections) {
+        RilCall[] calls = new RilCall[mConnections.size()];
+        for (int i = 0; i < mConnections.size(); i++) {
+            calls[i] = new RilCall();
+            calls[i].index = i;
+            convertConnectionToRilCall(mConnections.get(i), calls[i]);
+        }
+        return calls;
+    }
+
+    private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call) {
+        if (conn.isIncoming()) {
+            call.type = Type.MT;
+        } else {
+            call.type = Type.MO;
+        }
+        switch (conn.getState()) {
+            case IDLE:
+                call.state = CallState.CALL_IDLE;
+                break;
+            case ACTIVE:
+                call.state = CallState.CALL_ACTIVE;
+                break;
+            case HOLDING:
+                call.state = CallState.CALL_HOLDING;
+                break;
+            case DIALING:
+                call.state = CallState.CALL_DIALING;
+                break;
+            case ALERTING:
+                call.state = CallState.CALL_ALERTING;
+                break;
+            case INCOMING:
+                call.state = CallState.CALL_INCOMING;
+                break;
+            case WAITING:
+                call.state = CallState.CALL_WAITING;
+                break;
+            case DISCONNECTED:
+                call.state = CallState.CALL_DISCONNECTED;
+                break;
+            case DISCONNECTING:
+                call.state = CallState.CALL_DISCONNECTING;
+                break;
+            default:
+                call.state = CallState.CALL_UNKNOWN;
+                break;
+        }
+        call.callEndReason = conn.getDisconnectCause();
+        call.isMultiparty = conn.isMultiparty();
+    }
+
+    /**
      * Write dial event
      *
      * @param phoneId Phone id
-     * @param rilSerial RIL request serial number
+     * @param conn Connection object created to track this call
      * @param clirMode CLIR (Calling Line Identification Restriction) mode
      * @param uusInfo User-to-User signaling Info
      */
-    public void writeRilDial(int phoneId, int rilSerial, int clirMode, UUSInfo uusInfo) {
+    public void writeRilDial(int phoneId, GsmCdmaConnection conn, int clirMode, UUSInfo uusInfo) {
 
         InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
-
-        callSession.addEvent(callSession.startElapsedTimeMs,
-                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
-                        .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL)
-                        .setRilRequestId(rilSerial)
-        );
+        if (VDBG) Rlog.v(TAG, "Logging Dial Connection = " + conn);
+        if (callSession == null) {
+            Rlog.e(TAG, "writeRilDial: Call session is missing");
+        } else {
+            RilCall[] calls = new RilCall[1];
+            calls[0] = new RilCall();
+            calls[0].index = -1;
+            convertConnectionToRilCall(conn, calls[0]);
+            callSession.addEvent(callSession.startElapsedTimeMs,
+                    new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
+                            .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL)
+                            .setRilCalls(calls));
+            if (VDBG) Rlog.v(TAG, "Logged Dial event");
+        }
     }
 
     /**
@@ -1035,19 +1201,23 @@
      * Write call hangup event
      *
      * @param phoneId Phone id
-     * @param rilSerial RIL request serial number
+     * @param conn Connection object associated with the call that is being hung-up
      * @param callId Call id
      */
-    public void writeRilHangup(int phoneId, int rilSerial, int callId) {
+    public void writeRilHangup(int phoneId, GsmCdmaConnection conn, int callId) {
         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
         if (callSession == null) {
-            Rlog.e(TAG, "Call session is missing");
+            Rlog.e(TAG, "writeRilHangup: Call session is missing");
         } else {
+            RilCall[] calls = new RilCall[1];
+            calls[0] = new RilCall();
+            calls[0].index = callId;
+            convertConnectionToRilCall(conn, calls[0]);
             callSession.addEvent(
                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
                             .setRilRequest(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP)
-                            .setRilRequestId(rilSerial)
-                            .setCallIndex(callId));
+                            .setRilCalls(calls));
+            if (VDBG) Rlog.v(TAG, "Logged Hangup event");
         }
     }
 
@@ -1060,7 +1230,7 @@
     public void writeRilAnswer(int phoneId, int rilSerial) {
         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
         if (callSession == null) {
-            Rlog.e(TAG, "Call session is missing");
+            Rlog.e(TAG, "writeRilAnswer: Call session is missing");
         } else {
             callSession.addEvent(
                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_REQUEST)
@@ -1078,7 +1248,7 @@
     public void writeRilSrvcc(int phoneId, int rilSrvccState) {
         InProgressCallSession callSession =  mInProgressCallSessions.get(phoneId);
         if (callSession == null) {
-            Rlog.e(TAG, "Call session is missing");
+            Rlog.e(TAG, "writeRilSrvcc: Call session is missing");
         } else {
             callSession.addEvent(
                     new CallSessionEventBuilder(TelephonyCallSession.Event.Type.RIL_CALL_SRVCC)
@@ -1137,17 +1307,17 @@
         RilDataCall dataCall = new RilDataCall();
 
         if (response != null) {
-            setupDataCallResponse.setStatus(
-                    response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status);
-            setupDataCallResponse.setSuggestedRetryTimeMillis(response.suggestedRetryTime);
+            setupDataCallResponse.status =
+                    (response.status == 0 ? RilDataCallFailCause.PDP_FAIL_NONE : response.status);
+            setupDataCallResponse.suggestedRetryTimeMillis = response.suggestedRetryTime;
 
-            dataCall.setCid(response.cid);
-            if (response.type != null) {
-                dataCall.setType(toPdpType(response.type));
+            dataCall.cid = response.cid;
+            if (!TextUtils.isEmpty(response.type)) {
+                dataCall.type = toPdpType(response.type);
             }
 
-            if (response.ifname != null) {
-                dataCall.setIframe(response.ifname);
+            if (!TextUtils.isEmpty(response.ifname)) {
+                dataCall.iframe = response.ifname;
             }
         }
         setupDataCallResponse.call = dataCall;
@@ -1168,13 +1338,13 @@
                                               int rilRequest) {
         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
         if (callSession == null) {
-            Rlog.e(TAG, "Call session is missing");
+            Rlog.e(TAG, "writeOnCallSolicitedResponse: Call session is missing");
         } else {
             callSession.addEvent(new CallSessionEventBuilder(
                     TelephonyCallSession.Event.Type.RIL_RESPONSE)
                     .setRilRequest(toCallSessionRilRequest(rilRequest))
                     .setRilRequestId(rilSerial)
-                    .setRilError(rilError));
+                    .setRilError(rilError + 1));
         }
     }
 
@@ -1202,7 +1372,7 @@
             smsSession.addEvent(new SmsSessionEventBuilder(
                     SmsSession.Event.Type.SMS_SEND_RESULT)
                     .setErrorCode(errorCode)
-                    .setRilErrno(rilError)
+                    .setRilErrno(rilError + 1)
                     .setRilRequestId(rilSerial)
             );
 
@@ -1283,9 +1453,14 @@
 
         InProgressCallSession callSession = mInProgressCallSessions.get(phoneId);
         if (callSession == null) {
-            Rlog.e(TAG, "Call session is missing");
+            Rlog.e(TAG, "writePhoneState: Call session is missing");
         } else {
-            if (state == TelephonyCallSession.Event.PhoneState.STATE_IDLE) {
+            // For CS Calls Finish the Call Session after Receiving the Last Call Fail Cause
+            // For IMS calls we receive the Disconnect Cause along with Call End event.
+            // So we can finish the call session here.
+            callSession.setLastKnownPhoneState(state);
+            if ((state == TelephonyCallSession.Event.PhoneState.STATE_IDLE)
+                    && (!callSession.containsCsCalls())) {
                 finishCallSession(callSession);
             }
             callSession.addEvent(new CallSessionEventBuilder(
@@ -1378,7 +1553,11 @@
      * @param session IMS call session
      */
     public void writeOnImsCallReceive(int phoneId, ImsCallSession session) {
-        writeOnImsCallStart(phoneId, session);
+        InProgressCallSession callSession = startNewCallSessionIfNeeded(phoneId);
+
+        callSession.addEvent(
+                new CallSessionEventBuilder(TelephonyCallSession.Event.Type.IMS_CALL_RECEIVE)
+                        .setCallIndex(getCallId(session)));
     }
 
     /**
@@ -1410,11 +1589,11 @@
     private TelephonyProto.ImsReasonInfo toImsReasonInfoProto(ImsReasonInfo reasonInfo) {
         TelephonyProto.ImsReasonInfo ri = new TelephonyProto.ImsReasonInfo();
         if (reasonInfo != null) {
-            ri.setReasonCode(reasonInfo.getCode());
-            ri.setExtraCode(reasonInfo.getExtraCode());
+            ri.reasonCode = reasonInfo.getCode();
+            ri.extraCode = reasonInfo.getExtraCode();
             String extraMessage = reasonInfo.getExtraMessage();
             if (extraMessage != null) {
-                ri.setExtraMessage(extraMessage);
+                ri.extraMessage = extraMessage;
             }
         }
         return ri;
@@ -1514,12 +1693,28 @@
         TelephonyEvent event = new TelephonyEventBuilder(phoneId).setNITZ(timestamp).build();
         addTelephonyEvent(event);
 
-        annotateInProgressCallSession(event.getTimestampMillis(), phoneId,
+        annotateInProgressCallSession(event.timestampMillis, phoneId,
                 new CallSessionEventBuilder(
                         TelephonyCallSession.Event.Type.NITZ_TIME)
                         .setNITZ(timestamp));
     }
 
+    /**
+     * Write Modem Restart event
+     *
+     * @param phoneId Phone id
+     * @param reason Reason for the modem reset.
+     */
+    public void writeModemRestartEvent(int phoneId, String reason) {
+        final ModemRestart modemRestart = new ModemRestart();
+        String basebandVersion = Build.getRadioVersion();
+        if (basebandVersion != null) modemRestart.basebandVersion = basebandVersion;
+        if (reason != null) modemRestart.reason = reason;
+        TelephonyEvent event = new TelephonyEventBuilder(phoneId).setModemRestart(
+                modemRestart).build();
+        addTelephonyEvent(event);
+    }
+
     //TODO: Expand the proto in the future
     public void writeOnImsCallProgressing(int phoneId, ImsCallSession session) {}
     public void writeOnImsCallStarted(int phoneId, ImsCallSession session) {}
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 934583c..fe1d7c5 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -20,6 +20,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.BaseCommands;
 import com.android.internal.telephony.CommandsInterface;
@@ -259,9 +261,8 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, int profile,
-            String apn, String user, String password, int authType,
-            String protocol, Message result) {
+    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, Message result) {
     }
 
     @Override
@@ -335,6 +336,14 @@
     }
 
     @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+    }
+
+    @Override
+    public void stopNetworkScan(Message response) {
+    }
+
+    @Override
     public void setCallForward(int action, int cfReason, int serviceClass,
                 String number, int timeSeconds, Message response) {
     }
@@ -426,10 +435,6 @@
     }
 
     @Override
-    public void getNeighboringCids(Message response) {
-    }
-
-    @Override
     public void setLocationUpdates(boolean enable, Message response) {
     }
 
@@ -556,24 +561,15 @@
     }
 
     @Override
-    public void getCellInfoList(Message result) {
+    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
     }
 
     @Override
-    public void setCellInfoListRate(int rateInMillis, Message response) {
+    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
     }
 
     @Override
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-            String password, Message result) {
-    }
-
-    @Override
-    public void setDataProfile(DataProfile[] dps, Message result) {
-    }
-
-    @Override
-    public void iccOpenLogicalChannel(String AID, Message response) {
+    public void iccOpenLogicalChannel(String AID, int p2, Message response) {
     }
 
     @Override
@@ -631,6 +627,11 @@
     }
 
     @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+                                                Message result) {
+    }
+
+    @Override
     public void setAllowedCarriers(List<CarrierIdentifier> carriers, Message result) {
     }
 
@@ -638,4 +639,15 @@
     public void getAllowedCarriers(Message result) {
     }
 
+    @Override
+    public void sendDeviceState(int stateType, boolean state, Message result) {
+    }
+
+    @Override
+    public void setUnsolResponseFilter(int filter, Message result){
+    }
+
+    @Override
+    public void setSimCardPower(int state, Message result) {
+    }
 }
diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java
index cea20f1..9a4df0c 100644
--- a/src/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhone.java
@@ -121,7 +121,7 @@
             try {
                 SipAudioCall sipAudioCall = (SipAudioCall) incomingCall;
                 if (DBG) log("takeIncomingCall: taking call from: "
-                        + sipAudioCall.getPeerProfile().getUriString());
+                        + hidePii(sipAudioCall.getPeerProfile().getUriString()));
                 String localUri = sipAudioCall.getLocalProfile().getUriString();
                 if (localUri.equals(mProfile.getUriString())) {
                     boolean makeCallWait = mForegroundCall.getState().isAlive();
@@ -813,9 +813,10 @@
                         setState(newState);
                     }
                     mOwner.onConnectionStateChanged(SipConnection.this);
-                    if (SCN_DBG) log("onChanged: "
-                            + mPeer.getUriString() + ": " + mState
-                            + " on phone " + getPhone());
+                    if (SCN_DBG) {
+                        log("onChanged: " + hidePii(mPeer.getUriString()) + ": " + mState
+                                + " on phone " + getPhone());
+                    }
                 }
             }
 
@@ -950,9 +951,11 @@
         @Override
         public void hangup() throws CallStateException {
             synchronized (SipPhone.class) {
-                if (SCN_DBG) log("hangup: conn=" + mPeer.getUriString()
-                        + ": " + mState + ": on phone "
-                        + getPhone().getPhoneName());
+                if (SCN_DBG) {
+                    log("hangup: conn=" + hidePii(mPeer.getUriString())
+                            + ": " + mState + ": on phone "
+                            + getPhone().getPhoneName());
+                }
                 if (!mState.isAlive()) return;
                 try {
                     SipAudioCall sipAudioCall = mSipAudioCall;
@@ -1076,6 +1079,6 @@
     }
 
     public static String hidePii(String s) {
-        return VDBG ? s : "xxxxx";
+        return VDBG ? Rlog.pii(LOG_TAG, s) : "xxxxx";
     }
 }
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index 77bc4ce..7bdfad8 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -23,25 +23,28 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.RegistrantList;
+import android.os.ResultReceiver;
 import android.os.SystemProperties;
+import android.os.WorkSource;
 import android.telephony.CellLocation;
+import android.telephony.NetworkScanRequest;
+import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
-import android.telephony.Rlog;
 
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.IccCard;
 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.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
+import com.android.internal.telephony.dataconnection.DataConnection;
 import com.android.internal.telephony.uicc.IccFileHandler;
 
 import java.util.ArrayList;
@@ -110,7 +113,7 @@
     }
 
     @Override
-    public CellLocation getCellLocation() {
+    public CellLocation getCellLocation(WorkSource workSource) {
         return null;
     }
 
@@ -239,6 +242,11 @@
     }
 
     @Override
+    public boolean handleUssdRequest(String dialString, ResultReceiver wrappedCallback) {
+        return false;
+    }
+
+    @Override
     public void sendUssdResponse(String ussdMessge) {
     }
 
@@ -387,6 +395,14 @@
     }
 
     @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+    }
+
+    @Override
+    public void stopNetworkScan(Message response) {
+    }
+
+    @Override
     public void setNetworkSelectionModeAutomatic(Message response) {
     }
 
@@ -396,10 +412,6 @@
     }
 
     @Override
-    public void getNeighboringCids(Message response) {
-    }
-
-    @Override
     public void setOnPostDialCharacter(Handler h, int what, Object obj) {
     }
 
@@ -450,7 +462,7 @@
     }
 
     @Override
-    public boolean isDataConnectivityPossible() {
+    public boolean isDataAllowed() {
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index e44b1d8..3360058 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -16,44 +16,48 @@
 
 package com.android.internal.telephony.test;
 
+import android.hardware.radio.V1_0.DataRegStateResult;
+import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
-import android.os.Registrant;
 import android.os.SystemClock;
+import android.os.WorkSource;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
+import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
-import android.telephony.IccOpenLogicalChannelResponse;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.BaseCommands;
+import com.android.internal.telephony.CallFailCause;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.LastCallFailCause;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.SmsResponse;
 import com.android.internal.telephony.RadioCapability;
+import com.android.internal.telephony.SmsResponse;
+import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
 import com.android.internal.telephony.dataconnection.DataProfile;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.UUSInfo;
-import com.android.internal.telephony.CallFailCause;
 import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
-import com.android.internal.telephony.LastCallFailCause;
 import com.android.internal.telephony.uicc.IccCardStatus;
 import com.android.internal.telephony.uicc.IccIoResult;
 
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class SimulatedCommands extends BaseCommands
@@ -148,6 +152,12 @@
         mPin2Code = DEFAULT_SIM_PIN2_CODE;
     }
 
+    public void dispose() {
+        if (mHandlerThread != null) {
+            mHandlerThread.quit();
+        }
+    }
+
     private void log(String str) {
         Rlog.d(LOG_TAG, str);
     }
@@ -916,10 +926,10 @@
     @Override
     public void getVoiceRegistrationState(Message result) {
         mGetVoiceRegistrationStateCallCount.incrementAndGet();
-        String ret[] = new String[14];
 
-        ret[0] = Integer.toString(mVoiceRegState);
-        ret[3] = Integer.toString(mVoiceRadioTech);
+        VoiceRegStateResult ret = new VoiceRegStateResult();
+        ret.regState = mVoiceRegState;
+        ret.rat = mVoiceRadioTech;
 
         resultSuccess(result, ret);
     }
@@ -942,10 +952,10 @@
     @Override
     public void getDataRegistrationState (Message result) {
         mGetDataRegistrationStateCallCount.incrementAndGet();
-        String ret[] = new String[11];
 
-        ret[0] = Integer.toString(mDataRegState);
-        ret[3] = Integer.toString(mDataRadioTech);
+        DataRegStateResult ret = new DataRegStateResult();
+        ret.regState = mDataRegState;
+        ret.rat = mDataRadioTech;
 
         resultSuccess(result, ret);
     }
@@ -1086,26 +1096,15 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, int profile,
-            String apn, String user, String password, int authType,
-            String protocol, Message result) {
-        SimulatedCommandsVerifier.getInstance().setupDataCall(radioTechnology, profile, apn, user,
-                password, authType, protocol, result);
+    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, Message result) {
+
+        SimulatedCommandsVerifier.getInstance().setupDataCall(radioTechnology, dataProfile,
+                isRoaming, allowRoaming, result);
 
         if (mDcResponse == null) {
-            mDcResponse = new DataCallResponse();
-            mDcResponse.version = 11;
-            mDcResponse.status = 0;
-            mDcResponse.suggestedRetryTime = -1;
-            mDcResponse.cid = 1;
-            mDcResponse.active = 2;
-            mDcResponse.type = "IP";
-            mDcResponse.ifname = "rmnet_data7";
-            mDcResponse.mtu = 1440;
-            mDcResponse.addresses = new String[]{"12.34.56.78"};
-            mDcResponse.dnses = new String[]{"98.76.54.32"};
-            mDcResponse.gateways = new String[]{"11.22.33.44"};
-            mDcResponse.pcscf = new String[]{};
+            mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", "rmnet_data7",
+                    "12.34.56.78", "98.76.54.32", "11.22.33.44", "", 1440);
         }
 
         if (mDcSuccess) {
@@ -1138,7 +1137,7 @@
     }
 
     @Override
-    public void getNeighboringCids(Message result) {
+    public void getNeighboringCids(Message result, WorkSource workSource) {
         int ret[] = new int[7];
 
         ret[0] = 6;
@@ -1363,7 +1362,25 @@
      * ((AsyncResult)response.obj).result  is a List of NetworkInfo objects
      */
     @Override
-    public void getAvailableNetworks(Message result) {unimplemented(result);}
+    public void getAvailableNetworks(Message result) {
+        unimplemented(result);
+    }
+
+    /**
+     * Starts a network scan
+     */
+    @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message result) {
+        unimplemented(result);
+    }
+
+    /**
+     * Stops an ongoing network scan
+     */
+    @Override
+    public void stopNetworkScan(Message result) {
+        unimplemented(result);
+    }
 
     @Override
     public void getBasebandVersion (Message result) {
@@ -1438,6 +1455,16 @@
     }
 
     @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+                                                Message response) {
+        // Just echo back data
+        if (response != null) {
+            AsyncResult.forMessage(response).result = imsiEncryptionInfo;
+            response.sendToTarget();
+        }
+    }
+
+    @Override
     public void invokeOemRilRequestStrings(String[] strings, Message response) {
         // Just echo back data
         if (response != null) {
@@ -1802,7 +1829,7 @@
     }
 
     @Override
-    public void getCellInfoList(Message response) {
+    public void getCellInfoList(Message response, WorkSource WorkSource) {
         if (mCellInfoList == null) {
             Parcel p = Parcel.obtain();
             p.writeInt(1);
@@ -1832,17 +1859,16 @@
     }
 
     @Override
-    public void setCellInfoListRate(int rateInMillis, Message response) {
+    public void setCellInfoListRate(int rateInMillis, Message response, WorkSource workSource) {
         unimplemented(response);
     }
 
     @Override
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-            String password, Message result) {
+    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
     }
 
     @Override
-    public void setDataProfile(DataProfile[] dps, Message result) {
+    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
     }
 
     public void setImsRegistrationState(int[] regState) {
@@ -1874,8 +1900,8 @@
     }
 
     @Override
-    public void iccOpenLogicalChannel(String AID, Message response) {
-        SimulatedCommandsVerifier.getInstance().iccOpenLogicalChannel(AID, response);
+    public void iccOpenLogicalChannel(String AID, int p2, Message response) {
+        SimulatedCommandsVerifier.getInstance().iccOpenLogicalChannel(AID, p2, response);
         Object result = new int[]{mChannelId};
         resultSuccess(response, result);
     }
@@ -2025,8 +2051,8 @@
     }
 
     @VisibleForTesting
-    public void notifyVoiceNetworkStateChanged() {
-        mVoiceNetworkStateRegistrants.notifyRegistrants();
+    public void notifyNetworkStateChanged() {
+        mNetworkStateRegistrants.notifyRegistrants();
     }
 
     @VisibleForTesting
@@ -2101,4 +2127,34 @@
     @Override
     public void unregisterForPcoData(Handler h) {
     }
+
+    @Override
+    public void sendDeviceState(int stateType, boolean state, Message result) {
+        SimulatedCommandsVerifier.getInstance().sendDeviceState(stateType, state, result);
+        resultSuccess(result, null);
+    }
+
+    @Override
+    public void setUnsolResponseFilter(int filter, Message result) {
+        SimulatedCommandsVerifier.getInstance().setUnsolResponseFilter(filter, result);
+        resultSuccess(result, null);
+    }
+
+    @Override
+    public void setSimCardPower(int state, Message result) {
+    }
+
+    @VisibleForTesting
+    public void triggerRestrictedStateChanged(int restrictedState) {
+        if (mRestrictedStateRegistrant != null) {
+            mRestrictedStateRegistrant.notifyRegistrant(
+                    new AsyncResult(null, restrictedState, null));
+        }
+    }
+
+    @Override
+    public void setOnRestrictedStateChanged(Handler h, int what, Object obj) {
+        super.setOnRestrictedStateChanged(h, what, obj);
+        SimulatedCommandsVerifier.getInstance().setOnRestrictedStateChanged(h, what, obj);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index fa7d3e8..91b86e3 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -19,6 +19,8 @@
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.NetworkScanRequest;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.RadioCapability;
@@ -144,22 +146,22 @@
     }
 
     @Override
-    public void registerForVoiceNetworkStateChanged(Handler h, int what, Object obj) {
+    public void registerForNetworkStateChanged(Handler h, int what, Object obj) {
 
     }
 
     @Override
-    public void unregisterForVoiceNetworkStateChanged(Handler h) {
+    public void unregisterForNetworkStateChanged(Handler h) {
 
     }
 
     @Override
-    public void registerForDataNetworkStateChanged(Handler h, int what, Object obj) {
+    public void registerForDataCallListChanged(Handler h, int what, Object obj) {
 
     }
 
     @Override
-    public void unregisterForDataNetworkStateChanged(Handler h) {
+    public void unregisterForDataCallListChanged(Handler h) {
 
     }
 
@@ -945,6 +947,16 @@
     }
 
     @Override
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+
+    }
+
+    @Override
+    public void stopNetworkScan(Message response) {
+
+    }
+
+    @Override
     public void getBasebandVersion(Message response) {
 
     }
@@ -1009,11 +1021,6 @@
     }
 
     @Override
-    public void getNeighboringCids(Message response) {
-
-    }
-
-    @Override
     public void setLocationUpdates(boolean enable, Message response) {
 
     }
@@ -1144,9 +1151,8 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, int profile, String apn, String user,
-                              String password, int authType, String protocol, Message result) {
-
+    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, Message result) {
     }
 
     @Override
@@ -1201,16 +1207,6 @@
     }
 
     @Override
-    public void getCellInfoList(Message result) {
-
-    }
-
-    @Override
-    public void setCellInfoListRate(int rateInMillis, Message response) {
-
-    }
-
-    @Override
     public void registerForCellInfoList(Handler h, int what, Object obj) {
 
     }
@@ -1221,13 +1217,12 @@
     }
 
     @Override
-    public void setInitialAttachApn(String apn, String protocol, int authType, String username,
-                                    String password, Message result) {
+    public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming, Message result) {
 
     }
 
     @Override
-    public void setDataProfile(DataProfile[] dps, Message result) {
+    public void setDataProfile(DataProfile[] dps, boolean isRoaming, Message result) {
 
     }
 
@@ -1237,7 +1232,7 @@
     }
 
     @Override
-    public void iccOpenLogicalChannel(String AID, Message response) {
+    public void iccOpenLogicalChannel(String AID, int p2, Message response) {
 
     }
 
@@ -1355,6 +1350,12 @@
     }
 
     @Override
+    public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
+                                                Message result) {
+
+    }
+
+    @Override
     public void setAllowedCarriers(List<CarrierIdentifier> carriers, Message result) {
 
     }
@@ -1371,4 +1372,32 @@
     @Override
     public void unregisterForPcoData(Handler h) {
     }
+
+    @Override
+    public void sendDeviceState(int stateType, boolean state, Message result) {
+    }
+
+    @Override
+    public void setUnsolResponseFilter(int filter, Message result){
+    }
+
+    @Override
+    public void setSimCardPower(int state, Message result) {
+    }
+
+    @Override
+    public void registerForCarrierInfoForImsiEncryption(Handler h, int what, Object obj) {
+    }
+
+    @Override
+    public void registerForNetworkScanResult(Handler h, int what, Object obj) {
+    }
+
+    @Override
+    public void unregisterForNetworkScanResult(Handler h) {
+    }
+
+    @Override
+    public void unregisterForCarrierInfoForImsiEncryption(Handler h) {
+    }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecord.java b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
index 203236c..4414caf 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecord.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecord.java
@@ -19,8 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.GsmAlphabet;
 
@@ -248,7 +248,8 @@
             Rlog.w(LOG_TAG, "[buildAdnString] Max length of tag is " + footerOffset);
             return null;
         } else {
-            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(mNumber);
+            bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+                    mNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
             System.arraycopy(bcdNumber, 0, adnString,
                     footerOffset + ADN_TON_AND_NPI, bcdNumber.length);
@@ -289,7 +290,10 @@
             }
 
             mNumber += PhoneNumberUtils.calledPartyBCDFragmentToString(
-                                        extRecord, 2, 0xff & extRecord[1]);
+                    extRecord,
+                    2,
+                    0xff & extRecord[1],
+                    PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
             // We don't support ext record chaining.
 
@@ -327,7 +331,10 @@
             // the ME (see note 2)."
 
             mNumber = PhoneNumberUtils.calledPartyBCDToString(
-                            record, footerOffset + 1, numberLength);
+                    record,
+                    footerOffset + 1,
+                    numberLength,
+                    PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
 
             mExtRecord = 0xff & record[record.length - 1];
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
index 90f1bf3..ce3545a 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordCache.java
@@ -359,8 +359,12 @@
                 Message response = mUserWriteResponse.get(efid);
                 mUserWriteResponse.delete(efid);
 
-                AsyncResult.forMessage(response, null, ar.exception);
-                response.sendToTarget();
+                // response may be cleared when simrecord is reset,
+                // so we should check if it is null.
+                if (response != null) {
+                    AsyncResult.forMessage(response, null, ar.exception);
+                    response.sendToTarget();
+                }
                 break;
         }
     }
diff --git a/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
new file mode 100644
index 0000000..18d2937
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/CarrierTestOverride.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.uicc;
+
+import android.os.Environment;
+import android.telephony.Rlog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Provide a machanism to override MVNO paramteres under CarrierConfig through a config file.
+ */
+public class CarrierTestOverride {
+    static final String LOG_TAG = "CarrierTestOverride";
+
+    /**
+     * Config file that can be created and adb-pushed by tester/developer
+     *
+     * Sample xml:
+     * <carrierTestOverrides>
+       <carrierTestOverride key="isInTestMode" value="true"/>
+       <carrierTestOverride key="gid1" value="bae0000000000000"/>
+       <carrierTestOverride key="gid2" value="ffffffffffffffff"/>
+       <carrierTestOverride key="imsi" value="310010123456789"/>
+       <carrierTestOverride key="spn" value="Verizon"/>
+       </carrierTestOverrides>
+     */
+    static final String DATA_CARRIER_TEST_OVERRIDE_PATH =
+            "/user_de/0/com.android.phone/files/carrier_test_conf.xml";
+    static final String CARRIER_TEST_XML_HEADER = "carrierTestOverrides";
+    static final String CARRIER_TEST_XML_SUBHEADER = "carrierTestOverride";
+    static final String CARRIER_TEST_XML_ITEM_KEY = "key";
+    static final String CARRIER_TEST_XML_ITEM_VALUE = "value";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE = "isInTestMode";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID1 = "gid1";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_GID2 = "gid2";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI = "imsi";
+    static final String CARRIER_TEST_XML_ITEM_KEY_STRING_SPN = "spn";
+
+    private HashMap<String, String> mCarrierTestParamMap;
+
+    CarrierTestOverride() {
+        mCarrierTestParamMap = new HashMap<String, String>();
+        loadCarrierTestOverrides();
+    }
+
+    boolean isInTestMode() {
+        return mCarrierTestParamMap.containsKey(CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE)
+                && mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_ISINTESTMODE)
+                .equals("true");
+    }
+
+    String getFakeSpn() {
+        try {
+            String spn = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_SPN);
+            Rlog.d(LOG_TAG, "reading spn from CarrierTestConfig file: " + spn);
+            return spn;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No spn in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    String getFakeIMSI() {
+        try {
+            String imsi = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_IMSI);
+            Rlog.d(LOG_TAG, "reading imsi from CarrierTestConfig file: " + imsi);
+            return imsi;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No imsi in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    String getFakeGid1() {
+        try {
+            String gid1 = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_GID1);
+            Rlog.d(LOG_TAG, "reading gid1 from CarrierTestConfig file: " + gid1);
+            return gid1;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No gid1 in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    String getFakeGid2() {
+        try {
+            String gid2 = mCarrierTestParamMap.get(CARRIER_TEST_XML_ITEM_KEY_STRING_GID2);
+            Rlog.d(LOG_TAG, "reading gid2 from CarrierTestConfig file: " + gid2);
+            return gid2;
+        } catch (NullPointerException e) {
+            Rlog.w(LOG_TAG, "No gid2 in CarrierTestConfig file ");
+            return null;
+        }
+    }
+
+    private void loadCarrierTestOverrides() {
+
+        FileReader carrierTestConfigReader;
+
+        File carrierTestConfigFile = new File(Environment.getDataDirectory(),
+                DATA_CARRIER_TEST_OVERRIDE_PATH);
+
+        try {
+            carrierTestConfigReader = new FileReader(carrierTestConfigFile);
+            Rlog.d(LOG_TAG, "CarrierTestConfig file Modified Timestamp: "
+                    + carrierTestConfigFile.lastModified());
+        } catch (FileNotFoundException e) {
+            Rlog.w(LOG_TAG, "Can not open " + carrierTestConfigFile.getAbsolutePath());
+            return;
+        }
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(carrierTestConfigReader);
+
+            XmlUtils.beginDocument(parser, CARRIER_TEST_XML_HEADER);
+
+            while (true) {
+                XmlUtils.nextElement(parser);
+
+                String name = parser.getName();
+                if (!CARRIER_TEST_XML_SUBHEADER.equals(name)) {
+                    break;
+                }
+
+                String key = parser.getAttributeValue(null, CARRIER_TEST_XML_ITEM_KEY);
+                String value = parser.getAttributeValue(null, CARRIER_TEST_XML_ITEM_VALUE);
+
+                Rlog.d(LOG_TAG,
+                        "extracting key-values from CarrierTestConfig file: " + key + "|" + value);
+                mCarrierTestParamMap.put(key, value);
+            }
+            carrierTestConfigReader.close();
+        } catch (XmlPullParserException e) {
+            Rlog.w(LOG_TAG, "Exception in carrier_test_conf parser " + e);
+        } catch (IOException e) {
+            Rlog.w(LOG_TAG, "Exception in carrier_test_conf parser " + e);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 0293287..241f211 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -16,9 +16,7 @@
 
 package com.android.internal.telephony.uicc;
 
-import static android.Manifest.permission.READ_PHONE_STATE;
-
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
@@ -26,29 +24,29 @@
 import android.os.Message;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.CommandsInterface.RadioState;
 import com.android.internal.telephony.IccCard;
 import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.IntentBroadcaster;
 import com.android.internal.telephony.MccTable;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.telephony.cdma.CdmaSubscriptionSourceManager;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.PersoSubState;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-import com.android.internal.telephony.uicc.UiccController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -89,7 +87,7 @@
     private static final int EVENT_ICC_RECORD_EVENTS = 500;
     private static final int EVENT_SUBSCRIPTION_ACTIVATED = 501;
     private static final int EVENT_SUBSCRIPTION_DEACTIVATED = 502;
-    private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 503;
+    private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 503;
 
     private Integer mPhoneId = null;
 
@@ -108,7 +106,7 @@
     private UiccCardApplication mUiccApplication = null;
     private IccRecords mIccRecords = null;
     private CdmaSubscriptionSourceManager mCdmaSSM = null;
-    private boolean mRadioOn = false;
+    private RadioState mRadioState = RadioState.RADIO_UNAVAILABLE;
     private boolean mQuietMode = false; // when set to true IccCardProxy will not broadcast
                                         // ACTION_SIM_STATE_CHANGED intents
     private boolean mInitialized = false;
@@ -131,7 +129,6 @@
         ci.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_UNAVAILABLE, null);
 
         resetProperties();
-        setExternalState(State.NOT_READY, false);
     }
 
     public void dispose() {
@@ -170,17 +167,31 @@
      */
     private void updateQuietMode() {
         synchronized (mLock) {
+            boolean oldQuietMode = mQuietMode;
             boolean newQuietMode;
             int cdmaSource = Phone.CDMA_SUBSCRIPTION_UNKNOWN;
+            boolean isLteOnCdmaMode = TelephonyManager.getLteOnCdmaModeStatic()
+                    == PhoneConstants.LTE_ON_CDMA_TRUE;
             if (mCurrentAppType == UiccController.APP_FAM_3GPP) {
                 newQuietMode = false;
                 if (DBG) log("updateQuietMode: 3GPP subscription -> newQuietMode=" + newQuietMode);
             } else {
+                if (isLteOnCdmaMode) {
+                    log("updateQuietMode: is cdma/lte device, force IccCardProxy into 3gpp mode");
+                    mCurrentAppType = UiccController.APP_FAM_3GPP;
+                }
                 cdmaSource = mCdmaSSM != null ?
                         mCdmaSSM.getCdmaSubscriptionSource() : Phone.CDMA_SUBSCRIPTION_UNKNOWN;
 
                 newQuietMode = (cdmaSource == Phone.CDMA_SUBSCRIPTION_NV)
-                        && (mCurrentAppType == UiccController.APP_FAM_3GPP2);
+                        && (mCurrentAppType == UiccController.APP_FAM_3GPP2)
+                        && !isLteOnCdmaMode;
+                if (DBG) {
+                    log("updateQuietMode: cdmaSource=" + cdmaSource
+                            + " mCurrentAppType=" + mCurrentAppType
+                            + " isLteOnCdmaMode=" + isLteOnCdmaMode
+                            + " newQuietMode=" + newQuietMode);
+                }
             }
 
             if (mQuietMode == false && newQuietMode == true) {
@@ -201,7 +212,8 @@
             }
             if (DBG) {
                 log("updateQuietMode: QuietMode is " + mQuietMode + " (app_type="
-                    + mCurrentAppType + " cdmaSource=" + cdmaSource + ")");
+                    + mCurrentAppType + " isLteOnCdmaMode=" + isLteOnCdmaMode
+                    + " cdmaSource=" + cdmaSource + ")");
             }
             mInitialized = true;
             sendMessage(obtainMessage(EVENT_ICC_CHANGED));
@@ -212,15 +224,18 @@
     public void handleMessage(Message msg) {
         switch (msg.what) {
             case EVENT_RADIO_OFF_OR_UNAVAILABLE:
-                mRadioOn = false;
-                if (CommandsInterface.RadioState.RADIO_UNAVAILABLE == mCi.getRadioState()) {
-                    setExternalState(State.NOT_READY);
-                }
+                mRadioState = mCi.getRadioState();
+                updateExternalState();
                 break;
             case EVENT_RADIO_ON:
-                mRadioOn = true;
+                mRadioState = RadioState.RADIO_ON;
                 if (!mInitialized) {
                     updateQuietMode();
+                } else {
+                    // updateQuietMode() triggers ICC_CHANGED, which eventually
+                    // calls updateExternalState; thus, we don't need this in the
+                    // above case
+                    updateExternalState();
                 }
                 break;
             case EVENT_ICC_CHANGED:
@@ -244,7 +259,7 @@
                     String operator = mIccRecords.getOperatorNumeric();
                     log("operator=" + operator + " mPhoneId=" + mPhoneId);
 
-                    if (operator != null) {
+                    if (!TextUtils.isEmpty(operator)) {
                         mTelephonyManager.setSimOperatorNumericForPhone(mPhoneId, operator);
                         String countryCode = operator.substring(0,3);
                         if (countryCode != null) {
@@ -259,7 +274,7 @@
                 }
                 if (mUiccCard != null && !mUiccCard.areCarrierPriviligeRulesLoaded()) {
                     mUiccCard.registerForCarrierPrivilegeRulesLoaded(
-                        this, EVENT_CARRIER_PRIVILIGES_LOADED, null);
+                            this, EVENT_CARRIER_PRIVILEGES_LOADED, null);
                 } else {
                     onRecordsLoaded();
                 }
@@ -295,7 +310,7 @@
                 }
                 break;
 
-            case EVENT_CARRIER_PRIVILIGES_LOADED:
+            case EVENT_CARRIER_PRIVILEGES_LOADED:
                 log("EVENT_CARRIER_PRIVILEGES_LOADED");
                 if (mUiccCard != null) {
                     mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
@@ -327,11 +342,9 @@
     private void updateIccAvailability() {
         synchronized (mLock) {
             UiccCard newCard = mUiccController.getUiccCard(mPhoneId);
-            CardState state = CardState.CARDSTATE_ABSENT;
             UiccCardApplication newApp = null;
             IccRecords newRecords = null;
             if (newCard != null) {
-                state = newCard.getCardState();
                 newApp = newCard.getApplication(mCurrentAppType);
                 if (newApp != null) {
                     newRecords = newApp.getIccRecords();
@@ -339,7 +352,7 @@
             }
 
             if (mIccRecords != newRecords || mUiccApplication != newApp || mUiccCard != newCard) {
-                if (DBG) log("Icc changed. Reregestering.");
+                if (DBG) log("Icc changed. Reregistering.");
                 unregisterUiccCardEvents();
                 mUiccCard = newCard;
                 mUiccApplication = newApp;
@@ -369,15 +382,27 @@
         // mUiccCard could be null at bootup, before valid card states have
         // been received from UiccController.
         if (mUiccCard == null) {
-            setExternalState(State.NOT_READY);
+            setExternalState(State.UNKNOWN);
             return;
         }
 
         if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
-            if (mRadioOn) {
-                setExternalState(State.ABSENT);
+            /*
+             * Both IccCardProxy and UiccController are registered for
+             * RadioState changes. When the UiccController receives a radio
+             * state changed to Unknown it will dispose of all of the IccCard
+             * objects, which will then notify the IccCardProxy and the null
+             * object will force the state to unknown. However, because the
+             * IccCardProxy is also registered for RadioState changes, it will
+             * recieve that signal first. By triggering on radio state changes
+             * directly, we reduce the time window during which the modem is
+             * UNAVAILABLE but the IccStatus is reported as something valid.
+             * This is not ideal.
+             */
+            if (mRadioState == RadioState.RADIO_UNAVAILABLE) {
+                setExternalState(State.UNKNOWN);
             } else {
-                setExternalState(State.NOT_READY);
+                setExternalState(State.ABSENT);
             }
             return;
         }
@@ -397,9 +422,20 @@
             return;
         }
 
+        // By process of elimination, the UICC Card State = PRESENT
         switch (mUiccApplication.getState()) {
             case APPSTATE_UNKNOWN:
-                setExternalState(State.UNKNOWN);
+                /*
+                 * APPSTATE_UNKNOWN is a catch-all state reported whenever the app
+                 * is not explicitly in one of the other states. To differentiate the
+                 * case where we know that there is a card present, but the APP is not
+                 * ready, we choose NOT_READY here instead of unknown. This is possible
+                 * in at least two cases:
+                 * 1) A transient during the process of the SIM bringup
+                 * 2) There is no valid App on the SIM to load, which can be the case with an
+                 *    eSIM/soft SIM.
+                 */
+                setExternalState(State.NOT_READY);
                 break;
             case APPSTATE_DETECTED:
                 HandleDetectedState();
@@ -419,9 +455,8 @@
                 if (mUiccApplication.getPersoSubState() ==
                         PersoSubState.PERSOSUBSTATE_SIM_NETWORK) {
                     setExternalState(State.NETWORK_LOCKED);
-                } else {
-                    setExternalState(State.UNKNOWN);
                 }
+                // Otherwise don't change external SIM state.
                 break;
             case APPSTATE_READY:
                 setExternalState(State.READY);
@@ -447,6 +482,7 @@
 
     private void unregisterUiccCardEvents() {
         if (mUiccCard != null) mUiccCard.unregisterForAbsent(this);
+        if (mUiccCard != null) mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
         if (mUiccApplication != null) mUiccApplication.unregisterForReady(this);
         if (mUiccApplication != null) mUiccApplication.unregisterForLocked(this);
         if (mUiccApplication != null) mUiccApplication.unregisterForNetworkLocked(this);
@@ -461,7 +497,7 @@
 
     private void broadcastIccStateChangedIntent(String value, String reason) {
         synchronized (mLock) {
-            if (mPhoneId == null || !SubscriptionManager.isValidSlotId(mPhoneId)) {
+            if (mPhoneId == null || !SubscriptionManager.isValidSlotIndex(mPhoneId)) {
                 loge("broadcastIccStateChangedIntent: mPhoneId=" + mPhoneId
                         + " is invalid; Return!!");
                 return;
@@ -486,8 +522,7 @@
             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhoneId);
             log("broadcastIccStateChangedIntent intent ACTION_SIM_STATE_CHANGED value=" + value
                 + " reason=" + reason + " for mPhoneId=" + mPhoneId);
-            ActivityManagerNative.broadcastStickyIntent(intent, READ_PHONE_STATE,
-                    UserHandle.USER_ALL);
+            IntentBroadcaster.getInstance().broadcastStickyIntent(intent, mPhoneId);
         }
     }
 
@@ -505,28 +540,30 @@
             intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE, value);
             intent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, reason);
             intent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId);  // SubId may not be valid.
-            log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED" + " for mPhoneId : " + mPhoneId);
-            ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+            log("Sending intent ACTION_INTERNAL_SIM_STATE_CHANGED value=" + value
+                    + " for mPhoneId : " + mPhoneId);
+            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
         }
     }
 
     private void setExternalState(State newState, boolean override) {
         synchronized (mLock) {
-            if (mPhoneId == null || !SubscriptionManager.isValidSlotId(mPhoneId)) {
+            if (mPhoneId == null || !SubscriptionManager.isValidSlotIndex(mPhoneId)) {
                 loge("setExternalState: mPhoneId=" + mPhoneId + " is invalid; Return!!");
                 return;
             }
 
             if (!override && newState == mExternalState) {
-                loge("setExternalState: !override and newstate unchanged from " + newState);
+                log("setExternalState: !override and newstate unchanged from " + newState);
                 return;
             }
             mExternalState = newState;
-            loge("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
+            log("setExternalState: set mPhoneId=" + mPhoneId + " mExternalState=" + mExternalState);
             mTelephonyManager.setSimStateForPhone(mPhoneId, getState().toString());
 
             // For locked states, we should be sending internal broadcast.
-            if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(getIccStateIntentString(mExternalState))) {
+            if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(
+                        getIccStateIntentString(mExternalState))) {
                 broadcastInternalIccStateChangedIntent(getIccStateIntentString(mExternalState),
                         getIccStateReason(mExternalState));
             } else {
@@ -939,7 +976,7 @@
         pw.println(" mUiccApplication=" + mUiccApplication);
         pw.println(" mIccRecords=" + mIccRecords);
         pw.println(" mCdmaSSM=" + mCdmaSSM);
-        pw.println(" mRadioOn=" + mRadioOn);
+        pw.println(" mRadioState=" + mRadioState);
         pw.println(" mQuietMode=" + mQuietMode);
         pw.println(" mInitialized=" + mInitialized);
         pw.println(" mExternalState=" + mExternalState);
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
index 8992e9f..f14f21d 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardStatus.java
@@ -116,30 +116,33 @@
         StringBuilder sb = new StringBuilder();
         sb.append("IccCardState {").append(mCardState).append(",")
         .append(mUniversalPinState)
-        .append(",num_apps=").append(mApplications.length)
-        .append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
-        if (mGsmUmtsSubscriptionAppIndex >=0
-                && mGsmUmtsSubscriptionAppIndex <CARD_MAX_APPS) {
+        .append(",num_apps=").append(mApplications.length);
+
+        sb.append(",gsm_id=").append(mGsmUmtsSubscriptionAppIndex);
+        if (mApplications != null
+                && mGsmUmtsSubscriptionAppIndex >= 0
+                && mGsmUmtsSubscriptionAppIndex < mApplications.length) {
             app = mApplications[mGsmUmtsSubscriptionAppIndex];
             sb.append(app == null ? "null" : app);
         }
 
         sb.append(",cdma_id=").append(mCdmaSubscriptionAppIndex);
-        if (mCdmaSubscriptionAppIndex >=0
-                && mCdmaSubscriptionAppIndex <CARD_MAX_APPS) {
+        if (mApplications != null
+                && mCdmaSubscriptionAppIndex >= 0
+                && mCdmaSubscriptionAppIndex < mApplications.length) {
             app = mApplications[mCdmaSubscriptionAppIndex];
             sb.append(app == null ? "null" : app);
         }
 
         sb.append(",ims_id=").append(mImsSubscriptionAppIndex);
-        if (mImsSubscriptionAppIndex >=0
-                && mImsSubscriptionAppIndex <CARD_MAX_APPS) {
+        if (mApplications != null
+                && mImsSubscriptionAppIndex >= 0
+                && mImsSubscriptionAppIndex < mApplications.length) {
             app = mApplications[mImsSubscriptionAppIndex];
             sb.append(app == null ? "null" : app);
         }
 
         sb.append("}");
-
         return sb.toString();
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 970bf35..0f41f1e 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -97,11 +97,6 @@
     //Search interval for higher priority PLMNs
     static final int EF_HPPLMN = 0x6F31;
 
-    // SMS record length from TS 51.011 10.5.3
-    static public final int SMS_RECORD_LENGTH = 176;
-    // SMS record length from C.S0023 3.4.27
-    static public final int CDMA_SMS_RECORD_LENGTH = 255;
-
     static final String MF_SIM = "3F00";
     static final String DF_TELECOM = "7F10";
     static final String DF_PHONEBOOK = "5F3A";
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 847f76b..af26f5c 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -25,7 +25,6 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
-import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
@@ -34,7 +33,9 @@
 import java.io.PrintWriter;
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * {@hide}
@@ -77,15 +78,20 @@
     protected String mNewVoiceMailTag = null;
     protected boolean mIsVoiceMailFixed = false;
     protected String mImsi;
+    protected String mFakeImsi;
     private IccIoResult auth_rsp;
 
     protected int mMncLength = UNINITIALIZED;
     protected int mMailboxIndex = 0; // 0 is no mailbox dailing number associated
 
     private String mSpn;
+    private String mFakeSpn;
 
     protected String mGid1;
+    protected String mFakeGid1;
     protected String mGid2;
+    protected String mFakeGid2;
+
     protected String mPrefLang;
 
     protected PlmnActRecord[] mHplmnActRecords;
@@ -97,6 +103,15 @@
 
     private final Object mLock = new Object();
 
+    CarrierTestOverride mCarrierTestOverride;
+
+    //Arbitrary offset for the Handler
+    protected static final int HANDLER_ACTION_BASE = 0x12E500;
+    protected static final int HANDLER_ACTION_NONE = HANDLER_ACTION_BASE + 0;
+    protected static final int HANDLER_ACTION_SEND_RESPONSE = HANDLER_ACTION_BASE + 1;
+    protected static AtomicInteger sNextRequestId = new AtomicInteger(1);
+    protected final HashMap<Integer, Message> mPendingResponses = new HashMap<>();
+
     // ***** Constants
 
     // Markers for mncLength
@@ -120,6 +135,9 @@
     public static final int CALL_FORWARDING_STATUS_ENABLED = 1;
     public static final int CALL_FORWARDING_STATUS_UNKNOWN = -1;
 
+    public static final int DEFAULT_VOICE_MESSAGE_COUNT = -2;
+    public static final int UNKNOWN_VOICE_MESSAGE_COUNT = -1;
+
     @Override
     public String toString() {
         String iccIdToPrint = SubscriptionInfo.givePrintableIccid(mFullIccId);
@@ -146,9 +164,13 @@
                 + " isVoiceMailFixed=" + mIsVoiceMailFixed
                 + " mImsi=" + ((mImsi != null) ?
                 mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null")
+                + (mCarrierTestOverride.isInTestMode()
+                ? (" mFakeImsi=" + ((mFakeImsi != null) ? mFakeImsi : "null")) : "")
                 + " mncLength=" + mMncLength
                 + " mailboxIndex=" + mMailboxIndex
-                + " spn=" + mSpn;
+                + " spn=" + mSpn
+                + (mCarrierTestOverride.isInTestMode()
+                ? (" mFakeSpn=" + ((mFakeSpn != null) ? mFakeSpn : "null")) : "");
 
     }
 
@@ -176,6 +198,22 @@
         mParentApp = app;
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
+
+        mCarrierTestOverride = new CarrierTestOverride();
+
+        if (mCarrierTestOverride.isInTestMode()) {
+            mFakeImsi = mCarrierTestOverride.getFakeIMSI();
+            log("load mFakeImsi: " + mFakeImsi);
+
+            mFakeGid1 = mCarrierTestOverride.getFakeGid1();
+            log("load mFakeGid1: " + mFakeGid1);
+
+            mFakeGid2 = mCarrierTestOverride.getFakeGid2();
+            log("load mFakeGid2: " + mFakeGid2);
+
+            mFakeSpn = mCarrierTestOverride.getFakeSpn();
+            log("load mFakeSpn: " + mFakeSpn);
+        }
     }
 
     /**
@@ -205,6 +243,28 @@
     }
 
     /**
+     * Adds a message to the pending requests list by generating a unique
+     * (integer) hash key and returning it. The message should never be null.
+     */
+    public int storePendingResponseMessage(Message msg) {
+        int key = sNextRequestId.getAndIncrement();
+        synchronized (mPendingResponses) {
+            mPendingResponses.put(key, msg);
+        }
+        return key;
+    }
+
+    /**
+     * Returns the pending request, if any or null
+     */
+    public Message retrievePendingResponseMessage(Integer key) {
+        Message m;
+        synchronized (mPendingResponses) {
+            return mPendingResponses.remove(key);
+        }
+    }
+
+    /**
      * Returns the ICC ID stripped at the first hex character. Some SIMs have ICC IDs
      * containing hex digits; {@link #getFullIccId()} should be used to get the full ID including
      * hex digits.
@@ -246,7 +306,7 @@
         Registrant r = new Registrant(h, what, obj);
         mImsiReadyRegistrants.add(r);
 
-        if (mImsi != null) {
+        if (getIMSI() != null) {
             r.notifyRegistrant(new AsyncResult(null, null, null));
         }
     }
@@ -292,7 +352,11 @@
      * @return null if SIM is not yet ready or unavailable
      */
     public String getIMSI() {
-        return null;
+        if (mCarrierTestOverride.isInTestMode() && mFakeImsi != null) {
+            return mFakeImsi;
+        } else {
+            return mImsi;
+        }
     }
 
     /**
@@ -323,7 +387,11 @@
      * @return null if SIM is not yet ready
      */
     public String getGid1() {
-        return null;
+        if (mCarrierTestOverride.isInTestMode() && mFakeGid1 != null) {
+            return mFakeGid1;
+        } else {
+            return mGid1;
+        }
     }
 
     /**
@@ -331,7 +399,11 @@
      * @return null if SIM is not yet ready
      */
     public String getGid2() {
-        return null;
+        if (mCarrierTestOverride.isInTestMode() && mFakeGid2 != null) {
+            return mFakeGid2;
+        } else {
+            return mGid2;
+        }
     }
 
     public void setMsisdnNumber(String alphaTag, String number,
@@ -357,6 +429,9 @@
      * @return null if SIM is not yet ready or no RUIM entry
      */
     public String getServiceProviderName() {
+        if (mCarrierTestOverride.isInTestMode() && mFakeSpn != null) {
+            return mFakeSpn;
+        }
         String providerName = mSpn;
 
         // Check for null pointers, mParentApp can be null after dispose,
@@ -443,6 +518,7 @@
      */
     protected void onIccRefreshInit() {
         mAdnCache.reset();
+        mMncLength = UNINITIALIZED;
         UiccCardApplication parentApp = mParentApp;
         if ((parentApp != null) &&
                 (parentApp.getState() == AppState.APPSTATE_READY)) {
@@ -744,9 +820,15 @@
         pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
         pw.println(" mImsi=" + ((mImsi != null) ?
                 mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)) : "null"));
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeImsi=" + ((mFakeImsi != null) ? mFakeImsi : "null"));
+        }
         pw.println(" mMncLength=" + mMncLength);
         pw.println(" mMailboxIndex=" + mMailboxIndex);
         pw.println(" mSpn=" + mSpn);
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeSpn=" + ((mFakeSpn != null) ? mFakeSpn : "null"));
+        }
         pw.flush();
     }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/IccUtils.java b/src/java/com/android/internal/telephony/uicc/IccUtils.java
deleted file mode 100644
index 67de87f..0000000
--- a/src/java/com/android/internal/telephony/uicc/IccUtils.java
+++ /dev/null
@@ -1,570 +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.uicc;
-
-import android.content.res.Resources;
-import android.content.res.Resources.NotFoundException;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.telephony.Rlog;
-
-import com.android.internal.telephony.GsmAlphabet;
-
-import java.io.UnsupportedEncodingException;
-
-/**
- * Various methods, useful for dealing with SIM data.
- */
-public class IccUtils {
-    static final String LOG_TAG="IccUtils";
-
-    /**
-     * Many fields in GSM SIM's are stored as nibble-swizzled BCD
-     *
-     * Assumes left-justified field that may be padded right with 0xf
-     * values.
-     *
-     * Stops on invalid BCD value, returning string so far
-     */
-    public static String
-    bcdToString(byte[] data, int offset, int length) {
-        StringBuilder ret = new StringBuilder(length*2);
-
-        for (int i = offset ; i < offset + length ; i++) {
-            int v;
-
-            v = data[i] & 0xf;
-            if (v > 9)  break;
-            ret.append((char)('0' + v));
-
-            v = (data[i] >> 4) & 0xf;
-            // Some PLMNs have 'f' as high nibble, ignore it
-            if (v == 0xf) continue;
-            if (v > 9)  break;
-            ret.append((char)('0' + v));
-        }
-
-        return ret.toString();
-    }
-
-    /**
-     * PLMN (MCC/MNC) is encoded as per 24.008 10.5.1.3
-     * Returns a concatenated string of MCC+MNC, stripping
-     * a trailing character for a 2-digit MNC
-     */
-    public static String bcdPlmnToString(byte[] data, int offset) {
-        if (offset + 3 > data.length) {
-            return null;
-        }
-        byte[] trans = new byte[3];
-        trans[0] = (byte) ((data[0 + offset] << 4) | ((data[0 + offset] >> 4) & 0xF));
-        trans[1] = (byte) ((data[1 + offset] << 4) | (data[2 + offset] & 0xF));
-        trans[2] = (byte) ((data[2 + offset] & 0xF0) | ((data[1 + offset] >> 4) & 0xF));
-        String ret = bytesToHexString(trans);
-
-        // For a 2-digit MNC we trim the trailing 'f'
-        if (ret.endsWith("f")) {
-            ret = ret.substring(0, ret.length() - 1);
-        }
-        return ret;
-    }
-
-    /**
-     * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH
-     */
-    public static String
-    bchToString(byte[] data, int offset, int length) {
-        StringBuilder ret = new StringBuilder(length*2);
-
-        for (int i = offset ; i < offset + length ; i++) {
-            int v;
-
-            v = data[i] & 0xf;
-            ret.append("0123456789abcdef".charAt(v));
-
-            v = (data[i] >> 4) & 0xf;
-            ret.append("0123456789abcdef".charAt(v));
-        }
-
-        return ret.toString();
-    }
-
-    /**
-     * Decode cdma byte into String.
-     */
-    public static String
-    cdmaBcdToString(byte[] data, int offset, int length) {
-        StringBuilder ret = new StringBuilder(length);
-
-        int count = 0;
-        for (int i = offset; count < length; i++) {
-            int v;
-            v = data[i] & 0xf;
-            if (v > 9)  v = 0;
-            ret.append((char)('0' + v));
-
-            if (++count == length) break;
-
-            v = (data[i] >> 4) & 0xf;
-            if (v > 9)  v = 0;
-            ret.append((char)('0' + v));
-            ++count;
-        }
-        return ret.toString();
-    }
-
-    /**
-     * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
-     *
-     * In GSM land, the least significant BCD digit is stored in the most
-     * significant nibble.
-     *
-     * Out-of-range digits are treated as 0 for the sake of the time stamp,
-     * because of this:
-     *
-     * TS 23.040 section 9.2.3.11
-     * "if the MS receives a non-integer value in the SCTS, it shall
-     * assume the digit is set to 0 but shall store the entire field
-     * exactly as received"
-     */
-    public static int
-    gsmBcdByteToInt(byte b) {
-        int ret = 0;
-
-        // treat out-of-range BCD values as 0
-        if ((b & 0xf0) <= 0x90) {
-            ret = (b >> 4) & 0xf;
-        }
-
-        if ((b & 0x0f) <= 0x09) {
-            ret +=  (b & 0xf) * 10;
-        }
-
-        return ret;
-    }
-
-    /**
-     * Decodes a CDMA style BCD byte like {@link #gsmBcdByteToInt}, but
-     * opposite nibble format. The least significant BCD digit
-     * is in the least significant nibble and the most significant
-     * is in the most significant nibble.
-     */
-    public static int
-    cdmaBcdByteToInt(byte b) {
-        int ret = 0;
-
-        // treat out-of-range BCD values as 0
-        if ((b & 0xf0) <= 0x90) {
-            ret = ((b >> 4) & 0xf) * 10;
-        }
-
-        if ((b & 0x0f) <= 0x09) {
-            ret +=  (b & 0xf);
-        }
-
-        return ret;
-    }
-
-    /**
-     * Decodes a string field that's formatted like the EF[ADN] alpha
-     * identifier
-     *
-     * From TS 51.011 10.5.1:
-     *   Coding:
-     *       this alpha tagging shall use either
-     *      -    the SMS default 7 bit coded alphabet as defined in
-     *          TS 23.038 [12] with bit 8 set to 0. The alpha identifier
-     *          shall be left justified. Unused bytes shall be set to 'FF'; or
-     *      -    one of the UCS2 coded options as defined in annex B.
-     *
-     * Annex B from TS 11.11 V8.13.0:
-     *      1)  If the first octet in the alpha string is '80', then the
-     *          remaining octets are 16 bit UCS2 characters ...
-     *      2)  if the first octet in the alpha string is '81', then the
-     *          second octet contains a value indicating the number of
-     *          characters in the string, and the third octet contains an
-     *          8 bit number which defines bits 15 to 8 of a 16 bit
-     *          base pointer, where bit 16 is set to zero and bits 7 to 1
-     *          are also set to zero.  These sixteen bits constitute a
-     *          base pointer to a "half page" in the UCS2 code space, to be
-     *          used with some or all of the remaining octets in the string.
-     *          The fourth and subsequent octets contain codings as follows:
-     *          If bit 8 of the octet is set to zero, the remaining 7 bits
-     *          of the octet contain a GSM Default Alphabet character,
-     *          whereas if bit 8 of the octet is set to one, then the
-     *          remaining seven bits are an offset value added to the
-     *          16 bit base pointer defined earlier...
-     *      3)  If the first octet of the alpha string is set to '82', then
-     *          the second octet contains a value indicating the number of
-     *          characters in the string, and the third and fourth octets
-     *          contain a 16 bit number which defines the complete 16 bit
-     *          base pointer to a "half page" in the UCS2 code space...
-     */
-    public static String
-    adnStringFieldToString(byte[] data, int offset, int length) {
-        if (length == 0) {
-            return "";
-        }
-        if (length >= 1) {
-            if (data[offset] == (byte) 0x80) {
-                int ucslen = (length - 1) / 2;
-                String ret = null;
-
-                try {
-                    ret = new String(data, offset + 1, ucslen * 2, "utf-16be");
-                } catch (UnsupportedEncodingException ex) {
-                    Rlog.e(LOG_TAG, "implausible UnsupportedEncodingException",
-                          ex);
-                }
-
-                if (ret != null) {
-                    // trim off trailing FFFF characters
-
-                    ucslen = ret.length();
-                    while (ucslen > 0 && ret.charAt(ucslen - 1) == '\uFFFF')
-                        ucslen--;
-
-                    return ret.substring(0, ucslen);
-                }
-            }
-        }
-
-        boolean isucs2 = false;
-        char base = '\0';
-        int len = 0;
-
-        if (length >= 3 && data[offset] == (byte) 0x81) {
-            len = data[offset + 1] & 0xFF;
-            if (len > length - 3)
-                len = length - 3;
-
-            base = (char) ((data[offset + 2] & 0xFF) << 7);
-            offset += 3;
-            isucs2 = true;
-        } else if (length >= 4 && data[offset] == (byte) 0x82) {
-            len = data[offset + 1] & 0xFF;
-            if (len > length - 4)
-                len = length - 4;
-
-            base = (char) (((data[offset + 2] & 0xFF) << 8) |
-                            (data[offset + 3] & 0xFF));
-            offset += 4;
-            isucs2 = true;
-        }
-
-        if (isucs2) {
-            StringBuilder ret = new StringBuilder();
-
-            while (len > 0) {
-                // UCS2 subset case
-
-                if (data[offset] < 0) {
-                    ret.append((char) (base + (data[offset] & 0x7F)));
-                    offset++;
-                    len--;
-                }
-
-                // GSM character set case
-
-                int count = 0;
-                while (count < len && data[offset + count] >= 0)
-                    count++;
-
-                ret.append(GsmAlphabet.gsm8BitUnpackedToString(data,
-                           offset, count));
-
-                offset += count;
-                len -= count;
-            }
-
-            return ret.toString();
-        }
-
-        Resources resource = Resources.getSystem();
-        String defaultCharset = "";
-        try {
-            defaultCharset =
-                    resource.getString(com.android.internal.R.string.gsm_alphabet_default_charset);
-        } catch (NotFoundException e) {
-            // Ignore Exception and defaultCharset is set to a empty string.
-        }
-        return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
-    }
-
-    static int
-    hexCharToInt(char c) {
-        if (c >= '0' && c <= '9') return (c - '0');
-        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
-        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
-
-        throw new RuntimeException ("invalid hex char '" + c + "'");
-    }
-
-    /**
-     * Converts a hex String to a byte array.
-     *
-     * @param s A string of hexadecimal characters, must be an even number of
-     *          chars long
-     *
-     * @return byte array representation
-     *
-     * @throws RuntimeException on invalid format
-     */
-    public static byte[]
-    hexStringToBytes(String s) {
-        byte[] ret;
-
-        if (s == null) return null;
-
-        int sz = s.length();
-
-        ret = new byte[sz/2];
-
-        for (int i=0 ; i <sz ; i+=2) {
-            ret[i/2] = (byte) ((hexCharToInt(s.charAt(i)) << 4)
-                                | hexCharToInt(s.charAt(i+1)));
-        }
-
-        return ret;
-    }
-
-
-    /**
-     * Converts a byte array into a String of hexadecimal characters.
-     *
-     * @param bytes an array of bytes
-     *
-     * @return hex string representation of bytes array
-     */
-    public static String
-    bytesToHexString(byte[] bytes) {
-        if (bytes == null) return null;
-
-        StringBuilder ret = new StringBuilder(2*bytes.length);
-
-        for (int i = 0 ; i < bytes.length ; i++) {
-            int b;
-
-            b = 0x0f & (bytes[i] >> 4);
-
-            ret.append("0123456789abcdef".charAt(b));
-
-            b = 0x0f & bytes[i];
-
-            ret.append("0123456789abcdef".charAt(b));
-        }
-
-        return ret.toString();
-    }
-
-
-    /**
-     * Convert a TS 24.008 Section 10.5.3.5a Network Name field to a string
-     * "offset" points to "octet 3", the coding scheme byte
-     * empty string returned on decode error
-     */
-    public static String
-    networkNameToString(byte[] data, int offset, int length) {
-        String ret;
-
-        if ((data[offset] & 0x80) != 0x80 || length < 1) {
-            return "";
-        }
-
-        switch ((data[offset] >>> 4) & 0x7) {
-            case 0:
-                // SMS character set
-                int countSeptets;
-                int unusedBits = data[offset] & 7;
-                countSeptets = (((length - 1) * 8) - unusedBits) / 7 ;
-                ret =  GsmAlphabet.gsm7BitPackedToString(data, offset + 1, countSeptets);
-            break;
-            case 1:
-                // UCS2
-                try {
-                    ret = new String(data,
-                            offset + 1, length - 1, "utf-16");
-                } catch (UnsupportedEncodingException ex) {
-                    ret = "";
-                    Rlog.e(LOG_TAG,"implausible UnsupportedEncodingException", ex);
-                }
-            break;
-
-            // unsupported encoding
-            default:
-                ret = "";
-            break;
-        }
-
-        // "Add CI"
-        // "The MS should add the letters for the Country's Initials and
-        //  a separator (e.g. a space) to the text string"
-
-        if ((data[offset] & 0x40) != 0) {
-            // FIXME(mkf) add country initials here
-
-        }
-
-        return ret;
-    }
-
-    /**
-     * Convert a TS 131.102 image instance of code scheme '11' into Bitmap
-     * @param data The raw data
-     * @param length The length of image body
-     * @return The bitmap
-     */
-    public static Bitmap parseToBnW(byte[] data, int length){
-        int valueIndex = 0;
-        int width = data[valueIndex++] & 0xFF;
-        int height = data[valueIndex++] & 0xFF;
-        int numOfPixels = width*height;
-
-        int[] pixels = new int[numOfPixels];
-
-        int pixelIndex = 0;
-        int bitIndex = 7;
-        byte currentByte = 0x00;
-        while (pixelIndex < numOfPixels) {
-            // reassign data and index for every byte (8 bits).
-            if (pixelIndex % 8 == 0) {
-                currentByte = data[valueIndex++];
-                bitIndex = 7;
-            }
-            pixels[pixelIndex++] = bitToRGB((currentByte >> bitIndex-- ) & 0x01);
-        }
-
-        if (pixelIndex != numOfPixels) {
-            Rlog.e(LOG_TAG, "parse end and size error");
-        }
-        return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888);
-    }
-
-    private static int bitToRGB(int bit){
-        if(bit == 1){
-            return Color.WHITE;
-        } else {
-            return Color.BLACK;
-        }
-    }
-
-    /**
-     * a TS 131.102 image instance of code scheme '11' into color Bitmap
-     *
-     * @param data The raw data
-     * @param length the length of image body
-     * @param transparency with or without transparency
-     * @return The color bitmap
-     */
-    public static Bitmap parseToRGB(byte[] data, int length,
-            boolean transparency) {
-        int valueIndex = 0;
-        int width = data[valueIndex++] & 0xFF;
-        int height = data[valueIndex++] & 0xFF;
-        int bits = data[valueIndex++] & 0xFF;
-        int colorNumber = data[valueIndex++] & 0xFF;
-        int clutOffset = ((data[valueIndex++] & 0xFF) << 8)
-                | (data[valueIndex++] & 0xFF);
-
-        int[] colorIndexArray = getCLUT(data, clutOffset, colorNumber);
-        if (true == transparency) {
-            colorIndexArray[colorNumber - 1] = Color.TRANSPARENT;
-        }
-
-        int[] resultArray = null;
-        if (0 == (8 % bits)) {
-            resultArray = mapTo2OrderBitColor(data, valueIndex,
-                    (width * height), colorIndexArray, bits);
-        } else {
-            resultArray = mapToNon2OrderBitColor(data, valueIndex,
-                    (width * height), colorIndexArray, bits);
-        }
-
-        return Bitmap.createBitmap(resultArray, width, height,
-                Bitmap.Config.RGB_565);
-    }
-
-    private static int[] mapTo2OrderBitColor(byte[] data, int valueIndex,
-            int length, int[] colorArray, int bits) {
-        if (0 != (8 % bits)) {
-            Rlog.e(LOG_TAG, "not event number of color");
-            return mapToNon2OrderBitColor(data, valueIndex, length, colorArray,
-                    bits);
-        }
-
-        int mask = 0x01;
-        switch (bits) {
-        case 1:
-            mask = 0x01;
-            break;
-        case 2:
-            mask = 0x03;
-            break;
-        case 4:
-            mask = 0x0F;
-            break;
-        case 8:
-            mask = 0xFF;
-            break;
-        }
-
-        int[] resultArray = new int[length];
-        int resultIndex = 0;
-        int run = 8 / bits;
-        while (resultIndex < length) {
-            byte tempByte = data[valueIndex++];
-            for (int runIndex = 0; runIndex < run; ++runIndex) {
-                int offset = run - runIndex - 1;
-                resultArray[resultIndex++] = colorArray[(tempByte >> (offset * bits))
-                        & mask];
-            }
-        }
-        return resultArray;
-    }
-
-    private static int[] mapToNon2OrderBitColor(byte[] data, int valueIndex,
-            int length, int[] colorArray, int bits) {
-        if (0 == (8 % bits)) {
-            Rlog.e(LOG_TAG, "not odd number of color");
-            return mapTo2OrderBitColor(data, valueIndex, length, colorArray,
-                    bits);
-        }
-
-        int[] resultArray = new int[length];
-        // TODO fix me:
-        return resultArray;
-    }
-
-    private static int[] getCLUT(byte[] rawData, int offset, int number) {
-        if (null == rawData) {
-            return null;
-        }
-
-        int[] result = new int[number];
-        int endIndex = offset + (number * 3); // 1 color use 3 bytes
-        int valueIndex = offset;
-        int colorIndex = 0;
-        int alpha = 0xff << 24;
-        do {
-            result[colorIndex++] = alpha
-                    | ((rawData[valueIndex++] & 0xFF) << 16)
-                    | ((rawData[valueIndex++] & 0xFF) << 8)
-                    | ((rawData[valueIndex++] & 0xFF));
-        } while (valueIndex < endIndex);
-        return result;
-    }
-}
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index 83f7e02..194d259 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -16,13 +16,18 @@
 
 package com.android.internal.telephony.uicc;
 
+import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
+import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
+import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
+
 import android.content.Context;
+import android.content.Intent;
 import android.os.AsyncResult;
-import android.os.Handler;
 import android.os.Message;
 import android.telephony.Rlog;
-import android.content.Intent;
-
+import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.gsm.SimTlv;
@@ -34,12 +39,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 
-import static com.android.internal.telephony.uicc.IccConstants.EF_DOMAIN;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IMPI;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IMPU;
-import static com.android.internal.telephony.uicc.IccConstants.EF_IST;
-import static com.android.internal.telephony.uicc.IccConstants.EF_PCSCF;
-
 /**
  * {@hide}
  */
@@ -349,8 +348,8 @@
             return;
         }
 
-        if (refreshResponse.aid != null &&
-                !refreshResponse.aid.equals(mParentApp.getAid())) {
+        if (!TextUtils.isEmpty(refreshResponse.aid)
+                && !refreshResponse.aid.equals(mParentApp.getAid())) {
             // This is for different app. Ignore.
             if (DBG) log("handleIsimRefresh received different app");
             return;
diff --git a/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java b/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java
index a4cf7cc..2218280 100644
--- a/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java
+++ b/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java
@@ -42,7 +42,7 @@
     public final String plmn;
     public final int accessTechs;
 
-    private static final boolean VDBG = true;
+    private static final boolean VDBG = false;
 
     public static final Parcelable.Creator<PlmnActRecord> CREATOR =
             new Parcelable.Creator<PlmnActRecord>() {
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 4e486d2..b303ca8 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -18,31 +18,31 @@
 
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_TEST_CSIM;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
 import android.content.Context;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
-import android.content.res.Resources;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.SubscriptionController;
-
 import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.util.BitwiseInputStream;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
 /**
  * {@hide}
  */
@@ -153,11 +153,6 @@
         mRecordsRequested = false;
     }
 
-    @Override
-    public String getIMSI() {
-        return mImsi;
-    }
-
     public String getMdnNumber() {
         return mMyMobileNumber;
     }
@@ -215,21 +210,23 @@
      *  provided the RUIM card. Returns null of RUIM is not yet ready
      */
     public String getRUIMOperatorNumeric() {
-        if (mImsi == null) {
+        String imsi = getIMSI();
+
+        if (imsi == null) {
             return null;
         }
 
         if (mMncLength != UNINITIALIZED && mMncLength != UNKNOWN) {
             // Length = length of MCC + length of MNC
             // length of mcc = 3 (3GPP2 C.S0005 - Section 2.3)
-            return mImsi.substring(0, 3 + mMncLength);
+            return imsi.substring(0, 3 + mMncLength);
         }
 
         // Guess the MNC length based on the MCC if we don't
         // have a valid value in ef[ad]
 
-        int mcc = Integer.parseInt(mImsi.substring(0,3));
-        return mImsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
+        int mcc = Integer.parseInt(imsi.substring(0, 3));
+        return imsi.substring(0, 3 + MccTable.smallestDigitsMccForMnc(mcc));
     }
 
     // Refer to ETSI TS 102.221
@@ -774,12 +771,14 @@
                 log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
             }
 
-            if (!TextUtils.isEmpty(mImsi)) {
-                log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : ""));
+            String imsi = getIMSI();
+
+            if (!TextUtils.isEmpty(imsi)) {
+                log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + imsi) : ""));
                 mTelephonyManager.setSimCountryIsoForPhone(
                         mParentApp.getPhoneId(),
                         MccTable.countryCodeForMcc(
-                        Integer.parseInt(mImsi.substring(0,3))));
+                        Integer.parseInt(imsi.substring(0, 3))));
             } else {
                 log("onAllRecordsLoaded empty imsi skipping setting mcc");
             }
@@ -796,9 +795,9 @@
         // TODO: The below is hacky since the SubscriptionController may not be ready at this time.
         if (!TextUtils.isEmpty(mMdn)) {
             int phoneId = mParentApp.getUiccCard().getPhoneId();
-            int[] subIds = SubscriptionController.getInstance().getSubId(phoneId);
-            if (subIds != null) {
-                SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subIds[0]);
+            int subId = SubscriptionController.getInstance().getSubIdUsingPhoneId(phoneId);
+            if (SubscriptionManager.isValidSubscriptionId(subId)) {
+                SubscriptionManager.from(mContext).setDisplayNumber(mMdn, subId);
             } else {
                 log("Cannot call setDisplayNumber: invalid subId");
             }
@@ -918,8 +917,8 @@
             return;
         }
 
-        if (refreshResponse.aid != null &&
-                !refreshResponse.aid.equals(mParentApp.getAid())) {
+        if (!TextUtils.isEmpty(refreshResponse.aid)
+                && !refreshResponse.aid.equals(mParentApp.getAid())) {
             // This is for different app. Ignore.
             return;
         }
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
old mode 100644
new mode 100755
index 6894d14..dad1ee2
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -33,7 +33,6 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.gsm.SimTlv;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
@@ -98,7 +97,7 @@
     public String toString() {
         return "SimRecords: " + super.toString()
                 + " mVmConfig" + mVmConfig
-                + " mSpnOverride=" + "mSpnOverride"
+                + " mSpnOverride=" + mSpnOverride
                 + " callForwardingEnabled=" + mCallForwardingStatus
                 + " spnState=" + mSpnState
                 + " mCphsInfo=" + mCphsInfo
@@ -180,8 +179,6 @@
     private static final int EVENT_APP_LOCKED = 2 + SYSTEM_EVENT_BASE;
     private static final int EVENT_SIM_REFRESH = 3 + SYSTEM_EVENT_BASE;
 
-
-
     // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
 
     private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
@@ -253,13 +250,14 @@
         mCi.unSetOnSmsOnSim(this);
         mParentApp.unregisterForReady(this);
         mParentApp.unregisterForLocked(this);
+        mContext.unregisterReceiver(mReceiver);
         resetRecords();
         super.dispose();
     }
 
     @Override
     protected void finalize() {
-        if(DBG) log("finalized");
+        if (DBG) log("finalized");
     }
 
     protected void resetRecords() {
@@ -298,33 +296,14 @@
         mRecordsRequested = false;
     }
 
-
     //***** Public Methods
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public String getIMSI() {
-        return mImsi;
-    }
-
     @Override
     public String getMsisdnNumber() {
         return mMsisdn;
     }
 
     @Override
-    public String getGid1() {
-        return mGid1;
-    }
-
-    @Override
-    public String getGid2() {
-        return mGid2;
-    }
-
-    @Override
     public UsimServiceTable getUsimServiceTable() {
         return mUsimServiceTable;
     }
@@ -354,7 +333,7 @@
      * When the operation is complete, onComplete will be sent to its handler
      *
      * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
-     * @param number dailing nubmer (up to 20 digits)
+     * @param number dialing number (up to 20 digits)
      *        if the number starts with '+', then set to international TOA
      * @param onComplete
      *        onComplete.obj will be an AsyncResult
@@ -503,15 +482,22 @@
         }
     }
 
-    // Validate data is !null and the MSP (Multiple Subscriber Profile)
-    // byte is between 1 and 4. See ETSI TS 131 102 v11.3.0 section 4.2.64.
+    // Validate data is !null.
     private boolean validEfCfis(byte[] data) {
-        return ((data != null) && (data[0] >= 1) && (data[0] <= 4));
+        if (data != null) {
+            if (data[0] < 1 || data[0] > 4) {
+                // The MSP (Multiple Subscriber Profile) byte should be between
+                // 1 and 4 according to ETSI TS 131 102 v11.3.0 section 4.2.64.
+                logw("MSP byte: " + data[0] + " is not between 1 and 4", null);
+            }
+            return true;
+        }
+        return false;
     }
 
     public int getVoiceMessageCount() {
         boolean voiceMailWaiting = false;
-        int countVoiceMessages = 0;
+        int countVoiceMessages = DEFAULT_VOICE_MESSAGE_COUNT;
         if (mEfMWIS != null) {
             // Use this data if the EF[MWIS] exists and
             // has been loaded
@@ -519,11 +505,11 @@
             voiceMailWaiting = ((mEfMWIS[0] & 0x01) != 0);
             countVoiceMessages = mEfMWIS[1] & 0xff;
 
-            if (voiceMailWaiting && countVoiceMessages == 0) {
+            if (voiceMailWaiting && (countVoiceMessages == 0 || countVoiceMessages == 0xff)) {
                 // Unknown count = -1
-                countVoiceMessages = -1;
+                countVoiceMessages = UNKNOWN_VOICE_MESSAGE_COUNT;
             }
-            if(DBG) log(" VoiceMessageCount from SIM MWIS = " + countVoiceMessages);
+            if (DBG) log(" VoiceMessageCount from SIM MWIS = " + countVoiceMessages);
         } else if (mEfCPHS_MWI != null) {
             // use voice mail count from CPHS
             int indicator = (int) (mEfCPHS_MWI[0] & 0xf);
@@ -531,11 +517,11 @@
             // Refer CPHS4_2.WW6 B4.2.3
             if (indicator == 0xA) {
                 // Unknown count = -1
-                countVoiceMessages = -1;
+                countVoiceMessages = UNKNOWN_VOICE_MESSAGE_COUNT;
             } else if (indicator == 0x5) {
                 countVoiceMessages = 0;
             }
-            if(DBG) log(" VoiceMessageCount from SIM CPHS = " + countVoiceMessages);
+            if (DBG) log(" VoiceMessageCount from SIM CPHS = " + countVoiceMessages);
         }
         return countVoiceMessages;
     }
@@ -577,7 +563,8 @@
                 // Spec reference for EF_CFIS contents, TS 51.011 section 10.3.46.
                 if (enable && !TextUtils.isEmpty(dialNumber)) {
                     logv("EF_CFIS: updating cf number, " + Rlog.pii(LOG_TAG, dialNumber));
-                    byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(dialNumber);
+                    byte[] bcdNumber = PhoneNumberUtils.numberToCalledPartyBCD(
+                            dialNumber, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN);
 
                     System.arraycopy(bcdNumber, 0, mEfCfis, CFIS_TON_NPI_OFFSET, bcdNumber.length);
 
@@ -634,7 +621,8 @@
      */
     @Override
     public String getOperatorNumeric() {
-        if (mImsi == null) {
+        String imsi = getIMSI();
+        if (imsi == null) {
             log("getOperatorNumeric: IMSI == null");
             return null;
         }
@@ -645,7 +633,11 @@
 
         // Length = length of MCC + length of MNC
         // length of mcc = 3 (TS 23.003 Section 2.2)
-        return mImsi.substring(0, 3 + mMncLength);
+        if (imsi.length() >= 3 + mMncLength) {
+            return imsi.substring(0, 3 + mMncLength);
+        } else {
+            return null;
+        }
     }
 
     // ***** Overridden from Handler
@@ -664,683 +656,725 @@
             return;
         }
 
-        try { switch (msg.what) {
-            case EVENT_APP_READY:
-                onReady();
-                break;
-
-            case EVENT_APP_LOCKED:
-                onLocked();
-                break;
-
-            /* IO events */
-            case EVENT_GET_IMSI_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-
-                if (ar.exception != null) {
-                    loge("Exception querying IMSI, Exception:" + ar.exception);
+        try {
+            switch (msg.what) {
+                case EVENT_APP_READY:
+                    onReady();
                     break;
-                }
 
-                mImsi = (String) ar.result;
-
-                // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
-                // than 15 (and usually 15).
-                if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
-                    loge("invalid IMSI " + mImsi);
-                    mImsi = null;
-                }
-
-                log("IMSI: mMncLength=" + mMncLength);
-                log("IMSI: " + mImsi.substring(0, 6) + Rlog.pii(LOG_TAG, mImsi.substring(6)));
-
-                if (((mMncLength == UNKNOWN) || (mMncLength == 2)) &&
-                        ((mImsi != null) && (mImsi.length() >= 6))) {
-                    String mccmncCode = mImsi.substring(0, 6);
-                    for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
-                        if (mccmnc.equals(mccmncCode)) {
-                            mMncLength = 3;
-                            log("IMSI: setting1 mMncLength=" + mMncLength);
-                            break;
-                        }
-                    }
-                }
-
-                if (mMncLength == UNKNOWN) {
-                    // the SIM has told us all it knows, but it didn't know the mnc length.
-                    // guess using the mcc
-                    try {
-                        int mcc = Integer.parseInt(mImsi.substring(0,3));
-                        mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
-                        log("setting2 mMncLength=" + mMncLength);
-                    } catch (NumberFormatException e) {
-                        mMncLength = UNKNOWN;
-                        loge("Corrupt IMSI! setting3 mMncLength=" + mMncLength);
-                    }
-                }
-
-                if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED) {
-                    log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
-                    // finally have both the imsi and the mncLength and can parse the imsi properly
-                    MccTable.updateMccMncConfiguration(mContext,
-                            mImsi.substring(0, 3 + mMncLength), false);
-                }
-                mImsiReadyRegistrants.notifyRegistrants();
-            break;
-
-            case EVENT_GET_MBI_DONE:
-                boolean isValidMbdn;
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[]) ar.result;
-
-                isValidMbdn = false;
-                if (ar.exception == null) {
-                    // Refer TS 51.011 Section 10.3.44 for content details
-                    log("EF_MBI: " + IccUtils.bytesToHexString(data));
-
-                    // Voice mail record number stored first
-                    mMailboxIndex = data[0] & 0xff;
-
-                    // check if dailing numbe id valid
-                    if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
-                        log("Got valid mailbox number for MBDN");
-                        isValidMbdn = true;
-                    }
-                }
-
-                // one more record to load
-                mRecordsToLoad += 1;
-
-                if (isValidMbdn) {
-                    // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
-                    new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
-                            mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
-                } else {
-                    // If this EF not present, try mailbox as in CPHS standard
-                    // CPHS (CPHS4_2.WW6) is a european standard.
-                    new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS,
-                            EF_EXT1, 1,
-                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
-                }
-
-                break;
-            case EVENT_GET_CPHS_MAILBOX_DONE:
-            case EVENT_GET_MBDN_DONE:
-                //Resetting the voice mail number and voice mail tag to null
-                //as these should be updated from the data read from EF_MBDN.
-                //If they are not reset, incase of invalid data/exception these
-                //variables are retaining their previous values and are
-                //causing invalid voice mailbox info display to user.
-                mVoiceMailNum = null;
-                mVoiceMailTag = null;
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-
-                if (ar.exception != null) {
-
-                    log("Invalid or missing EF"
-                        + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
-
-                    // Bug #645770 fall back to CPHS
-                    // FIXME should use SST to decide
-
-                    if (msg.what == EVENT_GET_MBDN_DONE) {
-                        //load CPHS on fail...
-                        // FIXME right now, only load line1's CPHS voice mail entry
-
-                        mRecordsToLoad += 1;
-                        new AdnRecordLoader(mFh).loadFromEF(
-                                EF_MAILBOX_CPHS, EF_EXT1, 1,
-                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
-                    }
+                case EVENT_APP_LOCKED:
+                    onLocked();
                     break;
-                }
 
-                adn = (AdnRecord)ar.result;
-
-                log("VM: " + adn +
-                        ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
-
-                if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
-                    // Bug #645770 fall back to CPHS
-                    // FIXME should use SST to decide
-                    // FIXME right now, only load line1's CPHS voice mail entry
-                    mRecordsToLoad += 1;
-                    new AdnRecordLoader(mFh).loadFromEF(
-                            EF_MAILBOX_CPHS, EF_EXT1, 1,
-                            obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
-
-                    break;
-                }
-
-                mVoiceMailNum = adn.getNumber();
-                mVoiceMailTag = adn.getAlphaTag();
-            break;
-
-            case EVENT_GET_MSISDN_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-
-                if (ar.exception != null) {
-                    log("Invalid or missing EF[MSISDN]");
-                    break;
-                }
-
-                adn = (AdnRecord)ar.result;
-
-                mMsisdn = adn.getNumber();
-                mMsisdnTag = adn.getAlphaTag();
-
-                log("MSISDN: " + /*mMsisdn*/ Rlog.pii(LOG_TAG, mMsisdn));
-            break;
-
-            case EVENT_SET_MSISDN_DONE:
-                isRecordLoadResponse = false;
-                ar = (AsyncResult)msg.obj;
-
-                if (ar.exception == null) {
-                    mMsisdn = mNewMsisdn;
-                    mMsisdnTag = mNewMsisdnTag;
-                    log("Success to update EF[MSISDN]");
-                }
-
-                if (ar.userObj != null) {
-                    AsyncResult.forMessage(((Message) ar.userObj)).exception
-                            = ar.exception;
-                    ((Message) ar.userObj).sendToTarget();
-                }
-                break;
-
-            case EVENT_GET_MWIS_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
-
-                if(DBG) log("EF_MWIS : " + IccUtils.bytesToHexString(data));
-
-                if (ar.exception != null) {
-                    if(DBG) log("EVENT_GET_MWIS_DONE exception = "
-                            + ar.exception);
-                    break;
-                }
-
-                if ((data[0] & 0xff) == 0xff) {
-                    if(DBG) log("SIMRecords: Uninitialized record MWIS");
-                    break;
-                }
-
-                mEfMWIS = data;
-                break;
-
-            case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
-
-                if(DBG) log("EF_CPHS_MWI: " + IccUtils.bytesToHexString(data));
-
-                if (ar.exception != null) {
-                    if(DBG) log("EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE exception = "
-                            + ar.exception);
-                    break;
-                }
-
-                mEfCPHS_MWI = data;
-                break;
-
-            case EVENT_GET_ICCID_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
-
-                if (ar.exception != null) {
-                    break;
-                }
-
-                mIccId = IccUtils.bcdToString(data, 0, data.length);
-                mFullIccId = IccUtils.bchToString(data, 0, data.length);
-
-                log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));
-
-            break;
-
-
-            case EVENT_GET_AD_DONE:
-                try {
+                /* IO events */
+                case EVENT_GET_IMSI_DONE:
                     isRecordLoadResponse = true;
 
-                    ar = (AsyncResult)msg.obj;
-                    data = (byte[])ar.result;
+                    ar = (AsyncResult) msg.obj;
 
                     if (ar.exception != null) {
+                        loge("Exception querying IMSI, Exception:" + ar.exception);
                         break;
                     }
 
-                    log("EF_AD: " + IccUtils.bytesToHexString(data));
+                    mImsi = (String) ar.result;
 
-                    if (data.length < 3) {
-                        log("Corrupt AD data on SIM");
-                        break;
+                    // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
+                    // than 15 (and usually 15).
+                    if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
+                        loge("invalid IMSI " + mImsi);
+                        mImsi = null;
                     }
 
-                    if (data.length == 3) {
-                        log("MNC length not present in EF_AD");
-                        break;
+                    log("IMSI: mMncLength=" + mMncLength);
+
+                    if (mImsi != null && mImsi.length() >= 6) {
+                        log("IMSI: " + mImsi.substring(0, 6)
+                                + Rlog.pii(LOG_TAG, mImsi.substring(6)));
                     }
 
-                    mMncLength = data[3] & 0xf;
-                    log("setting4 mMncLength=" + mMncLength);
+                    String imsi = getIMSI();
 
-                    if (mMncLength == 0xf) {
-                        mMncLength = UNKNOWN;
-                        log("setting5 mMncLength=" + mMncLength);
-                    } else if (mMncLength != 2 && mMncLength != 3) {
-                        mMncLength = UNINITIALIZED;
-                        log("setting5 mMncLength=" + mMncLength);
-                    }
-                } finally {
-                    if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN) ||
-                            (mMncLength == 2)) && ((mImsi != null) && (mImsi.length() >= 6))) {
-                        String mccmncCode = mImsi.substring(0, 6);
-                        log("mccmncCode=" + mccmncCode);
+                    if (((mMncLength == UNKNOWN) || (mMncLength == 2))
+                            && ((imsi != null) && (imsi.length() >= 6))) {
+                        String mccmncCode = imsi.substring(0, 6);
                         for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
                             if (mccmnc.equals(mccmncCode)) {
                                 mMncLength = 3;
-                                log("setting6 mMncLength=" + mMncLength);
+                                log("IMSI: setting1 mMncLength=" + mMncLength);
                                 break;
                             }
                         }
                     }
 
-                    if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
-                        if (mImsi != null) {
-                            try {
-                                int mcc = Integer.parseInt(mImsi.substring(0,3));
-
-                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
-                                log("setting7 mMncLength=" + mMncLength);
-                            } catch (NumberFormatException e) {
-                                mMncLength = UNKNOWN;
-                                loge("Corrupt IMSI! setting8 mMncLength=" + mMncLength);
-                            }
-                        } else {
-                            // Indicate we got this info, but it didn't contain the length.
+                    if (mMncLength == UNKNOWN) {
+                        // the SIM has told us all it knows, but it didn't know the mnc length.
+                        // guess using the mcc
+                        try {
+                            int mcc = Integer.parseInt(imsi.substring(0, 3));
+                            mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+                            log("setting2 mMncLength=" + mMncLength);
+                        } catch (NumberFormatException e) {
                             mMncLength = UNKNOWN;
-                            log("MNC length not present in EF_AD setting9 mMncLength=" + mMncLength);
+                            loge("Corrupt IMSI! setting3 mMncLength=" + mMncLength);
                         }
                     }
-                    if (mImsi != null && mMncLength != UNKNOWN) {
-                        // finally have both imsi and the length of the mnc and can parse
-                        // the imsi properly
-                        log("update mccmnc=" + mImsi.substring(0, 3 + mMncLength));
+
+                    if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED
+                            && imsi.length() >= 3 + mMncLength) {
+                        log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
+                        // finally have both the imsi and the mncLength and
+                        // can parse the imsi properly
                         MccTable.updateMccMncConfiguration(mContext,
-                                mImsi.substring(0, 3 + mMncLength), false);
+                                imsi.substring(0, 3 + mMncLength), false);
                     }
-                }
-            break;
-
-            case EVENT_GET_SPN_DONE:
-                isRecordLoadResponse = true;
-                ar = (AsyncResult) msg.obj;
-                getSpnFsm(false, ar);
-            break;
-
-            case EVENT_GET_CFF_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult) msg.obj;
-                data = (byte[]) ar.result;
-
-                if (ar.exception != null) {
-                    mEfCff = null;
-                } else {
-                    log("EF_CFF_CPHS: " + IccUtils.bytesToHexString(data));
-                    mEfCff = data;
-                }
-
-                break;
-
-            case EVENT_GET_SPDI_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
-
-                if (ar.exception != null) {
+                    mImsiReadyRegistrants.notifyRegistrants();
                     break;
-                }
 
-                parseEfSpdi(data);
-            break;
+                case EVENT_GET_MBI_DONE:
+                    boolean isValidMbdn;
+                    isRecordLoadResponse = true;
 
-            case EVENT_UPDATE_DONE:
-                ar = (AsyncResult)msg.obj;
-                if (ar.exception != null) {
-                    logw("update failed. ", ar.exception);
-                }
-            break;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
 
-            case EVENT_GET_PNN_DONE:
-                isRecordLoadResponse = true;
+                    isValidMbdn = false;
+                    if (ar.exception == null) {
+                        // Refer TS 51.011 Section 10.3.44 for content details
+                        log("EF_MBI: " + IccUtils.bytesToHexString(data));
 
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
+                        // Voice mail record number stored first
+                        mMailboxIndex = data[0] & 0xff;
 
-                if (ar.exception != null) {
+                        // check if dailing numbe id valid
+                        if (mMailboxIndex != 0 && mMailboxIndex != 0xff) {
+                            log("Got valid mailbox number for MBDN");
+                            isValidMbdn = true;
+                        }
+                    }
+
+                    // one more record to load
+                    mRecordsToLoad += 1;
+
+                    if (isValidMbdn) {
+                        // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
+                        new AdnRecordLoader(mFh).loadFromEF(EF_MBDN, EF_EXT6,
+                                mMailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
+                    } else {
+                        // If this EF not present, try mailbox as in CPHS standard
+                        // CPHS (CPHS4_2.WW6) is a european standard.
+                        new AdnRecordLoader(mFh).loadFromEF(EF_MAILBOX_CPHS,
+                                EF_EXT1, 1,
+                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
+                    }
+
                     break;
-                }
+                case EVENT_GET_CPHS_MAILBOX_DONE:
+                case EVENT_GET_MBDN_DONE:
+                    //Resetting the voice mail number and voice mail tag to null
+                    //as these should be updated from the data read from EF_MBDN.
+                    //If they are not reset, incase of invalid data/exception these
+                    //variables are retaining their previous values and are
+                    //causing invalid voice mailbox info display to user.
+                    mVoiceMailNum = null;
+                    mVoiceMailTag = null;
+                    isRecordLoadResponse = true;
 
-                SimTlv tlv = new SimTlv(data, 0, data.length);
+                    ar = (AsyncResult) msg.obj;
 
-                for ( ; tlv.isValidObject() ; tlv.nextObject()) {
-                    if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
-                        mPnnHomeName
-                            = IccUtils.networkNameToString(
-                                tlv.getData(), 0, tlv.getData().length);
+                    if (ar.exception != null) {
+
+                        log("Invalid or missing EF"
+                                + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE)
+                                    ? "[MAILBOX]" : "[MBDN]"));
+
+                        // Bug #645770 fall back to CPHS
+                        // FIXME should use SST to decide
+
+                        if (msg.what == EVENT_GET_MBDN_DONE) {
+                            //load CPHS on fail...
+                            // FIXME right now, only load line1's CPHS voice mail entry
+
+                            mRecordsToLoad += 1;
+                            new AdnRecordLoader(mFh).loadFromEF(
+                                    EF_MAILBOX_CPHS, EF_EXT1, 1,
+                                    obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
+                        }
                         break;
                     }
-                }
-            break;
 
-            case EVENT_GET_ALL_SMS_DONE:
-                isRecordLoadResponse = true;
+                    adn = (AdnRecord) ar.result;
 
-                ar = (AsyncResult)msg.obj;
-                if (ar.exception != null)
-                    break;
+                    log("VM: " + adn
+                            + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE)
+                                ? " EF[MAILBOX]" : " EF[MBDN]"));
 
-                handleSmses((ArrayList<byte []>) ar.result);
-                break;
+                    if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
+                        // Bug #645770 fall back to CPHS
+                        // FIXME should use SST to decide
+                        // FIXME right now, only load line1's CPHS voice mail entry
+                        mRecordsToLoad += 1;
+                        new AdnRecordLoader(mFh).loadFromEF(
+                                EF_MAILBOX_CPHS, EF_EXT1, 1,
+                                obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
 
-            case EVENT_MARK_SMS_READ_DONE:
-                Rlog.i("ENF", "marked read: sms " + msg.arg1);
-                break;
-
-
-            case EVENT_SMS_ON_SIM:
-                isRecordLoadResponse = false;
-
-                ar = (AsyncResult)msg.obj;
-
-                int[] index = (int[])ar.result;
-
-                if (ar.exception != null || index.length != 1) {
-                    loge("Error on SMS_ON_SIM with exp "
-                            + ar.exception + " length " + index.length);
-                } else {
-                    log("READ EF_SMS RECORD index=" + index[0]);
-                    mFh.loadEFLinearFixed(EF_SMS,index[0],
-                            obtainMessage(EVENT_GET_SMS_DONE));
-                }
-                break;
-
-            case EVENT_GET_SMS_DONE:
-                isRecordLoadResponse = false;
-                ar = (AsyncResult)msg.obj;
-                if (ar.exception == null) {
-                    handleSms((byte[])ar.result);
-                } else {
-                    loge("Error on GET_SMS with exp " + ar.exception);
-                }
-                break;
-            case EVENT_GET_SST_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
-
-                if (ar.exception != null) {
-                    break;
-                }
-
-                mUsimServiceTable = new UsimServiceTable(data);
-                if (DBG) log("SST: " + mUsimServiceTable);
-                break;
-
-            case EVENT_GET_INFO_CPHS_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-
-                if (ar.exception != null) {
-                    break;
-                }
-
-                mCphsInfo = (byte[])ar.result;
-
-                if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
-            break;
-
-            case EVENT_SET_MBDN_DONE:
-                isRecordLoadResponse = false;
-                ar = (AsyncResult)msg.obj;
-
-                if (DBG) log("EVENT_SET_MBDN_DONE ex:" + ar.exception);
-                if (ar.exception == null) {
-                    mVoiceMailNum = mNewVoiceMailNum;
-                    mVoiceMailTag = mNewVoiceMailTag;
-                }
-
-                if (isCphsMailboxEnabled()) {
-                    adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
-                    Message onCphsCompleted = (Message) ar.userObj;
-
-                    /* write to cphs mailbox whenever it is available but
-                    * we only need notify caller once if both updating are
-                    * successful.
-                    *
-                    * so if set_mbdn successful, notify caller here and set
-                    * onCphsCompleted to null
-                    */
-                    if (ar.exception == null && ar.userObj != null) {
-                        AsyncResult.forMessage(((Message) ar.userObj)).exception
-                                = null;
-                        ((Message) ar.userObj).sendToTarget();
-
-                        if (DBG) log("Callback with MBDN successful.");
-
-                        onCphsCompleted = null;
+                        break;
                     }
 
-                    new AdnRecordLoader(mFh).
-                            updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
-                            obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
-                                    onCphsCompleted));
-                } else {
+                    mVoiceMailNum = adn.getNumber();
+                    mVoiceMailTag = adn.getAlphaTag();
+                    break;
+
+                case EVENT_GET_MSISDN_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+
+                    if (ar.exception != null) {
+                        log("Invalid or missing EF[MSISDN]");
+                        break;
+                    }
+
+                    adn = (AdnRecord) ar.result;
+
+                    mMsisdn = adn.getNumber();
+                    mMsisdnTag = adn.getAlphaTag();
+
+                    log("MSISDN: " + /*mMsisdn*/ Rlog.pii(LOG_TAG, mMsisdn));
+                    break;
+
+                case EVENT_SET_MSISDN_DONE:
+                    isRecordLoadResponse = false;
+                    ar = (AsyncResult) msg.obj;
+
+                    if (ar.exception == null) {
+                        mMsisdn = mNewMsisdn;
+                        mMsisdnTag = mNewMsisdnTag;
+                        log("Success to update EF[MSISDN]");
+                    }
+
                     if (ar.userObj != null) {
-                        CarrierConfigManager configLoader = (CarrierConfigManager)
-                                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-                        if (ar.exception != null && configLoader != null &&
-                                configLoader.getConfig().getBoolean(
-                                CarrierConfigManager.KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL)) {
-                            // GsmCdmaPhone will store vm number on device
-                            // when IccVmNotSupportedException occurred
-                            AsyncResult.forMessage(((Message) ar.userObj)).exception
-                                = new IccVmNotSupportedException(
-                                        "Update SIM voice mailbox error");
-                        } else {
-                            AsyncResult.forMessage(((Message) ar.userObj)).exception
-                                = ar.exception;
-                        }
+                        AsyncResult.forMessage(((Message) ar.userObj)).exception = ar.exception;
                         ((Message) ar.userObj).sendToTarget();
                     }
-                }
-                break;
-            case EVENT_SET_CPHS_MAILBOX_DONE:
-                isRecordLoadResponse = false;
-                ar = (AsyncResult)msg.obj;
-                if(ar.exception == null) {
-                    mVoiceMailNum = mNewVoiceMailNum;
-                    mVoiceMailTag = mNewVoiceMailTag;
-                } else {
-                    if (DBG) log("Set CPHS MailBox with exception: "
-                            + ar.exception);
-                }
-                if (ar.userObj != null) {
-                    if (DBG) log("Callback with CPHS MB successful.");
-                    AsyncResult.forMessage(((Message) ar.userObj)).exception
-                            = ar.exception;
-                    ((Message) ar.userObj).sendToTarget();
-                }
-                break;
-            case EVENT_SIM_REFRESH:
-                isRecordLoadResponse = false;
-                ar = (AsyncResult)msg.obj;
-                if (DBG) log("Sim REFRESH with exception: " + ar.exception);
-                if (ar.exception == null) {
-                    handleSimRefresh((IccRefreshResponse)ar.result);
-                }
-                break;
-            case EVENT_GET_CFIS_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-                data = (byte[])ar.result;
-
-                if (ar.exception != null) {
-                    mEfCfis = null;
-                } else {
-                    log("EF_CFIS: " + IccUtils.bytesToHexString(data));
-                    mEfCfis = data;
-                }
-
-                break;
-
-            case EVENT_GET_CSP_CPHS_DONE:
-                isRecordLoadResponse = true;
-
-                ar = (AsyncResult)msg.obj;
-
-                if (ar.exception != null) {
-                    loge("Exception in fetching EF_CSP data " + ar.exception);
                     break;
-                }
 
-                data = (byte[])ar.result;
+                case EVENT_GET_MWIS_DONE:
+                    isRecordLoadResponse = true;
 
-                log("EF_CSP: " + IccUtils.bytesToHexString(data));
-                handleEfCspData(data);
-                break;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
 
-            case EVENT_GET_GID1_DONE:
-                isRecordLoadResponse = true;
+                    if (DBG) log("EF_MWIS : " + IccUtils.bytesToHexString(data));
 
-                ar = (AsyncResult)msg.obj;
-                data =(byte[])ar.result;
+                    if (ar.exception != null) {
+                        if (DBG) log("EVENT_GET_MWIS_DONE exception = " + ar.exception);
+                        break;
+                    }
 
-                if (ar.exception != null) {
-                    loge("Exception in get GID1 " + ar.exception);
-                    mGid1 = null;
+                    if ((data[0] & 0xff) == 0xff) {
+                        if (DBG) log("SIMRecords: Uninitialized record MWIS");
+                        break;
+                    }
+
+                    mEfMWIS = data;
                     break;
-                }
-                mGid1 = IccUtils.bytesToHexString(data);
-                log("GID1: " + mGid1);
 
-                break;
+                case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
+                    isRecordLoadResponse = true;
 
-            case EVENT_GET_GID2_DONE:
-                isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
 
-                ar = (AsyncResult)msg.obj;
-                data =(byte[])ar.result;
+                    if (DBG) log("EF_CPHS_MWI: " + IccUtils.bytesToHexString(data));
 
-                if (ar.exception != null) {
-                    loge("Exception in get GID2 " + ar.exception);
-                    mGid2 = null;
+                    if (ar.exception != null) {
+                        if (DBG) {
+                            log("EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE exception = "
+                                    + ar.exception);
+                        }
+                        break;
+                    }
+
+                    mEfCPHS_MWI = data;
                     break;
-                }
-                mGid2 = IccUtils.bytesToHexString(data);
-                log("GID2: " + mGid2);
 
-                break;
+                case EVENT_GET_ICCID_DONE:
+                    isRecordLoadResponse = true;
 
-            case EVENT_GET_PLMN_W_ACT_DONE:
-                isRecordLoadResponse = true;
-                ar = (AsyncResult) msg.obj;
-                data = (byte[]) ar.result;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
 
-                if (ar.exception != null || data == null) {
-                    loge("Failed getting User PLMN with Access Tech Records: " + ar.exception);
+                    if (ar.exception != null) {
+                        break;
+                    }
+
+                    mIccId = IccUtils.bcdToString(data, 0, data.length);
+                    mFullIccId = IccUtils.bchToString(data, 0, data.length);
+
+                    log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));
                     break;
-                } else {
-                    log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data));
-                    mPlmnActRecords = PlmnActRecord.getRecords(data);
-                    if (VDBG) log("PlmnActRecords=" + Arrays.toString(mPlmnActRecords));
-                }
-                break;
 
-            case EVENT_GET_OPLMN_W_ACT_DONE:
-                isRecordLoadResponse = true;
-                ar = (AsyncResult) msg.obj;
-                data = (byte[]) ar.result;
 
-                if (ar.exception != null || data == null) {
-                    loge("Failed getting Operator PLMN with Access Tech Records: "
-                            + ar.exception);
+                case EVENT_GET_AD_DONE:
+                    try {
+                        isRecordLoadResponse = true;
+
+                        if (mCarrierTestOverride.isInTestMode() && getIMSI() != null) {
+                            imsi = getIMSI();
+                            try {
+                                int mcc = Integer.parseInt(imsi.substring(0, 3));
+                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+                                log("[TestMode] mMncLength=" + mMncLength);
+                            } catch (NumberFormatException e) {
+                                mMncLength = UNKNOWN;
+                                loge("[TestMode] Corrupt IMSI! mMncLength=" + mMncLength);
+                            }
+                        } else {
+                            ar = (AsyncResult) msg.obj;
+                            data = (byte[]) ar.result;
+
+                            if (ar.exception != null) {
+                                break;
+                            }
+
+                            log("EF_AD: " + IccUtils.bytesToHexString(data));
+
+                            if (data.length < 3) {
+                                log("Corrupt AD data on SIM");
+                                break;
+                            }
+
+                            if (data.length == 3) {
+                                log("MNC length not present in EF_AD");
+                                break;
+                            }
+
+                            mMncLength = data[3] & 0xf;
+                            log("setting4 mMncLength=" + mMncLength);
+                        }
+
+                        if (mMncLength == 0xf) {
+                            mMncLength = UNKNOWN;
+                            log("setting5 mMncLength=" + mMncLength);
+                        } else if (mMncLength != 2 && mMncLength != 3) {
+                            mMncLength = UNINITIALIZED;
+                            log("setting5 mMncLength=" + mMncLength);
+                        }
+                    } finally {
+
+                        // IMSI could be a value reading from Sim or a fake IMSI if in the test mode
+                        imsi = getIMSI();
+
+                        if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN)
+                                    || (mMncLength == 2)) && ((imsi != null)
+                                    && (imsi.length() >= 6))) {
+                            String mccmncCode = imsi.substring(0, 6);
+                            log("mccmncCode=" + mccmncCode);
+                            for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
+                                if (mccmnc.equals(mccmncCode)) {
+                                    mMncLength = 3;
+                                    log("setting6 mMncLength=" + mMncLength);
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
+                            if (imsi != null) {
+                                try {
+                                    int mcc = Integer.parseInt(imsi.substring(0, 3));
+
+                                    mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+                                    log("setting7 mMncLength=" + mMncLength);
+                                } catch (NumberFormatException e) {
+                                    mMncLength = UNKNOWN;
+                                    loge("Corrupt IMSI! setting8 mMncLength=" + mMncLength);
+                                }
+                            } else {
+                                // Indicate we got this info, but it didn't contain the length.
+                                mMncLength = UNKNOWN;
+                                log("MNC length not present in EF_AD setting9 "
+                                        + "mMncLength=" + mMncLength);
+                            }
+                        }
+                        if (imsi != null && mMncLength != UNKNOWN
+                                && imsi.length() >= 3 + mMncLength) {
+                            // finally have both imsi and the length of the mnc and can parse
+                            // the imsi properly
+                            log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
+                            MccTable.updateMccMncConfiguration(mContext,
+                                    imsi.substring(0, 3 + mMncLength), false);
+                        }
+                    }
                     break;
-                } else {
-                    log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data));
-                    mOplmnActRecords = PlmnActRecord.getRecords(data);
-                    if (VDBG) log("OplmnActRecord[]=" + Arrays.toString(mOplmnActRecords));
-                }
-                break;
 
-            case EVENT_GET_HPLMN_W_ACT_DONE:
-                isRecordLoadResponse = true;
-                ar = (AsyncResult) msg.obj;
-                data = (byte[]) ar.result;
-
-                if (ar.exception != null || data == null) {
-                    loge("Failed getting Home PLMN with Access Tech Records: " + ar.exception);
+                case EVENT_GET_SPN_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    getSpnFsm(false, ar);
                     break;
-                } else {
-                    log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data));
-                    mHplmnActRecords = PlmnActRecord.getRecords(data);
-                    log("HplmnActRecord[]=" + Arrays.toString(mHplmnActRecords));
-                }
-                break;
 
-            case EVENT_GET_EHPLMN_DONE:
-                isRecordLoadResponse = true;
-                ar = (AsyncResult) msg.obj;
-                data = (byte[]) ar.result;
-                if (ar.exception != null || data == null) {
-                    loge("Failed getting Equivalent Home PLMNs: " + ar.exception);
+                case EVENT_GET_CFF_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null) {
+                        mEfCff = null;
+                    } else {
+                        log("EF_CFF_CPHS: " + IccUtils.bytesToHexString(data));
+                        mEfCff = data;
+                    }
+
                     break;
-                } else {
-                    mEhplmns = parseBcdPlmnList(data, "Equivalent Home");
-                }
-                break;
 
-            case EVENT_GET_FPLMN_DONE:
-                isRecordLoadResponse = true;
-                ar = (AsyncResult) msg.obj;
-                data = (byte[]) ar.result;
-                if (ar.exception != null || data == null) {
-                    loge("Failed getting Forbidden PLMNs: " + ar.exception);
+                case EVENT_GET_SPDI_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null) {
+                        break;
+                    }
+
+                    parseEfSpdi(data);
                     break;
-                } else {
-                    mFplmns = parseBcdPlmnList(data, "Forbidden");
-                }
-                break;
 
-            case EVENT_CARRIER_CONFIG_CHANGED:
-                handleCarrierNameOverride();
-                break;
+                case EVENT_UPDATE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        logw("update failed. ", ar.exception);
+                    }
+                    break;
 
-            default:
-                super.handleMessage(msg);   // IccRecords handles generic record load responses
+                case EVENT_GET_PNN_DONE:
+                    isRecordLoadResponse = true;
 
-        }}catch (RuntimeException exc) {
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null) {
+                        break;
+                    }
+
+                    SimTlv tlv = new SimTlv(data, 0, data.length);
+
+                    for (; tlv.isValidObject(); tlv.nextObject()) {
+                        if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
+                            mPnnHomeName = IccUtils.networkNameToString(
+                                    tlv.getData(), 0, tlv.getData().length);
+                            break;
+                        }
+                    }
+                    break;
+
+                case EVENT_GET_ALL_SMS_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception != null) {
+                        break;
+                    }
+
+                    handleSmses((ArrayList<byte []>) ar.result);
+                    break;
+
+                case EVENT_MARK_SMS_READ_DONE:
+                    Rlog.i("ENF", "marked read: sms " + msg.arg1);
+                    break;
+
+
+                case EVENT_SMS_ON_SIM:
+                    isRecordLoadResponse = false;
+
+                    ar = (AsyncResult) msg.obj;
+
+                    Integer index = (Integer) ar.result;
+
+                    if (ar.exception != null || index == null) {
+                        loge("Error on SMS_ON_SIM with exp "
+                                + ar.exception + " index " + index);
+                    } else {
+                        log("READ EF_SMS RECORD index=" + index);
+                        mFh.loadEFLinearFixed(EF_SMS, index, obtainMessage(EVENT_GET_SMS_DONE));
+                    }
+                    break;
+
+                case EVENT_GET_SMS_DONE:
+                    isRecordLoadResponse = false;
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception == null) {
+                        handleSms((byte[]) ar.result);
+                    } else {
+                        loge("Error on GET_SMS with exp " + ar.exception);
+                    }
+                    break;
+                case EVENT_GET_SST_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null) {
+                        break;
+                    }
+
+                    mUsimServiceTable = new UsimServiceTable(data);
+                    if (DBG) log("SST: " + mUsimServiceTable);
+                    break;
+
+                case EVENT_GET_INFO_CPHS_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+
+                    if (ar.exception != null) {
+                        break;
+                    }
+
+                    mCphsInfo = (byte[]) ar.result;
+
+                    if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
+                    break;
+
+                case EVENT_SET_MBDN_DONE:
+                    isRecordLoadResponse = false;
+                    ar = (AsyncResult) msg.obj;
+
+                    if (DBG) log("EVENT_SET_MBDN_DONE ex:" + ar.exception);
+                    if (ar.exception == null) {
+                        mVoiceMailNum = mNewVoiceMailNum;
+                        mVoiceMailTag = mNewVoiceMailTag;
+                    }
+
+                    if (isCphsMailboxEnabled()) {
+                        adn = new AdnRecord(mVoiceMailTag, mVoiceMailNum);
+                        Message onCphsCompleted = (Message) ar.userObj;
+
+                        /* write to cphs mailbox whenever it is available but
+                        * we only need notify caller once if both updating are
+                        * successful.
+                        *
+                        * so if set_mbdn successful, notify caller here and set
+                        * onCphsCompleted to null
+                        */
+                        if (ar.exception == null && ar.userObj != null) {
+                            AsyncResult.forMessage(((Message) ar.userObj)).exception = null;
+                            ((Message) ar.userObj).sendToTarget();
+
+                            if (DBG) log("Callback with MBDN successful.");
+
+                            onCphsCompleted = null;
+                        }
+
+                        new AdnRecordLoader(mFh)
+                                .updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
+                                obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
+                                        onCphsCompleted));
+                    } else {
+                        if (ar.userObj != null) {
+                            CarrierConfigManager configLoader = (CarrierConfigManager)
+                                    mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                            if (ar.exception != null && configLoader != null
+                                    && configLoader.getConfig().getBoolean(
+                                    CarrierConfigManager.KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL)) {
+                                // GsmCdmaPhone will store vm number on device
+                                // when IccVmNotSupportedException occurred
+                                AsyncResult.forMessage(((Message) ar.userObj)).exception =
+                                        new IccVmNotSupportedException(
+                                            "Update SIM voice mailbox error");
+                            } else {
+                                AsyncResult.forMessage(((Message) ar.userObj))
+                                    .exception = ar.exception;
+                            }
+                            ((Message) ar.userObj).sendToTarget();
+                        }
+                    }
+                    break;
+                case EVENT_SET_CPHS_MAILBOX_DONE:
+                    isRecordLoadResponse = false;
+                    ar = (AsyncResult) msg.obj;
+                    if (ar.exception == null) {
+                        mVoiceMailNum = mNewVoiceMailNum;
+                        mVoiceMailTag = mNewVoiceMailTag;
+                    } else {
+                        if (DBG) log("Set CPHS MailBox with exception: " + ar.exception);
+                    }
+                    if (ar.userObj != null) {
+                        if (DBG) log("Callback with CPHS MB successful.");
+                        AsyncResult.forMessage(((Message) ar.userObj)).exception
+                                = ar.exception;
+                        ((Message) ar.userObj).sendToTarget();
+                    }
+                    break;
+                case EVENT_SIM_REFRESH:
+                    isRecordLoadResponse = false;
+                    ar = (AsyncResult) msg.obj;
+                    if (DBG) log("Sim REFRESH with exception: " + ar.exception);
+                    if (ar.exception == null) {
+                        handleSimRefresh((IccRefreshResponse) ar.result);
+                    }
+                    break;
+                case EVENT_GET_CFIS_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null) {
+                        mEfCfis = null;
+                    } else {
+                        log("EF_CFIS: " + IccUtils.bytesToHexString(data));
+                        mEfCfis = data;
+                    }
+
+                    break;
+
+                case EVENT_GET_CSP_CPHS_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+
+                    if (ar.exception != null) {
+                        loge("Exception in fetching EF_CSP data " + ar.exception);
+                        break;
+                    }
+
+                    data = (byte[]) ar.result;
+
+                    log("EF_CSP: " + IccUtils.bytesToHexString(data));
+                    handleEfCspData(data);
+                    break;
+
+                case EVENT_GET_GID1_DONE:
+                    isRecordLoadResponse = true;
+
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null) {
+                        loge("Exception in get GID1 " + ar.exception);
+                        mGid1 = null;
+                        break;
+                    }
+
+                    mGid1 = IccUtils.bytesToHexString(data);
+
+                    log("GID1: " + mGid1);
+
+                    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;
+
+                case EVENT_GET_PLMN_W_ACT_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null || data == null) {
+                        loge("Failed getting User PLMN with Access Tech Records: " + ar.exception);
+                        break;
+                    } else {
+                        log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data));
+                        mPlmnActRecords = PlmnActRecord.getRecords(data);
+                        if (VDBG) log("PlmnActRecords=" + Arrays.toString(mPlmnActRecords));
+                    }
+                    break;
+
+                case EVENT_GET_OPLMN_W_ACT_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null || data == null) {
+                        loge("Failed getting Operator PLMN with Access Tech Records: "
+                                + ar.exception);
+                        break;
+                    } else {
+                        log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data));
+                        mOplmnActRecords = PlmnActRecord.getRecords(data);
+                        if (VDBG) log("OplmnActRecord[]=" + Arrays.toString(mOplmnActRecords));
+                    }
+                    break;
+
+                case EVENT_GET_HPLMN_W_ACT_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+
+                    if (ar.exception != null || data == null) {
+                        loge("Failed getting Home PLMN with Access Tech Records: " + ar.exception);
+                        break;
+                    } else {
+                        log("Received a PlmnActRecord, raw=" + IccUtils.bytesToHexString(data));
+                        mHplmnActRecords = PlmnActRecord.getRecords(data);
+                        log("HplmnActRecord[]=" + Arrays.toString(mHplmnActRecords));
+                    }
+                    break;
+
+                case EVENT_GET_EHPLMN_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+                    if (ar.exception != null || data == null) {
+                        loge("Failed getting Equivalent Home PLMNs: " + ar.exception);
+                        break;
+                    } else {
+                        mEhplmns = parseBcdPlmnList(data, "Equivalent Home");
+                    }
+                    break;
+
+                case EVENT_GET_FPLMN_DONE:
+                    isRecordLoadResponse = true;
+                    ar = (AsyncResult) msg.obj;
+                    data = (byte[]) ar.result;
+                    if (ar.exception != null || data == null) {
+                        loge("Failed getting Forbidden PLMNs: " + ar.exception);
+                        break;
+                    } else {
+                        mFplmns = parseBcdPlmnList(data, "Forbidden");
+                    }
+                    if (msg.arg1 == HANDLER_ACTION_SEND_RESPONSE) {
+                        if (VDBG) logv("getForbiddenPlmns(): send async response");
+                        isRecordLoadResponse = false;
+                        Message response = retrievePendingResponseMessage(msg.arg2);
+                        if (response != null) {
+                            AsyncResult.forMessage(
+                                    response, Arrays.copyOf(mFplmns, mFplmns.length), null);
+                            response.sendToTarget();
+                        } else {
+                            loge("Failed to retrieve a response message for FPLMN");
+                            break;
+                        }
+                    }
+                    break;
+
+                case EVENT_CARRIER_CONFIG_CHANGED:
+                    handleCarrierNameOverride();
+                    break;
+
+                default:
+                    super.handleMessage(msg);   // IccRecords handles generic record load responses
+            }
+        } catch (RuntimeException exc) {
             // I don't want these exceptions to be fatal
             logw("Exception parsing SIM record", exc);
         } finally {
@@ -1423,8 +1457,8 @@
             return;
         }
 
-        if (refreshResponse.aid != null &&
-                !refreshResponse.aid.equals(mParentApp.getAid())) {
+        if (!TextUtils.isEmpty(refreshResponse.aid)
+                && !refreshResponse.aid.equals(mParentApp.getAid())) {
             // This is for different app. Ignore.
             return;
         }
@@ -1573,20 +1607,19 @@
         if (!TextUtils.isEmpty(operator)) {
             log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
                     operator + "'");
-            log("update icc_operator_numeric=" + operator);
             mTelephonyManager.setSimOperatorNumericForPhone(
                     mParentApp.getPhoneId(), operator);
-            final SubscriptionController subController = SubscriptionController.getInstance();
-            subController.setMccMnc(operator, subController.getDefaultSubId());
         } else {
             log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
         }
 
-        if (!TextUtils.isEmpty(mImsi)) {
-            log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + mImsi) : ""));
+        String imsi = getIMSI();
+
+        if (!TextUtils.isEmpty(imsi) && imsi.length() >= 3) {
+            log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + imsi) : ""));
             mTelephonyManager.setSimCountryIsoForPhone(
                     mParentApp.getPhoneId(), MccTable.countryCodeForMcc(
-                    Integer.parseInt(mImsi.substring(0,3))));
+                    Integer.parseInt(imsi.substring(0, 3))));
         } else {
             log("onAllRecordsLoaded empty imsi skipping setting mcc");
         }
@@ -1631,6 +1664,16 @@
         }
     }
 
+    /**
+     * String[] of forbidden PLMNs will be sent to the Message's handler
+     * in the result field of an AsyncResult in the response.obj.
+     */
+    public void getForbiddenPlmns(Message response) {
+        int key = storePendingResponseMessage(response);
+        mFh.loadEFTransparent(EF_FPLMN, obtainMessage(
+                    EVENT_GET_FPLMN_DONE, HANDLER_ACTION_SEND_RESPONSE, key));
+    }
+
     @Override
     public void onReady() {
         fetchSimRecords();
@@ -1739,7 +1782,8 @@
         mFh.loadEFTransparent(EF_EHPLMN, obtainMessage(EVENT_GET_EHPLMN_DONE));
         mRecordsToLoad++;
 
-        mFh.loadEFTransparent(EF_FPLMN, obtainMessage(EVENT_GET_FPLMN_DONE));
+        mFh.loadEFTransparent(EF_FPLMN, obtainMessage(
+                    EVENT_GET_FPLMN_DONE, HANDLER_ACTION_NONE, -1));
         mRecordsToLoad++;
 
         loadEfLiAndEfPl();
@@ -1854,10 +1898,10 @@
         if (start) {
             // Check previous state to see if there is outstanding
             // SPN read
-            if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
-               mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
-               mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
-               mSpnState == GetSpnFsmState.INIT) {
+            if (mSpnState == GetSpnFsmState.READ_SPN_3GPP
+                    || mSpnState == GetSpnFsmState.READ_SPN_CPHS
+                    || mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS
+                    || mSpnState == GetSpnFsmState.INIT) {
                 // Set INIT then return so the INIT code
                 // will run when the outstanding read done.
                 mSpnState = GetSpnFsmState.INIT;
@@ -1883,7 +1927,7 @@
                     mSpnDisplayCondition = 0xff & data[0];
 
                     setServiceProviderName(IccUtils.adnStringFieldToString(
-                            data, 1, data.length - 1));
+                                data, 1, data.length - 1));
                     // for card double-check and brand override
                     // we have to do this:
                     final String spn = getServiceProviderName();
@@ -1917,7 +1961,7 @@
                     data = (byte[]) ar.result;
 
                     setServiceProviderName(IccUtils.adnStringFieldToString(
-                            data, 0, data.length));
+                                data, 0, data.length));
                     // for card double-check and brand override
                     // we have to do this:
                     final String spn = getServiceProviderName();
@@ -1949,7 +1993,7 @@
                     data = (byte[]) ar.result;
 
                     setServiceProviderName(IccUtils.adnStringFieldToString(
-                            data, 0, data.length));
+                                data, 0, data.length));
                     // for card double-check and brand override
                     // we have to do this:
                     final String spn = getServiceProviderName();
@@ -2007,10 +2051,10 @@
 
         for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
             String plmnCode;
-            plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
+            plmnCode = IccUtils.bcdPlmnToString(plmnEntries, i);
 
             // Valid operator codes are 5 or 6 digits
-            if (plmnCode.length() >= 5) {
+            if (plmnCode != null && plmnCode.length() >= 5) {
                 log("EF_SPDI network: " + plmnCode);
                 mSpdiNetworks.add(plmnCode);
             }
@@ -2132,7 +2176,13 @@
         pw.println(" mPnnHomeName=" + mPnnHomeName);
         pw.println(" mUsimServiceTable=" + mUsimServiceTable);
         pw.println(" mGid1=" + mGid1);
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeGid1=" + ((mFakeGid1 != null) ? mFakeGid1 : "null"));
+        }
         pw.println(" mGid2=" + mGid2);
+        if (mCarrierTestOverride.isInTestMode()) {
+            pw.println(" mFakeGid2=" + ((mFakeGid2 != null) ? mFakeGid2 : "null"));
+        }
         pw.println(" mPlmnActRecords[]=" + Arrays.toString(mPlmnActRecords));
         pw.println(" mOplmnActRecords[]=" + Arrays.toString(mOplmnActRecords));
         pw.println(" mHplmnActRecords[]=" + Arrays.toString(mHplmnActRecords));
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index 9181507..baad60b 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -44,18 +44,16 @@
 import android.util.LocalLog;
 import android.view.WindowManager;
 
+import com.android.internal.R;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.CommandsInterface.RadioState;
+import com.android.internal.telephony.cat.CatService;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccCardStatus.CardState;
 import com.android.internal.telephony.uicc.IccCardStatus.PinState;
-import com.android.internal.telephony.cat.CatService;
-
-import com.android.internal.R;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -96,27 +94,19 @@
     private static final int EVENT_TRANSMIT_APDU_LOGICAL_CHANNEL_DONE = 17;
     private static final int EVENT_TRANSMIT_APDU_BASIC_CHANNEL_DONE = 18;
     private static final int EVENT_SIM_IO_DONE = 19;
-    private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 20;
+    private static final int EVENT_CARRIER_PRIVILEGES_LOADED = 20;
 
     private static final LocalLog mLocalLog = new LocalLog(100);
 
-    private int mPhoneId;
-
-    public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
-        if (DBG) log("Creating");
-        mCardState = ics.mCardState;
-        update(c, ci, ics);
-    }
+    private final int mPhoneId;
 
     public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics, int phoneId) {
+        if (DBG) log("Creating");
         mCardState = ics.mCardState;
         mPhoneId = phoneId;
         update(c, ci, ics);
     }
 
-    protected UiccCard() {
-    }
-
     public void dispose() {
         synchronized (mLock) {
             if (DBG) log("Disposing card");
@@ -162,18 +152,19 @@
                 }
             }
 
-            createAndUpdateCatService();
+            createAndUpdateCatServiceLocked();
 
             // Reload the carrier privilege rules if necessary.
             log("Before privilege rules: " + mCarrierPrivilegeRules + " : " + mCardState);
             if (mCarrierPrivilegeRules == null && mCardState == CardState.CARDSTATE_PRESENT) {
                 mCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(this,
-                        mHandler.obtainMessage(EVENT_CARRIER_PRIVILIGES_LOADED));
-            } else if (mCarrierPrivilegeRules != null && mCardState != CardState.CARDSTATE_PRESENT) {
+                        mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
+            } else if (mCarrierPrivilegeRules != null
+                    && mCardState != CardState.CARDSTATE_PRESENT) {
                 mCarrierPrivilegeRules = null;
             }
 
-            sanitizeApplicationIndexes();
+            sanitizeApplicationIndexesLocked();
 
             RadioState radioState = mCi.getRadioState();
             if (DBG) log("update: radioState=" + radioState + " mLastRadioState="
@@ -195,13 +186,13 @@
         }
     }
 
-    protected void createAndUpdateCatService() {
+    private void createAndUpdateCatServiceLocked() {
         if (mUiccApplications.length > 0 && mUiccApplications[0] != null) {
             // Initialize or Reinitialize CatService
             if (mCatService == null) {
                 mCatService = CatService.getInstance(mCi, mContext, this, mPhoneId);
             } else {
-                ((CatService)mCatService).update(mCi, mContext, this);
+                mCatService.update(mCi, mContext, this);
             }
         } else {
             if (mCatService != null) {
@@ -211,10 +202,6 @@
         }
     }
 
-    public CatService getCatService() {
-        return mCatService;
-    }
-
     @Override
     protected void finalize() {
         if (DBG) log("UiccCard finalized");
@@ -225,16 +212,18 @@
      * and resets invalid indexes. (This should never happen, but in case
      * RIL misbehaves we need to manage situation gracefully)
      */
-    private void sanitizeApplicationIndexes() {
+    private void sanitizeApplicationIndexesLocked() {
         mGsmUmtsSubscriptionAppIndex =
-                checkIndex(mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
+                checkIndexLocked(
+                        mGsmUmtsSubscriptionAppIndex, AppType.APPTYPE_SIM, AppType.APPTYPE_USIM);
         mCdmaSubscriptionAppIndex =
-                checkIndex(mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
+                checkIndexLocked(
+                        mCdmaSubscriptionAppIndex, AppType.APPTYPE_RUIM, AppType.APPTYPE_CSIM);
         mImsSubscriptionAppIndex =
-                checkIndex(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
+                checkIndexLocked(mImsSubscriptionAppIndex, AppType.APPTYPE_ISIM, null);
     }
 
-    private int checkIndex(int index, AppType expectedAppType, AppType altExpectedAppType) {
+    private int checkIndexLocked(int index, AppType expectedAppType, AppType altExpectedAppType) {
         if (mUiccApplications == null || index >= mUiccApplications.length) {
             loge("App index " + index + " is invalid since there are no applications");
             return -1;
@@ -395,7 +384,7 @@
                     AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
                     ((Message)ar.userObj).sendToTarget();
                     break;
-                case EVENT_CARRIER_PRIVILIGES_LOADED:
+                case EVENT_CARRIER_PRIVILEGES_LOADED:
                     onCarrierPriviligesLoadedMessage();
                     break;
                 default:
@@ -562,32 +551,41 @@
         synchronized (mLock) {
             boolean changed = false;
             for (int i = 0; i < mUiccApplications.length; i++) {
-                if (mUiccApplications[i] != null &&
-                    (aid == null || aid.equals(mUiccApplications[i].getAid()))) {
+                if (mUiccApplications[i] != null
+                        && (TextUtils.isEmpty(aid) || aid.equals(mUiccApplications[i].getAid()))) {
                     // Delete removed applications
                     mUiccApplications[i].dispose();
                     mUiccApplications[i] = null;
                     changed = true;
                 }
             }
+            if (TextUtils.isEmpty(aid)) {
+                if (mCarrierPrivilegeRules != null) {
+                    mCarrierPrivilegeRules = null;
+                    changed = true;
+                }
+                if (mCatService != null) {
+                    mCatService.dispose();
+                    mCatService = null;
+                    changed = true;
+                }
+            }
             return changed;
         }
-        // TODO: For a card level notification, we should delete the CarrierPrivilegeRules and the
-        // CAT service.
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccOpenLogicalChannel}
+     * Exposes {@link CommandsInterface#iccOpenLogicalChannel}
      */
-    public void iccOpenLogicalChannel(String AID, Message response) {
-        loglocal("Open Logical Channel: " + AID + " by pid:" + Binder.getCallingPid()
+    public void iccOpenLogicalChannel(String AID, int p2, Message response) {
+        loglocal("Open Logical Channel: " + AID + " , " + p2 + " by pid:" + Binder.getCallingPid()
                 + " uid:" + Binder.getCallingUid());
-        mCi.iccOpenLogicalChannel(AID,
+        mCi.iccOpenLogicalChannel(AID, p2,
                 mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccCloseLogicalChannel}
+     * Exposes {@link CommandsInterface#iccCloseLogicalChannel}
      */
     public void iccCloseLogicalChannel(int channel, Message response) {
         loglocal("Close Logical Channel: " + channel);
@@ -596,7 +594,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccTransmitApduLogicalChannel}
+     * Exposes {@link CommandsInterface#iccTransmitApduLogicalChannel}
      */
     public void iccTransmitApduLogicalChannel(int channel, int cla, int command,
             int p1, int p2, int p3, String data, Message response) {
@@ -605,7 +603,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccTransmitApduBasicChannel}
+     * Exposes {@link CommandsInterface#iccTransmitApduBasicChannel}
      */
     public void iccTransmitApduBasicChannel(int cla, int command,
             int p1, int p2, int p3, String data, Message response) {
@@ -614,7 +612,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.iccIO}
+     * Exposes {@link CommandsInterface#iccIO}
      */
     public void iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
             String pathID, Message response) {
@@ -623,7 +621,7 @@
     }
 
     /**
-     * Exposes {@link CommandsInterface.sendEnvelopeWithStatus}
+     * Exposes {@link CommandsInterface#sendEnvelopeWithStatus}
      */
     public void sendEnvelopeWithStatus(String contents, Message response) {
         mCi.sendEnvelopeWithStatus(contents, response);
@@ -648,62 +646,76 @@
      * Returns true iff carrier privileges rules are null (dont need to be loaded) or loaded.
      */
     public boolean areCarrierPriviligeRulesLoaded() {
-        return mCarrierPrivilegeRules == null
-            || mCarrierPrivilegeRules.areCarrierPriviligeRulesLoaded();
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                || carrierPrivilegeRules.areCarrierPriviligeRulesLoaded();
     }
 
     /**
      * Returns true if there are some carrier privilege rules loaded and specified.
      */
     public boolean hasCarrierPrivilegeRules() {
-        return mCarrierPrivilegeRules != null
-                && mCarrierPrivilegeRules.hasCarrierPrivilegeRules();
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules != null && carrierPrivilegeRules.hasCarrierPrivilegeRules();
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
      */
     public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatus(signature, packageName);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
      */
     public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatus(packageManager, packageName);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatus}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatus}.
      */
     public int getCarrierPrivilegeStatus(PackageInfo packageInfo) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatus(packageInfo);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatus(packageInfo);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPrivilegeStatusForCurrentTransaction}.
      */
     public int getCarrierPrivilegeStatusForCurrentTransaction(PackageManager packageManager) {
-        return mCarrierPrivilegeRules == null ?
-            TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
-            mCarrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(packageManager);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null
+                ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED :
+                carrierPrivilegeRules.getCarrierPrivilegeStatusForCurrentTransaction(
+                        packageManager);
     }
 
     /**
-     * Exposes {@link UiccCarrierPrivilegeRules.getCarrierPackageNamesForIntent}.
+     * Exposes {@link UiccCarrierPrivilegeRules#getCarrierPackageNamesForIntent}.
      */
     public List<String> getCarrierPackageNamesForIntent(
             PackageManager packageManager, Intent intent) {
-        return mCarrierPrivilegeRules == null ? null :
-            mCarrierPrivilegeRules.getCarrierPackageNamesForIntent(
-                    packageManager, intent);
+        UiccCarrierPrivilegeRules carrierPrivilegeRules = getCarrierPrivilegeRules();
+        return carrierPrivilegeRules == null ? null :
+                carrierPrivilegeRules.getCarrierPackageNamesForIntent(
+                        packageManager, intent);
+    }
+
+    /** Returns a reference to the current {@link UiccCarrierPrivilegeRules}. */
+    private UiccCarrierPrivilegeRules getCarrierPrivilegeRules() {
+        synchronized (mLock) {
+            return mCarrierPrivilegeRules;
+        }
     }
 
     public boolean setOperatorBrandOverride(String brand) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 3d6de5f..68b72c8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -31,20 +31,11 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandException;
-import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.uicc.IccUtils;
 
-import java.io.ByteArrayInputStream;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.lang.IllegalArgumentException;
-import java.lang.IndexOutOfBoundsException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -65,7 +56,10 @@
     private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
     private static final boolean DBG = false;
 
-    private static final String AID = "A00000015141434C00";
+    private static final String ARAM_AID = "A00000015141434C00";
+    private static final String ARAD_AID = "A00000015144414300";
+    private static final int ARAM = 1;
+    private static final int ARAD = 0;
     private static final int CLA = 0x80;
     private static final int COMMAND = 0xCA;
     private static final int P1 = 0xFF;
@@ -103,6 +97,8 @@
     private static final String TAG_PKG_REF_DO = "CA";
     private static final String TAG_AR_DO = "E3";
     private static final String TAG_PERM_AR_DO = "DB";
+    private static final String TAG_AID_REF_DO = "4F";
+    private static final String CARRIER_PRIVILEGE_AID = "FFFFFFFFFFFF";
 
     private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
     private static final int EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE = 2;
@@ -217,17 +213,21 @@
     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 boolean mCheckedRules = false;  // Flag that used to mark whether get rules from ARA-D.
+    private int mAIDInUse;  // Message component to identify which AID is currently in-use.
     private final Runnable mRetryRunnable = new Runnable() {
         @Override
         public void run() {
-            openChannel();
+            openChannel(mAIDInUse);
         }
     };
 
-    private void openChannel() {
+    private void openChannel(int aidId) {
         // Send open logical channel request.
-        mUiccCard.iccOpenLogicalChannel(AID,
-            obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+        String aid = (aidId == ARAD) ? ARAD_AID : ARAM_AID;
+        int p2 = 0x00;
+        mUiccCard.iccOpenLogicalChannel(aid, p2, /* supported p2 value */
+                obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, 0, aidId, null));
     }
 
     public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
@@ -239,7 +239,9 @@
         mRules = "";
         mAccessRules = new ArrayList<AccessRule>();
 
-        openChannel();
+        // Open logical channel with ARA_D.
+        mAIDInUse = ARAD;
+        openChannel(mAIDInUse);
     }
 
     /**
@@ -418,91 +420,120 @@
     @Override
     public void handleMessage(Message msg) {
         AsyncResult ar;
+        mAIDInUse = msg.arg2;  // 0 means ARA-D and 1 means ARA-M.
 
         switch (msg.what) {
 
-          case 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 {
-                  // 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 {
-                      // if rules cannot be read from ARA applet,
-                      // fallback to PKCS15-based ARF.
-                      log("No ARA, try ARF next.");
-                      mUiccPkcs15 = new UiccPkcs15(mUiccCard,
-                              obtainMessage(EVENT_PKCS15_READ_DONE));
-                  }
-              }
-              break;
+            case 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, mChannelId,
+                                    mAIDInUse));
+                } else {
+                    // 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 {
+                        if (mAIDInUse == ARAD) {
+                            // Open logical channel with ARA_M.
+                            mRules = "";
+                            openChannel(1);
+                        }
+                        if (mAIDInUse == ARAM) {
+                            if (mCheckedRules) {
+                                updateState(STATE_LOADED, "Success!");
+                            } else {
+                                // if rules cannot be read from both ARA_D and ARA_M applet,
+                                // fallback to PKCS15-based ARF.
+                                log("No ARA, try ARF next.");
+                                mUiccPkcs15 = new UiccPkcs15(mUiccCard,
+                                        obtainMessage(EVENT_PKCS15_READ_DONE));
+                            }
+                        }
+                    }
+                }
+                break;
 
-          case 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;
-                  if (response.sw1 == 0x90 && response.sw2 == 0x00 &&
-                      response.payload != null && response.payload.length > 0) {
-                      try {
-                          mRules += IccUtils.bytesToHexString(response.payload).toUpperCase(Locale.US);
-                          if (isDataComplete()) {
-                              mAccessRules = parseRules(mRules);
-                              updateState(STATE_LOADED, "Success!");
-                          } else {
-                              mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2_EXTENDED_DATA, P3, DATA,
-                                  obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
-                              break;
-                          }
-                      } catch (IllegalArgumentException ex) {
-                          updateState(STATE_ERROR, "Error parsing rules: " + ex);
-                      } catch (IndexOutOfBoundsException ex) {
-                          updateState(STATE_ERROR, "Error parsing rules: " + ex);
-                      }
-                   } else {
-                      String errorMsg = "Invalid response: payload=" + response.payload +
-                              " sw1=" + response.sw1 + " sw2=" + response.sw2;
-                      updateState(STATE_ERROR, errorMsg);
-                   }
-              } else {
-                  updateState(STATE_ERROR, "Error reading value from SIM.");
-              }
+            case 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;
+                    if (response.sw1 == 0x90 && response.sw2 == 0x00
+                            && response.payload != null && response.payload.length > 0) {
+                        try {
+                            mRules += IccUtils.bytesToHexString(response.payload)
+                                    .toUpperCase(Locale.US);
+                            if (isDataComplete()) {
+                                mAccessRules.addAll(parseRules(mRules));
+                                if (mAIDInUse == ARAD) {
+                                    mCheckedRules = true;
+                                } else {
+                                    updateState(STATE_LOADED, "Success!");
+                                }
+                            } else {
+                                mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND,
+                                        P1, P2_EXTENDED_DATA, P3, DATA,
+                                        obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE,
+                                                mChannelId, mAIDInUse));
+                                break;
+                            }
+                        } catch (IllegalArgumentException | IndexOutOfBoundsException ex) {
+                            if (mAIDInUse == ARAM) {
+                                updateState(STATE_ERROR, "Error parsing rules: " + ex);
+                            }
+                        }
+                    } else {
+                        if (mAIDInUse == ARAM) {
+                            String errorMsg = "Invalid response: payload=" + response.payload
+                                    + " sw1=" + response.sw1 + " sw2=" + response.sw2;
+                            updateState(STATE_ERROR, errorMsg);
+                        }
+                    }
+                } else {
+                    if (mAIDInUse == ARAM) {
+                        updateState(STATE_ERROR, "Error reading value from SIM.");
+                    }
+                }
 
-              mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
-                      EVENT_CLOSE_LOGICAL_CHANNEL_DONE));
-              mChannelId = -1;
-              break;
+                mUiccCard.iccCloseLogicalChannel(mChannelId, obtainMessage(
+                        EVENT_CLOSE_LOGICAL_CHANNEL_DONE, 0, mAIDInUse));
+                mChannelId = -1;
+                break;
 
-          case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
-              log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
-              break;
+            case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
+                log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
+                if (mAIDInUse == ARAD) {
+                    // Close logical channel with ARA_D and then open logical channel with ARA_M.
+                    mRules = "";
+                    openChannel(1);
+                }
+                break;
 
-          case EVENT_PKCS15_READ_DONE:
-              log("EVENT_PKCS15_READ_DONE");
-              if (mUiccPkcs15 == null || mUiccPkcs15.getRules() == null) {
-                  updateState(STATE_ERROR, "No ARA or ARF.");
-              } else {
-                  for (String cert : mUiccPkcs15.getRules()) {
-                      AccessRule accessRule = new AccessRule(
-                              IccUtils.hexStringToBytes(cert), "", 0x00);
-                      mAccessRules.add(accessRule);
-                  }
-                  updateState(STATE_LOADED, "Success!");
-              }
-              break;
+            case EVENT_PKCS15_READ_DONE:
+                log("EVENT_PKCS15_READ_DONE");
+                if (mUiccPkcs15 == null || mUiccPkcs15.getRules() == null) {
+                    updateState(STATE_ERROR, "No ARA or ARF.");
+                } else {
+                    for (String cert : mUiccPkcs15.getRules()) {
+                        AccessRule accessRule = new AccessRule(
+                                IccUtils.hexStringToBytes(cert), "", 0x00);
+                        mAccessRules.add(accessRule);
+                    }
+                    updateState(STATE_LOADED, "Success!");
+                }
+                break;
 
-          default:
-              Rlog.e(LOG_TAG, "Unknown event " + msg.what);
+            default:
+                Rlog.e(LOG_TAG, "Unknown event " + msg.what);
         }
     }
 
@@ -517,7 +548,7 @@
             String lengthBytes = allRules.parseLength(mRules);
             log("isDataComplete lengthBytes: " + lengthBytes);
             if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() +
-                                   allRules.length) {
+                    allRules.length) {
                 log("isDataComplete yes");
                 return true;
             } else {
@@ -547,7 +578,7 @@
             if (accessRule != null) {
                 accessRules.add(accessRule);
             } else {
-              Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
+                Rlog.e(LOG_TAG, "Skip unrecognized rule." + refArDo.value);
             }
         }
         return accessRules;
@@ -568,37 +599,52 @@
             if (rule.startsWith(TAG_REF_DO)) {
                 TLV refDo = new TLV(TAG_REF_DO); //E1
                 rule = refDo.parse(rule, false);
-
-                // Skip unrelated rules.
-                if (!refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                // Allow 4F tag with a default value "FF FF FF FF FF FF" to be compatible with
+                // devices having GP access control enforcer:
+                //  - If no 4F tag is present, it's a CP rule.
+                //  - If 4F tag has value "FF FF FF FF FF FF", it's a CP rule.
+                //  - If 4F tag has other values, it's not a CP rule and Android should ignore it.
+                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
+                if (refDo.value.startsWith(TAG_AID_REF_DO)) {
+                    TLV cpDo = new TLV(TAG_AID_REF_DO); //4F
+                    String remain = cpDo.parse(refDo.value, false);
+                    if (!cpDo.lengthBytes.equals("06") || !cpDo.value.equals(CARRIER_PRIVILEGE_AID)
+                            || remain.isEmpty() || !remain.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                        return null;
+                    }
+                    tmp = deviceDo.parse(remain, false);
+                    certificateHash = deviceDo.value;
+                } else if (refDo.value.startsWith(TAG_DEVICE_APP_ID_REF_DO)) {
+                    tmp = deviceDo.parse(refDo.value, false);
+                    certificateHash = deviceDo.value;
+                } else {
                     return null;
                 }
-
-                TLV deviceDo = new TLV(TAG_DEVICE_APP_ID_REF_DO); //C1
-                tmp = deviceDo.parse(refDo.value, false);
-                certificateHash = deviceDo.value;
-
                 if (!tmp.isEmpty()) {
-                  if (!tmp.startsWith(TAG_PKG_REF_DO)) {
-                      return null;
-                  }
-                  TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
-                  pkgDo.parse(tmp, true);
-                  packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
+                    if (!tmp.startsWith(TAG_PKG_REF_DO)) {
+                        return null;
+                    }
+                    TLV pkgDo = new TLV(TAG_PKG_REF_DO); //CA
+                    pkgDo.parse(tmp, true);
+                    packageName = new String(IccUtils.hexStringToBytes(pkgDo.value));
                 } else {
-                  packageName = null;
+                    packageName = null;
                 }
             } else if (rule.startsWith(TAG_AR_DO)) {
                 TLV arDo = new TLV(TAG_AR_DO); //E3
                 rule = arDo.parse(rule, false);
-
-                // Skip unrelated rules.
-                if (!arDo.value.startsWith(TAG_PERM_AR_DO)) {
+                // Skip all the irrelevant tags (All the optional tags here are two bytes
+                // according to the spec GlobalPlatform Secure Element Access Control).
+                String remain = arDo.value;
+                while (!remain.isEmpty() && !remain.startsWith(TAG_PERM_AR_DO)) {
+                    TLV tmpDo = new TLV(remain.substring(0, 2));
+                    remain = tmpDo.parse(remain, false);
+                }
+                if (remain.isEmpty()) {
                     return null;
                 }
-
                 TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
-                permDo.parse(arDo.value, true);
+                permDo.parse(remain, true);
             } else  {
                 // Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
                 throw new RuntimeException("Invalid Rule type");
@@ -667,15 +713,15 @@
      * Converts state into human readable format.
      */
     private String getStateString(int state) {
-      switch (state) {
-        case STATE_LOADING:
-            return "STATE_LOADING";
-        case STATE_LOADED:
-            return "STATE_LOADED";
-        case STATE_ERROR:
-            return "STATE_ERROR";
-        default:
-            return "UNKNOWN";
-      }
+        switch (state) {
+            case STATE_LOADING:
+                return "STATE_LOADING";
+            case STATE_LOADED:
+                return "STATE_LOADED";
+            case STATE_ERROR:
+                return "STATE_ERROR";
+            default:
+                return "UNKNOWN";
+        }
     }
-}
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 65aacd1..a948b75 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -89,8 +89,6 @@
     private static final int EVENT_RADIO_UNAVAILABLE = 3;
     private static final int EVENT_SIM_REFRESH = 4;
 
-    private static final String DECRYPT_STATE = "trigger_restart_framework";
-
     private CommandsInterface[] mCis;
     private UiccCard[] mUiccCards = new UiccCard[TelephonyManager.getDefault().getPhoneCount()];
 
@@ -125,12 +123,11 @@
             Integer index = new Integer(i);
             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
             // TODO remove this once modem correctly notifies the unsols
-            // If the device has been decrypted or FBE is supported, read SIM when radio state is
-            // available.
+            // If the device is unencrypted or has been decrypted or FBE is supported,
+            // i.e. not in cryptkeeper bounce, read SIM when radio state isavailable.
             // Else wait for radio to be on. This is needed for the scenario when SIM is locked --
             // to avoid overlap of CryptKeeper and SIM unlock screen.
-            if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt")) ||
-                    StorageManager.isFileEncryptedNativeOrEmulated()) {
+            if (!StorageManager.inCryptKeeperBounce()) {
                 mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
             } else {
                 mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);
@@ -342,7 +339,7 @@
             if (requirePowerOffOnSimRefreshReset) {
                 mCis[index].setRadioPower(false, null);
             } else {
-                mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
+                mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index));
             }
             mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null));
         }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
index ca7bb9d..80ebcbf 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccPkcs15.java
@@ -144,7 +144,9 @@
 
         public Pkcs15Selector(Message callBack) {
             mCallback = callBack;
-            mUiccCard.iccOpenLogicalChannel(PKCS15_AID,
+            // Specified in ISO 7816-4 clause 7.1.1 0x04 means that FCP template is requested.
+            int p2 = 0x04;
+            mUiccCard.iccOpenLogicalChannel(PKCS15_AID, p2, /* supported P2 value */
                     obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE));
         }
 
diff --git a/src/java/com/android/internal/telephony/util/NotificationChannelController.java b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
new file mode 100644
index 0000000..09519c4
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.util;
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioAttributes;
+import android.net.Uri;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+
+import com.android.internal.R;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+public class NotificationChannelController {
+
+    /**
+     * list of {@link android.app.NotificationChannel} for telephony service.
+     */
+    public static final String CHANNEL_ID_ALERT = "alert";
+    public static final String CHANNEL_ID_CALL_FORWARD = "callForward";
+    public static final String CHANNEL_ID_MOBILE_DATA_ALERT = "mobileDataAlert";
+    public static final String CHANNEL_ID_SMS = "sms";
+    public static final String CHANNEL_ID_VOICE_MAIL = "voiceMail";
+    public static final String CHANNEL_ID_WFC = "wfc";
+
+    /**
+     * Creates all notification channels and registers with NotificationManager. If a channel
+     * with the same ID is already registered, NotificationManager will ignore this call.
+     */
+    private static void createAll(Context context) {
+        final NotificationChannel alertChannel = new NotificationChannel(
+                CHANNEL_ID_ALERT,
+                context.getText(R.string.notification_channel_network_alert),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        alertChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+
+        context.getSystemService(NotificationManager.class)
+                .createNotificationChannels(Arrays.asList(
+                new NotificationChannel(CHANNEL_ID_CALL_FORWARD,
+                        context.getText(R.string.notification_channel_call_forward),
+                        NotificationManager.IMPORTANCE_LOW),
+                new NotificationChannel(CHANNEL_ID_MOBILE_DATA_ALERT,
+                        context.getText(R.string.notification_channel_mobile_data_alert),
+                        NotificationManager.IMPORTANCE_DEFAULT),
+                new NotificationChannel(CHANNEL_ID_SMS,
+                        context.getText(R.string.notification_channel_sms),
+                        NotificationManager.IMPORTANCE_HIGH),
+                new NotificationChannel(CHANNEL_ID_WFC,
+                        context.getText(R.string.notification_channel_wfc),
+                        NotificationManager.IMPORTANCE_LOW),
+                alertChannel));
+        // only for update
+        if (getChannel(CHANNEL_ID_VOICE_MAIL, context) != null) {
+            migrateVoicemailNotificationSettings(context);
+        }
+    }
+
+    public NotificationChannelController(Context context) {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_LOCALE_CHANGED);
+        intentFilter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
+        context.registerReceiver(mBroadcastReceiver, intentFilter);
+        createAll(context);
+    }
+
+    public static NotificationChannel getChannel(String channelId, Context context) {
+        return context.getSystemService(NotificationManager.class)
+                .getNotificationChannel(channelId);
+    }
+
+    /**
+     * migrate deprecated voicemail notification settings to initial notification channel settings
+     * {@link VoicemailNotificationSettingsUtil#getRingTonePreference(Context)}}
+     * {@link VoicemailNotificationSettingsUtil#getVibrationPreference(Context)}
+     * notification settings are based on subId, only migrate if sub id matches.
+     * otherwise fallback to predefined voicemail channel settings.
+     * @param context
+     */
+    private static void migrateVoicemailNotificationSettings(Context context) {
+        final NotificationChannel voiceMailChannel = new NotificationChannel(
+                CHANNEL_ID_VOICE_MAIL,
+                context.getText(R.string.notification_channel_voice_mail),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        voiceMailChannel.enableVibration(
+                VoicemailNotificationSettingsUtil.getVibrationPreference(context));
+        Uri sound = VoicemailNotificationSettingsUtil.getRingTonePreference(context);
+        voiceMailChannel.setSound(
+                (sound == null) ? Settings.System.DEFAULT_NOTIFICATION_URI : sound,
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+        context.getSystemService(NotificationManager.class)
+                .createNotificationChannel(voiceMailChannel);
+    }
+
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+                // rename all notification channels on locale change
+                createAll(context);
+            } else if (Intent.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) {
+                // migrate voicemail notification settings on sim load
+                if (SubscriptionManager.INVALID_SUBSCRIPTION_ID !=
+                        SubscriptionManager.getDefaultSubscriptionId()) {
+                    migrateVoicemailNotificationSettings(context);
+                }
+            }
+        }
+    };
+}
diff --git a/src/java/com/android/internal/telephony/util/VoicemailNotificationSettingsUtil.java b/src/java/com/android/internal/telephony/util/VoicemailNotificationSettingsUtil.java
new file mode 100644
index 0000000..d8988e3
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/VoicemailNotificationSettingsUtil.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.util;
+
+import android.app.NotificationChannel;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+public class VoicemailNotificationSettingsUtil {
+    private static final String VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY_PREFIX =
+            "voicemail_notification_ringtone_";
+    private static final String VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY_PREFIX =
+            "voicemail_notification_vibrate_";
+
+    // Old voicemail notification vibration string constants used for migration.
+    private static final String OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY =
+            "button_voicemail_notification_ringtone_key";
+    private static final String OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY =
+            "button_voicemail_notification_vibrate_key";
+    private static final String OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY =
+            "button_voicemail_notification_vibrate_when_key";
+    private static final String OLD_VOICEMAIL_RINGTONE_SHARED_PREFS_KEY =
+            "button_voicemail_notification_ringtone_key";
+    private static final String OLD_VOICEMAIL_VIBRATION_ALWAYS = "always";
+    private static final String OLD_VOICEMAIL_VIBRATION_NEVER = "never";
+
+    public static void setVibrationEnabled(Context context, boolean isEnabled) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putBoolean(getVoicemailVibrationSharedPrefsKey(), isEnabled);
+        editor.commit();
+    }
+
+    public static boolean isVibrationEnabled(Context context) {
+        final NotificationChannel channel = NotificationChannelController.getChannel(
+                NotificationChannelController.CHANNEL_ID_VOICE_MAIL, context);
+        return (channel != null) ? channel.shouldVibrate() : getVibrationPreference(context);
+    }
+
+    public static boolean getVibrationPreference(Context context) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        migrateVoicemailVibrationSettingsIfNeeded(context, prefs);
+        return prefs.getBoolean(getVoicemailVibrationSharedPrefsKey(), false /* defValue */);
+    }
+
+   public static void setRingtoneUri(Context context, Uri ringtoneUri) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        String ringtoneUriStr = ringtoneUri != null ? ringtoneUri.toString() : "";
+
+        SharedPreferences.Editor editor = prefs.edit();
+        editor.putString(getVoicemailRingtoneSharedPrefsKey(), ringtoneUriStr);
+        editor.commit();
+    }
+
+    public static Uri getRingtoneUri(Context context) {
+        final NotificationChannel channel = NotificationChannelController.getChannel(
+                NotificationChannelController.CHANNEL_ID_VOICE_MAIL, context);
+        return (channel != null) ? channel.getSound() : getRingTonePreference(context);
+    }
+
+    public static Uri getRingTonePreference(Context context) {
+        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        migrateVoicemailRingtoneSettingsIfNeeded(context, prefs);
+        String uriString = prefs.getString(
+                getVoicemailRingtoneSharedPrefsKey(),
+                Settings.System.DEFAULT_NOTIFICATION_URI.toString());
+        return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
+    }
+
+    /**
+     * Migrate voicemail settings from {@link #OLD_VIBRATE_WHEN_KEY} or
+     * {@link #OLD_VOICEMAIL_NOTIFICATION_VIBRATE_KEY}.
+     *
+     * TODO: Add helper which migrates settings from old version to new version.
+     */
+    private static void migrateVoicemailVibrationSettingsIfNeeded(
+            Context context, SharedPreferences prefs) {
+        String key = getVoicemailVibrationSharedPrefsKey();
+        TelephonyManager telephonyManager = TelephonyManager.from(context);
+
+        // Skip if a preference exists, or if phone is MSIM.
+        if (prefs.contains(key) || telephonyManager.getPhoneCount() != 1) {
+            return;
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)) {
+            boolean voicemailVibrate = prefs.getBoolean(
+                    OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY, false /* defValue */);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putBoolean(key, voicemailVibrate)
+                    .remove(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)
+                    .commit();
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY)) {
+            // If vibrateWhen is always, then voicemailVibrate should be true.
+            // If it is "only in silent mode", or "never", then voicemailVibrate should be false.
+            String vibrateWhen = prefs.getString(
+                    OLD_VOICEMAIL_VIBRATE_WHEN_SHARED_PREFS_KEY, OLD_VOICEMAIL_VIBRATION_NEVER);
+            boolean voicemailVibrate = vibrateWhen.equals(OLD_VOICEMAIL_VIBRATION_ALWAYS);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putBoolean(key, voicemailVibrate)
+                    .remove(OLD_VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY)
+                    .commit();
+        }
+    }
+
+    /**
+     * Migrate voicemail settings from OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY.
+     *
+     * TODO: Add helper which migrates settings from old version to new version.
+     */
+    private static void migrateVoicemailRingtoneSettingsIfNeeded(
+            Context context, SharedPreferences prefs) {
+        String key = getVoicemailRingtoneSharedPrefsKey();
+        TelephonyManager telephonyManager = TelephonyManager.from(context);
+
+        // Skip if a preference exists, or if phone is MSIM.
+        if (prefs.contains(key) || telephonyManager.getPhoneCount() != 1) {
+            return;
+        }
+
+        if (prefs.contains(OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)) {
+            String uriString = prefs.getString(
+                    OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY, null /* defValue */);
+
+            SharedPreferences.Editor editor = prefs.edit();
+            editor.putString(key, uriString)
+                    .remove(OLD_VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY)
+                    .commit();
+        }
+    }
+
+    private static String getVoicemailVibrationSharedPrefsKey() {
+        return VOICEMAIL_NOTIFICATION_VIBRATION_SHARED_PREFS_KEY_PREFIX
+                + SubscriptionManager.getDefaultSubscriptionId();
+    }
+
+    private static String getVoicemailRingtoneSharedPrefsKey() {
+        return VOICEMAIL_NOTIFICATION_RINGTONE_SHARED_PREFS_KEY_PREFIX
+                + SubscriptionManager.getDefaultSubscriptionId();
+    }
+}
diff --git a/src/java/com/google/android/mms/pdu/PduPersister.java b/src/java/com/google/android/mms/pdu/PduPersister.java
index 4adcbf8..e32d121 100755
--- a/src/java/com/google/android/mms/pdu/PduPersister.java
+++ b/src/java/com/google/android/mms/pdu/PduPersister.java
@@ -17,15 +17,6 @@
 
 package com.google.android.mms.pdu;
 
-import com.google.android.mms.ContentType;
-import com.google.android.mms.InvalidHeaderValueException;
-import com.google.android.mms.MmsException;
-import com.google.android.mms.util.DownloadDrmHelper;
-import com.google.android.mms.util.DrmConvertSession;
-import com.google.android.mms.util.PduCache;
-import com.google.android.mms.util.PduCacheEntry;
-import com.google.android.mms.util.SqliteWrapper;
-
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -38,17 +29,26 @@
 import android.provider.MediaStore;
 import android.provider.Telephony;
 import android.provider.Telephony.Mms;
-import android.provider.Telephony.MmsSms;
-import android.provider.Telephony.Threads;
 import android.provider.Telephony.Mms.Addr;
 import android.provider.Telephony.Mms.Part;
+import android.provider.Telephony.MmsSms;
 import android.provider.Telephony.MmsSms.PendingMessages;
-import android.telephony.SubscriptionManager;
+import android.provider.Telephony.Threads;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.google.android.mms.ContentType;
+import com.google.android.mms.InvalidHeaderValueException;
+import com.google.android.mms.MmsException;
+import com.google.android.mms.util.DownloadDrmHelper;
+import com.google.android.mms.util.DrmConvertSession;
+import com.google.android.mms.util.PduCache;
+import com.google.android.mms.util.PduCacheEntry;
+import com.google.android.mms.util.SqliteWrapper;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -60,10 +60,8 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
-
-import com.google.android.mms.pdu.EncodedStringValue;
+import java.util.Set;
 
 /**
  * This class is the high-level manager of PDU storage.
@@ -838,7 +836,7 @@
                 os = mContentResolver.openOutputStream(uri);
                 if (data == null) {
                     dataUri = part.getDataUri();
-                    if ((dataUri == null) || (dataUri == uri)) {
+                    if ((dataUri == null) || (dataUri.equals(uri))) {
                         Log.w(TAG, "Can't find data for this part.");
                         return;
                     }
@@ -1138,7 +1136,7 @@
         // 1. New binary data supplied or
         // 2. The Uri of the part is different from the current one.
         if ((part.getData() != null)
-                || (uri != part.getDataUri())) {
+                || (!uri.equals(part.getDataUri()))) {
             persistData(part, uri, contentType, preOpenedFiles);
         }
     }
@@ -1183,7 +1181,8 @@
             for (int i = 0; i < partsNum; i++) {
                 PduPart part = body.getPart(i);
                 Uri partUri = part.getDataUri();
-                if ((partUri == null) || !partUri.getAuthority().startsWith("mms")) {
+                if ((partUri == null) || TextUtils.isEmpty(partUri.getAuthority())
+                        || !partUri.getAuthority().startsWith("mms")) {
                     toBeCreated.add(part);
                 } else {
                     toBeUpdated.put(partUri, part);
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 9534945..334d62f 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -9,9 +9,10 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core
 LOCAL_STATIC_JAVA_LIBRARIES := guava \
-                               mockito-target \
+                               mockito-target-minus-junit4 \
                                android-support-test \
-                               platform-test-annotations
+                               platform-test-annotations \
+                               legacy-android-test
 
 LOCAL_PACKAGE_NAME := FrameworksTelephonyTests
 
diff --git a/tests/telephonytests/src/java/android/telephony/SmsMessageTest.java b/tests/telephonytests/src/android/telephony/SmsMessageTest.java
similarity index 91%
rename from tests/telephonytests/src/java/android/telephony/SmsMessageTest.java
rename to tests/telephonytests/src/android/telephony/SmsMessageTest.java
index b8e3576..2bbad41 100644
--- a/tests/telephonytests/src/java/android/telephony/SmsMessageTest.java
+++ b/tests/telephonytests/src/android/telephony/SmsMessageTest.java
@@ -14,17 +14,16 @@
  * limitations under the License.
  */
 
-package java.android.telephony;
+package android.telephony;
 
-import android.telephony.SmsMessage;
 import android.test.suitebuilder.annotation.SmallTest;
+
 import com.android.internal.telephony.SmsConstants;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 
+import org.junit.Test;
+
 public class SmsMessageTest {
     @Test @SmallTest
     public void testCreateInvalidSmsMessage() {
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java b/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
new file mode 100644
index 0000000..bc8d4e1
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsCallProfileTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.
+ */
+
+// Note: Package name is intentionally wrong for this test; the internal junk class is used to test
+// that parcelables of types other than android.* are stripped out.
+package com.android.telephony.ims;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.test.runner.AndroidJUnit4;
+import android.telecom.DisconnectCause;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.ImsCallProfile;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link com.android.ims.ImsCallProfile} class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsCallProfileTest {
+    // A test-only parcelable class which is not in the android.* namespace.
+    private static class JunkParcelable implements Parcelable {
+        private int mTest;
+
+        JunkParcelable() {
+        }
+
+        protected JunkParcelable(Parcel in) {
+            mTest = in.readInt();
+        }
+
+        public static final Creator<JunkParcelable> CREATOR = new Creator<JunkParcelable>() {
+            @Override
+            public JunkParcelable createFromParcel(Parcel in) {
+                return new JunkParcelable(in);
+            }
+
+            @Override
+            public JunkParcelable[] newArray(int size) {
+                return new JunkParcelable[size];
+            }
+        };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mTest);
+        }
+    }
+
+    /**
+     * Ensures that the {@link ImsCallProfile} will discard invalid extras when it is parceled.
+     */
+    @Test
+    @SmallTest
+    public void testExtrasCleanup() {
+        ImsCallProfile srcParcel = new ImsCallProfile();
+        // Put in a private parcelable type.
+        srcParcel.mCallExtras.putParcelable("JUNK", new JunkParcelable());
+        // Put in an api defined parcelable type.
+        srcParcel.mCallExtras.putParcelable("NOTJUNK", new DisconnectCause(DisconnectCause.BUSY));
+        // Put in some valid things.
+        srcParcel.mCallExtras.putInt("INT", 1);
+        srcParcel.mCallExtras.putString("STRING", "hello");
+
+        // Parcel it.
+        Parcel parcel = Parcel.obtain();
+        srcParcel.writeToParcel(parcel, 0);
+        byte[] parcelBytes = parcel.marshall();
+        parcel.recycle();
+
+        // Unparcel it.
+        parcel = Parcel.obtain();
+        parcel.unmarshall(parcelBytes, 0, parcelBytes.length);
+        parcel.setDataPosition(0);
+        ImsCallProfile unparceledProfile = ImsCallProfile.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertNotNull(unparceledProfile.mCallExtras);
+        assertEquals(3, unparceledProfile.mCallExtras.size());
+        assertEquals(1, unparceledProfile.getCallExtraInt("INT"));
+        assertEquals("hello", unparceledProfile.getCallExtra("STRING"));
+        assertFalse(unparceledProfile.mCallExtras.containsKey("JUNK"));
+
+        DisconnectCause parceledCause = unparceledProfile.mCallExtras.getParcelable("NOTJUNK");
+        assertEquals(DisconnectCause.BUSY, parceledCause.getCode());
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
new file mode 100644
index 0000000..acd4e19
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsFeatureTest.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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 android.telephony.ims;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.feature.ImsFeature;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsFeatureTest {
+
+    private TestImsFeature mTestImsService;
+
+    @Mock
+    private IImsFeatureStatusCallback mTestStatusCallback;
+    @Mock
+    private IImsFeatureStatusCallback mTestStatusCallback2;
+    @Mock
+    private ImsFeature.INotifyFeatureRemoved mTestRemovedCallback;
+
+    private class TestImsFeature extends ImsFeature {
+
+        public boolean featureRemovedCalled = false;
+
+        @Override
+        public void onFeatureRemoved() {
+            featureRemovedCalled = true;
+        }
+
+        public void testSetFeatureState(int featureState) {
+            setFeatureState(featureState);
+        }
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTestImsService = new TestImsFeature();
+    }
+
+    @After
+    public void tearDown() {
+        mTestImsService = null;
+    }
+
+    @Test
+    @SmallTest
+    public void testSetCallbackAndNotify() throws Exception {
+        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback);
+        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback2);
+
+        verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_NOT_AVAILABLE));
+        verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_NOT_AVAILABLE));
+    }
+
+    @Test
+    @SmallTest
+    public void testSetFeatureAndCheckCallback() throws Exception {
+        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback);
+        mTestImsService.addImsFeatureStatusCallback(mTestStatusCallback2);
+
+        mTestImsService.testSetFeatureState(ImsFeature.STATE_READY);
+
+        verify(mTestStatusCallback).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
+        verify(mTestStatusCallback2).notifyImsFeatureStatus(eq(ImsFeature.STATE_READY));
+        assertEquals(ImsFeature.STATE_READY, mTestImsService.getFeatureState());
+    }
+
+    @Test
+    @SmallTest
+    public void testRegisterAndNotifyRemoveFeature() {
+        mTestImsService.addFeatureRemovedListener(mTestRemovedCallback);
+
+        mTestImsService.notifyFeatureRemoved(0);
+
+        verify(mTestRemovedCallback).onFeatureRemoved(eq(0));
+        assertTrue(mTestImsService.featureRemovedCalled);
+    }
+
+    @Test
+    @SmallTest
+    public void testRegisterAndUnregisterNotify() {
+        mTestImsService.addFeatureRemovedListener(mTestRemovedCallback);
+        mTestImsService.removeFeatureRemovedListener(mTestRemovedCallback);
+
+        mTestImsService.notifyFeatureRemoved(0);
+
+        verify(mTestRemovedCallback, never()).onFeatureRemoved(eq(0));
+        assertTrue(mTestImsService.featureRemovedCalled);
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
new file mode 100644
index 0000000..8887027
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/ImsServiceTest.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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 android.telephony.ims;
+
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
+import static com.android.internal.telephony.ims.ImsResolver.SERVICE_INTERFACE;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.feature.ImsFeature;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.SparseArray;
+
+import com.android.ims.ImsManager;
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsServiceController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for ImsService
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsServiceTest {
+
+    private static final int TEST_SLOT_0 = 0;
+    private static final int TEST_SLOT_1 = 1;
+
+    private TestImsService mTestImsService;
+    private IImsServiceController mTestImsServiceBinder;
+
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private IImsFeatureStatusCallback mTestCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mTestImsService = new TestImsService(mMockContext);
+        mTestImsServiceBinder = (IImsServiceController) mTestImsService.onBind(
+                new Intent(SERVICE_INTERFACE));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTestImsService = null;
+        mTestImsServiceBinder = null;
+    }
+
+    @Test
+    @SmallTest
+    public void testCreateMMTelFeature() throws RemoteException {
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+        when(mTestImsService.mSpyMMTelFeature.getFeatureState()).thenReturn(
+                ImsFeature.STATE_READY);
+
+        SparseArray<ImsFeature> features = mTestImsService.getImsFeatureMap(TEST_SLOT_0);
+        assertEquals(mTestImsService.mSpyMMTelFeature,
+                mTestImsService.getImsFeatureFromType(features, ImsFeature.MMTEL));
+        // Verify that upon creating a feature, we assign the callback and get the set feature state
+        // when querying it.
+        verify(mTestImsService.mSpyMMTelFeature).addImsFeatureStatusCallback(eq(mTestCallback));
+        assertEquals(ImsFeature.STATE_READY, mTestImsServiceBinder.getFeatureStatus(TEST_SLOT_0,
+                ImsFeature.MMTEL));
+    }
+
+    @Test
+    @SmallTest
+    public void testRemoveMMTelFeature() throws RemoteException {
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        mTestImsServiceBinder.removeImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        verify(mTestImsService.mSpyMMTelFeature).notifyFeatureRemoved(eq(0));
+        verify(mTestImsService.mSpyMMTelFeature).removeImsFeatureStatusCallback(mTestCallback);
+        SparseArray<ImsFeature> features = mTestImsService.getImsFeatureMap(TEST_SLOT_0);
+        assertNull(mTestImsService.getImsFeatureFromType(features, ImsFeature.MMTEL));
+    }
+
+    @Test
+    @SmallTest
+    public void testCallMethodOnCreatedFeature() throws RemoteException {
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        mTestImsServiceBinder.isConnected(TEST_SLOT_0, ImsFeature.MMTEL, 0 /*callSessionType*/,
+                0 /*callType*/);
+
+        verify(mTestImsService.mSpyMMTelFeature).isConnected(anyInt(), anyInt());
+    }
+
+    @Test
+    @SmallTest
+    public void testCallMethodWithNoCreatedFeature() throws RemoteException {
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        mTestImsServiceBinder.isConnected(TEST_SLOT_1, ImsFeature.MMTEL, 0 /*callSessionType*/,
+                0 /*callType*/);
+
+        verify(mTestImsService.mSpyMMTelFeature, never()).isConnected(anyInt(), anyInt());
+    }
+
+    @Test
+    @SmallTest
+    public void testCreateFeatureWithNoPermissions() throws RemoteException {
+        doThrow(new SecurityException()).when(mMockContext).enforceCallingOrSelfPermission(
+                eq(MODIFY_PHONE_STATE), anyString());
+
+        try {
+            mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+            fail();
+        } catch (SecurityException e) {
+            // Expected
+        }
+    }
+
+    @FlakyTest
+    @Ignore
+    @Test
+    public void testMethodWithNoPermissions() throws RemoteException {
+        when(mMockContext.checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)).thenReturn(
+                PackageManager.PERMISSION_DENIED);
+        doThrow(new SecurityException()).when(mMockContext).enforceCallingOrSelfPermission(
+                eq(READ_PHONE_STATE), nullable(String.class));
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        try {
+            mTestImsServiceBinder.isConnected(TEST_SLOT_1, ImsFeature.MMTEL, 0 /*callSessionType*/,
+                    0 /*callType*/);
+            fail();
+        } catch (SecurityException e) {
+            // Expected
+        }
+
+        verify(mTestImsService.mSpyMMTelFeature, never()).isConnected(anyInt(), anyInt());
+    }
+
+    /**
+     * Tests that the new ImsService still sends the IMS_SERVICE_UP broadcast when the feature is
+     * set to ready.
+     */
+    @Test
+    @SmallTest
+    public void testImsServiceUpSentCompat() throws RemoteException {
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        mTestImsService.mSpyMMTelFeature.sendSetFeatureState(ImsFeature.STATE_READY);
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext).sendBroadcast(intentCaptor.capture());
+        try {
+            // Verify IMS_SERVICE_UP is sent
+            assertNotNull(intentCaptor.getValue());
+            verifyServiceUpSent(intentCaptor.getValue());
+        } catch (IndexOutOfBoundsException e) {
+            fail("Did not receive all intents");
+        }
+    }
+
+    /**
+     * Tests that the new ImsService still sends the IMS_SERVICE_DOWN broadcast when the feature is
+     * set to initializing.
+     */
+    @Test
+    @SmallTest
+    public void testImsServiceDownSentCompatInitializing() throws RemoteException {
+        mTestImsServiceBinder.createImsFeature(TEST_SLOT_0, ImsFeature.MMTEL, mTestCallback);
+
+        mTestImsService.mSpyMMTelFeature.sendSetFeatureState(ImsFeature.STATE_INITIALIZING);
+
+        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+        verify(mMockContext).sendBroadcast(intentCaptor.capture());
+        try {
+            // IMS_SERVICE_DOWN is sent when the service is STATE_INITIALIZING.
+            assertNotNull(intentCaptor.getValue());
+            verifyServiceDownSent(intentCaptor.getValue());
+        } catch (IndexOutOfBoundsException e) {
+            fail("Did not receive all intents");
+        }
+    }
+
+    private void verifyServiceDownSent(Intent testIntent) {
+        assertEquals(ImsManager.ACTION_IMS_SERVICE_DOWN, testIntent.getAction());
+        assertEquals(TEST_SLOT_0, testIntent.getIntExtra(ImsManager.EXTRA_PHONE_ID, -1));
+    }
+
+    private void verifyServiceUpSent(Intent testIntent) {
+        assertEquals(ImsManager.ACTION_IMS_SERVICE_UP, testIntent.getAction());
+        assertEquals(TEST_SLOT_0, testIntent.getIntExtra(ImsManager.EXTRA_PHONE_ID, -1));
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestImsService.java b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
new file mode 100644
index 0000000..66e03fd
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/TestImsService.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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 android.telephony.ims;
+
+import android.content.Context;
+import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import static org.mockito.Mockito.spy;
+
+/**
+ * Test ImsService used by mockito to verify functionality.
+ */
+
+public class TestImsService extends ImsService {
+
+    public TestMMTelFeature mSpyMMTelFeature;
+    private TestMMTelFeature mTestMMTelFeature;
+
+    public TestImsService(Context context) {
+        attachBaseContext(context);
+        MockitoAnnotations.initMocks(this);
+        // Must create real MMTelFeature to initialize ImsFeature objects.
+        mTestMMTelFeature = new TestMMTelFeature();
+        mSpyMMTelFeature = spy(mTestMMTelFeature);
+    }
+
+    @Override
+    public MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId) {
+        return null;
+    }
+
+    @Override
+    public MMTelFeature onCreateMMTelImsFeature(int slotId) {
+        return mSpyMMTelFeature;
+    }
+
+    @Override
+    public RcsFeature onCreateRcsFeature(int slotId) {
+        return null;
+    }
+}
diff --git a/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java b/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java
new file mode 100644
index 0000000..6a41c2d
--- /dev/null
+++ b/tests/telephonytests/src/android/telephony/ims/TestMMTelFeature.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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 android.telephony.ims;
+
+import android.telephony.ims.feature.MMTelFeature;
+
+/**
+ * MMTelFeature implementation used by mockito to test functionality.
+ */
+
+public class TestMMTelFeature extends MMTelFeature {
+
+    @Override
+    public void onFeatureRemoved() {
+
+    }
+
+    public void sendSetFeatureState(int state) {
+        setFeatureState(state);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
index a255f26..8231d06 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallManagerTest.java
@@ -122,7 +122,7 @@
     @After
     public void tearDown() throws Exception {
         CallManager.getInstance().unregisterPhone(mPhone);
-        mCallManagerHandlerThread.quitSafely();
+        mCallManagerHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CallStateExceptionTest.java b/tests/telephonytests/src/com/android/internal/telephony/CallStateExceptionTest.java
index bac3dd1..7b6c2b7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CallStateExceptionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CallStateExceptionTest.java
@@ -15,10 +15,12 @@
  */
 package com.android.internal.telephony;
 
+import static org.junit.Assert.assertEquals;
+
 import android.test.suitebuilder.annotation.SmallTest;
+
 import org.junit.After;
 import org.junit.Test;
-import static org.junit.Assert.assertEquals;
 
 public class CallStateExceptionTest {
     private CallStateException mCallStateException;
@@ -39,9 +41,9 @@
     @Test
     @SmallTest
     public void testCallStateExceptionWithErrCode() {
-        mCallStateException = new CallStateException(mCallStateException.ERROR_DISCONNECTED,
+        mCallStateException = new CallStateException(mCallStateException.ERROR_OUT_OF_SERVICE,
                                                      "sanity test with err code");
         assertEquals("sanity test with err code", mCallStateException.getMessage());
-        assertEquals(mCallStateException.ERROR_DISCONNECTED, mCallStateException.getError());
+        assertEquals(mCallStateException.ERROR_OUT_OF_SERVICE, mCallStateException.getError());
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
new file mode 100644
index 0000000..ca21914
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierActionAgentTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.provider.Settings;
+import android.provider.Telephony;
+import android.telephony.CarrierConfigManager;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+public class CarrierActionAgentTest extends TelephonyTest {
+    private CarrierActionAgent mCarrierActionAgentUT;
+    private FakeContentResolver mFakeContentResolver;
+    private static int DATA_CARRIER_ACTION_EVENT = 0;
+    private static int RADIO_CARRIER_ACTION_EVENT = 1;
+    private CarrierActionAgentHandler mCarrierActionAgentHandler;
+    @Mock
+    private Handler mDataActionHandler;
+    @Mock
+    private Handler mRadioActionHandler;
+
+    private class FakeContentResolver extends MockContentResolver {
+        @Override
+        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+            super.notifyChange(uri, observer, syncToNetwork);
+            logd("onChanged(uri=" + uri + ")" + observer);
+            if (observer != null) {
+                observer.dispatchChange(false, uri);
+            } else {
+                mCarrierActionAgentUT.getContentObserver().dispatchChange(false, uri);
+            }
+        }
+    }
+
+    private class CarrierActionAgentHandler extends HandlerThread {
+
+        private CarrierActionAgentHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mCarrierActionAgentUT = new CarrierActionAgent(mPhone);
+            mCarrierActionAgentUT.registerForCarrierAction(
+                    CarrierActionAgent.CARRIER_ACTION_SET_METERED_APNS_ENABLED, mDataActionHandler,
+                    DATA_CARRIER_ACTION_EVENT, null, false);
+            mCarrierActionAgentUT.registerForCarrierAction(
+                    CarrierActionAgent.CARRIER_ACTION_SET_RADIO_ENABLED, mRadioActionHandler,
+                    RADIO_CARRIER_ACTION_EVENT, null, false);
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        logd("CarrierActionAgentTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+        mFakeContentResolver = new FakeContentResolver();
+        doReturn(mFakeContentResolver).when(mContext).getContentResolver();
+        mCarrierActionAgentHandler = new CarrierActionAgentHandler(getClass().getSimpleName());
+        mCarrierActionAgentHandler.start();
+        waitUntilReady();
+        logd("CarrierActionAgentTest -Setup!");
+    }
+
+    @Test
+    @SmallTest
+    public void testCarrierActionResetOnAPM() {
+        // setting observer register at sim loading
+        final Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mContext.sendBroadcast(intent);
+        waitForMs(200);
+
+        // carrier actions triggered from sim loading
+        ArgumentCaptor<Message> message = ArgumentCaptor.forClass(Message.class);
+        verify(mDataActionHandler).sendMessageAtTime(message.capture(), anyLong());
+        assertEquals(DATA_CARRIER_ACTION_EVENT, message.getValue().what);
+
+        verify(mRadioActionHandler).sendMessageAtTime(message.capture(), anyLong());
+        assertEquals(RADIO_CARRIER_ACTION_EVENT, message.getValue().what);
+
+        // simulate APM change from off -> on
+        Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 1);
+        mFakeContentResolver.notifyChange(
+                Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), null);
+        waitForMs(200);
+
+        // carrier actions triggered from APM
+        verify(mDataActionHandler, times(2)).sendMessageAtTime(message.capture(), anyLong());
+        assertEquals(DATA_CARRIER_ACTION_EVENT, message.getValue().what);
+
+        verify(mRadioActionHandler, times(2)).sendMessageAtTime(message.capture(), anyLong());
+        assertEquals(RADIO_CARRIER_ACTION_EVENT, message.getValue().what);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Settings.Global.putInt(mFakeContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
+        mCarrierActionAgentHandler.quit();
+        super.tearDown();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
new file mode 100644
index 0000000..6635c08
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServicesSmsFilterTest.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.service.carrier.CarrierMessagingService;
+import android.service.carrier.ICarrierMessagingCallback;
+import android.service.carrier.ICarrierMessagingService;
+import android.service.carrier.MessagePdu;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.uicc.UiccCard;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests SMS filtering by carrier services.
+ */
+public class CarrierServicesSmsFilterTest extends TelephonyTest {
+    private static final byte[] SMS_PDU = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
+    private static final String CARRIER_APP_PACKAGE_NAME = "com.android.carrier";
+    private static final String SYSTEM_APP_PACKAGE_NAME = "com.android.system";
+
+    private CarrierServicesSmsFilter mCarrierServicesSmsFilterUT;
+    @Mock
+    private CarrierServicesSmsFilter.CarrierServicesSmsFilterCallbackInterface mFilterCallback;
+    @Mock
+    private UiccCard mUiccCard;
+    @Mock
+    private ICarrierMessagingService.Stub mICarrierAppMessagingService;
+    @Mock
+    private ICarrierMessagingService.Stub mISystemCarrierMessagingService;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mCarrierServicesSmsFilterUT = new CarrierServicesSmsFilter(
+                mContext, mPhone, new byte[][]{SMS_PDU},
+                0, "3gpp", mFilterCallback, getClass().getSimpleName());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testFilter_noCarrierServicesFilter_notHandled() throws Exception {
+        assertFalse(mCarrierServicesSmsFilterUT.filter());
+    }
+
+    @Test
+    @SmallTest
+    public void testFilter_carrierAppPresent_handled() throws Exception {
+        mockCarrierApp();
+        mockCarrierAppStubResults(
+                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mICarrierAppMessagingService);
+        assertTrue(mCarrierServicesSmsFilterUT.filter());
+
+        verify(mFilterCallback, timeout(100))
+                .onFilterComplete(eq(CarrierMessagingService.RECEIVE_OPTIONS_DROP));
+    }
+
+    @Test
+    @SmallTest
+    public void testFilter_systemAppPresent_handled() throws Exception {
+        mockSystemApp();
+        mockCarrierAppStubResults(
+                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mISystemCarrierMessagingService);
+
+        assertTrue(mCarrierServicesSmsFilterUT.filter());
+
+        verify(mFilterCallback, timeout(100))
+                .onFilterComplete(eq(CarrierMessagingService.RECEIVE_OPTIONS_DROP));
+    }
+
+    @Test
+    @SmallTest
+    public void testFilter_bothCarrierAndSystemAppPresent_carrierAppDecides() throws Exception {
+        mockCarrierApp();
+        mockSystemApp();
+        mockCarrierAppStubResults(
+                CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT, mICarrierAppMessagingService);
+        mockCarrierAppStubResults(
+                CarrierMessagingService.RECEIVE_OPTIONS_DROP, mISystemCarrierMessagingService);
+
+        assertTrue(mCarrierServicesSmsFilterUT.filter());
+
+        verify(mFilterCallback, timeout(100))
+                .onFilterComplete(eq(CarrierMessagingService.RECEIVE_OPTIONS_DEFAULT));
+    }
+
+    private void mockCarrierApp()
+            throws RemoteException {
+        mContextFixture.addService(
+                CarrierMessagingService.SERVICE_INTERFACE,
+                new ComponentName(CARRIER_APP_PACKAGE_NAME, "CarrierAppFilterClass"),
+                CARRIER_APP_PACKAGE_NAME,
+                mICarrierAppMessagingService,
+                new ServiceInfo());
+        mockUiccWithCarrierApp();
+    }
+
+    private void mockUiccWithCarrierApp() {
+        when(mUiccController.getUiccCard(mPhone.getPhoneId())).thenReturn(mUiccCard);
+        List<String> carrierPackages = new ArrayList<>();
+        carrierPackages.add(CARRIER_APP_PACKAGE_NAME);
+        when(mUiccCard.getCarrierPackageNamesForIntent(
+                any(PackageManager.class), any(Intent.class))).thenReturn(carrierPackages);
+    }
+
+    private void mockSystemApp() {
+        ServiceInfo serviceInfo = new ServiceInfo();
+        serviceInfo.packageName = SYSTEM_APP_PACKAGE_NAME;
+        mContextFixture.addService(
+                CarrierMessagingService.SERVICE_INTERFACE,
+                new ComponentName(SYSTEM_APP_PACKAGE_NAME, "SystemFilterClass"),
+                SYSTEM_APP_PACKAGE_NAME,
+                mISystemCarrierMessagingService,
+                serviceInfo);
+    }
+
+    private void mockCarrierAppStubResults(final int result, ICarrierMessagingService.Stub stub)
+            throws RemoteException {
+        when(stub.queryLocalInterface(anyString())).thenReturn(stub);
+        when(stub.asBinder()).thenReturn(stub);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ICarrierMessagingCallback callback = (ICarrierMessagingCallback) args[4];
+                callback.onFilterComplete(result);
+                return null;
+            }
+        }).when(stub).filterSms(
+                any(MessagePdu.class), anyString(), anyInt(), anyInt(),
+                any(ICarrierMessagingCallback.class));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
new file mode 100644
index 0000000..7cb7d45
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierSignalAgentTest.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 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.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.Message;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.compat.ArgumentMatcher;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED;
+import static com.android.internal.telephony.TelephonyIntents.ACTION_CARRIER_SIGNAL_PCO_VALUE;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+
+public class CarrierSignalAgentTest extends TelephonyTest {
+
+    private CarrierSignalAgent mCarrierSignalAgentUT;
+    private PersistableBundle mBundle;
+    private static final String PCO_RECEIVER = "pak/PCO_RECEIVER";
+    private static final String DC_ERROR_RECEIVER = "pak/DC_ERROR_RECEIVER";
+    @Mock
+    ResolveInfo mResolveInfo;
+
+    @Before
+    public void setUp() throws Exception {
+        logd("CarrierSignalAgentTest +Setup!");
+        super.setUp(getClass().getSimpleName());
+        mCarrierSignalAgentUT = new CarrierSignalAgent(mPhone);
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        logd("CarrierSignalAgentTest -Setup!");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyManifestReceivers() throws Exception {
+        // Broadcast count
+        int count = 0;
+        Intent intent = new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE);
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE,
+                        DC_ERROR_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE
+                });
+
+        // Verify no broadcast has been sent without carrier config
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
+        ArgumentCaptor<Intent> mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+
+        // Trigger carrier config reloading
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        count++;
+
+        // Verify no broadcast has been sent due to no manifest receivers
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+
+        // Verify broadcast has been sent to two different registered manifest receivers
+        doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
+                .when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
+        count += 2;
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+
+        logd(mCaptorIntent.getAllValues().toString());
+        Intent capturedIntent = mCaptorIntent.getAllValues().get(1);
+        assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, capturedIntent.getAction());
+        assertEquals(DC_ERROR_RECEIVER, capturedIntent.getComponent().flattenToString());
+
+        capturedIntent = mCaptorIntent.getAllValues().get(2);
+        assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, capturedIntent.getAction());
+        assertEquals(PCO_RECEIVER, capturedIntent.getComponent().flattenToString());
+    }
+
+    @Test
+    @SmallTest
+    public void testNotifyRuntimeReceivers() throws Exception {
+        // Broadcast count
+        int count = 0;
+        Intent intent = new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE);
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE});
+
+        // Verify no broadcast without carrier configs
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
+        ArgumentCaptor<Intent> mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+
+        // Trigger carrier config reloading
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        count++;
+
+        // Verify broadcast has been sent to registered components
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
+        verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
+        assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE,
+                mCaptorIntent.getValue().getAction());
+        assertEquals(PCO_RECEIVER, mCaptorIntent.getValue().getComponent().flattenToString());
+
+        // Verify no broadcast has been sent to manifest receivers (bad config)
+        doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
+                .when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(intent);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+    }
+
+    @Test
+    @SmallTest
+    public void testNotify() {
+        // Broadcast count
+        int count = 0;
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE });
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ PCO_RECEIVER + ":"
+                        + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED });
+        // Only wake signal is declared in the manifest
+        doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
+                .when(mPackageManager).queryBroadcastReceivers(
+                argThat(new ArgumentMatcher<Intent>() {
+                    @Override
+                    public boolean matchesObject(Object o) {
+                        return o instanceof Intent && ((Intent) o).getAction()
+                                .equals(ACTION_CARRIER_SIGNAL_PCO_VALUE); }}), anyInt());
+
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        count++;
+        waitForMs(50);
+
+        // Wake signal for PAK_PCO_RECEIVER
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+                new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE));
+        ArgumentCaptor<Intent> mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
+        assertEquals(ACTION_CARRIER_SIGNAL_PCO_VALUE, mCaptorIntent.getValue().getAction());
+        assertEquals(PCO_RECEIVER, mCaptorIntent.getValue().getComponent().flattenToString());
+
+        // No wake signal for PAK_PCO_RECEIVER
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+                new Intent(ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED));
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
+        assertEquals(ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+                mCaptorIntent.getValue().getAction());
+        assertEquals(PCO_RECEIVER, mCaptorIntent.getValue().getComponent().flattenToString());
+
+        // Both wake and no-wake signals are declared in the manifest
+        doReturn(new ArrayList<>(Arrays.asList(mResolveInfo)))
+                .when(mPackageManager).queryBroadcastReceivers((Intent) any(), anyInt());
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+                new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE));
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+                new Intent(ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED));
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+
+        // Neither wake nor no-wake signals are declared in the manifest
+        doReturn(new ArrayList<>()).when(mPackageManager).queryBroadcastReceivers((Intent) any(),
+                anyInt());
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+                new Intent(ACTION_CARRIER_SIGNAL_PCO_VALUE));
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(count)).sendBroadcast(mCaptorIntent.capture());
+        mCarrierSignalAgentUT.notifyCarrierSignalReceivers(
+                new Intent(ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED));
+        mCaptorIntent = ArgumentCaptor.forClass(Intent.class);
+        verify(mContext, times(++count)).sendBroadcast(mCaptorIntent.capture());
+    }
+
+
+    @Test
+    @SmallTest
+    public void testCarrierConfigChange() {
+        // default config value
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_PCO_VALUE + ","
+                        + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED });
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        // verify no reset action on initial config load
+        verify(mCarrierActionAgent, times(0)).sendMessageAtTime(any(Message.class), anyLong());
+
+        // new carrier config with different receiver intent order
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ PCO_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+                        + "," + ACTION_CARRIER_SIGNAL_PCO_VALUE});
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        // verify no reset action for the same config (different order)
+        verify(mCarrierActionAgent, times(0)).sendMessageAtTime(any(Message.class), anyLong());
+
+        // new different config value
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+                new String[]{ DC_ERROR_RECEIVER + ":" + ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+                        + "," + ACTION_CARRIER_SIGNAL_PCO_VALUE});
+        mContext.sendBroadcast(new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
+        waitForMs(50);
+        // verify there is no reset action
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mCarrierActionAgent, times(1))
+                .sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
+        assertEquals(CarrierActionAgent.CARRIER_ACTION_RESET,
+                messageArgumentCaptor.getValue().what);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
new file mode 100644
index 0000000..f7132b8
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockAccountantTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import android.os.Build;
+import android.util.Log;
+import android.telephony.Rlog;
+import android.telephony.TelephonyHistogram;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+public class ClientWakelockAccountantTest extends TestCase {
+    private final static String LOG_TAG = "ClientWakelockAccountantTest";
+    ClientWakelockAccountant mClient;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mClient = new ClientWakelockAccountant("Package Name");
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testStartAttributingWakelock() throws Exception {
+
+        Assert.assertEquals(0, mClient.getPendingRequestCount());
+        mClient.startAttributingWakelock(15, 25, 1, 100);
+        Assert.assertEquals(1, mClient.getPendingRequestCount());
+        mClient.startAttributingWakelock(22, 26, 2, 150);
+        Assert.assertEquals(2, mClient.getPendingRequestCount());
+    }
+
+    /* This test only tests ClientWakelockAccountant to make sure that it applies the
+       multiplier as expected. Here we start 1 Client and add 2 requests.
+       First request multiplier stays at 1 and goes on for 200ms and second request
+       multiplier stays at 0.5 and request goes on for 500ms. So totally we
+       expect the wakelock time for the Client to be 200*1 + 500*0.5 = 450ms
+     */
+    public void testStopAttributingWakelock() throws Exception {
+        mClient.startAttributingWakelock(15, 25, 1, 100);
+        mClient.startAttributingWakelock(25, 26, 2, 200);
+        mClient.changeConcurrentRequests(2, 200);
+        Assert.assertEquals(2, mClient.getPendingRequestCount());
+        Assert.assertEquals(0, mClient.mRequestStats.getCompletedRequestsCount());
+        mClient.stopAttributingWakelock(15, 25, 300);
+        mClient.changeConcurrentRequests(1, 300);
+        Assert.assertEquals(1, mClient.mRequestStats.getRequestHistograms().size());
+        mClient.stopAttributingWakelock(25, 26, 700);
+        Assert.assertEquals(0, mClient.getPendingRequestCount());
+        Assert.assertEquals(2, mClient.mRequestStats.getCompletedRequestsCount());
+        Assert.assertEquals(600, mClient.mRequestStats.getCompletedRequestsWakelockTime());
+        Assert.assertEquals(0, mClient.updatePendingRequestWakelockTime(0));
+        Assert.assertEquals(2, mClient.mRequestStats.getRequestHistograms().size());
+    }
+
+    public void testStopAllPendingRequests() throws Exception {
+
+        mClient.startAttributingWakelock(15, 25, 1, 100);
+        mClient.startAttributingWakelock(22, 26, 2, 150);
+        Assert.assertEquals(2, mClient.getPendingRequestCount());
+        mClient.stopAllPendingRequests(300);
+        Assert.assertEquals(0, mClient.getPendingRequestCount());
+        Assert.assertEquals(2, mClient.mRequestStats.getCompletedRequestsCount());
+        Assert.assertEquals(275, mClient.mRequestStats.getCompletedRequestsWakelockTime());
+        Assert.assertEquals(0, mClient.updatePendingRequestWakelockTime(0));
+        Assert.assertEquals(2, mClient.mRequestStats.getRequestHistograms().size());
+    }
+
+    public void testStartAttributingWithZeroConcurrentRequests() throws Exception {
+        if(Build.IS_DEBUGGABLE) {
+            try {
+                mClient.startAttributingWakelock(15, 25, 0, 100);
+                fail("Expecting an illegal argument Exception to be thrown");
+            } catch (IllegalArgumentException e) { }
+        } else {
+            mClient.startAttributingWakelock(15, 25, 0, 100);
+            Assert.assertEquals(1, mClient.getPendingRequestCount());
+        }
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockTrackerTest.java
new file mode 100644
index 0000000..c18581a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ClientWakelockTrackerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import junit.framework.TestCase;
+
+public class ClientWakelockTrackerTest extends TestCase {
+    ClientWakelockTracker myTracker;
+
+    public void setUp() throws Exception {
+        super.setUp();
+        myTracker = new ClientWakelockTracker();
+    }
+
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /* This test has client "ABC" send 1 message at time t and gets response at t+40,
+    client "PQR" sends a message at t+20 and gets response at t+120. Verify that
+    "ABC" is attributed 30ms and "PQR" 90ms of the total wakelock time of 120ms
+     */
+    public void testTwoClients() throws Exception {
+        myTracker.startTracking("ABC", 101, 1, 1);
+        waitForMs(20);
+        assertEquals(1, myTracker.mActiveClients.size());
+        myTracker.startTracking("PQR", 102, 2, 2);
+        assertEquals(2, myTracker.mActiveClients.size());
+        ClientWakelockAccountant abc = myTracker.mClients.get("ABC");
+        ClientWakelockAccountant pqr = myTracker.mClients.get("PQR");
+        assertEquals(2, abc.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        assertEquals(2, pqr.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        waitForMs(20);
+        myTracker.stopTracking("ABC", 101, 1, 1);
+        assertEquals(1, myTracker.mActiveClients.size());
+        assertEquals(0, abc.getPendingRequestCount());
+        assertEquals(1, pqr.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        waitForMs(80);
+        myTracker.stopTracking("PQR", 102, 2, 0);
+        assertEquals(0, myTracker.mActiveClients.size());
+        assertEquals(0, abc.getPendingRequestCount());
+        assertEquals(0, pqr.getPendingRequestCount());
+
+        assertTimeTaken(abc, 30);
+        assertTimeTaken(pqr, 90);
+    }
+
+    private void assertTimeTaken(ClientWakelockAccountant abc, int time) {
+        assertTrue(abc.mRequestStats.getCompletedRequestsWakelockTime() > (time - 1));
+        assertTrue(abc.mRequestStats.getCompletedRequestsWakelockTime() < (time + 19));
+    }
+
+    /* This test has client "ABC" send 1 message at time t and gets response at t+40,
+    and sends another message at t+20 and gets response at t+120. Verify that
+    "ABC" is attributed 120ms
+     */
+    public void testOneClient() throws Exception {
+        myTracker.startTracking("ABC", 101, 1, 1);
+        waitForMs(20);
+        assertEquals(1, myTracker.mActiveClients.size());
+        myTracker.startTracking("ABC", 102, 2, 2);
+        assertEquals(1, myTracker.mActiveClients.size());
+        ClientWakelockAccountant abc = myTracker.mClients.get("ABC");
+        assertEquals(2, abc.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        assertEquals(2, abc.mPendingRilWakelocks.get(1).getConcurrentRequests());
+        waitForMs(20);
+        myTracker.stopTracking("ABC", 101, 1, 1);
+        assertEquals(1, myTracker.mActiveClients.size());
+        assertEquals(1, abc.getPendingRequestCount());
+        assertEquals(1, abc.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        waitForMs(80);
+        myTracker.stopTracking("ABC", 102, 2, 0);
+        assertEquals(0, myTracker.mActiveClients.size());
+        assertEquals(0, abc.getPendingRequestCount());
+        assertEquals(2, abc.mRequestStats.getCompletedRequestsCount());
+
+        assertTimeTaken(abc, 120);
+    }
+
+    /* This test has client "ABC" send 1 message at time t and another at time t+20
+    and gets response for all at t+40. Verify that "ABC" is attributed 40ms
+     */
+    public void testStopTrackingAllOneClient() throws Exception {
+        myTracker.startTracking("ABC", 101, 1, 1);
+        waitForMs(20);
+        assertEquals(1, myTracker.mActiveClients.size());
+        myTracker.startTracking("ABC", 102, 2, 2);
+        ClientWakelockAccountant abc = myTracker.mClients.get("ABC");
+        assertEquals(1, myTracker.mActiveClients.size());
+        assertEquals(2, abc.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        assertEquals(2, abc.mPendingRilWakelocks.get(1).getConcurrentRequests());
+        waitForMs(20);
+        myTracker.stopTrackingAll();
+        assertEquals(0, myTracker.mActiveClients.size());
+        assertEquals(0, abc.getPendingRequestCount());
+        assertEquals(2, abc.mRequestStats.getCompletedRequestsCount());
+
+        assertTimeTaken(abc, 40);
+    }
+
+    /* This test has client "ABC" send 1 message at time t and client "PQR" sends 1 message
+     at time (t+20)ms. Both of them get response at (t+40). Verify that
+    "ABC" is attributed 30ms amd PQR is attributed 10ms
+     */
+    public void testStopTrackingAllTwoClients() throws Exception {
+        myTracker.startTracking("ABC", 101, 1, 1);
+        waitForMs(20);
+        assertEquals(1, myTracker.mActiveClients.size());
+        myTracker.startTracking("PQR", 102, 2, 2);
+        ClientWakelockAccountant abc = myTracker.mClients.get("ABC");
+        ClientWakelockAccountant pqr = myTracker.mClients.get("PQR");
+        assertEquals(2, myTracker.mActiveClients.size());
+        assertEquals(2, abc.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        assertEquals(2, pqr.mPendingRilWakelocks.get(0).getConcurrentRequests());
+        waitForMs(20);
+        myTracker.stopTrackingAll();
+        assertEquals(0, myTracker.mActiveClients.size());
+        assertEquals(0, abc.getPendingRequestCount());
+        assertEquals(1, abc.mRequestStats.getCompletedRequestsCount());
+        assertEquals(0, pqr.getPendingRequestCount());
+
+        assertTimeTaken(pqr, 10);
+        assertTimeTaken(abc, 30);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index ef841f2..23e30a2 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -16,12 +16,14 @@
 
 package com.android.internal.telephony;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
@@ -47,6 +49,7 @@
 import android.net.ConnectivityManager;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.os.BatteryManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IInterface;
@@ -55,6 +58,8 @@
 import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
+import android.provider.Telephony.ServiceStateTable;
+import android.telecom.TelecomManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -63,6 +68,13 @@
 import android.test.mock.MockContext;
 import android.util.Log;
 
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -72,15 +84,6 @@
 import java.util.Locale;
 import java.util.Map;
 
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
 /**
  * Controls a test {@link Context} as would be provided by the Android framework to an
  * {@code Activity}, {@code Service} or other system-instantiated component.
@@ -181,8 +184,12 @@
             }
             IInterface service = mServiceByComponentName.get(serviceIntent.getComponent());
             if (service == null) {
-                throw new RuntimeException("ServiceConnection not found: "
-                        + serviceIntent.getComponent());
+                service = mServiceByPackageName.get(serviceIntent.getPackage());
+            }
+            if (service == null) {
+                throw new RuntimeException(
+                        String.format("ServiceConnection not found for component: %s, package: %s",
+                                serviceIntent.getComponent(), serviceIntent.getPackage()));
             }
             mServiceByServiceConnection.put(connection, service);
             connection.onServiceConnected(serviceIntent.getComponent(), service.asBinder());
@@ -216,9 +223,6 @@
                     return mUserManager;
                 case Context.CARRIER_CONFIG_SERVICE:
                     return mCarrierConfigManager;
-                case Context.POWER_SERVICE:
-                    // PowerManager is a final class so cannot be mocked, return real service
-                    return TestApplication.getAppContext().getSystemService(name);
                 case Context.TELEPHONY_SUBSCRIPTION_SERVICE:
                     return mSubscriptionManager;
                 case Context.WIFI_SERVICE:
@@ -229,6 +233,15 @@
                     return mConnectivityManager;
                 case Context.USAGE_STATS_SERVICE:
                     return mUsageStatManager;
+                case Context.BATTERY_SERVICE:
+                    return mBatteryManager;
+                case Context.TELECOM_SERVICE:
+                    return mTelecomManager;
+                case Context.DISPLAY_SERVICE:
+                case Context.POWER_SERVICE:
+                    // PowerManager and DisplayManager are final classes so cannot be mocked,
+                    // return real services.
+                    return TestApplication.getAppContext().getSystemService(name);
                 default:
                     return null;
             }
@@ -426,7 +439,14 @@
 
         @Override
         public int checkCallingOrSelfPermission(String permission) {
-            return PackageManager.PERMISSION_GRANTED;
+            if (mPermissionTable.contains(permission)
+                    || mPermissionTable.contains(PERMISSION_ENABLE_ALL)) {
+                logd("checkCallingOrSelfPermission: " + permission + " return GRANTED");
+                return PackageManager.PERMISSION_GRANTED;
+            } else {
+                logd("checkCallingOrSelfPermission: " + permission + " return DENIED");
+                return PackageManager.PERMISSION_DENIED;
+            }
         }
 
         @Override
@@ -444,6 +464,8 @@
             ArrayListMultimap.create();
     private final Map<ComponentName, IInterface> mServiceByComponentName =
             new HashMap<ComponentName, IInterface>();
+    private final Map<String, IInterface> mServiceByPackageName =
+            new HashMap<String, IInterface>();
     private final Map<ComponentName, ServiceInfo> mServiceInfoByComponentName =
             new HashMap<ComponentName, ServiceInfo>();
     private final Map<IInterface, ComponentName> mComponentNameByService =
@@ -479,6 +501,8 @@
     private final ConnectivityManager mConnectivityManager = mock(ConnectivityManager.class);
     private final UsageStatsManager mUsageStatManager = null;
     private final WifiManager mWifiManager = mock(WifiManager.class);
+    private final BatteryManager mBatteryManager = mock(BatteryManager.class);
+    private final TelecomManager mTelecomManager = mock(TelecomManager.class);
 
     private final ContentProvider mContentProvider = spy(new FakeContentProvider());
 
@@ -517,6 +541,10 @@
         doReturn(mConfiguration).when(mResources).getConfiguration();
 
         mContentResolver.addProvider(Settings.AUTHORITY, mContentProvider);
+        // Settings caches the provider after first get/set call, this is needed to make sure
+        // Settings is using mContentProvider as the cached provider across all tests.
+        Settings.Global.getInt(mContentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
+        mContentResolver.addProvider(ServiceStateTable.AUTHORITY, mContentProvider);
         mPermissionTable.add(PERMISSION_ENABLE_ALL);
     }
 
@@ -553,9 +581,11 @@
         return mBundle;
     }
 
-    private void addService(String action, ComponentName name, IInterface service) {
+    public void addService(String action, ComponentName name, String packageName,
+                           IInterface service, ServiceInfo serviceInfo) {
         mComponentNamesByAction.put(action, name);
-        mServiceByComponentName.put(name, service);
+        mServiceInfoByComponentName.put(name, serviceInfo);
+        mServiceByPackageName.put(packageName, service);
         mComponentNameByService.put(service, name);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
index 30b1cc1..5299b3f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/DefaultPhoneNotifierTest.java
@@ -40,6 +40,8 @@
 import android.telephony.VoLteServiceState;
 import android.telephony.gsm.GsmCellLocation;
 import android.os.Bundle;
+import android.os.Process;
+import android.os.WorkSource;
 import android.test.suitebuilder.annotation.SmallTest;
 
 public class DefaultPhoneNotifierTest extends TelephonyTest {
@@ -264,11 +266,11 @@
 
     @Test @SmallTest
     public void testNotifyOtaspChanged() throws Exception {
-        mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, ServiceStateTracker.OTASP_NEEDED);
-        verify(mTelephonyRegisteryMock).notifyOtaspChanged(ServiceStateTracker.OTASP_NEEDED);
+        mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, TelephonyManager.OTASP_NEEDED);
+        verify(mTelephonyRegisteryMock).notifyOtaspChanged(TelephonyManager.OTASP_NEEDED);
 
-        mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, ServiceStateTracker.OTASP_UNKNOWN);
-        verify(mTelephonyRegisteryMock).notifyOtaspChanged(ServiceStateTracker.OTASP_UNKNOWN);
+        mDefaultPhoneNotifierUT.notifyOtaspChanged(mPhone, TelephonyManager.OTASP_UNKNOWN);
+        verify(mTelephonyRegisteryMock).notifyOtaspChanged(TelephonyManager.OTASP_UNKNOWN);
     }
 
     @Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
new file mode 100644
index 0000000..591b111
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/DeviceStateMonitorTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static android.hardware.radio.V1_0.DeviceStateType.CHARGING_STATE;
+import static android.hardware.radio.V1_0.DeviceStateType.LOW_DATA_EXPECTED;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import static java.util.Arrays.asList;
+
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.os.BatteryManager;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+
+import java.util.ArrayList;
+
+@Ignore
+public class DeviceStateMonitorTest extends TelephonyTest {
+
+    private DeviceStateMonitor mDSM;
+
+    private DeviceStateMonitorTestHandler mDeviceStateMonitorTestHandler;
+
+    private class DeviceStateMonitorTestHandler extends HandlerThread {
+
+        private DeviceStateMonitorTestHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mDSM = new DeviceStateMonitor(mPhone);
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mDeviceStateMonitorTestHandler = new DeviceStateMonitorTestHandler(TAG);
+        mDeviceStateMonitorTestHandler.start();
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mDeviceStateMonitor = null;
+        mDeviceStateMonitorTestHandler.quit();
+        super.tearDown();
+    }
+
+    @FlakyTest
+    public void testTethering() throws Exception {
+        // Turn tethering on
+        Intent intent = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        intent.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, new ArrayList<>(asList("abc")));
+        mContext.sendBroadcast(intent);
+
+        waitForMs(100);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setUnsolResponseFilter(eq(6),
+                nullable(Message.class));
+
+        // Turn tethering off
+        intent = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+        intent.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, new ArrayList<>());
+        mContext.sendBroadcast(intent);
+        waitForMs(100);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setUnsolResponseFilter(eq(0),
+                nullable(Message.class));
+
+        verify(mSimulatedCommandsVerifier, times(1)).sendDeviceState(eq(LOW_DATA_EXPECTED),
+                eq(true), nullable(Message.class));
+    }
+
+    @FlakyTest
+    public void testCharging() throws Exception {
+        // Charging
+        Intent intent = new Intent(BatteryManager.ACTION_CHARGING);
+        mContext.sendBroadcast(intent);
+        waitForMs(100);
+
+        verify(mSimulatedCommandsVerifier, times(1)).sendDeviceState(eq(CHARGING_STATE),
+                eq(true), nullable(Message.class));
+
+        // Not charging
+        intent = new Intent(BatteryManager.ACTION_DISCHARGING);
+        mContext.sendBroadcast(intent);
+        waitForMs(100);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setUnsolResponseFilter(eq(0),
+                nullable(Message.class));
+
+        verify(mSimulatedCommandsVerifier, times(1)).sendDeviceState(eq(LOW_DATA_EXPECTED),
+                eq(true), nullable(Message.class));
+
+        verify(mSimulatedCommandsVerifier, times(1)).sendDeviceState(eq(CHARGING_STATE),
+                eq(false), nullable(Message.class));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeSmsContentProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeSmsContentProvider.java
index 1806c3c..17c18ec 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/FakeSmsContentProvider.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeSmsContentProvider.java
@@ -62,7 +62,8 @@
                     SubscriptionManager.INVALID_SUBSCRIPTION_ID + ", " +
                     "pdu TEXT," + // the raw PDU for this part
                     "deleted INTEGER DEFAULT 0," + // bool to indicate if row is deleted
-                    "message_body TEXT);"); // message body
+                    "message_body TEXT," + // message body
+                    "display_originating_addr TEXT);");// display address
         }
 
         @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
index ec05ee9..ebb8dad 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaCallTrackerTest.java
@@ -15,29 +15,35 @@
  */
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
 import android.os.HandlerThread;
-import android.platform.test.annotations.Postsubmit;
+import android.os.Message;
+import android.support.test.filters.FlakyTest;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import android.os.Message;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
-import org.mockito.Mock;
 import org.mockito.ArgumentCaptor;
-import android.os.Handler;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Mockito.doReturn;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.*;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
+import org.mockito.Mock;
 
 public class GsmCdmaCallTrackerTest extends TelephonyTest {
     private static final int VOICE_CALL_STARTED_EVENT = 0;
@@ -47,7 +53,7 @@
     private GsmCdmaCallTracker mCTUT;
     private GsmCdmaCTHandlerThread mGsmCdmaCTHandlerThread;
     @Mock
-    GsmCdmaCall mCall;
+    GsmCdmaConnection mConnection;
     @Mock
     private Handler mHandler;
 
@@ -68,8 +74,6 @@
         super.setUp(this.getClass().getSimpleName());
         mSimulatedCommands.setRadioPower(true, null);
         mPhone.mCi = this.mSimulatedCommands;
-        mContextFixture.putStringArrayResource(com.android.internal.R.array.dial_string_replace,
-                new String[]{});
 
         mGsmCdmaCTHandlerThread = new GsmCdmaCTHandlerThread(TAG);
         mGsmCdmaCTHandlerThread.start();
@@ -85,7 +89,7 @@
     @After
     public void tearDown() throws Exception {
         mCTUT = null;
-        mGsmCdmaCTHandlerThread.quitSafely();
+        mGsmCdmaCTHandlerThread.quit();
         super.tearDown();
     }
 
@@ -128,6 +132,8 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testMOCallHangup() {
@@ -150,6 +156,8 @@
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testMOCallDialPickUpHangup() {
@@ -158,8 +166,8 @@
         assertEquals(PhoneConstants.State.OFFHOOK, mCTUT.getState());
         assertEquals(1, mCTUT.mForegroundCall.getConnections().size());
          /* get the reference of the connection before reject */
-        Connection mConnection = mCTUT.mForegroundCall.getConnections().get(0);
-        assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
+        Connection connection = mCTUT.mForegroundCall.getConnections().get(0);
+        assertEquals(DisconnectCause.NOT_DISCONNECTED, connection.getDisconnectCause());
         logd("hang up MO call after pickup");
         try {
             mCTUT.hangup(mCTUT.mForegroundCall);
@@ -172,11 +180,11 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
         assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
         assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
-        assertEquals(DisconnectCause.LOCAL, mConnection.getDisconnectCause());
+        assertEquals(DisconnectCause.LOCAL, connection.getDisconnectCause());
 
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @MediumTest
     public void testMOCallPendingHangUp() {
@@ -223,6 +231,8 @@
 
     @Test
     @SmallTest
+    @FlakyTest
+    @Ignore
     public void testMTCallRinging() {
         /* Mock there is a MT call mRinging call and try to accept this MT call */
         /* if we got a active state followed by another MT call-> move to background call */
@@ -239,6 +249,8 @@
 
     @Test
     @SmallTest
+    @FlakyTest
+    @Ignore
     public void testMTCallAccept() {
         testMTCallRinging();
         assertEquals(mCTUT.mForegroundCall.getConnections().size(),0);
@@ -264,9 +276,9 @@
         testMTCallRinging();
         logd("MT call ringing and rejected ");
         /* get the reference of the connection before reject */
-        Connection mConnection = mCTUT.mRingingCall.getConnections().get(0);
-        assertNotNull(mConnection);
-        assertEquals(DisconnectCause.NOT_DISCONNECTED, mConnection.getDisconnectCause());
+        Connection connection = mCTUT.mRingingCall.getConnections().get(0);
+        assertNotNull(connection);
+        assertEquals(DisconnectCause.NOT_DISCONNECTED, connection.getDisconnectCause());
         try {
             mCTUT.rejectCall();
         } catch(Exception ex) {
@@ -278,10 +290,11 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mForegroundCall.getState());
         assertEquals(0, mCTUT.mForegroundCall.getConnections().size());
         /* ? why rejectCall didnt -> hang up locally to set the cause to LOCAL? */
-        assertEquals(DisconnectCause.INCOMING_MISSED, mConnection.getDisconnectCause());
+        assertEquals(DisconnectCause.INCOMING_MISSED, connection.getDisconnectCause());
 
     }
 
+    @FlakyTest
     @Test
     @MediumTest
     public void testMOCallSwitchHangupForeGround() {
@@ -300,6 +313,8 @@
         assertEquals(GsmCdmaCall.State.HOLDING, mCTUT.mBackgroundCall.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testMOCallPickUpHangUpResumeBackGround() {
@@ -338,11 +353,14 @@
         testMOCallPickUp();
         ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
         ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
-        verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
+        verify(mHandler, times(1))
+                .sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
         assertEquals(VOICE_CALL_STARTED_EVENT, mCaptorMessage.getValue().what);
 
     }
 
+    @FlakyTest
+    @Ignore
     @Test @SmallTest
     public void testVoiceCallEndedListener(){
         logd("register for voice call ended event");
@@ -350,7 +368,8 @@
         ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
         ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
         testMOCallHangup();
-        verify(mHandler,times(1)).sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
+        verify(mHandler, times(1))
+                .sendMessageAtTime(mCaptorMessage.capture(), mCaptorLong.capture());
         assertEquals(VOICE_CALL_ENDED_EVENT, mCaptorMessage.getValue().what);
     }
 
@@ -390,5 +409,27 @@
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mBackgroundCall.getState());
         assertEquals(GsmCdmaCall.State.IDLE, mCTUT.mRingingCall.getState());
     }
-}
 
+    @Test
+    @SmallTest
+    public void testUpdatePhoneTypeWithActiveCall() {
+        // verify getCurrentCalls is called on init
+        verify(mSimulatedCommandsVerifier).getCurrentCalls(any(Message.class));
+
+        // fake connection
+        mCTUT.mConnections[0] = mConnection;
+
+        // update phone type (call the function on same thread as the call tracker)
+        Handler updatePhoneTypeHandler = new Handler(mCTUT.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                mCTUT.updatePhoneType();
+            }
+        };
+        updatePhoneTypeHandler.sendEmptyMessage(0);
+        waitForMs(100);
+
+        // verify that the active call is disconnected
+        verify(mConnection).onDisconnect(DisconnectCause.ERROR_UNSPECIFIED);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
index df9a7ab..01f95d3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaConnectionTest.java
@@ -190,4 +190,26 @@
         assertEquals(DisconnectCause.LOCAL, connection.getDisconnectCause());
         assertTrue(connection.getDisconnectTime() <= System.currentTimeMillis());
     }
+
+    @Test @SmallTest
+    public void testAddressUpdate() {
+        String[] testAddressMappingSet[] = {
+                /* {"0:inputAddress", "1:updateAddress", "2:ExpectResult"} */
+                {"12345", "12345", "12345"},
+                {"12345", "67890", "67890"},
+                {"12345*00000", "12345", "12345*00000"},
+                {"12345*00000", "67890", "67890"},
+                {"12345*00000", "12345*00000", "12345*00000"},
+                {"12345;11111*00000", "12345", "12345"},
+                {"12345*00000;11111", "12345", "12345*00000"},
+                {"18412345*00000", "18412345", "18412345*00000"},
+                {"+8112345*00000", "+8112345", "+8112345*00000"}};
+        mDC.state = DriverCall.State.ALERTING;
+        for (String[] testAddress : testAddressMappingSet) {
+            connection = new GsmCdmaConnection(mPhone, testAddress[0], mCT, null, false);
+            mDC.number = testAddress[1];
+            connection.update(mDC);
+            assertEquals(testAddress[2], connection.getAddress());
+        }
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 1b79f3a..3a80fe0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -16,6 +16,24 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.Activity;
 import android.app.IApplicationThread;
 import android.content.IIntentReceiver;
@@ -26,7 +44,10 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.os.Process;
+import android.os.WorkSource;
 import android.preference.PreferenceManager;
+import android.support.test.filters.FlakyTest;
 import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.ServiceState;
@@ -41,31 +62,13 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.List;
 
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class GsmCdmaPhoneTest extends TelephonyTest {
     @Mock
     private Handler mTestHandler;
@@ -120,7 +123,7 @@
         waitUntilReady();
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mUiccController).registerForIccChanged(eq(mPhoneUT), integerArgumentCaptor.capture(),
-                anyObject());
+                nullable(Object.class));
         Message msg = Message.obtain();
         msg.what = integerArgumentCaptor.getValue();
         mPhoneUT.sendMessage(msg);
@@ -131,7 +134,7 @@
     public void tearDown() throws Exception {
         mPhoneUT.removeCallbacksAndMessages(null);
         mPhoneUT = null;
-        mGsmCdmaPhoneTestHandler.quitSafely();
+        mGsmCdmaPhoneTestHandler.quit();
         super.tearDown();
     }
 
@@ -169,8 +172,10 @@
     public void testGetCellLocation() {
         // GSM
         CellLocation cellLocation = new GsmCellLocation();
-        doReturn(cellLocation).when(mSST).getCellLocation();
-        assertEquals(cellLocation, mPhoneUT.getCellLocation());
+        WorkSource workSource = new WorkSource(Process.myUid(),
+            mContext.getPackageName());
+        doReturn(cellLocation).when(mSST).getCellLocation(workSource);
+        assertEquals(cellLocation, mPhoneUT.getCellLocation(workSource));
 
         // Switch to CDMA
         switchToCdma();
@@ -197,7 +202,8 @@
         waitForMs(50);
         */
 
-        CdmaCellLocation actualCellLocation = (CdmaCellLocation) mPhoneUT.getCellLocation();
+        CdmaCellLocation actualCellLocation =
+                (CdmaCellLocation) mPhoneUT.getCellLocation(workSource);
         assertEquals(CdmaCellLocation.INVALID_LAT_LONG,
                 actualCellLocation.getBaseStationLatitude());
         assertEquals(CdmaCellLocation.INVALID_LAT_LONG,
@@ -310,26 +316,26 @@
     public void testSendBurstDtmf() {
         //Should do nothing for GSM
         mPhoneUT.sendBurstDtmf("1234567890", 0, 0, null);
-        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(anyString(), anyInt(), anyInt(),
-                any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(nullable(String.class), anyInt(),
+                anyInt(), nullable(Message.class));
 
         switchToCdma();
         //invalid character
         mPhoneUT.sendBurstDtmf("12345a67890", 0, 0, null);
-        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(anyString(), anyInt(), anyInt(),
-                any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(nullable(String.class), anyInt(),
+                anyInt(), nullable(Message.class));
 
         //state IDLE
         mCT.mState = PhoneConstants.State.IDLE;
         mPhoneUT.sendBurstDtmf("1234567890", 0, 0, null);
-        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(anyString(), anyInt(), anyInt(),
-                any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(nullable(String.class), anyInt(),
+                anyInt(), nullable(Message.class));
 
         //state RINGING
         mCT.mState = PhoneConstants.State.RINGING;
         mPhoneUT.sendBurstDtmf("1234567890", 0, 0, null);
-        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(anyString(), anyInt(), anyInt(),
-                any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).sendBurstDtmf(nullable(String.class), anyInt(),
+                anyInt(), nullable(Message.class));
 
         mCT.mState = PhoneConstants.State.OFFHOOK;
         mPhoneUT.sendBurstDtmf("1234567890", 0, 0, null);
@@ -355,7 +361,7 @@
         voiceMailNumber = "1234567891";
         mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
         verify(mSimRecords).setVoiceMailNumber(eq("alphaTag"), eq(voiceMailNumber),
-                any(Message.class));
+                nullable(Message.class));
 
         doReturn(voiceMailNumber).when(mSimRecords).getVoiceMailNumber();
         assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
@@ -397,8 +403,9 @@
         assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
     }
 
+    @FlakyTest
     @Test
-    @SmallTest
+    @Ignore
     public void testVoiceMailCount() {
         // initial value
         assertEquals(0, mPhoneUT.getVoiceMessageCount());
@@ -454,16 +461,18 @@
         // invalid reason (-1)
         mPhoneUT.getCallForwardingOption(-1, null);
         verify(mSimulatedCommandsVerifier, times(0)).queryCallForwardStatus(
-                anyInt(), anyInt(), anyString(), any(Message.class));
+                anyInt(), anyInt(), nullable(String.class), nullable(Message.class));
 
         // valid reason
         String imsi = "1234567890";
         doReturn(imsi).when(mSimRecords).getIMSI();
         mPhoneUT.getCallForwardingOption(CF_REASON_UNCONDITIONAL, null);
         verify(mSimulatedCommandsVerifier).queryCallForwardStatus(
-                eq(CF_REASON_UNCONDITIONAL), anyInt(), anyString(), any(Message.class));
+                eq(CF_REASON_UNCONDITIONAL), anyInt(), nullable(String.class),
+                nullable(Message.class));
         waitForMs(50);
-        verify(mSimRecords).setVoiceCallForwardingFlag(anyInt(), anyBoolean(), anyString());
+        verify(mSimRecords).setVoiceCallForwardingFlag(anyInt(), anyBoolean(),
+                nullable(String.class));
 
         // should have updated shared preferences
         SharedPreferences sharedPreferences = PreferenceManager.
@@ -487,13 +496,14 @@
         mPhoneUT.setCallForwardingOption(-1, CF_REASON_UNCONDITIONAL,
                 cfNumber, 0, null);
         verify(mSimulatedCommandsVerifier, times(0)).setCallForward(anyInt(), anyInt(), anyInt(),
-                anyString(), anyInt(), any(Message.class));
+                nullable(String.class), anyInt(), nullable(Message.class));
 
         // valid action
         mPhoneUT.setCallForwardingOption(CF_ACTION_ENABLE, CF_REASON_UNCONDITIONAL, cfNumber, 0,
                 null);
         verify(mSimulatedCommandsVerifier).setCallForward(eq(CF_ACTION_ENABLE),
-                eq(CF_REASON_UNCONDITIONAL), anyInt(), eq(cfNumber), eq(0), any(Message.class));
+                eq(CF_REASON_UNCONDITIONAL), anyInt(), eq(cfNumber), eq(0),
+                nullable(Message.class));
         waitForMs(50);
         verify(mSimRecords).setVoiceCallForwardingFlag(anyInt(), anyBoolean(), eq(cfNumber));
     }
@@ -502,52 +512,53 @@
      * GsmCdmaPhone handles a lot of messages. This function verifies behavior for messages that are
      * received when obj is created and that are received on phone type switch
      */
+    @FlakyTest
     @Test
     @SmallTest
     public void testHandleInitialMessages() {
         // EVENT_RADIO_AVAILABLE
-        verify(mSimulatedCommandsVerifier).getBasebandVersion(any(Message.class));
-        verify(mSimulatedCommandsVerifier).getIMEI(any(Message.class));
-        verify(mSimulatedCommandsVerifier).getIMEISV(any(Message.class));
-        verify(mSimulatedCommandsVerifier).getRadioCapability(any(Message.class));
+        verify(mSimulatedCommandsVerifier).getBasebandVersion(nullable(Message.class));
+        verify(mSimulatedCommandsVerifier).getDeviceIdentity(nullable(Message.class));
+        verify(mSimulatedCommandsVerifier).getRadioCapability(nullable(Message.class));
         // once as part of constructor, and once on radio available
         verify(mSimulatedCommandsVerifier, times(2)).startLceService(anyInt(), anyBoolean(),
-                any(Message.class));
+                nullable(Message.class));
 
         // EVENT_RADIO_ON
-        verify(mSimulatedCommandsVerifier).getVoiceRadioTechnology(any(Message.class));
+        verify(mSimulatedCommandsVerifier).getVoiceRadioTechnology(nullable(Message.class));
         verify(mSimulatedCommandsVerifier).setPreferredNetworkType(
-                eq(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA), any(Message.class));
+                eq(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA), nullable(Message.class));
 
         // verify responses for above requests:
         // baseband version
         verify(mTelephonyManager).setBasebandVersionForPhone(eq(mPhoneUT.getPhoneId()),
-                anyString());
+                nullable(String.class));
         // IMEI
         assertEquals(SimulatedCommands.FAKE_IMEI, mPhoneUT.getImei());
         // IMEISV
         assertEquals(SimulatedCommands.FAKE_IMEISV, mPhoneUT.getDeviceSvn());
         // radio capability
-        verify(mSimulatedCommandsVerifier).getNetworkSelectionMode(any(Message.class));
+        verify(mSimulatedCommandsVerifier).getNetworkSelectionMode(nullable(Message.class));
 
         switchToCdma(); // this leads to eventRadioAvailable handling on cdma
 
         // EVENT_RADIO_AVAILABLE
-        verify(mSimulatedCommandsVerifier, times(2)).getBasebandVersion(any(Message.class));
-        verify(mSimulatedCommandsVerifier).getDeviceIdentity(any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(2)).getBasebandVersion(nullable(Message.class));
+        verify(mSimulatedCommandsVerifier, times(2)).getDeviceIdentity(nullable(Message.class));
         verify(mSimulatedCommandsVerifier, times(3)).startLceService(anyInt(), anyBoolean(),
-                any(Message.class));
+                nullable(Message.class));
 
         // EVENT_RADIO_ON
-        verify(mSimulatedCommandsVerifier, times(2)).getVoiceRadioTechnology(any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(2)).getVoiceRadioTechnology(
+                nullable(Message.class));
         // once on radio on, and once on get baseband version
         verify(mSimulatedCommandsVerifier, times(3)).setPreferredNetworkType(
-                eq(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA), any(Message.class));
+                eq(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA), nullable(Message.class));
 
         // verify responses for above requests:
         // baseband version
         verify(mTelephonyManager, times(2)).setBasebandVersionForPhone(eq(mPhoneUT.getPhoneId()),
-                anyString());
+                nullable(String.class));
         // device identity
         assertEquals(SimulatedCommands.FAKE_IMEI, mPhoneUT.getImei());
         assertEquals(SimulatedCommands.FAKE_IMEISV, mPhoneUT.getDeviceSvn());
@@ -559,9 +570,9 @@
     @SmallTest
     public void testEmergencyCallbackMessages() {
         verify(mSimulatedCommandsVerifier).setEmergencyCallbackMode(eq(mPhoneUT), anyInt(),
-                anyObject());
+                nullable(Object.class));
         verify(mSimulatedCommandsVerifier).registerForExitEmergencyCallbackMode(eq(mPhoneUT),
-                anyInt(), anyObject());
+                anyInt(), nullable(Object.class));
 
         // verify handling of emergency callback mode
         mSimulatedCommands.notifyEmergencyCallbackMode();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
index f2e5da1..06cd54f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmSmsTest.java
@@ -41,6 +41,10 @@
         assertEquals("+14155551212", sms.getServiceCenterAddress());
         assertEquals("+16505551111", sms.getOriginatingAddress());
         assertEquals("(Subject)Test", sms.getMessageBody());
+
+        pdu = "07914151551512F20409E1BADCBE5AF100006060605130308A04D4F29C0E";
+        sms = SmsMessage.createFromPdu(HexDump.hexStringToByteArray(pdu));
+        assertEquals("*#abc#*51", sms.getOriginatingAddress());
     }
 
     @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
index 24c1283..105e0f8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -24,11 +26,11 @@
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -36,13 +38,13 @@
 import android.content.IntentFilter;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.test.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Singleton;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -95,11 +97,11 @@
     @After
     public void tearDown() throws Exception {
         mImsSmsDispatcher = null;
-        mImsSmsDispatcherTestHandler.quitSafely();
+        mImsSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
-    @Test @SmallTest
+    @Test @SmallTest @FlakyTest @Ignore
     public void testSmsHandleStateUpdate() throws Exception {
         assertEquals(SmsConstants.FORMAT_UNKNOWN, mImsSmsDispatcher.getImsSmsFormat());
         //Mock ImsNetWorkStateChange with GSM phone type
@@ -113,7 +115,7 @@
         assertTrue(mImsSmsDispatcher.isIms());
     }
 
-    @Test @SmallTest
+    @Test @SmallTest @FlakyTest @Ignore
     public void testSendImsGmsTest() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
         mImsSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
@@ -172,7 +174,7 @@
         // unmock ActivityManager to be able to register receiver, create real PendingIntent and
         // receive TEST_INTENT
         restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
-        restoreInstance(ActivityManagerNative.class, "gDefault", null);
+        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
 
         Context realContext = TestApplication.getAppContext();
         realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
new file mode 100644
index 0000000..761bd5f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsiEncryptionInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.telephony.ImsiEncryptionInfo;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.PublicKey;
+import java.security.cert.CertificateFactory;
+import java.util.Date;
+
+public class ImsiEncryptionInfoTest {
+    private ImsiEncryptionInfo mImsiEncryptionInfo;
+    private PublicKey mPublicKey;
+    private Date mDate = new Date(1496795015);
+
+    private static final String TEST_CERT = ""
+            + "MIIDsjCCAxugAwIBAgIJAPLf2gS0zYGUMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYDVQQGEwJVUzET"
+            + "MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEPMA0GA1UEChMGR29v"
+            + "Z2xlMRAwDgYDVQQLEwd0ZXN0aW5nMRYwFAYDVQQDEw1HZXJlbXkgQ29uZHJhMSEwHwYJKoZIhvcN"
+            + "AQkBFhJnY29uZHJhQGdvb2dsZS5jb20wHhcNMTIwNzE0MTc1MjIxWhcNMTIwODEzMTc1MjIxWjCB"
+            + "mDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDU1vdW50YWluIFZp"
+            + "ZXcxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMHdGVzdGluZzEWMBQGA1UEAxMNR2VyZW15IENv"
+            + "bmRyYTEhMB8GCSqGSIb3DQEJARYSZ2NvbmRyYUBnb29nbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUA"
+            + "A4GNADCBiQKBgQCjGGHATBYlmas+0sEECkno8LZ1KPglb/mfe6VpCT3GhSr+7br7NG/ZwGZnEhLq"
+            + "E7YIH4fxltHmQC3Tz+jM1YN+kMaQgRRjo/LBCJdOKaMwUbkVynAH6OYsKevjrOPk8lfM5SFQzJMG"
+            + "sA9+Tfopr5xg0BwZ1vA/+E3mE7Tr3M2UvwIDAQABo4IBADCB/TAdBgNVHQ4EFgQUhzkS9E6G+x8W"
+            + "L4EsmRjDxu28tHUwgc0GA1UdIwSBxTCBwoAUhzkS9E6G+x8WL4EsmRjDxu28tHWhgZ6kgZswgZgx"
+            + "CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3"
+            + "MQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB3Rlc3RpbmcxFjAUBgNVBAMTDUdlcmVteSBDb25k"
+            + "cmExITAfBgkqhkiG9w0BCQEWEmdjb25kcmFAZ29vZ2xlLmNvbYIJAPLf2gS0zYGUMAwGA1UdEwQF"
+            + "MAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAYiugFDmbDOQ2U/+mqNt7o8ftlEo9SJrns6O8uTtK6AvR"
+            + "orDrR1AXTXkuxwLSbmVfedMGOZy7Awh7iZa8hw5x9XmUudfNxvmrKVEwGQY2DZ9PXbrnta/dwbhK"
+            + "mWfoepESVbo7CKIhJp8gRW0h1Z55ETXD57aGJRvQS4pxkP8ANhM=";
+
+    @After
+    public void tearDown() throws Exception {
+        mImsiEncryptionInfo = null;
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mPublicKey = createPublicKey(TEST_CERT);
+        mImsiEncryptionInfo = new ImsiEncryptionInfo("310", "270", TelephonyManager.KEY_TYPE_WLAN,
+                "key1=value", mPublicKey, mDate);
+    }
+
+    private static PublicKey createPublicKey(String cert) throws Exception {
+        byte[] derCert = Base64.decode(cert.getBytes(), Base64.DEFAULT);
+        InputStream istream = new ByteArrayInputStream(derCert);
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        return cf.generateCertificate(istream).getPublicKey();
+    }
+
+    /**
+     * Tests that all the class variables are set correctly.
+     */
+    @Test
+    @SmallTest
+    public void testSubProperties() {
+        assertEquals("310", mImsiEncryptionInfo.getMcc());
+        assertEquals("270", mImsiEncryptionInfo.getMnc());
+        assertEquals(TelephonyManager.KEY_TYPE_WLAN, mImsiEncryptionInfo.getKeyType());
+        assertEquals("key1=value", mImsiEncryptionInfo.getKeyIdentifier());
+        Date date = mImsiEncryptionInfo.getExpirationTime();
+        assertEquals(mDate, mImsiEncryptionInfo.getExpirationTime());
+    }
+
+    /**
+     * Tests the parceling/un-parceling of the object.
+     */
+    @Test
+    @SmallTest
+    public void testParcel() {
+        Parcel p = Parcel.obtain();
+        p.setDataPosition(0);
+        mImsiEncryptionInfo.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ImsiEncryptionInfo nw = new ImsiEncryptionInfo(p);
+        assertEquals("310", mImsiEncryptionInfo.getMcc());
+        assertEquals("270", mImsiEncryptionInfo.getMnc());
+        assertEquals(TelephonyManager.KEY_TYPE_WLAN, mImsiEncryptionInfo.getKeyType());
+        assertEquals("key1=value", mImsiEncryptionInfo.getKeyIdentifier());
+        assertEquals(mPublicKey, mImsiEncryptionInfo.getPublicKey());
+        assertEquals(mDate, mImsiEncryptionInfo.getExpirationTime());
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
index 4c8ccc1..a036c62 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/InboundSmsTrackerTest.java
@@ -34,6 +34,7 @@
     private static final long FAKE_TIMESTAMP = 123456L;
     private static final int FAKE_DEST_PORT = 1234;
     private static final String FAKE_ADDRESS = "address";
+    private static final String FAKE_DISPLAY_ADDRESS = "disp_addr";
     private static final int FAKE_REFERENCE_NUMBER = 345;
     private static final int FAKE_SEQUENCE_NUMBER = 3;
     private static final int FAKE_MESSAGE_COUNT = 5;
@@ -42,19 +43,22 @@
     @Before
     public void setUp() throws Exception {
         mInboundSmsTracker = new InboundSmsTracker(FAKE_PDU, FAKE_TIMESTAMP, FAKE_DEST_PORT, false,
-                FAKE_ADDRESS, FAKE_REFERENCE_NUMBER, FAKE_SEQUENCE_NUMBER, FAKE_MESSAGE_COUNT,
-                false, FAKE_MESSAGE_BODY);
+                FAKE_ADDRESS, FAKE_DISPLAY_ADDRESS, FAKE_REFERENCE_NUMBER, FAKE_SEQUENCE_NUMBER,
+                FAKE_MESSAGE_COUNT, false, FAKE_MESSAGE_BODY);
     }
 
     public static MatrixCursor createFakeCursor() {
         MatrixCursor mc = new MatrixCursor(
-                new String[]{"pdu", "seq", "dest", "date", "ref", "cnt", "addr", "id", "msg_body"});
+                new String[]{"pdu", "seq", "dest", "date", "ref", "cnt", "addr", "id", "msg_body",
+                        "display_originating_addr"});
         mc.addRow(new Object[]{HexDump.toHexString(FAKE_PDU),
                 FAKE_SEQUENCE_NUMBER, FAKE_DEST_PORT, FAKE_TIMESTAMP,
-                FAKE_REFERENCE_NUMBER, FAKE_MESSAGE_COUNT, FAKE_ADDRESS, 1, FAKE_MESSAGE_BODY});
+                FAKE_REFERENCE_NUMBER, FAKE_MESSAGE_COUNT, FAKE_ADDRESS, 1, FAKE_MESSAGE_BODY,
+                FAKE_DISPLAY_ADDRESS});
         mc.addRow(new Object[]{HexDump.toHexString(FAKE_PDU),
                 FAKE_SEQUENCE_NUMBER, FAKE_DEST_PORT, FAKE_TIMESTAMP,
-                FAKE_REFERENCE_NUMBER, FAKE_MESSAGE_COUNT, FAKE_ADDRESS, 2, FAKE_MESSAGE_BODY});
+                FAKE_REFERENCE_NUMBER, FAKE_MESSAGE_COUNT, FAKE_ADDRESS, 2, FAKE_MESSAGE_BODY,
+                FAKE_DISPLAY_ADDRESS});
         mc.moveToFirst();
         return mc;
     }
@@ -73,6 +77,7 @@
         assertEquals(1, mInboundSmsTracker.getIndexOffset());
         assertEquals(SmsConstants.FORMAT_3GPP, mInboundSmsTracker.getFormat());
         assertEquals(FAKE_MESSAGE_BODY, mInboundSmsTracker.getMessageBody());
+        assertEquals(FAKE_DISPLAY_ADDRESS, mInboundSmsTracker.getDisplayAddress());
 
         String[] args = new String[]{"123"};
         mInboundSmsTracker.setDeleteWhere(InboundSmsHandler.SELECT_BY_ID, args);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java b/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
index 7030440..de98c67 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/MccTableTest.java
@@ -16,67 +16,84 @@
 
 package com.android.internal.telephony;
 
-import com.android.internal.telephony.MccTable;
-
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import android.telephony.Rlog;
+import java.util.Locale;
 
 public class MccTableTest extends AndroidTestCase {
     private final static String LOG_TAG = "GSM";
 
     @SmallTest
     public void testTimeZone() throws Exception {
-        assertEquals(MccTable.defaultTimeZoneForMcc(208), "Europe/Paris");
-        assertEquals(MccTable.defaultTimeZoneForMcc(232), "Europe/Vienna");
-        assertEquals(MccTable.defaultTimeZoneForMcc(655), "Africa/Johannesburg");
-        assertEquals(MccTable.defaultTimeZoneForMcc(440), "Asia/Tokyo");
-        assertEquals(MccTable.defaultTimeZoneForMcc(441), "Asia/Tokyo");
-        assertEquals(MccTable.defaultTimeZoneForMcc(525), "Asia/Singapore");
-        assertEquals(MccTable.defaultTimeZoneForMcc(240), "Europe/Stockholm");
-        assertEquals(MccTable.defaultTimeZoneForMcc(0), null);    // mcc not defined, hence default
-        assertEquals(MccTable.defaultTimeZoneForMcc(2000), null); // mcc not defined, hence default
+        assertEquals("Europe/Paris", MccTable.defaultTimeZoneForMcc(208));
+        assertEquals("Europe/Vienna", MccTable.defaultTimeZoneForMcc(232));
+        assertEquals("Africa/Johannesburg", MccTable.defaultTimeZoneForMcc(655));
+        assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(440));
+        assertEquals("Asia/Tokyo", MccTable.defaultTimeZoneForMcc(441));
+        assertEquals("Asia/Singapore", MccTable.defaultTimeZoneForMcc(525));
+        assertEquals("Europe/Stockholm", MccTable.defaultTimeZoneForMcc(240));
+
+        /* A test for the special handling for MCC 505. http://b/33228250. */
+        assertEquals("Australia/Sydney", MccTable.defaultTimeZoneForMcc(505));
+        assertEquals(null, MccTable.defaultTimeZoneForMcc(0));    // mcc not defined, hence default
+        assertEquals(null, MccTable.defaultTimeZoneForMcc(2000)); // mcc not defined, hence default
     }
 
     @SmallTest
     public void testCountryCode() throws Exception {
-        assertEquals(MccTable.countryCodeForMcc(270), "lu");
-        assertEquals(MccTable.countryCodeForMcc(202), "gr");
-        assertEquals(MccTable.countryCodeForMcc(750), "fk");
-        assertEquals(MccTable.countryCodeForMcc(646), "mg");
-        assertEquals(MccTable.countryCodeForMcc(314), "us");
-        assertEquals(MccTable.countryCodeForMcc(300), "");  // mcc not defined, hence default
-        assertEquals(MccTable.countryCodeForMcc(0), "");    // mcc not defined, hence default
-        assertEquals(MccTable.countryCodeForMcc(2000), ""); // mcc not defined, hence default
+        assertEquals("lu", MccTable.countryCodeForMcc(270));
+        assertEquals("gr", MccTable.countryCodeForMcc(202));
+        assertEquals("fk", MccTable.countryCodeForMcc(750));
+        assertEquals("mg", MccTable.countryCodeForMcc(646));
+        assertEquals("us", MccTable.countryCodeForMcc(314));
+        assertEquals("", MccTable.countryCodeForMcc(300));  // mcc not defined, hence default
+        assertEquals("", MccTable.countryCodeForMcc(0));    // mcc not defined, hence default
+        assertEquals("", MccTable.countryCodeForMcc(2000)); // mcc not defined, hence default
     }
 
     @SmallTest
     public void testLang() throws Exception {
-        assertEquals(MccTable.defaultLanguageForMcc(311), "en");
-        assertEquals(MccTable.defaultLanguageForMcc(232), "de");
-        assertEquals(MccTable.defaultLanguageForMcc(230), "cs");
-        assertEquals(MccTable.defaultLanguageForMcc(204), "nl");
-        assertEquals(MccTable.defaultLanguageForMcc(274), "is");
-        assertEquals(MccTable.defaultLanguageForMcc(0), null);    // mcc not defined, hence default
-        assertEquals(MccTable.defaultLanguageForMcc(2000), null); // mcc not defined, hence default
+        assertEquals("en", MccTable.defaultLanguageForMcc(311));
+        assertEquals("de", MccTable.defaultLanguageForMcc(232));
+        assertEquals("cs", MccTable.defaultLanguageForMcc(230));
+        assertEquals("nl", MccTable.defaultLanguageForMcc(204));
+        assertEquals("is", MccTable.defaultLanguageForMcc(274));
+        assertEquals(null, MccTable.defaultLanguageForMcc(0));    // mcc not defined, hence default
+        assertEquals(null, MccTable.defaultLanguageForMcc(2000)); // mcc not defined, hence default
     }
 
     @SmallTest
     public void testLang_India() throws Exception {
-        assertEquals(MccTable.defaultLanguageForMcc(404), "en");
-        assertEquals(MccTable.defaultLanguageForMcc(405), "en");
-        assertEquals(MccTable.defaultLanguageForMcc(406), "en");
+        assertEquals("en", MccTable.defaultLanguageForMcc(404));
+        assertEquals("en", MccTable.defaultLanguageForMcc(405));
+        assertEquals("en", MccTable.defaultLanguageForMcc(406));
+    }
+
+    @SmallTest
+    public void testLocale() throws Exception {
+        assertEquals(Locale.forLanguageTag("en-CA"),
+                MccTable.getLocaleFromMcc(getContext(), 302, null));
+        assertEquals(Locale.forLanguageTag("en-GB"),
+                MccTable.getLocaleFromMcc(getContext(), 234, null));
+        assertEquals(Locale.forLanguageTag("en-US"),
+                MccTable.getLocaleFromMcc(getContext(), 0, "en"));
+        assertEquals(Locale.forLanguageTag("zh-HK"),
+                MccTable.getLocaleFromMcc(getContext(), 454, null));
+        assertEquals(Locale.forLanguageTag("en-HK"),
+                MccTable.getLocaleFromMcc(getContext(), 454, "en"));
+        assertEquals(Locale.forLanguageTag("zh-TW"),
+                MccTable.getLocaleFromMcc(getContext(), 466, null));
     }
 
     @SmallTest
     public void testSmDigits() throws Exception {
-        assertEquals(MccTable.smallestDigitsMccForMnc(312), 3);
-        assertEquals(MccTable.smallestDigitsMccForMnc(430), 2);
-        assertEquals(MccTable.smallestDigitsMccForMnc(365), 3);
-        assertEquals(MccTable.smallestDigitsMccForMnc(536), 2);
-        assertEquals(MccTable.smallestDigitsMccForMnc(352), 2);  // sd not defined, hence default
-        assertEquals(MccTable.smallestDigitsMccForMnc(0), 2);    // mcc not defined, hence default
-        assertEquals(MccTable.smallestDigitsMccForMnc(2000), 2); // mcc not defined, hence default
+        assertEquals(3, MccTable.smallestDigitsMccForMnc(312));
+        assertEquals(2, MccTable.smallestDigitsMccForMnc(430));
+        assertEquals(3, MccTable.smallestDigitsMccForMnc(365));
+        assertEquals(2, MccTable.smallestDigitsMccForMnc(536));
+        assertEquals(2, MccTable.smallestDigitsMccForMnc(352));  // sd not defined, hence default
+        assertEquals(2, MccTable.smallestDigitsMccForMnc(0));    // mcc not defined, hence default
+        assertEquals(2, MccTable.smallestDigitsMccForMnc(2000)); // mcc not defined, hence default
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NeighboringCellInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/NeighboringCellInfoTest.java
index b63dc71..f49fffd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NeighboringCellInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NeighboringCellInfoTest.java
@@ -18,7 +18,7 @@
 import android.os.Parcel;
 import android.test.AndroidTestCase;
 import android.telephony.NeighboringCellInfo;
-import android.test. suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.SmallTest;
 
 import static android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN;
 import static android.telephony.TelephonyManager.NETWORK_TYPE_EDGE;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
new file mode 100644
index 0000000..fe75088
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanRequestTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.RadioNetworkConstants.EutranBands;
+import android.telephony.RadioNetworkConstants.GeranBands;
+import android.telephony.RadioNetworkConstants.RadioAccessNetworks;
+
+import org.junit.Test;
+
+/** Unit tests for {@link NetworkScanRequest}. */
+
+public class NetworkScanRequestTest {
+
+    @Test
+    @SmallTest
+    public void testParcel() {
+        int ranGsm = RadioAccessNetworks.GERAN;
+        int[] gsmBands = {GeranBands.BAND_T380, GeranBands.BAND_T410};
+        int[] gsmChannels = {1, 2, 3, 4};
+        RadioAccessSpecifier gsm = new RadioAccessSpecifier(ranGsm, gsmBands, gsmChannels);
+        int ranLte = RadioAccessNetworks.EUTRAN;
+        int[] lteBands = {EutranBands.BAND_10, EutranBands.BAND_11};
+        int[] lteChannels = {5, 6, 7, 8};
+        RadioAccessSpecifier lte = new RadioAccessSpecifier(ranLte, lteBands, lteChannels);
+        RadioAccessSpecifier[] ras = {gsm, lte};
+        NetworkScanRequest nsq = new NetworkScanRequest(NetworkScanRequest.SCAN_TYPE_ONE_SHOT, ras);
+
+        Parcel p = Parcel.obtain();
+        nsq.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        NetworkScanRequest newNsq = NetworkScanRequest.CREATOR.createFromParcel(p);
+        assertEquals(nsq, newNsq);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
new file mode 100644
index 0000000..f149211
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellIdentityLte;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+/** Unit tests for {@link NetworkScanResult}. */
+
+public class NetworkScanResultTest {
+
+    @Test
+    @SmallTest
+    public void testParcel() {
+        ArrayList<CellInfo> infos = new ArrayList<CellInfo>();
+
+        CellIdentityGsm cig = new CellIdentityGsm(310, 310, 1, 2, 3, 4);
+        CellSignalStrengthGsm cssg = new CellSignalStrengthGsm();
+        cssg.initialize(5, 6, 7);
+        CellInfoGsm gsm = new CellInfoGsm();
+        gsm.setRegistered(true);
+        gsm.setTimeStampType(8);
+        gsm.setTimeStamp(9);
+        gsm.setCellIdentity(cig);
+        gsm.setCellSignalStrength(cssg);
+        infos.add(gsm);
+
+        CellIdentityLte cil = new CellIdentityLte(320, 320, 11, 12, 13, 14);
+        CellSignalStrengthLte cssl = new CellSignalStrengthLte();
+        cssl.initialize(15, 16, 17, 18, 19, 20);
+        CellInfoLte lte = new CellInfoLte();
+        lte.setRegistered(false);
+        lte.setTimeStampType(21);
+        lte.setTimeStamp(22);
+        lte.setCellIdentity(cil);
+        lte.setCellSignalStrength(cssl);
+        infos.add(lte);
+
+        NetworkScanResult nsr = new NetworkScanResult(0, 0, infos);
+
+        Parcel p = Parcel.obtain();
+        nsr.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        NetworkScanResult newNsr = NetworkScanResult.CREATOR.createFromParcel(p);
+        assertEquals(nsr, newNsr);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index 5af733b..faeabd5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -16,16 +16,24 @@
 
 package com.android.internal.telephony;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
 import android.net.Uri;
-import android.platform.test.annotations.Postsubmit;
-import android.test.AndroidTestCase;
+import android.support.test.filters.FlakyTest;
+import android.telephony.PhoneNumberUtils;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.SpannableStringBuilder;
-import android.telephony.PhoneNumberUtils;
 
-public class PhoneNumberUtilsTest extends AndroidTestCase {
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class PhoneNumberUtilsTest {
 
     @SmallTest
+    @Test
     public void testExtractNetworkPortion() throws Exception {
         assertEquals(
                 "+17005554141",
@@ -192,6 +200,21 @@
     }
 
     @SmallTest
+    @Test
+    public void testNonIntegerAddress() {
+        byte[] b = new byte[6];
+        b[0] = (byte) 0x81; b[1] = (byte) 0xba; b[2] = (byte) 0xdc; b[3] = (byte) 0xbe;
+        b[4] = (byte) 0x5a; b[5] = (byte) 0xf1;
+        assertEquals("*#abc#*51",
+                PhoneNumberUtils.calledPartyBCDToString(
+                        b, 0, 6, PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY));
+        assertEquals("*#,N;#*51",
+                PhoneNumberUtils.calledPartyBCDToString(
+                        b, 0, 6, PhoneNumberUtils.BCD_EXTENDED_TYPE_EF_ADN));
+    }
+
+    @SmallTest
+    @Test
     public void testExtractNetworkPortionAlt() throws Exception {
         assertEquals(
                 "+17005554141",
@@ -254,6 +277,7 @@
     }
 
     @SmallTest
+    @Test
     public void testB() throws Exception {
         assertEquals("", PhoneNumberUtils.extractPostDialPortion("+17005554141"));
         assertEquals("", PhoneNumberUtils.extractPostDialPortion("+1 (700).555-4141"));
@@ -265,6 +289,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCompare() throws Exception {
         // this is odd
         assertFalse(PhoneNumberUtils.compare("", ""));
@@ -330,8 +355,8 @@
         assertTrue(PhoneNumberUtils.compare("404-04", "40404"));
     }
 
-
     @SmallTest
+    @Test
     public void testToCallerIDIndexable() throws Exception {
         assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("17005554141"));
         assertEquals("1414555", PhoneNumberUtils.toCallerIDMinMatch("1-700-555-4141"));
@@ -349,6 +374,7 @@
     }
 
     @SmallTest
+    @Test
     public void testGetIndexable() throws Exception {
         assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141"));
         assertEquals("14145550071", PhoneNumberUtils.getStrippedReversed("1-700-555-4141,1234"));
@@ -365,6 +391,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNanpFormatting() {
         SpannableStringBuilder number = new SpannableStringBuilder();
         number.append("8005551212");
@@ -393,6 +420,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConvertKeypadLettersToDigits() {
         assertEquals("1-800-4664-411",
                      PhoneNumberUtils.convertKeypadLettersToDigits("1-800-GOOG-411"));
@@ -413,7 +441,7 @@
     }
 
     // To run this test, the device has to be registered with network
-    @Postsubmit
+    @FlakyTest
     public void testCheckAndProcessPlusCode() {
         assertEquals("0118475797000",
                 PhoneNumberUtils.cdmaCheckAndProcessPlusCode("+8475797000"));
@@ -467,6 +495,7 @@
     }
 
     @SmallTest
+    @Test
     public void testCheckAndProcessPlusCodeByNumberFormat() {
         assertEquals("18475797000",
                 PhoneNumberUtils.cdmaCheckAndProcessPlusCodeByNumberFormat("+18475797000",
@@ -477,6 +506,7 @@
      * Basic checks for the VoiceMail number.
      */
     @SmallTest
+    @Test
     public void testWithNumberNotEqualToVoiceMail() throws Exception {
         assertFalse(PhoneNumberUtils.isVoiceMailNumber("911"));
         assertFalse(PhoneNumberUtils.isVoiceMailNumber("tel:911"));
@@ -492,6 +522,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatNumberToE164() {
         // Note: ISO 3166-1 only allows upper case country codes.
         assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "US"));
@@ -500,6 +531,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatNumber() {
         assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US"));
         assertEquals("223-4567", PhoneNumberUtils.formatNumber("2234567", "US"));
@@ -510,7 +542,32 @@
         assertEquals("800-GOOG-114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US"));
     }
 
+    /**
+     * Tests ability to format phone numbers from Japan using the international format when the
+     * current country is not Japan.
+     */
     @SmallTest
+    @Test
+    public void testFormatJapanInternational() {
+        assertEquals("+81 90-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "US"));
+    }
+
+    /**
+     * Tests ability to format phone numbers from Japan using the national format when the current
+     * country is Japan.
+     */
+    @SmallTest
+    @Test
+    public void testFormatJapanNational() {
+        assertEquals("090-6657-0660", PhoneNumberUtils.formatNumber("09066570660", "JP"));
+        assertEquals("090-6657-1180", PhoneNumberUtils.formatNumber("+819066571180", "JP"));
+
+        // US number should still be internationally formatted
+        assertEquals("+1 650-555-1212", PhoneNumberUtils.formatNumber("+16505551212", "JP"));
+    }
+
+    @SmallTest
+    @Test
     public void testFormatNumber_LeadingStarAndHash() {
         // Numbers with a leading '*' or '#' should be left unchanged.
         assertEquals("*650 2910000", PhoneNumberUtils.formatNumber("*650 2910000", "US"));
@@ -524,6 +581,7 @@
     }
 
     @SmallTest
+    @Test
     public void testNormalizeNumber() {
         assertEquals("6502910000", PhoneNumberUtils.normalizeNumber("650 2910000"));
         assertEquals("1234567", PhoneNumberUtils.normalizeNumber("12,3#4*567"));
@@ -532,6 +590,7 @@
     }
 
     @SmallTest
+    @Test
     public void testFormatDailabeNumber() {
         // Using the phoneNumberE164's country code
         assertEquals("(650) 291-0000",
@@ -562,7 +621,9 @@
                 PhoneNumberUtils.formatNumber("011861088880000", "", "GB"));
     }
 
-    @SmallTest
+    @FlakyTest
+    @Test
+    @Ignore
     public void testIsEmergencyNumber() {
         // There are two parallel sets of tests here: one for the
         // regular isEmergencyNumber() method, and the other for
@@ -616,6 +677,7 @@
     }
 
     @SmallTest
+    @Test
     public void testStripSeparators() {
         // Smoke tests which should never fail.
         assertEquals("1234567890", PhoneNumberUtils.stripSeparators("1234567890"));
@@ -631,6 +693,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConvertAndStrip() {
         // Smoke tests which should never fail.
         assertEquals("1234567890", PhoneNumberUtils.convertAndStrip("1234567890"));
@@ -647,6 +710,7 @@
     }
 
     @SmallTest
+    @Test
     public void testConvertSipUriToTelUri1() {
         // Nominal case, a tel Uri came in, so we expect one out.
         Uri source = Uri.fromParts("tel", "+16505551212", null);
@@ -667,4 +731,37 @@
         converted = PhoneNumberUtils.convertSipUriToTelUri(source);
         assertEquals(expected, converted);
     }
+
+    @SmallTest
+    @Test
+    public void testIsInternational() {
+        assertFalse(PhoneNumberUtils.isInternationalNumber("", "US"));
+        assertFalse(PhoneNumberUtils.isInternationalNumber(null, "US"));
+        assertFalse(PhoneNumberUtils.isInternationalNumber("+16505551212", "US"));
+        assertTrue(PhoneNumberUtils.isInternationalNumber("+16505551212", "UK"));
+        assertTrue(PhoneNumberUtils.isInternationalNumber("+16505551212", "JP"));
+        assertTrue(PhoneNumberUtils.isInternationalNumber("+86 10 8888 0000", "US"));
+        assertTrue(PhoneNumberUtils.isInternationalNumber("001-541-754-3010", "DE"));
+        assertFalse(PhoneNumberUtils.isInternationalNumber("001-541-754-3010", "US"));
+        assertTrue(PhoneNumberUtils.isInternationalNumber("01161396694916", "US"));
+        assertTrue(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "US"));
+        assertFalse(PhoneNumberUtils.isInternationalNumber("011-613-966-94916", "AU"));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsUriNumber() {
+        assertTrue(PhoneNumberUtils.isUriNumber("foo@google.com"));
+        assertTrue(PhoneNumberUtils.isUriNumber("xyz@zzz.org"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+15103331245"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+659231235"));
+    }
+
+    @SmallTest
+    @Test
+    public void testGetUsernameFromUriNumber() {
+        assertEquals("john", PhoneNumberUtils.getUsernameFromUriNumber("john@myorg.com"));
+        assertEquals("tim_123", PhoneNumberUtils.getUsernameFromUriNumber("tim_123@zzz.org"));
+        assertEquals("5103331245", PhoneNumberUtils.getUsernameFromUriNumber("5103331245"));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
index 4a17199..fdda80f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneStateListenerTest.java
@@ -60,7 +60,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mPhoneStateListenerHandler.quitSafely();
+        mPhoneStateListenerHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
index 087712c..b5ec320 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/PhoneSwitcherTest.java
@@ -16,28 +16,25 @@
 
 package com.android.internal.telephony;
 
-import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.mocks.ConnectivityServiceMock;
-import com.android.internal.telephony.mocks.SubscriptionControllerMock;
-import com.android.internal.telephony.mocks.TelephonyRegistryMock;
-import com.android.internal.telephony.test.SimulatedCommands;
-
 import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.StringNetworkSpecifier;
 import android.os.AsyncResult;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
-import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-
+import android.telephony.Rlog;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import android.telephony.Rlog;
+import com.android.internal.telephony.mocks.ConnectivityServiceMock;
+import com.android.internal.telephony.mocks.SubscriptionControllerMock;
+import com.android.internal.telephony.mocks.TelephonyRegistryMock;
+import com.android.internal.telephony.test.SimulatedCommands;
 
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
@@ -159,7 +156,7 @@
                 addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
                 addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        netCap.setNetworkSpecifier(Integer.toString(subId));
+        netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
         return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
     }
 
@@ -168,7 +165,7 @@
                 addCapability(NetworkCapabilities.NET_CAPABILITY_MMS).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
                 addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        netCap.setNetworkSpecifier(Integer.toString(subId));
+        netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
         return cs.requestNetwork(netCap, null, 0, new Binder(), -1);
     }
 
@@ -391,6 +388,12 @@
 //        }
 //        if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
 //        if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
+
+        for (int i = 0; i < numPhones; i++) {
+            commandsInterfaces[i].dispose();
+        }
+
+        connectivityServiceMock.die();
         testHandler.die();
         handlerThread.quit();
     }
@@ -471,6 +474,11 @@
         if (commandsInterfaces[0].isDataAllowed()) fail("data allowed");
         if (commandsInterfaces[1].isDataAllowed() == false) fail("data not allowed");
 
+        for (int i = 0; i < numPhones; i++) {
+            commandsInterfaces[i].dispose();
+        }
+
+        connectivityServiceMock.die();
         testHandler.die();
         handlerThread.quit();
     }
@@ -540,6 +548,11 @@
         if (commandsInterfaces[0].isDataAllowed() == false) fail("data not allowed");
         if (commandsInterfaces[1].isDataAllowed()) fail("data allowed");
 
+        for (int i = 0; i < numPhones; i++) {
+            commandsInterfaces[i].dispose();
+        }
+
+        connectivityServiceMock.die();
         testHandler.die();
         handlerThread.quit();
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java b/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java
new file mode 100644
index 0000000..653c357
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/RadioAccessSpecifierTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.RadioNetworkConstants.GeranBands;
+import android.telephony.RadioNetworkConstants.RadioAccessNetworks;
+
+import org.junit.Test;
+
+/** Unit tests for {@link RadioAccessSpecifier}. */
+
+public class RadioAccessSpecifierTest {
+
+    @Test
+    @SmallTest
+    public void testParcel() {
+        int ranGsm = RadioAccessNetworks.GERAN;
+        int[] gsmBands = {GeranBands.BAND_T380, GeranBands.BAND_T410};
+        int[] gsmChannels = {1, 2, 3, 4};
+        RadioAccessSpecifier ras = new RadioAccessSpecifier(ranGsm, gsmBands, gsmChannels);
+
+        Parcel p = Parcel.obtain();
+        ras.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        RadioAccessSpecifier newRas = RadioAccessSpecifier.CREATOR.createFromParcel(p);
+        assertEquals(ras, newRas);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
index e3a94a3..15897d4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ServiceStateTrackerTest.java
@@ -16,19 +16,41 @@
 
 package com.android.internal.telephony;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.IAlarmManager;
 import android.content.Context;
 import android.content.Intent;
+import android.hardware.radio.V1_0.CellIdentityGsm;
+import android.hardware.radio.V1_0.CellInfoType;
+import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.os.IBinder;
 import android.os.Message;
 import android.os.Parcel;
-import android.os.SystemClock;
+import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.UserHandle;
-import android.platform.test.annotations.Postsubmit;
+import android.os.WorkSource;
+import android.support.test.filters.FlakyTest;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellInfo;
 import android.telephony.CellInfoGsm;
 import android.telephony.ServiceState;
@@ -51,20 +73,8 @@
 import org.mockito.Mock;
 
 import java.util.ArrayList;
-
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
+import java.util.HashSet;
+import java.util.List;
 
 public class ServiceStateTrackerTest extends TelephonyTest {
 
@@ -79,16 +89,19 @@
 
     private ServiceStateTracker sst;
     private ServiceStateTrackerTestHandler mSSTTestHandler;
+    private PersistableBundle mBundle;
 
     private static final int EVENT_REGISTERED_TO_NETWORK = 1;
     private static final int EVENT_SUBSCRIPTION_INFO_READY = 2;
-    private static final int EVENT_ROAMING_ON = 3;
-    private static final int EVENT_ROAMING_OFF = 4;
+    private static final int EVENT_DATA_ROAMING_ON = 3;
+    private static final int EVENT_DATA_ROAMING_OFF = 4;
     private static final int EVENT_DATA_CONNECTION_ATTACHED = 5;
     private static final int EVENT_DATA_CONNECTION_DETACHED = 6;
     private static final int EVENT_DATA_RAT_CHANGED = 7;
     private static final int EVENT_PS_RESTRICT_ENABLED = 8;
     private static final int EVENT_PS_RESTRICT_DISABLED = 9;
+    private static final int EVENT_VOICE_ROAMING_ON = 10;
+    private static final int EVENT_VOICE_ROAMING_OFF = 11;
 
     private class ServiceStateTrackerTestHandler extends HandlerThread {
 
@@ -113,14 +126,12 @@
         mPhone.mDcTracker = mDct;
 
         replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
+        mBundle = mContextFixture.getCarrierConfigBundle();
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
 
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_sameNamedOperatorConsideredRoaming,
-                new String[]{"123456"});
-
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_operatorConsideredNonRoaming,
-                new String[]{"123456"});
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, new String[]{"123456"});
 
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setVoiceRadioTech(ServiceState.RIL_RADIO_TECHNOLOGY_HSPA);
@@ -140,7 +151,7 @@
     @After
     public void tearDown() throws Exception {
         sst = null;
-        mSSTTestHandler.quitSafely();
+        mSSTTestHandler.quit();
         super.tearDown();
     }
 
@@ -215,7 +226,7 @@
 
         // Note that if the poll is triggered by a network change notification
         // and the modem is supposed to be off, we should still do the poll
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
         waitForMs(250);
 
         assertEquals(getOperatorCallCount + 2 , mSimulatedCommands.getGetOperatorCallCount());
@@ -227,6 +238,7 @@
                 mSimulatedCommands.getGetNetworkSelectionModeCallCount());
     }
 
+    @FlakyTest
     @Test
     @MediumTest
     public void testSpnUpdateShowPlmnOnly() {
@@ -239,11 +251,16 @@
         waitForMs(750);
 
         ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContextFixture.getTestDouble(), atLeast(2)).
-                sendStickyBroadcastAsUser(intentArgumentCaptor.capture(), eq(UserHandle.ALL));
+        verify(mContextFixture.getTestDouble(), times(3))
+                .sendStickyBroadcastAsUser(intentArgumentCaptor.capture(), eq(UserHandle.ALL));
 
         // We only want to verify the intent SPN_STRINGS_UPDATED_ACTION.
-        Intent intent = intentArgumentCaptor.getValue();
+        List<Intent> intents = intentArgumentCaptor.getAllValues();
+        logd("Total " + intents.size() + " intents");
+        for (Intent intent : intents) {
+            logd("  " + intent.getAction());
+        }
+        Intent intent = intents.get(2);
         assertEquals(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION, intent.getAction());
 
         Bundle b = intent.getExtras();
@@ -289,7 +306,9 @@
         list.add(cellInfo);
         mSimulatedCommands.setCellInfoList(list);
 
-        assertEquals(sst.getAllCellInfo(), list);
+        WorkSource workSource = new WorkSource(Process.myUid(),
+                mContext.getPackageName());
+        assertEquals(sst.getAllCellInfo(workSource), list);
     }
 
     @Test
@@ -312,7 +331,6 @@
         assertFalse(sst.isImsRegistered());
     }
 
-    @Postsubmit
     @Test
     @MediumTest
     public void testSignalStrength() {
@@ -346,14 +364,15 @@
         sst.mSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
 
         mSimulatedCommands.notifySignalStrength();
-        waitForMs(200);
+        waitForMs(300);
         assertEquals(sst.getSignalStrength(), ss);
         assertEquals(sst.getSignalStrength().isGsm(), true);
 
         // notify signal strength again, but this time data RAT is not LTE
+        sst.mSS.setRilVoiceRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT);
         sst.mSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD);
         mSimulatedCommands.notifySignalStrength();
-        waitForMs(200);
+        waitForMs(300);
         assertEquals(sst.getSignalStrength(), ss);
         assertEquals(sst.getSignalStrength().isGsm(), false);
     }
@@ -362,12 +381,18 @@
     @MediumTest
     public void testGsmCellLocation() {
 
+        VoiceRegStateResult result = new VoiceRegStateResult();
+        result.cellIdentity.cellInfoType = CellInfoType.GSM;
+        result.cellIdentity.cellIdentityGsm.add(new CellIdentityGsm());
+        result.cellIdentity.cellIdentityGsm.get(0).lac = 2;
+        result.cellIdentity.cellIdentityGsm.get(0).cid = 3;
+
         sst.sendMessage(sst.obtainMessage(ServiceStateTracker.EVENT_GET_LOC_DONE,
-                new AsyncResult(null, new String[]{"1", "2", "3", "4", "5", "6", "7", "8", "9",
-                        "10", "11", "12", "13", "14", "15"}, null)));
+                new AsyncResult(null, result, null)));
 
         waitForMs(200);
-        GsmCellLocation cl = (GsmCellLocation) sst.getCellLocation();
+        WorkSource workSource = new WorkSource(Process.myUid(), mContext.getPackageName());
+        GsmCellLocation cl = (GsmCellLocation) sst.getCellLocation(workSource);
         assertEquals(2, cl.getLac());
         assertEquals(3, cl.getCid());
     }
@@ -386,7 +411,7 @@
 
         ArgumentCaptor<Integer> integerArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mRuimRecords).registerForRecordsLoaded(eq(sst), integerArgumentCaptor.capture(),
-                any(Object.class));
+                nullable(Object.class));
 
         // response for mRuimRecords.registerForRecordsLoaded()
         Message msg = Message.obtain();
@@ -419,25 +444,25 @@
     @Test
     @MediumTest
     public void testRegAndUnregForVoiceRoamingOn() throws Exception {
-        sst.registerForVoiceRoamingOn(mTestHandler, EVENT_ROAMING_ON, null);
+        sst.registerForVoiceRoamingOn(mTestHandler, EVENT_DATA_ROAMING_ON, null);
 
         // Enable roaming and trigger events to notify handler registered
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_ON, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
 
         // Disable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -447,9 +472,9 @@
         // Enable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify that no new message posted to handler
         verify(mTestHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
@@ -462,29 +487,29 @@
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
-        sst.registerForVoiceRoamingOff(mTestHandler, EVENT_ROAMING_OFF, null);
+        sst.registerForVoiceRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null);
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_OFF, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
 
         // Enable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -494,7 +519,7 @@
         // Disable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -505,25 +530,25 @@
     @Test
     @MediumTest
     public void testRegAndUnregForDataRoamingOn() throws Exception {
-        sst.registerForDataRoamingOn(mTestHandler, EVENT_ROAMING_ON, null);
+        sst.registerForDataRoamingOn(mTestHandler, EVENT_DATA_ROAMING_ON, null);
 
         // Enable roaming and trigger events to notify handler registered
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_ON, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_ON, messageArgumentCaptor.getValue().what);
 
         // Disable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -533,9 +558,9 @@
         // Enable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify that no new message posted to handler
         verify(mTestHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
@@ -548,29 +573,29 @@
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
-        sst.registerForDataRoamingOff(mTestHandler, EVENT_ROAMING_OFF, null);
+        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null, true);
 
         // Disable roaming
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
         verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
-        assertEquals(EVENT_ROAMING_OFF, messageArgumentCaptor.getValue().what);
+        assertEquals(EVENT_DATA_ROAMING_OFF, messageArgumentCaptor.getValue().what);
 
         // Enable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -580,7 +605,53 @@
         // Disable roaming
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_HOME);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_HOME);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        // verify that no new message posted to handler
+        verify(mTestHandler, times(1)).sendMessageAtTime(any(Message.class), anyLong());
+    }
+
+    @Test
+    @MediumTest
+    public void testRegAndInvalidregForDataConnAttach() throws Exception {
+        // Initially set service state out of service
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+        mSimulatedCommands.setVoiceRegState(23);
+        mSimulatedCommands.setDataRegState(23);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        sst.registerForDataConnectionAttached(mTestHandler, EVENT_DATA_CONNECTION_ATTACHED, null);
+
+        // set service state in service and trigger events to post message on handler
+        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(200);
+
+        // verify if registered handler has message posted to it
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
+        assertEquals(EVENT_DATA_CONNECTION_ATTACHED, messageArgumentCaptor.getValue().what);
+
+        // set service state out of service
+        mSimulatedCommands.setVoiceRegState(-1);
+        mSimulatedCommands.setDataRegState(-1);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        // Unregister registrant
+        sst.unregisterForDataConnectionAttached(mTestHandler);
+
+        // set service state in service
+        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -595,7 +666,7 @@
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -604,7 +675,7 @@
         // set service state in service and trigger events to post message on handler
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(200);
 
@@ -616,7 +687,7 @@
         // set service state out of service
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -626,7 +697,7 @@
         // set service state in service
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -641,16 +712,16 @@
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         sst.registerForDataConnectionDetached(mTestHandler, EVENT_DATA_CONNECTION_DETACHED, null);
 
         // set service state out of service and trigger events to post message on handler
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
-        waitForMs(100);
+        waitForMs(200);
 
         // verify if registered handler has message posted to it
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
@@ -660,7 +731,7 @@
         // set service state in service
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -670,7 +741,7 @@
         // set service state out of service
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -704,7 +775,7 @@
         doReturn(true).when(mPhone).isPhoneTypeGsm();
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -713,7 +784,7 @@
         // set service state in service and trigger events to post message on handler
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -725,7 +796,7 @@
         // set service state out of service
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_UNKNOWN);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -735,7 +806,7 @@
         // set service state in service
         mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
         mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
-        mSimulatedCommands.notifyVoiceNetworkStateChanged();
+        mSimulatedCommands.notifyNetworkStateChanged();
 
         waitForMs(100);
 
@@ -745,6 +816,60 @@
 
     @Test
     @MediumTest
+    public void testRegAndInvalidRegForNetworkAttached() throws Exception {
+        // Initially set service state out of service
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+        mSimulatedCommands.setVoiceRegState(23);
+        mSimulatedCommands.setDataRegState(23);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
+
+        // set service state in service and trigger events to post message on handler
+        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        // verify if registered handler has message posted to it
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mTestHandler).sendMessageAtTime(messageArgumentCaptor.capture(), anyLong());
+        assertEquals(EVENT_REGISTERED_TO_NETWORK, messageArgumentCaptor.getValue().what);
+
+        // set service state out of service
+        mSimulatedCommands.setVoiceRegState(-1);
+        mSimulatedCommands.setDataRegState(-1);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        // Unregister registrant
+        sst.unregisterForNetworkAttached(mTestHandler);
+
+
+        waitForMs(100);
+
+        sst.registerForNetworkAttached(mTestHandler, EVENT_REGISTERED_TO_NETWORK, null);
+
+        // set service state in service
+        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(100);
+
+        // verify if registered handler has message posted to it
+        messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mTestHandler, times(2)).sendMessageAtTime(messageArgumentCaptor.capture(),
+                anyLong());
+        assertEquals(EVENT_REGISTERED_TO_NETWORK, messageArgumentCaptor.getValue().what);
+    }
+
+    @Test
+    @MediumTest
     public void testRegisterForPsRestrictedEnabled() throws Exception {
         sst.mRestrictedState.setPsRestricted(true);
         // Since PsRestricted is set to true, registerForPsRestrictedEnabled will
@@ -777,6 +902,77 @@
 
     @Test
     @MediumTest
+    public void testOnRestrictedStateChanged() throws Exception {
+        ServiceStateTracker spySst = spy(sst);
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+        doReturn(IccCardApplicationStatus.AppState.APPSTATE_READY).when(
+                mUiccCardApplication3gpp).getState();
+
+        ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mSimulatedCommandsVerifier).setOnRestrictedStateChanged(any(Handler.class),
+                intArgumentCaptor.capture(), eq(null));
+        // Since spy() creates a copy of sst object we need to call
+        // setOnRestrictedStateChanged() explicitly.
+        mSimulatedCommands.setOnRestrictedStateChanged(spySst,
+                intArgumentCaptor.getValue().intValue(), null);
+
+        // Combination of restricted state and expected notification type.
+        final int CS_ALL[] = {RILConstants.RIL_RESTRICTED_STATE_CS_ALL,
+                ServiceStateTracker.CS_ENABLED};
+        final int CS_NOR[] = {RILConstants.RIL_RESTRICTED_STATE_CS_NORMAL,
+                ServiceStateTracker.CS_NORMAL_ENABLED};
+        final int CS_EME[] = {RILConstants.RIL_RESTRICTED_STATE_CS_EMERGENCY,
+                ServiceStateTracker.CS_EMERGENCY_ENABLED};
+        final int CS_NON[] = {RILConstants.RIL_RESTRICTED_STATE_NONE,
+                ServiceStateTracker.CS_DISABLED};
+        final int PS_ALL[] = {RILConstants.RIL_RESTRICTED_STATE_PS_ALL,
+                ServiceStateTracker.PS_ENABLED};
+        final int PS_NON[] = {RILConstants.RIL_RESTRICTED_STATE_NONE,
+                ServiceStateTracker.PS_DISABLED};
+
+        int notifyCount = 0;
+        // cs not restricted -> cs emergency/normal restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_ALL);
+        // cs emergency/normal restricted -> cs normal restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_NOR);
+        // cs normal restricted -> cs emergency restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_EME);
+        // cs emergency restricted -> cs not restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_NON);
+        // cs not restricted -> cs normal restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_NOR);
+        // cs normal restricted -> cs emergency/normal restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_ALL);
+        // cs emergency/normal restricted -> cs emergency restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_EME);
+        // cs emergency restricted -> cs emergency/normal restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_ALL);
+        // cs emergency/normal restricted -> cs not restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_NON);
+        // cs not restricted -> cs emergency restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_EME);
+        // cs emergency restricted -> cs normal restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_NOR);
+        // cs normal restricted -> cs not restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, CS_NON);
+
+        // ps not restricted -> ps restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, PS_ALL);
+        // ps restricted -> ps not restricted
+        internalCheckForRestrictedStateChange(spySst, ++notifyCount, PS_NON);
+    }
+
+    private void internalCheckForRestrictedStateChange(ServiceStateTracker serviceStateTracker,
+                int times, int[] restrictedState) {
+        mSimulatedCommands.triggerRestrictedStateChanged(restrictedState[0]);
+        waitForMs(100);
+        ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(serviceStateTracker, times(times)).setNotification(intArgumentCaptor.capture());
+        assertEquals(intArgumentCaptor.getValue().intValue(), restrictedState[1]);
+    }
+
+    @Test
+    @MediumTest
     public void testRegisterForSubscriptionInfoReady() {
         sst.registerForSubscriptionInfoReady(mTestHandler, EVENT_SUBSCRIPTION_INFO_READY, null);
 
@@ -794,6 +990,40 @@
     }
 
     @Test
+    @MediumTest
+    public void testRoamingPhoneTypeSwitch() {
+        // Enable roaming
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+
+        mSimulatedCommands.setVoiceRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.setDataRegState(ServiceState.RIL_REG_STATE_ROAMING);
+        mSimulatedCommands.notifyNetworkStateChanged();
+
+        waitForMs(200);
+
+        sst.registerForDataRoamingOff(mTestHandler, EVENT_DATA_ROAMING_OFF, null, true);
+        sst.registerForVoiceRoamingOff(mTestHandler, EVENT_VOICE_ROAMING_OFF, null);
+        sst.registerForDataConnectionDetached(mTestHandler, EVENT_DATA_CONNECTION_DETACHED, null);
+
+        // Call functions which would trigger posting of message on test handler
+        doReturn(false).when(mPhone).isPhoneTypeGsm();
+        sst.updatePhoneType();
+
+        // verify if registered handler has message posted to it
+        ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
+        verify(mTestHandler, atLeast(3)).sendMessageAtTime(
+                messageArgumentCaptor.capture(), anyLong());
+        HashSet<Integer> messageSet = new HashSet<>();
+        for (Message m : messageArgumentCaptor.getAllValues()) {
+            messageSet.add(m.what);
+        }
+
+        assertTrue(messageSet.contains(EVENT_DATA_ROAMING_OFF));
+        assertTrue(messageSet.contains(EVENT_VOICE_ROAMING_OFF));
+        assertTrue(messageSet.contains(EVENT_DATA_CONNECTION_DETACHED));
+    }
+
+    @Test
     @SmallTest
     public void testGetDesiredPowerState() {
         sst.setRadioPower(true);
@@ -813,7 +1043,7 @@
     public void testDisableLocationUpdates() throws Exception {
         sst.disableLocationUpdates();
         verify(mSimulatedCommandsVerifier, times(1)).setLocationUpdates(eq(false),
-                any(Message.class));
+                nullable(Message.class));
     }
 
     @Test
@@ -826,21 +1056,19 @@
     @Test
     @SmallTest
     public void testIsConcurrentVoiceAndDataAllowed() {
-        // Verify all 3 branches in the function isConcurrentVoiceAndDataAllowed
-        doReturn(true).when(mPhone).isPhoneTypeGsm();
-        sst.mSS.setRilVoiceRadioTechnology(sst.mSS.RIL_RADIO_TECHNOLOGY_HSPA);
-        assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
-
         doReturn(false).when(mPhone).isPhoneTypeGsm();
-        doReturn(true).when(mPhone).isPhoneTypeCdma();
-        assertEquals(false, sst.isConcurrentVoiceAndDataAllowed());
-
-        doReturn(false).when(mPhone).isPhoneTypeGsm();
-        doReturn(false).when(mPhone).isPhoneTypeCdma();
         sst.mSS.setCssIndicator(1);
         assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
         sst.mSS.setCssIndicator(0);
         assertEquals(false, sst.isConcurrentVoiceAndDataAllowed());
+
+        doReturn(true).when(mPhone).isPhoneTypeGsm();
+        sst.mSS.setRilDataRadioTechnology(sst.mSS.RIL_RADIO_TECHNOLOGY_HSPA);
+        assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
+        sst.mSS.setRilDataRadioTechnology(sst.mSS.RIL_RADIO_TECHNOLOGY_GPRS);
+        assertEquals(false, sst.isConcurrentVoiceAndDataAllowed());
+        sst.mSS.setCssIndicator(1);
+        assertEquals(true, sst.isConcurrentVoiceAndDataAllowed());
     }
 
     @Test
@@ -867,14 +1095,14 @@
 
         // Mock sending incorrect nitz str from RIL
         mSimulatedCommands.triggerNITZupdate("38/06/20,00:00:00+0");
-        waitForMs(100);
+        waitForMs(200);
         // AlarmManger.setTime is triggered by SystemClock.setCurrentTimeMillis().
         // Verify system time is not set to incorrect NITZ time
         verify(mAlarmManager, times(0)).setTime(anyLong());
 
         // Mock sending correct nitz str from RIL
         mSimulatedCommands.triggerNITZupdate("15/06/20,00:00:00+0");
-        waitForMs(100);
+        waitForMs(200);
         verify(mAlarmManager, times(1)).setTime(anyLong());
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimActivationTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimActivationTrackerTest.java
new file mode 100644
index 0000000..b99736f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimActivationTrackerTest.java
@@ -0,0 +1,118 @@
+
+/*
+ * Copyright (C) 2016 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 junit.framework.AssertionFailedError;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.security.InvalidParameterException;
+
+import static org.junit.Assert.assertEquals;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_ACTIVATED;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_RESTRICTED;
+import static android.telephony.TelephonyManager.SIM_ACTIVATION_STATE_DEACTIVATED;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class SimActivationTrackerTest extends TelephonyTest {
+    private SimActivationTracker mSAT;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp("SimActivaitonTrackerTest");
+        mSAT = new SimActivationTracker(mPhone);
+    }
+
+    @Test
+    public void testSetVoiceActivationState() {
+        // verify initial state is unknown
+        assertEquals(SIM_ACTIVATION_STATE_UNKNOWN, mSAT.getVoiceActivationState());
+
+        // verify activated state is set successfully
+        try {
+            mSAT.setVoiceActivationState(SIM_ACTIVATION_STATE_ACTIVATED);
+        } catch (IllegalArgumentException ex) {
+            fail("Exception in setVoiceActivationState: " + ex);
+        }
+
+        assertEquals(SIM_ACTIVATION_STATE_ACTIVATED, mSAT.getVoiceActivationState());
+        verify(mPhone, times(1)).notifyVoiceActivationStateChanged(
+                eq(SIM_ACTIVATION_STATE_ACTIVATED));
+
+        // verify fails to set restricted voice activation state
+        try {
+            mSAT.setVoiceActivationState(SIM_ACTIVATION_STATE_RESTRICTED);
+            fail("Expect exception in setVoiceActivationState with wrong state: "
+                    + SIM_ACTIVATION_STATE_RESTRICTED);
+        } catch (IllegalArgumentException ex) {
+            //test pass
+        }
+        assertEquals(SIM_ACTIVATION_STATE_ACTIVATED, mSAT.getVoiceActivationState());
+        verify(mPhone, times(0)).notifyVoiceActivationStateChanged(
+                eq(SIM_ACTIVATION_STATE_RESTRICTED));
+    }
+
+    @Test
+    public void testSetDataActivationState() {
+        // verify initial state is unknown
+        assertEquals(SIM_ACTIVATION_STATE_UNKNOWN, mSAT.getDataActivationState());
+
+        // verify deactivated state is set successfully
+        try {
+            mSAT.setDataActivationState(SIM_ACTIVATION_STATE_DEACTIVATED);
+        } catch (InvalidParameterException ex) {
+            fail("Exception in setDataActivationState: " + ex);
+        }
+        assertEquals(SIM_ACTIVATION_STATE_DEACTIVATED, mSAT.getDataActivationState());
+        verify(mPhone, times(1)).notifyDataActivationStateChanged(
+                eq(SIM_ACTIVATION_STATE_DEACTIVATED));
+
+        // verify set restricted data activation state successfully
+        try {
+            mSAT.setDataActivationState(SIM_ACTIVATION_STATE_RESTRICTED);
+        } catch (InvalidParameterException ex) {
+            fail("Exception in setDataActivationState: " + ex);
+        }
+
+        assertEquals(SIM_ACTIVATION_STATE_RESTRICTED, mSAT.getDataActivationState());
+        verify(mPhone, times(1)).notifyDataActivationStateChanged(
+                eq(SIM_ACTIVATION_STATE_RESTRICTED));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mSAT = null;
+        super.tearDown();
+    }
+
+    /**
+     * Fails a test with the given message.
+     */
+    static public void fail(String message) {
+        if (message == null) {
+            throw new AssertionFailedError();
+        }
+        throw new AssertionFailedError(message);
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java b/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
index e66ffe1..283b170 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/Sms7BitEncodingTranslatorTest.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony;
 
-import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.After;
@@ -50,14 +50,14 @@
         super.tearDown();
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @SmallTest
     public void testNoTranslate() {
         assertEquals("123", Sms7BitEncodingTranslator.translate("123"));
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @SmallTest
     public void testCommonTranslate() {
@@ -71,7 +71,7 @@
         assertEquals("OIA", Sms7BitEncodingTranslator.translate(s));
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @SmallTest
     public void testGsmTranslate() {
@@ -85,7 +85,7 @@
         assertEquals("??Ç", Sms7BitEncodingTranslator.translate(s));
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @SmallTest
     public void testCdmaTranslate() {
@@ -101,4 +101,4 @@
         }
         assertEquals("OUc", Sms7BitEncodingTranslator.translate(s));
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
index 43d70b5..05c2cb8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsNumberUtilsTest.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
+import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
@@ -122,9 +123,9 @@
 
         ((MockContentResolver) mContextFixture.getTestDouble().getContentResolver())
                 .addProvider(HbpcdLookup.MccIdd.CONTENT_URI.getAuthority(), mHbpcdContentProvider);
-        mContextFixture.putStringArrayResource(
-                com.android.internal.R.array.config_sms_convert_destination_number_support,
-                new String[]{"true"});
+        mContextFixture.getCarrierConfigBundle().
+                putBoolean(CarrierConfigManager.KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL,
+                        true);
 
         logd("SmsNumberUtilsTest -Setup!");
     }
@@ -243,4 +244,4 @@
         assertEquals("01118582345678",
                 SmsNumberUtils.filterDestAddr(mPhone, "+011-1-858-234-5678"));
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
index 7c8c15c..cbc56ae 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsStorageMonitorTest.java
@@ -67,7 +67,7 @@
     @After
     public void tearDown() throws Exception {
         mSmsStorageMonitor = null;
-        mSmsStorageMonitorTestHandler.quitSafely();
+        mSmsStorageMonitorTestHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
index ef0c990..76fce94 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsUsageMonitorShortCodeTest.java
@@ -16,21 +16,24 @@
 
 package com.android.internal.telephony;
 
-import android.os.Looper;
-import android.platform.test.annotations.Postsubmit;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE;
 import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE;
 
+import static org.junit.Assert.assertEquals;
+
+import android.os.Looper;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Ignore;
+
 /**
  * Test cases for SMS short code pattern matching in SmsUsageMonitor.
  */
-public class SmsUsageMonitorShortCodeTest extends AndroidTestCase {
+@Ignore
+public class SmsUsageMonitorShortCodeTest {
 
     private static final class ShortCodeTest {
         final String countryIso;
@@ -457,15 +460,14 @@
             new ShortCodeTest(null, "112", CATEGORY_NOT_SHORT_CODE),
     };
 
-    @Postsubmit
-    @SmallTest
+    @FlakyTest
     public void testSmsUsageMonitor() {
         // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
         // http://b/25897652 .
         if (Looper.myLooper() == null) {
             Looper.prepare();
         }
-        SmsUsageMonitor monitor = new SmsUsageMonitor(getContext());
+        SmsUsageMonitor monitor = new SmsUsageMonitor(TestApplication.getAppContext());
         for (ShortCodeTest test : sShortCodeTests) {
             assertEquals("country: " + test.countryIso + " number: " + test.address,
                     test.category, monitor.checkDestination(test.address, test.countryIso));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 7ef441e..568238f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -201,7 +201,7 @@
         for (int i = 0; i < mSubList.size(); i++) {
             assertTrue(SubscriptionManager.isValidSubscriptionId(
                     mSubList.get(i).getSubscriptionId()));
-            assertTrue(SubscriptionManager.isValidSlotId(mSubList.get(i).getSimSlotIndex()));
+            assertTrue(SubscriptionManager.isValidSlotIndex(mSubList.get(i).getSimSlotIndex()));
         }
     }
 
@@ -247,7 +247,7 @@
         mSubscriptionControllerUT.clearSubInfo();
         assertFalse(mSubscriptionControllerUT.isActiveSubId(0));
         assertEquals(SubscriptionManager.SIM_NOT_INSERTED,
-                mSubscriptionControllerUT.getSlotId(0));
+                mSubscriptionControllerUT.getSlotIndex(0));
     }
 
     @Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 2834304..feea80e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -15,7 +15,18 @@
  */
 package com.android.internal.telephony;
 
-import android.Manifest;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -25,7 +36,6 @@
 import android.os.AsyncResult;
 import android.os.HandlerThread;
 import android.os.Message;
-import android.provider.Telephony;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
@@ -39,10 +49,8 @@
 import com.android.internal.telephony.uicc.IccUtils;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -50,22 +58,12 @@
 import java.util.Arrays;
 import java.util.HashMap;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class SubscriptionInfoUpdaterTest extends TelephonyTest {
 
-    private static final int FAKE_SUB_ID = 1;
-    private static final String FAKE_PLMN = "123456";
+    private static final int FAKE_SUB_ID_1 = 0;
+    private static final int FAKE_SUB_ID_2 = 1;
+    private static final String FAKE_MCC_MNC_1 = "123456";
+    private static final String FAKE_MCC_MNC_2 = "456789";
 
     private SubscriptionInfoUpdaterHandlerThread mSubscriptionInfoUpdaterHandlerThread;
     private IccRecords mIccRecord;
@@ -109,21 +107,17 @@
     public void setUp() throws Exception {
         super.setUp(this.getClass().getSimpleName());
 
-        replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
-                new String[SubscriptionInfoUpdater.STATUS_SIM1_INSERTED]);
-        replaceInstance(SubscriptionInfoUpdater.class, "mInsertSimState", null,
-                new int[SubscriptionInfoUpdater.STATUS_SIM1_INSERTED]);
+        replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null, new String[1]);
+        replaceInstance(SubscriptionInfoUpdater.class, "mInsertSimState", null, new int[1]);
         replaceInstance(SubscriptionInfoUpdater.class, "mContext", null, null);
-        replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null,
-                SubscriptionInfoUpdater.STATUS_SIM1_INSERTED);
+        replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null, 1);
 
-        doReturn(SubscriptionInfoUpdater.STATUS_SIM1_INSERTED)
-                .when(mTelephonyManager).getSimCount();
-        doReturn(SubscriptionInfoUpdater.STATUS_SIM1_INSERTED)
-                .when(mTelephonyManager).getPhoneCount();
+        doReturn(1).when(mTelephonyManager).getSimCount();
+        doReturn(1).when(mTelephonyManager).getPhoneCount();
 
         doReturn(mUserInfo).when(mIActivityManager).getCurrentUser();
-        doReturn(new int[]{FAKE_SUB_ID}).when(mSubscriptionController).getSubId(0);
+        doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionController).getSubId(0);
+        doReturn(new int[]{FAKE_SUB_ID_1}).when(mSubscriptionManager).getActiveSubscriptionIdList();
         mContentProvider = new FakeSubscriptionContentProvider();
         ((MockContentResolver) mContext.getContentResolver()).addProvider(
                 SubscriptionManager.CONTENT_URI.getAuthority(),
@@ -137,19 +131,19 @@
 
     @After
     public void tearDown() throws Exception {
-        mSubscriptionInfoUpdaterHandlerThread.quitSafely();
+        mSubscriptionInfoUpdaterHandlerThread.quit();
         super.tearDown();
     }
 
     @Test
     @SmallTest
-    public void testSimAbsent() {
+    public void testSimAbsent() throws Exception {
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
-                .getSubInfoUsingSlotIdWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
         Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_ABSENT);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
 
@@ -159,18 +153,18 @@
 
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_ABSENT));
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
     }
 
     @Test
     @SmallTest
-    public void testSimUnknown() {
+    public void testSimUnknown() throws Exception {
         Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
 
@@ -178,32 +172,32 @@
         verify(mSubscriptionContent, times(0)).put(anyString(), any());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(1),
+        verify(mConfigManager).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_UNKNOWN));
         verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
     }
 
     @Test
     @SmallTest
-    public void testSimError() {
+    public void testSimError() throws Exception {
         Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 2);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
         verify(mSubscriptionContent, times(0)).put(anyString(), any());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager).updateConfigForPhoneId(eq(2),
+        verify(mConfigManager).updateConfigForPhoneId(eq(0),
                 eq(IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR));
-        verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
+        verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
     }
 
     @Test
     @SmallTest
-    public void testWrongSimState() {
+    public void testWrongSimState() throws Exception {
         Intent mIntent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_IMSI);
@@ -221,17 +215,17 @@
 
     @Test
     @SmallTest
-    public void testSimLoaded() {
+    public void testSimLoaded() throws Exception {
         /* mock new sim got loaded and there is no sim loaded before */
         doReturn(null).when(mSubscriptionController)
-                .getSubInfoUsingSlotIdWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
         doReturn("89012604200000000000").when(mIccRecord).getIccId();
-        doReturn(FAKE_PLMN).when(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         Intent intentInternalSimStateChanged =
                 new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         intentInternalSimStateChanged.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        intentInternalSimStateChanged.putExtra(PhoneConstants.PHONE_KEY, 0);
+        intentInternalSimStateChanged.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(intentInternalSimStateChanged);
         waitForMs(100);
@@ -255,14 +249,14 @@
                 stringArgumentCaptor.getAllValues().get(1)); */
 
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
-        verify(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
-                eq("89012604200000000000"), eq(0));
+                eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
-        verify(mSubscriptionController, times(1)).setMccMnc(FAKE_PLMN, FAKE_SUB_ID);
+        verify(mSubscriptionController, times(1)).setMccMnc(FAKE_MCC_MNC_1, FAKE_SUB_ID_1);
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
 
         // ACTION_USER_UNLOCKED should trigger another SIM_STATE_CHANGED
@@ -288,35 +282,35 @@
 
     @Test
     @SmallTest
-    public void testSimLoadedEmptyOperatorNumeric() {
+    public void testSimLoadedEmptyOperatorNumeric() throws Exception {
         /* mock new sim got loaded and there is no sim loaded before */
         doReturn(null).when(mSubscriptionController)
-                .getSubInfoUsingSlotIdWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
         doReturn("89012604200000000000").when(mIccRecord).getIccId();
         // operator numeric is empty
-        doReturn("").when(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        doReturn("").when(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOADED);
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
-        verify(mTelephonyManager).getSimOperatorNumericForPhone(0);
+        verify(mTelephonyManager).getSimOperatorNumeric(FAKE_SUB_ID_1);
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
-                eq("89012604200000000000"), eq(0));
+                eq("89012604200000000000"), eq(FAKE_SUB_ID_1));
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
         verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOADED));
     }
 
     @Test
     @SmallTest
-    public void testSimLockedWithOutIccId() {
+    public void testSimLockedWithOutIccId() throws Exception {
         /* mock no IccId Info present and try to query IccId
          after IccId query, update subscriptionDB */
         doReturn(mIccFileHandler).when(mIccCardProxy).getIccFileHandler();
@@ -332,13 +326,13 @@
         }).when(mIccFileHandler).loadEFTransparent(anyInt(), any(Message.class));
 
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
-                .getSubInfoUsingSlotIdWithCheck(eq(0), anyBoolean(), anyString());
+                .getSubInfoUsingSlotIndexWithCheck(eq(FAKE_SUB_ID_1), anyBoolean(), anyString());
 
         Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOCKED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
@@ -348,32 +342,86 @@
                 eq(SubscriptionManager.INVALID_SIM_SLOT_INDEX));
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
         verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(
-                eq("98106240020000000000"), eq(0));
+                eq("98106240020000000000"), eq(FAKE_SUB_ID_1));
 
         verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOCKED));
     }
 
     @Test
     @SmallTest
-    public void testSimLockWIthIccId() {
+    public void testDualSimLoaded() throws Exception {
+        // Mock there is two sim cards
+
+        replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
+                new String[]{null, null});
+        replaceInstance(SubscriptionInfoUpdater.class, "PROJECT_SIM_NUM", null, 2);
+        replaceInstance(SubscriptionInfoUpdater.class, "mPhone", null,
+                new Phone[]{mPhone, mPhone});
+        replaceInstance(SubscriptionInfoUpdater.class, "mInsertSimState", null,
+                new int[]{SubscriptionInfoUpdater.SIM_NOT_CHANGE,
+                        SubscriptionInfoUpdater.SIM_NOT_CHANGE});
+
+        doReturn(new int[]{FAKE_SUB_ID_1, FAKE_SUB_ID_2}).when(mSubscriptionManager)
+                .getActiveSubscriptionIdList();
+        doReturn(FAKE_SUB_ID_1).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_1));
+        doReturn(FAKE_SUB_ID_2).when(mSubscriptionController).getPhoneId(eq(FAKE_SUB_ID_2));
+        doReturn(2).when(mTelephonyManager).getSimCount();
+        doReturn(FAKE_MCC_MNC_1).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_1));
+        doReturn(FAKE_MCC_MNC_2).when(mTelephonyManager).getSimOperatorNumeric(eq(FAKE_SUB_ID_2));
+        // Mock there is no sim inserted before
+        doReturn(null).when(mSubscriptionController)
+                .getSubInfoUsingSlotIndexWithCheck(anyInt(), anyBoolean(), anyString());
+        doReturn("89012604200000000000").when(mIccRecord).getIccId();
+
+        // Mock sending a sim loaded for SIM 1
+        Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
+        mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
+        mContext.sendBroadcast(mIntent);
+        waitForMs(100);
+
+        SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
+        verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(anyString(), anyInt());
+        verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
+        verify(mSubscriptionController, times(0)).setMccMnc(anyString(), anyInt());
+
+        // Mock sending a sim loaded for SIM 2
+        doReturn("89012604200000000001").when(mIccRecord).getIccId();
+        mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
+        mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
+                IccCardConstants.INTENT_VALUE_ICC_LOADED);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_2);
+        mContext.sendBroadcast(mIntent);
+        waitForMs(100);
+
+        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000000"),
+                eq(FAKE_SUB_ID_1));
+        verify(mSubscriptionManager, times(1)).addSubscriptionInfoRecord(eq("89012604200000000001"),
+                eq(FAKE_SUB_ID_2));
+        verify(mSubscriptionController, times(1)).setMccMnc(eq(FAKE_MCC_MNC_1), eq(FAKE_SUB_ID_1));
+        verify(mSubscriptionController, times(1)).setMccMnc(eq(FAKE_MCC_MNC_2), eq(FAKE_SUB_ID_2));
+        verify(mSubscriptionController, times(1)).notifySubscriptionInfoChanged();
+    }
+
+    @Test
+    @SmallTest
+    public void testSimLockWithIccId() throws Exception {
         /* no need for IccId query */
-        try {
-            replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
-                    new String[]{"89012604200000000000"});
-        } catch (Exception ex) {
-            Assert.fail("unexpected exception thrown" + ex.getMessage());
-        }
+
+        replaceInstance(SubscriptionInfoUpdater.class, "mIccId", null,
+                new String[]{"89012604200000000000"});
         doReturn(mIccFileHandler).when(mIccCardProxy).getIccFileHandler();
 
         Intent mIntent = new Intent(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_ICC_STATE,
                 IccCardConstants.INTENT_VALUE_ICC_LOCKED);
         mIntent.putExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON, "TESTING");
-        mIntent.putExtra(PhoneConstants.PHONE_KEY, 0);
+        mIntent.putExtra(PhoneConstants.PHONE_KEY, FAKE_SUB_ID_1);
 
         mContext.sendBroadcast(mIntent);
         waitForMs(100);
@@ -381,12 +429,12 @@
         verify(mIccFileHandler, times(0)).loadEFTransparent(anyInt(), any(Message.class));
         SubscriptionManager mSubscriptionManager = SubscriptionManager.from(mContext);
         verify(mSubscriptionManager, times(0)).addSubscriptionInfoRecord(
-                anyString(), eq(0));
+                anyString(), eq(FAKE_SUB_ID_1));
         verify(mSubscriptionController, times(0)).notifySubscriptionInfoChanged();
         CarrierConfigManager mConfigManager = (CarrierConfigManager)
                 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
         /* broadcast is done */
-        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(0),
+        verify(mConfigManager, times(1)).updateConfigForPhoneId(eq(FAKE_SUB_ID_1),
                 eq(IccCardConstants.INTENT_VALUE_ICC_LOCKED));
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 805ee93..eaaaa64 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -19,14 +19,13 @@
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.any;
+import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.content.IIntentSender;
@@ -43,13 +42,12 @@
 import android.os.ServiceManager;
 import android.provider.BlockedNumberContract;
 import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.util.Log;
 import android.util.Singleton;
-import android.util.SparseArray;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsCallProfile;
@@ -61,7 +59,6 @@
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.imsphone.ImsPhoneCallTracker;
-import com.android.internal.telephony.mocks.TelephonyRegistryMock;
 import com.android.internal.telephony.test.SimulatedCommands;
 import com.android.internal.telephony.test.SimulatedCommandsVerifier;
 import com.android.internal.telephony.uicc.IccCardProxy;
@@ -83,10 +80,14 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 public abstract class TelephonyTest {
     protected static String TAG;
 
+    private static final int MAX_INIT_WAIT_MS = 30000; // 30 seconds
+
     @Mock
     protected GsmCdmaPhone mPhone;
     @Mock
@@ -174,7 +175,15 @@
     @Mock
     protected CarrierSignalAgent mCarrierSignalAgent;
     @Mock
+    protected CarrierActionAgent mCarrierActionAgent;
+    @Mock
     protected ImsExternalCallTracker mImsExternalCallTracker;
+    @Mock
+    protected AppSmsManager mAppSmsManager;
+    @Mock
+    protected DeviceStateMonitor mDeviceStateMonitor;
+    @Mock
+    protected IntentBroadcaster mIntentBroadcaster;
 
     protected TelephonyManager mTelephonyManager;
     protected SubscriptionManager mSubscriptionManager;
@@ -221,11 +230,14 @@
     }
 
     protected void waitUntilReady() {
-        while (true) {
-            synchronized (mLock) {
-                if (mReady) {
-                    break;
-                }
+        synchronized (mLock) {
+            try {
+                mLock.wait(MAX_INIT_WAIT_MS);
+            } catch (InterruptedException ie) {
+            }
+
+            if (!mReady) {
+                fail("Telephony tests failed to initialize");
             }
         }
     }
@@ -233,6 +245,7 @@
     protected void setReady(boolean ready) {
         synchronized (mLock) {
             mReady = ready;
+            mLock.notifyAll();
         }
     }
 
@@ -289,7 +302,8 @@
         replaceInstance(ImsManager.class, "sImsManagerInstances", null, mImsManagerInstances);
         replaceInstance(SubscriptionController.class, "sInstance", null, mSubscriptionController);
         replaceInstance(ProxyController.class, "sProxyController", null, mProxyController);
-        replaceInstance(ActivityManagerNative.class, "gDefault", null, mIActivityManagerSingleton);
+        replaceInstance(ActivityManager.class, "IActivityManagerSingleton", null,
+                mIActivityManagerSingleton);
         replaceInstance(CdmaSubscriptionSourceManager.class,
                 "mCdmaSubscriptionSourceChangedRegistrants", mCdmaSSM, mRegistrantList);
         replaceInstance(SimulatedCommandsVerifier.class, "sInstance", null,
@@ -297,6 +311,7 @@
         replaceInstance(Singleton.class, "mInstance", mIActivityManagerSingleton,
                 mIActivityManager);
         replaceInstance(ServiceManager.class, "sCache", null, mServiceManagerMockedServices);
+        replaceInstance(IntentBroadcaster.class, "sIntentBroadcaster", null, mIntentBroadcaster);
 
         mSimulatedCommands = new SimulatedCommands();
         mContextFixture = new ContextFixture();
@@ -316,35 +331,47 @@
 
         //mTelephonyComponentFactory
         doReturn(mSST).when(mTelephonyComponentFactory)
-                .makeServiceStateTracker(any(GsmCdmaPhone.class), any(CommandsInterface.class));
+                .makeServiceStateTracker(nullable(GsmCdmaPhone.class),
+                        nullable(CommandsInterface.class));
         doReturn(mIccCardProxy).when(mTelephonyComponentFactory)
-                .makeIccCardProxy(any(Context.class), any(CommandsInterface.class), anyInt());
+                .makeIccCardProxy(nullable(Context.class), nullable(CommandsInterface.class),
+                        anyInt());
         doReturn(mCT).when(mTelephonyComponentFactory)
-                .makeGsmCdmaCallTracker(any(GsmCdmaPhone.class));
+                .makeGsmCdmaCallTracker(nullable(GsmCdmaPhone.class));
         doReturn(mIccPhoneBookIntManager).when(mTelephonyComponentFactory)
-                .makeIccPhoneBookInterfaceManager(any(Phone.class));
+                .makeIccPhoneBookInterfaceManager(nullable(Phone.class));
         doReturn(mDcTracker).when(mTelephonyComponentFactory)
-                .makeDcTracker(any(Phone.class));
+                .makeDcTracker(nullable(Phone.class));
         doReturn(mWspTypeDecoder).when(mTelephonyComponentFactory)
-                .makeWspTypeDecoder(any(byte[].class));
+                .makeWspTypeDecoder(nullable(byte[].class));
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyBoolean(), anyString(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        anyBoolean(), nullable(String.class), nullable(String.class),
+                        nullable(String.class));
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(Cursor.class), anyBoolean());
+                .makeInboundSmsTracker(nullable(Cursor.class), anyBoolean());
         doReturn(mImsCT).when(mTelephonyComponentFactory)
-                .makeImsPhoneCallTracker(any(ImsPhone.class));
+                .makeImsPhoneCallTracker(nullable(ImsPhone.class));
         doReturn(mCdmaSSM).when(mTelephonyComponentFactory)
-                .getCdmaSubscriptionSourceManagerInstance(any(Context.class),
-                        any(CommandsInterface.class), any(Handler.class),
-                        anyInt(), any(Object.class));
+                .getCdmaSubscriptionSourceManagerInstance(nullable(Context.class),
+                        nullable(CommandsInterface.class), nullable(Handler.class),
+                        anyInt(), nullable(Object.class));
         doReturn(mIDeviceIdleController).when(mTelephonyComponentFactory)
                 .getIDeviceIdleController();
-        doReturn(mImsExternalCallTracker).when(mTelephonyComponentFactory).
-                makeImsExternalCallTracker(any(ImsPhone.class));
+        doReturn(mImsExternalCallTracker).when(mTelephonyComponentFactory)
+                .makeImsExternalCallTracker(nullable(ImsPhone.class));
+        doReturn(mAppSmsManager).when(mTelephonyComponentFactory)
+                .makeAppSmsManager(nullable(Context.class));
+        doReturn(mCarrierSignalAgent).when(mTelephonyComponentFactory)
+                .makeCarrierSignalAgent(nullable(Phone.class));
+        doReturn(mCarrierActionAgent).when(mTelephonyComponentFactory)
+                .makeCarrierActionAgent(nullable(Phone.class));
+        doReturn(mDeviceStateMonitor).when(mTelephonyComponentFactory)
+                .makeDeviceStateMonitor(nullable(Phone.class));
 
         //mPhone
         doReturn(mContext).when(mPhone).getContext();
@@ -359,6 +386,8 @@
         doReturn(mCT).when(mPhone).getCallTracker();
         doReturn(mSST).when(mPhone).getServiceStateTracker();
         doReturn(mCarrierSignalAgent).when(mPhone).getCarrierSignalAgent();
+        doReturn(mCarrierActionAgent).when(mPhone).getCarrierActionAgent();
+        doReturn(mAppSmsManager).when(mPhone).getAppSmsManager();
         mPhone.mEriManager = mEriManager;
 
         //mUiccController
@@ -400,7 +429,7 @@
 
         //SMS
         doReturn(true).when(mSmsStorageMonitor).isStorageAvailable();
-        doReturn(true).when(mSmsUsageMonitor).check(anyString(), anyInt());
+        doReturn(true).when(mSmsUsageMonitor).check(nullable(String.class), anyInt());
         doReturn(true).when(mTelephonyManager).getSmsReceiveCapableForPhone(anyInt(), anyBoolean());
         doReturn(true).when(mTelephonyManager).getSmsSendCapableForPhone(
                 anyInt(), anyBoolean());
@@ -415,8 +444,9 @@
         doReturn(mImsCallProfile).when(mImsCall).getCallProfile();
         doReturn(mIBinder).when(mIIntentSender).asBinder();
         doReturn(mIIntentSender).when(mIActivityManager).getIntentSender(anyInt(),
-                anyString(), any(IBinder.class), anyString(), anyInt(), any(Intent[].class),
-                any(String[].class), anyInt(), any(Bundle.class), anyInt());
+                nullable(String.class), nullable(IBinder.class), nullable(String.class), anyInt(),
+                nullable(Intent[].class), nullable(String[].class), anyInt(),
+                nullable(Bundle.class), anyInt());
         mSST.mSS = mServiceState;
         mServiceManagerMockedServices.put("connectivity_metrics_logger", mConnMetLoggerBinder);
 
@@ -425,6 +455,8 @@
 
     protected void tearDown() throws Exception {
 
+        mSimulatedCommands.dispose();
+
         SharedPreferences sharedPreferences = mContext.getSharedPreferences((String) null, 0);
         sharedPreferences.edit().clear().commit();
 
@@ -460,5 +492,31 @@
     protected void setupMockPackagePermissionChecks() throws Exception {
         doReturn(new String[]{TAG}).when(mPackageManager).getPackagesForUid(anyInt());
         doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(eq(TAG), anyInt());
+        doReturn(mPackageInfo).when(mPackageManager).getPackageInfoAsUser(
+                eq(TAG), anyInt(), anyInt());
+    }
+
+    protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
+        final CountDownLatch lock = new CountDownLatch(1);
+        h.post(lock::countDown);
+        while (lock.getCount() > 0) {
+            try {
+                lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+    }
+
+    protected final void waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs) {
+        final CountDownLatch lock = new CountDownLatch(1);
+        h.postDelayed(lock::countDown, delayMs);
+        while (lock.getCount() > 0) {
+            try {
+                lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java b/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
index 8c6b24f..fab16be 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/VisualVoicemailSmsFilterTest.java
@@ -16,40 +16,156 @@
 
 package com.android.internal.telephony;
 
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
 import android.content.Context;
+import android.telecom.PhoneAccountHandle;
 import android.telephony.TelephonyManager;
 import android.telephony.VisualVoicemailSmsFilterSettings;
 
+import com.android.internal.telephony.VisualVoicemailSmsFilter.PhoneAccountHandleConverter;
+
 import junit.framework.TestCase;
 
 import org.mockito.Mockito;
 
+import java.util.Arrays;
+
+/**
+ * Unit test for {@link VisualVoicemailSmsFilter}
+ */
 public class VisualVoicemailSmsFilterTest extends TestCase {
 
     /**
-     * b/29123941 iPhone style notification SMS is neither 3GPP nor 3GPP2, but some plain text
-     * message. {@link android.telephony.SmsMessage.createFromPdu()} will fail to parse it and
-     * return an invalid object, causing {@link NullPointerException} on any operation if not
-     * handled.
+     * PDU for the following message:
+     * <p>originating number: 129
+     * <p>message: //VVM:SYNC:ev=NM;id=143;c=6;t=v;s=11111111111;dt=07/03/2017 18:17 -0800;l=4
+     */
+    private static final byte[][] SYNC_PDU = {{
+            (byte) 0x07, (byte) 0x91, (byte) 0x41, (byte) 0x50, (byte) 0x74, (byte) 0x02,
+            (byte) 0x50, (byte) 0xF5, (byte) 0x44, (byte) 0x03, (byte) 0xC9, (byte) 0x21,
+            (byte) 0xF9, (byte) 0x00, (byte) 0x00, (byte) 0x71, (byte) 0x30, (byte) 0x70,
+            (byte) 0x81, (byte) 0x71, (byte) 0x81, (byte) 0x2B, (byte) 0x53, (byte) 0x06,
+            (byte) 0x05, (byte) 0x04, (byte) 0x07, (byte) 0x10, (byte) 0x01, (byte) 0x01,
+            (byte) 0xAF, (byte) 0x97, (byte) 0xD5, (byte) 0xDA, (byte) 0xD4, (byte) 0x4D,
+            (byte) 0xB3, (byte) 0xCE, (byte) 0xA1, (byte) 0xAE, (byte) 0x6C, (byte) 0xEF,
+            (byte) 0x39, (byte) 0x9B, (byte) 0xBB, (byte) 0x34, (byte) 0xB9, (byte) 0x17,
+            (byte) 0xA3, (byte) 0xCD, (byte) 0x76, (byte) 0xE3, (byte) 0x9E, (byte) 0x6D,
+            (byte) 0x47, (byte) 0xEF, (byte) 0xD9, (byte) 0x77, (byte) 0xF3, (byte) 0x5E,
+            (byte) 0x2C, (byte) 0x16, (byte) 0x8B, (byte) 0xC5, (byte) 0x62, (byte) 0xB1,
+            (byte) 0x58, (byte) 0x2C, (byte) 0x16, (byte) 0xDB, (byte) 0x91, (byte) 0xE9,
+            (byte) 0x3D, (byte) 0xD8, (byte) 0xED, (byte) 0x05, (byte) 0x9B, (byte) 0xBD,
+            (byte) 0x64, (byte) 0xB0, (byte) 0xD8, (byte) 0x0D, (byte) 0x14, (byte) 0xC3,
+            (byte) 0xE9, (byte) 0x62, (byte) 0x37, (byte) 0x50, (byte) 0x0B, (byte) 0x86,
+            (byte) 0x83, (byte) 0xC1, (byte) 0x76, (byte) 0xEC, (byte) 0x1E, (byte) 0x0D}};
+
+    private Context mContext;
+    private TelephonyManager mTelephonyManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mContext = Mockito.mock(Context.class);
+        mTelephonyManager = Mockito.mock(TelephonyManager.class);
+        when(mContext.getSystemServiceName(TelephonyManager.class))
+                .thenReturn(Context.TELEPHONY_SERVICE);
+        when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
+                .thenReturn(mTelephonyManager);
+
+        VisualVoicemailSmsFilter.setPhoneAccountHandleConverterForTest(
+                new PhoneAccountHandleConverter() {
+                    @Override
+                    public PhoneAccountHandle fromSubId(int subId) {
+                        return new PhoneAccountHandle(
+                                new ComponentName("com.android.internal.telephony",
+                                        "VisualVoicemailSmsFilterTest"), "foo");
+                    }
+                });
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        VisualVoicemailSmsFilter.setPhoneAccountHandleConverterForTest(null);
+        super.tearDown();
+    }
+
+
+    /**
+     * Notification SMS targeting over devices do not follow 3GPP or 3GPP2 standards, but instead
+     * use a plain text message. {@link android.telephony.SmsMessage#createFromPdu(byte[], String)}
+     * will fail to parse it and return an invalid object, causing {@link NullPointerException} on
+     * any operation if not handled.
      */
     public void testUnsupportedPdu() {
-        Context context = Mockito.mock(Context.class);
-        TelephonyManager telephonyManager = Mockito.mock(TelephonyManager.class);
-        Mockito.when(context.getSystemServiceName(TelephonyManager.class))
-                .thenReturn(Context.TELEPHONY_SERVICE);
-        Mockito.when(context.getSystemService(Mockito.anyString())).thenReturn(telephonyManager);
 
-        VisualVoicemailSmsFilterSettings settings = new VisualVoicemailSmsFilterSettings.Builder()
-                .build();
-
-        Mockito.when(telephonyManager
-                .getVisualVoicemailSmsFilterSettings(Mockito.anyString(), Mockito.anyInt()))
-                .thenReturn(settings);
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder().build());
 
         byte[][] pdus = {
                 ("MBOXUPDATE?m=11;server=example.com;"
                         + "port=143;name=1234567890@example.com;pw=CphQJKnYS4jEiDO").getBytes()};
-        VisualVoicemailSmsFilter.filter(context, pdus, SmsConstants.FORMAT_3GPP2, 0, 0);
+        assertFalse(
+                VisualVoicemailSmsFilter.filter(mContext, pdus, SmsConstants.FORMAT_3GPP, 0, 0));
     }
 
+    public void testOriginatingNumber_unspecified_filtered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder().build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 0, 0));
+    }
+
+    public void testOriginatingNumber_match_filtered() {
+        setSettings(
+                new VisualVoicemailSmsFilterSettings.Builder().setOriginatingNumbers(
+                        Arrays.asList("129")
+                ).build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 0, 0));
+    }
+
+    public void testOriginatingNumber_mismatch_notFiltered() {
+        setSettings(
+                new VisualVoicemailSmsFilterSettings.Builder().setOriginatingNumbers(
+                        Arrays.asList("128")
+                ).build());
+        assertFalse(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 0, 0));
+    }
+
+    public void testDestinationPort_anyMatch_filtered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(123).build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 123, 0));
+    }
+
+    public void testDestinationPort_anyData_filtered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS)
+                .build());
+        assertTrue(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 456, 0));
+    }
+
+    public void testDestinationPort_anyData_textReceived_notFiltered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(VisualVoicemailSmsFilterSettings.DESTINATION_PORT_DATA_SMS)
+                .build());
+        assertFalse(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, -1, 0));
+    }
+
+
+    public void testDestinationPort_mismatch_notFiltered() {
+        setSettings(new VisualVoicemailSmsFilterSettings.Builder()
+                .setDestinationPort(123).build());
+        assertFalse(VisualVoicemailSmsFilter
+                .filter(mContext, SYNC_PDU, SmsConstants.FORMAT_3GPP, 456, 0));
+    }
+
+    private void setSettings(VisualVoicemailSmsFilterSettings settings) {
+        when(mTelephonyManager.getActiveVisualVoicemailSmsFilterSettings(anyInt()))
+                .thenReturn(settings);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
index 6d3e303..e1ac5f3 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/WapPushOverSmsTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
@@ -89,7 +90,7 @@
         verify(mInboundSmsHandler).dispatchIntent(intentArgumentCaptor.capture(),
                 eq(android.Manifest.permission.RECEIVE_WAP_PUSH),
                 eq(AppOpsManager.OP_RECEIVE_WAP_PUSH),
-                any(Bundle.class),
+                nullable(Bundle.class),
                 isNull(BroadcastReceiver.class),
                 eq(UserHandle.SYSTEM));
         Intent intent = intentArgumentCaptor.getValue();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index e635793..5786a70 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -16,6 +16,19 @@
 
 package com.android.internal.telephony.cdma;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -24,8 +37,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.platform.test.annotations.Postsubmit;
 import android.provider.Telephony;
+import android.support.test.filters.FlakyTest;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
 
@@ -40,6 +53,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -47,18 +61,6 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class CdmaInboundSmsHandlerTest extends TelephonyTest {
     @Mock
     private SmsStorageMonitor mSmsStorageMonitor;
@@ -159,7 +161,7 @@
         assertFalse(mCdmaInboundSmsHandler.getWakeLock().isHeld());
         mCdmaInboundSmsHandler = null;
         mContentProvider.shutdown();
-        mCdmaInboundSmsHandlerTestHandler.quitSafely();
+        mCdmaInboundSmsHandlerTestHandler.quit();
         super.tearDown();
     }
 
@@ -174,9 +176,10 @@
         assertEquals("IdleState", getCurrentState().getName());
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @MediumTest
+    @Ignore
     public void testNewSms() {
         transitionFromStartupToIdle();
 
@@ -210,7 +213,7 @@
     @MediumTest
     public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
         String blockedNumber = "123456789";
-        doReturn(blockedNumber).when(mInboundSmsTracker).getAddress();
+        doReturn(blockedNumber).when(mInboundSmsTracker).getDisplayAddress();
         mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
 
         transitionFromStartupToIdle();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
index b16fde0..ab7fd7f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java
@@ -16,8 +16,9 @@
 
 package com.android.internal.telephony.cdma;
 
+import android.hardware.radio.V1_0.CdmaSmsMessage;
 import android.os.Parcel;
-import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
 import android.telephony.SmsCbCmasInfo;
 import android.telephony.SmsCbMessage;
 import android.telephony.cdma.CdmaSmsCbProgramData;
@@ -26,6 +27,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.cdma.SmsMessageConverter;
 import com.android.internal.telephony.cdma.sms.BearerData;
 import com.android.internal.telephony.cdma.sms.CdmaSmsAddress;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
@@ -35,6 +37,7 @@
 
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Random;
@@ -57,23 +60,21 @@
      * @param serviceCategory the CDMA service category
      * @return the initialized Parcel
      */
-    private static Parcel createBroadcastParcel(int serviceCategory) {
-        Parcel p = Parcel.obtain();
+    private static CdmaSmsMessage createBroadcastParcel(int serviceCategory) {
+        CdmaSmsMessage msg = new CdmaSmsMessage();
 
-        p.writeInt(SmsEnvelope.TELESERVICE_NOT_SET);
-        p.writeByte((byte) 1);  // non-zero for MESSAGE_TYPE_BROADCAST
-        p.writeInt(serviceCategory);
+        msg.teleserviceId = SmsEnvelope.TELESERVICE_NOT_SET;
+        msg.isServicePresent = true;
+        msg.serviceCategory = serviceCategory;
 
         // dummy address (RIL may generate a different dummy address for broadcasts)
-        p.writeInt(CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);            // sAddress.digit_mode
-        p.writeInt(CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);    // sAddress.number_mode
-        p.writeInt(CdmaSmsAddress.TON_UNKNOWN);                     // sAddress.number_type
-        p.writeInt(CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY);   // sAddress.number_plan
-        p.writeByte((byte) 0);      // sAddress.number_of_digits
-        p.writeInt((byte) 0);       // sSubAddress.subaddressType
-        p.writeByte((byte) 0);      // sSubAddress.odd
-        p.writeByte((byte) 0);      // sSubAddress.number_of_digits
-        return p;
+        msg.address.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
+        msg.address.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
+        msg.address.numberType = CdmaSmsAddress.TON_UNKNOWN;
+        msg.address.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY;
+        msg.subAddress.subaddressType = 0;
+        msg.subAddress.odd = false;
+        return msg;
     }
 
     /**
@@ -113,18 +114,15 @@
 
     /**
      * Write the bearer data array to the parcel, then return a new SmsMessage from the parcel.
-     * @param p the parcel containing the CDMA SMS headers
+     * @param msg CdmaSmsMessage containing the CDMA SMS headers
      * @param bearerData the bearer data byte array to append to the parcel
      * @return the new SmsMessage created from the parcel
      */
-    private static SmsMessage createMessageFromParcel(Parcel p, byte[] bearerData) {
-        p.writeInt(bearerData.length);
+    private static SmsMessage createMessageFromParcel(CdmaSmsMessage msg, byte[] bearerData) {
         for (byte b : bearerData) {
-            p.writeByte(b);
+            msg.bearerData.add(b);
         }
-        p.setDataPosition(0);   // reset position for reading
-        SmsMessage message = SmsMessage.newFromParcel(p);
-        p.recycle();
+        SmsMessage message = SmsMessageConverter.newCdmaSmsMessageFromRil(msg);
         return message;
     }
 
@@ -167,7 +165,7 @@
 
         byte[] cmasUserData = cmasBos.toByteArray();
 
-        Parcel p = createBroadcastParcel(serviceCategory);
+        CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
         BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
 
         bos.write(8, SUBPARAM_USER_DATA);
@@ -177,7 +175,7 @@
         bos.writeByteArray(cmasUserData.length * 8, cmasUserData);
         bos.write(3, 0);    // pad to byte boundary
 
-        return createMessageFromParcel(p, bos.toByteArray());
+        return createMessageFromParcel(msg, bos.toByteArray());
     }
 
     /**
@@ -193,13 +191,13 @@
      */
     private static SmsMessage createBroadcastSmsMessage(int serviceCategory, int messageId,
             int priority, int language, int encoding, String body) throws Exception {
-        Parcel p = createBroadcastParcel(serviceCategory);
+        CdmaSmsMessage msg = createBroadcastParcel(serviceCategory);
         BitwiseOutputStream bos = createBearerDataStream(messageId, priority, language);
 
         bos.write(8, SUBPARAM_USER_DATA);
         encodeBody(encoding, body, false, bos);
 
-        return createMessageFromParcel(p, bos.toByteArray());
+        return createMessageFromParcel(msg, bos.toByteArray());
     }
 
     /**
@@ -483,12 +481,12 @@
 
     // VZW requirement is to discard message with unsupported charset. Verify that we return null
     // for this unsupported character set.
-    @Postsubmit
+    @FlakyTest
     @Test @SmallTest
     public void testCmasUnsupportedCharSet() throws Exception {
         SmsMessage msg = createCmasSmsMessage(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT,
                 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH,
-                UserData.ENCODING_GSM_DCS, EXTREME_ALERT, -1, -1, -1, -1, -1);
+                0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1);
 
         SmsCbMessage cbMessage = msg.parseBroadcastSms();
         assertNull("expected null for unsupported charset", cbMessage);
@@ -547,8 +545,8 @@
             // Rlog.d("CdmaSmsCbTest", "trying random bearer data run " + run + " length " + len);
             try {
                 int category = 0x0ff0 + r.nextInt(32);  // half CMAS, half non-CMAS
-                Parcel p = createBroadcastParcel(category);
-                SmsMessage msg = createMessageFromParcel(p, data);
+                CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
+                SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data);
                 SmsCbMessage cbMessage = msg.parseBroadcastSms();
                 // with random input, cbMessage will almost always be null (log when it isn't)
                 if (cbMessage != null) {
@@ -567,7 +565,7 @@
         Random r = new Random(94040);
         for (int run = 0; run < 1000; run++) {
             int category = 0x0ff0 + r.nextInt(32);  // half CMAS, half non-CMAS
-            Parcel p = createBroadcastParcel(category);
+            CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category);
             int len = r.nextInt(140);
             // Rlog.d("CdmaSmsCbTest", "trying random user data run " + run + " length " + len);
 
@@ -582,7 +580,7 @@
                     bos.write(8, r.nextInt(256));
                 }
 
-                SmsMessage msg = createMessageFromParcel(p, bos.toByteArray());
+                SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
                 SmsCbMessage cbMessage = msg.parseBroadcastSms();
             } catch (Exception e) {
                 Rlog.d("CdmaSmsCbTest", "exception thrown", e);
@@ -596,23 +594,21 @@
      * write the bearer data and then convert it to an SmsMessage.
      * @return the initialized Parcel
      */
-    private static Parcel createServiceCategoryProgramDataParcel() {
-        Parcel p = Parcel.obtain();
+    private static CdmaSmsMessage createServiceCategoryProgramDataParcel() {
+        CdmaSmsMessage msg = new CdmaSmsMessage();
 
-        p.writeInt(SmsEnvelope.TELESERVICE_SCPT);
-        p.writeByte((byte) 0);  // non-zero for MESSAGE_TYPE_BROADCAST
-        p.writeInt(0);
+        msg.teleserviceId = SmsEnvelope.TELESERVICE_SCPT;
+        msg.isServicePresent = false;
+        msg.serviceCategory = 0;
 
         // dummy address (RIL may generate a different dummy address for broadcasts)
-        p.writeInt(CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF);            // sAddress.digit_mode
-        p.writeInt(CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK);    // sAddress.number_mode
-        p.writeInt(CdmaSmsAddress.TON_UNKNOWN);                     // sAddress.number_type
-        p.writeInt(CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY);   // sAddress.number_plan
-        p.writeByte((byte) 0);      // sAddress.number_of_digits
-        p.writeInt((byte) 0);       // sSubAddress.subaddressType
-        p.writeByte((byte) 0);      // sSubAddress.odd
-        p.writeByte((byte) 0);      // sSubAddress.number_of_digits
-        return p;
+        msg.address.digitMode = CdmaSmsAddress.DIGIT_MODE_4BIT_DTMF;
+        msg.address.numberMode = CdmaSmsAddress.NUMBER_MODE_NOT_DATA_NETWORK;
+        msg.address.numberType = CdmaSmsAddress.TON_UNKNOWN;
+        msg.address.numberPlan = CdmaSmsAddress.NUMBERING_PLAN_ISDN_TELEPHONY;
+        msg.subAddress.subaddressType = 0;
+        msg.subAddress.odd = false;
+        return msg;
     }
 
     private static final String CAT_EXTREME_THREAT = "Extreme Threat to Life and Property";
@@ -621,7 +617,7 @@
 
     @Test @SmallTest
     public void testServiceCategoryProgramDataAddCategory() throws Exception {
-        Parcel p = createServiceCategoryProgramDataParcel();
+        CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
         BitwiseOutputStream bos = createBearerDataStream(123, -1, -1);
 
         int categoryNameLength = CAT_EXTREME_THREAT.length();
@@ -646,7 +642,7 @@
         }
         bos.write(subparamPadBits, 0);
 
-        SmsMessage msg = createMessageFromParcel(p, bos.toByteArray());
+        SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
         assertNotNull(msg);
         msg.parseSms();
         List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
@@ -663,7 +659,7 @@
 
     @Test @SmallTest
     public void testServiceCategoryProgramDataDeleteTwoCategories() throws Exception {
-        Parcel p = createServiceCategoryProgramDataParcel();
+        CdmaSmsMessage cdmaSmsMessage = createServiceCategoryProgramDataParcel();
         BitwiseOutputStream bos = createBearerDataStream(456, -1, -1);
 
         int category1NameLength = CAT_SEVERE_THREAT.length();
@@ -703,7 +699,7 @@
 
         bos.write(subparamPadBits, 0);
 
-        SmsMessage msg = createMessageFromParcel(p, bos.toByteArray());
+        SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray());
         assertNotNull(msg);
         msg.parseSms();
         List<CdmaSmsCbProgramData> programDataList = msg.getSmsCbProgramData();
@@ -745,8 +741,8 @@
     // Test case for CMAS test message received on the Sprint network.
     @Test @SmallTest
     public void testDecodeRawBearerData() throws Exception {
-        Parcel p = createBroadcastParcel(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE);
-        SmsMessage msg = createMessageFromParcel(p, CMAS_TEST_BEARER_DATA);
+        CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE);
+        SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA);
 
         SmsCbMessage cbMessage = msg.parseBroadcastSms();
         assertNotNull("expected non-null for bearer data", cbMessage);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
index 37f09b5..a308762 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
@@ -72,7 +72,7 @@
     @After
     public void tearDown() throws Exception {
         mCdmaSmsDispatcher = null;
-        mCdmaSmsDispatcherTestHandler.quitSafely();
+        mCdmaSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
@@ -80,7 +80,7 @@
     public void testSendSms() {
         doReturn(mServiceState).when(mPhone).getServiceState();
         mCdmaSmsDispatcher.sendSms(mSmsTracker);
-        verify(mSimulatedCommandsVerifier).sendCdmaSms(any(byte[].class), any(Message.class));
+        verify(mSimulatedCommandsVerifier).sendCdmaSms(nullable(byte[].class), any(Message.class));
     }
 
     @Test @SmallTest
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index eee3a74..3624ec1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -16,15 +16,29 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_ALL;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_DEFAULT;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_HIPRI;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_IA;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_MMS;
+import static com.android.internal.telephony.PhoneConstants.APN_TYPE_SUPL;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doReturn;
+
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
 
 import org.junit.After;
-import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -33,15 +47,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-import static org.junit.Assert.assertEquals;
-
 public class ApnSettingTest extends TelephonyTest {
 
     private PersistableBundle mBundle;
-    private boolean isRoaming = false;
 
     @Before
     public void setUp() throws Exception {
@@ -54,7 +62,15 @@
         super.tearDown();
     }
 
-    private ApnSetting createApnSetting(String[] apnTypes) {
+    static ApnSetting createApnSetting(String[] apnTypes) {
+        return createApnSettingInternal(apnTypes, true);
+    }
+
+    private static ApnSetting createDisabledApnSetting(String[] apnTypes) {
+        return createApnSettingInternal(apnTypes, false);
+    }
+
+    private static ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
         return new ApnSetting(
                 2163,                   // id
                 "44010",                // numeric
@@ -71,7 +87,7 @@
                 apnTypes,               // types
                 "IP",                   // protocol
                 "IP",                   // roaming_protocol
-                true,                   // carrier_enabled
+                carrierEnabled,         // carrier_enabled
                 0,                      // bearer
                 0,                      // bearer_bitmask
                 0,                      // profile_id
@@ -214,63 +230,52 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
 
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(1).when(mPhone).getSubId();
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 1, isRoaming));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI,
-                mContext, 1, isRoaming));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
 
         // Carrier config settings changes.
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT});
 
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 1, isRoaming));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
     }
 
     @Test
@@ -278,50 +283,93 @@
     public void testIsRoamingMetered() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
-        isRoaming = true;
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(1).when(mPhone).getSubId();
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 1, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 1, isRoaming));
+                isMetered(mPhone));
 
         // Carrier config settings changes.
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_FOTA});
 
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 1, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 1, isRoaming));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA,
-                mContext, 1, isRoaming));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+    }
+
+    @Test
+    @SmallTest
+    public void testIsIwlanMetered() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(1).when(mPhone).getSubId();
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_MMS}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_MMS, PhoneConstants.APN_TYPE_SUPL})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_DUN})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_SUPL})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        // Carrier config settings changes.
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_FOTA});
+
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
     }
 
     @Test
@@ -330,38 +378,35 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
 
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(1).when(mPhone).getSubId();
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
-
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
     }
 
     @Test
@@ -369,56 +414,91 @@
     public void testIsRoamingMeteredAnother() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
-        isRoaming = true;
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(2).when(mPhone).getSubId();
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_SUPL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 2, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
 
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL,
-                mContext, 2, isRoaming));
-        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA,
-                mContext, 2, isRoaming));
-        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI,
-                mContext, 2, isRoaming));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
+    }
 
+    @Test
+    @SmallTest
+    public void testIsIwlanMeteredAnother() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS});
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(2).when(mPhone).getSubId();
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_SUPL}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_CBS}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_SUPL, PhoneConstants.APN_TYPE_IA})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_IMS})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL, mPhone));
+        assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DUN, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_FOTA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_IA, mPhone));
+        assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI, mPhone));
     }
 
     @Test
@@ -427,21 +507,22 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{});
 
-        assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 3, isRoaming));
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(3).when(mPhone).getSubId();
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
     }
 
     @Test
@@ -449,23 +530,48 @@
     public void testIsRoamingMeteredNothingCharged() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{});
-        isRoaming = true;
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(3).when(mPhone).getSubId();
 
         assertFalse(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_IMS}).
-                isMetered(mContext, 3, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 3, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA}).
-                isMetered(mContext, 3, isRoaming));
+                isMetered(mPhone));
 
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 3, isRoaming));
+                isMetered(mPhone));
+    }
+
+    @Test
+    @SmallTest
+    public void testIsIwlanMeteredNothingCharged() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{});
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(3).when(mPhone).getSubId();
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IMS}).isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IMS, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_FOTA})
+                .isMetered(mPhone));
+
+        assertFalse(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
     }
 
     @Test
@@ -474,21 +580,24 @@
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_ALL});
 
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(4).when(mPhone).getSubId();
+
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
     }
 
@@ -497,24 +606,111 @@
     public void testIsRoamingMeteredNothingFree() throws Exception {
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_ALL});
-        isRoaming = true;
+
+        doReturn(true).when(mServiceState).getDataRoaming();
+        doReturn(4).when(mPhone).getSubId();
 
         assertTrue(createApnSetting(
-                new String[]{PhoneConstants.APN_TYPE_ALL}).
-                isMetered(mContext, 4, isRoaming));
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS})
+                .isMetered(mPhone));
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN})
+                .isMetered(mPhone));
+    }
+
+    @Test
+    @SmallTest
+    public void testIsIwlanMeteredNothingFree() throws Exception {
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_IWLAN_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_ALL});
+
+        doReturn(false).when(mServiceState).getDataRoaming();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        doReturn(4).when(mPhone).getSubId();
+
+        assertTrue(createApnSetting(
+                new String[]{PhoneConstants.APN_TYPE_ALL}).isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_FOTA, PhoneConstants.APN_TYPE_CBS}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
 
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
-                isMetered(mContext, 4, isRoaming));
+                isMetered(mPhone));
+    }
 
+    @Test
+    @SmallTest
+    public void testCanHandleType() throws Exception {
+        String types[] = {"mms"};
+
+        // empty string replaced with ALL ('*') when loaded to db
+        assertFalse(createApnSetting(new String[]{}).
+                canHandleType(APN_TYPE_MMS));
+
+        assertTrue(createApnSetting(new String[]{APN_TYPE_ALL}).
+                canHandleType(APN_TYPE_MMS));
+
+        assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
+                canHandleType(APN_TYPE_MMS));
+
+        assertTrue(createApnSetting(new String[]{"DEfAULT"}).
+                canHandleType("defAult"));
+
+        // Hipri is asymmetric
+        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT}).
+                canHandleType(APN_TYPE_HIPRI));
+        assertFalse(createApnSetting(new String[]{APN_TYPE_HIPRI}).
+                canHandleType(APN_TYPE_DEFAULT));
+
+
+        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
+                canHandleType(APN_TYPE_DEFAULT));
+
+        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
+                canHandleType(APN_TYPE_MMS));
+
+        assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
+                canHandleType(APN_TYPE_SUPL));
+
+        // special IA case - doesn't match wildcards
+        assertFalse(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
+                canHandleType(APN_TYPE_IA));
+        assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
+                canHandleType(APN_TYPE_IA));
+        assertFalse(createApnSetting(new String[]{APN_TYPE_ALL}).
+                canHandleType("iA"));
+        assertTrue(createApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
+                canHandleType(APN_TYPE_IA));
+
+        // check carrier disabled
+        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_ALL}).
+                canHandleType(APN_TYPE_MMS));
+        assertFalse(createDisabledApnSetting(new String[]{"DEfAULT"}).
+                canHandleType("defAult"));
+        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT}).
+                canHandleType(APN_TYPE_HIPRI));
+        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
+                canHandleType(APN_TYPE_DEFAULT));
+        assertFalse(createDisabledApnSetting(new String[]{APN_TYPE_DEFAULT, APN_TYPE_MMS}).
+                canHandleType(APN_TYPE_MMS));
+        assertFalse(createDisabledApnSetting(new String[]
+                {APN_TYPE_DEFAULT, APN_TYPE_MMS, APN_TYPE_IA}).
+                canHandleType(APN_TYPE_IA));
     }
 
     @Test
@@ -553,4 +749,67 @@
             }
         }
     }
-}
+
+    @Test
+    @SmallTest
+    public void testEqualsRoamingProtocol() throws Exception {
+        ApnSetting apn1 = new ApnSetting(
+                1234,
+                "310260",
+                "",
+                "ims",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                -1,
+                 new String[]{"ims"},
+                "IPV6",
+                "",
+                true,
+                0,
+                131071,
+                0,
+                false,
+                0,
+                0,
+                0,
+                1440,
+                "",
+                "");
+
+        ApnSetting apn2 = new ApnSetting(
+                1235,
+                "310260",
+                "",
+                "ims",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                "",
+                -1,
+                new String[]{"ims"},
+                "IPV6",
+                "IPV6",
+                true,
+                0,
+                131072,
+                0,
+                false,
+                0,
+                0,
+                0,
+                1440,
+                "",
+                "");
+
+        assertTrue(apn1.equals(apn2, false));
+        assertFalse(apn1.equals(apn2, true));
+    }
+}
\ No newline at end of file
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
index 6a4616c..6a8a49a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataCallResponseTest.java
@@ -16,6 +16,14 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
+
+import static org.junit.Assert.assertEquals;
+
 import android.net.LinkProperties;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -26,8 +34,6 @@
 import org.junit.Before;
 import org.junit.Test;
 
-import static org.junit.Assert.assertEquals;
-
 public class DataCallResponseTest extends TelephonyTest {
 
     DataCallResponse mDcResponse;
@@ -35,19 +41,8 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        mDcResponse = new DataCallResponse();
-        mDcResponse.version = 11;
-        mDcResponse.status = 0;
-        mDcResponse.suggestedRetryTime = -1;
-        mDcResponse.cid = 1;
-        mDcResponse.active = 2;
-        mDcResponse.type = "IP";
-        mDcResponse.ifname = "rmnet_data7";
-        mDcResponse.mtu = 1440;
-        mDcResponse.addresses = new String[]{"12.34.56.78"};
-        mDcResponse.dnses = new String[]{"98.76.54.32"};
-        mDcResponse.gateways = new String[]{"11.22.33.44"};
-        mDcResponse.pcscf = new String[]{};
+        mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME, FAKE_ADDRESS,
+                FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
     }
 
     @After
@@ -88,7 +83,9 @@
     @SmallTest
     public void testSetLinkPropertiesInvalidAddress() throws Exception {
 
-        mDcResponse.addresses = new String[]{"224.224.224.224"};
+        // 224.224.224.224 is an invalid address.
+        mDcResponse = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME, "224.224.224.224",
+                FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
 
         LinkProperties linkProperties = new LinkProperties();
         assertEquals(SetupResult.ERR_UnacceptableParameter,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 5b02a38..76380c7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -16,13 +16,28 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
-import android.platform.test.annotations.Postsubmit;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -36,26 +51,15 @@
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
 
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class DataConnectionTest extends TelephonyTest {
 
     @Mock
@@ -160,7 +164,7 @@
         logd("tearDown");
         mDc = null;
         mDcc = null;
-        mDataConnectionTestHandler.quitSafely();
+        mDataConnectionTestHandler.quit();
         super.tearDown();
     }
 
@@ -197,9 +201,12 @@
         verify(mCT, times(1)).registerForVoiceCallEnded(any(Handler.class),
                 eq(DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED), eq(null));
 
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq("spmode.ne.jp"),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
+                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+
+        assertEquals("spmode.ne.jp", dpCaptor.getValue().apn);
 
         assertEquals("DcActiveState", getCurrentState().getName());
     }
@@ -221,38 +228,46 @@
     @Test
     @SmallTest
     public void testModemSuggestRetry() throws Exception {
-        DataCallResponse response = new DataCallResponse();
-        response.suggestedRetryTime = 0;
+        DataCallResponse response = new DataCallResponse(0, 0, 1, 2, "IP",
+                FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
         AsyncResult ar = new AsyncResult(null, response, null);
         assertEquals(response.suggestedRetryTime, getSuggestedRetryDelay(ar));
 
-        response.suggestedRetryTime = 1000;
+        response = new DataCallResponse(0, 1000, 1, 2, "IP",
+                FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+        ar = new AsyncResult(null, response, null);
         assertEquals(response.suggestedRetryTime, getSuggestedRetryDelay(ar));
 
-        response.suggestedRetryTime = 9999;
+        response = new DataCallResponse(0, 9999, 1, 2, "IP",
+                FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+        ar = new AsyncResult(null, response, null);
         assertEquals(response.suggestedRetryTime, getSuggestedRetryDelay(ar));
     }
 
     @Test
     @SmallTest
     public void testModemNotSuggestRetry() throws Exception {
-        DataCallResponse response = new DataCallResponse();
-        response.suggestedRetryTime = -1;
+        DataCallResponse response = new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
         AsyncResult ar = new AsyncResult(null, response, null);
         assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
 
-        response.suggestedRetryTime = -5;
+        response = new DataCallResponse(0, -5, 1, 2, "IP", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+        ar = new AsyncResult(null, response, null);
         assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
 
-        response.suggestedRetryTime = Integer.MIN_VALUE;
+        response = new DataCallResponse(0, Integer.MIN_VALUE, 1, 2, "IP", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+        ar = new AsyncResult(null, response, null);
         assertEquals(RetryManager.NO_SUGGESTED_RETRY_DELAY, getSuggestedRetryDelay(ar));
     }
 
     @Test
     @SmallTest
     public void testModemSuggestNoRetry() throws Exception {
-        DataCallResponse response = new DataCallResponse();
-        response.suggestedRetryTime = Integer.MAX_VALUE;
+        DataCallResponse response = new DataCallResponse(0, Integer.MAX_VALUE, 1, 2, "IP",
+                FAKE_IFNAME, FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
         AsyncResult ar = new AsyncResult(null, response, null);
         assertEquals(RetryManager.NO_RETRY, getSuggestedRetryDelay(ar));
     }
@@ -263,8 +278,8 @@
         return (NetworkInfo) f.get(mDc);
     }
 
-    private NetworkCapabilities getCopyNetworkCapabilities() throws Exception {
-        Method method = DataConnection.class.getDeclaredMethod("getCopyNetworkCapabilities");
+    private NetworkCapabilities getNetworkCapabilities() throws Exception {
+        Method method = DataConnection.class.getDeclaredMethod("getNetworkCapabilities");
         method.setAccessible(true);
         return (NetworkCapabilities) method.invoke(mDc);
     }
@@ -279,8 +294,8 @@
 
         testConnectEvent();
 
-        assertFalse(getCopyNetworkCapabilities().
-                hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+        assertFalse(getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
         assertTrue(getNetworkInfo().isMetered());
     }
 
@@ -295,8 +310,19 @@
 
         testConnectEvent();
 
-        assertTrue(getCopyNetworkCapabilities().
-                hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+        assertTrue(getNetworkCapabilities()
+                .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
         assertFalse(getNetworkInfo().isMetered());
     }
-}
\ No newline at end of file
+
+    @SmallTest
+    public void testIsIpAddress() throws Exception {
+        // IPv4
+        assertTrue(DataConnection.isIpAddress("1.2.3.4"));
+        assertTrue(DataConnection.isIpAddress("127.0.0.1"));
+
+        // IPv6
+        assertTrue(DataConnection.isIpAddress("::1"));
+        assertTrue(DataConnection.isIpAddress("2001:4860:800d::68"));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
index ccbd084..4df3693 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataProfileTest.java
@@ -16,9 +16,10 @@
 
 package com.android.internal.telephony.dataconnection;
 
-import android.os.Parcel;
 import android.test.suitebuilder.annotation.SmallTest;
 
+import com.android.internal.telephony.RILConstants;
+
 import junit.framework.TestCase;
 
 public class DataProfileTest extends TestCase {
@@ -81,11 +82,11 @@
 
     @SmallTest
     public void testCreateFromApnSetting() throws Exception {
-        DataProfile dp = new DataProfile(mApn1, false);
+        DataProfile dp = new DataProfile(mApn1);
         assertEquals(mApn1.profileId, dp.profileId);
         assertEquals(mApn1.apn, dp.apn);
         assertEquals(mApn1.protocol, dp.protocol);
-        assertEquals(mApn1.authType, dp.authType);
+        assertEquals(RILConstants.SETUP_DATA_AUTH_PAP_CHAP, dp.authType);
         assertEquals(mApn1.user, dp.user);
         assertEquals(mApn1.password, dp.password);
         assertEquals(0, dp.type);
@@ -96,41 +97,12 @@
     }
 
     @SmallTest
-    public void testParcel() throws Exception {
-        Parcel p = Parcel.obtain();
-
-        DataProfile[] dps = new DataProfile[]{new DataProfile(mApn1, false),
-                new DataProfile(mApn1, false)};
-
-        DataProfile.toParcel(p, dps);
-        p.setDataPosition(0);
-
-        assertEquals(dps.length, p.readInt());
-        for (int i = 0; i < dps.length; i++) {
-            assertEquals("i = " + i, mApn1.profileId, p.readInt());
-            assertEquals("i = " + i, mApn1.apn, p.readString());
-            assertEquals("i = " + i, mApn1.protocol, p.readString());
-            assertEquals("i = " + i, mApn1.authType, p.readInt());
-            assertEquals("i = " + i, mApn1.user, p.readString());
-            assertEquals("i = " + i, mApn1.password, p.readString());
-            assertEquals("i = " + i, 0, p.readInt());
-            assertEquals("i = " + i, mApn1.maxConnsTime, p.readInt());
-            assertEquals("i = " + i, mApn1.maxConns, p.readInt());
-            assertEquals("i = " + i, mApn1.waitTime, p.readInt());
-            assertEquals("i = " + i, mApn1.carrierEnabled?1:0, p.readInt());
-        }
-    }
-
-    @SmallTest
     public void testEquals() throws Exception {
-        DataProfile dp1 = new DataProfile(mApn1, false);
-        DataProfile dp2 = new DataProfile(mApn1, false);
+        DataProfile dp1 = new DataProfile(mApn1);
+        DataProfile dp2 = new DataProfile(mApn1);
         assertEquals(dp1, dp2);
 
-        dp2 = new DataProfile(mApn1, true);
-        assertFalse(dp1.equals(dp2));
-
-        dp2 = new DataProfile(mApn2, false);
+        dp2 = new DataProfile(mApn2);
         assertFalse(dp1.equals(dp2));
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
index cb4232c..d209639 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcControllerTest.java
@@ -16,6 +16,21 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.net.LinkProperties;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -38,15 +53,6 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class DcControllerTest extends TelephonyTest {
 
     private static final int DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT = 1;
@@ -108,7 +114,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mDcControllerTestHandler.quitSafely();
+        mDcControllerTestHandler.quit();
         super.tearDown();
     }
 
@@ -117,8 +123,9 @@
     public void testDataDormant() {
         assertEquals("DccDefaultState", getCurrentState().getName());
         ArrayList<DataCallResponse> l = new ArrayList<DataCallResponse>();
-        DataCallResponse dcResponse = DcTrackerTest.createDataCallResponse();
-        dcResponse.active = DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT;
+        DataCallResponse dcResponse = new DataCallResponse(0, -1, 1,
+                DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT, "IP", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
         l.add(dcResponse);
 
         mDc.mCid = 1;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java
index 2f22420..6ef1e39 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcFailCauseTest.java
@@ -16,22 +16,22 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.dataconnection.ApnSetting;
-
-import junit.framework.TestCase;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
 import java.util.ArrayList;
-import java.util.List;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
 
 public class DcFailCauseTest extends TelephonyTest {
 
@@ -144,12 +144,38 @@
 
     @Test
     @SmallTest
-    public void testPermanentFail() throws Exception {
+    public void testPermanentFailDefault() throws Exception {
         for (DcFailCauseData data : mFailCauseDataList) {
-            assertEquals("cause = " + data.mCause, data.mPermanentFailure,
-                    DcFailCause.fromInt(data.mCause).isPermanentFail());
+            assertEquals("cause = " + data.mCause, data.mPermanentFailure, DcFailCause.fromInt(
+                    data.mCause).isPermanentFailure(mContext, mPhone.getSubId()));
         }
-        assertFalse(DcFailCause.fromInt(123456).isPermanentFail());
+        assertFalse(DcFailCause.fromInt(123456).isPermanentFailure(mContext, mPhone.getSubId()));
+    }
+
+    @Test
+    @SmallTest
+    public void testPermanentFailConfigured() throws Exception {
+
+        doReturn(2).when(mPhone).getSubId();
+        PersistableBundle mBundle = mContextFixture.getCarrierConfigBundle();
+        mBundle.putStringArray(
+                CarrierConfigManager.KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS,
+                new String[]{"SERVICE_OPTION_NOT_SUBSCRIBED", "TETHERED_CALL_ACTIVE"});
+
+        // Run it twice to make sure the cached carrier config is working as expected.
+        for (int i = 0; i < 2; i++) {
+            for (DcFailCauseData data : mFailCauseDataList) {
+                if (DcFailCause.fromInt(data.mCause).equals(
+                        DcFailCause.SERVICE_OPTION_NOT_SUBSCRIBED) ||
+                        DcFailCause.fromInt(data.mCause).equals(DcFailCause.TETHERED_CALL_ACTIVE)) {
+                    assertTrue("cause = " + data.mCause, DcFailCause.fromInt(data.mCause).
+                            isPermanentFailure(mContext, mPhone.getSubId()));
+                } else {
+                    assertFalse("cause = " + data.mCause, DcFailCause.fromInt(data.mCause).
+                            isPermanentFailure(mContext, mPhone.getSubId()));
+                }
+            }
+        }
     }
 
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 4298262..1ca9acc 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -16,9 +16,27 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static com.android.internal.telephony.dataconnection.ApnSettingTest.createApnSetting;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.AlarmManager;
 import android.app.PendingIntent;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
@@ -27,13 +45,14 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.Uri;
+import android.os.AsyncResult;
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.PersistableBundle;
-import android.platform.test.annotations.Postsubmit;
 import android.provider.Settings;
 import android.provider.Telephony;
+import android.support.test.filters.FlakyTest;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
@@ -41,6 +60,7 @@
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
 import android.util.LocalLog;
 
 import com.android.internal.telephony.DctConstants;
@@ -48,11 +68,10 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.TestApplication;
-import com.android.internal.telephony.dataconnection.DcTracker.DataAllowFailReason;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -65,23 +84,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class DcTrackerTest extends TelephonyTest {
 
     private final static String[] sNetworkAttributes = new String[]{
@@ -93,15 +95,19 @@
 
     private final static List<String> sApnTypes = Arrays.asList(
             "default", "mms", "cbs", "fota", "supl", "ia", "emergency", "dun", "hipri", "ims");
-
-    private static final String FAKE_APN1 = "FAKE APN 1";
-    private static final String FAKE_APN2 = "FAKE APN 2";
-    private static final String FAKE_APN3 = "FAKE APN 3";
-    private static final String FAKE_IFNAME = "FAKE IFNAME";
-    private static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
-    private static final String FAKE_GATEWAY = "11.22.33.44";
-    private static final String FAKE_DNS = "55.66.77.88";
-    private static final String FAKE_ADDRESS = "99.88.77.66";
+    private static final int LTE_BEARER_BITMASK = 1 << (ServiceState.RIL_RADIO_TECHNOLOGY_LTE - 1);
+    private static final int EHRPD_BEARER_BITMASK =
+            1 << (ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD - 1);
+    public static final String FAKE_APN1 = "FAKE APN 1";
+    public static final String FAKE_APN2 = "FAKE APN 2";
+    public static final String FAKE_APN3 = "FAKE APN 3";
+    public static final String FAKE_APN4 = "FAKE APN 4";
+    public static final String FAKE_APN5 = "FAKE APN 5";
+    public static final String FAKE_IFNAME = "FAKE IFNAME";
+    public static final String FAKE_PCSCF_ADDRESS = "22.33.44.55";
+    public static final String FAKE_GATEWAY = "11.22.33.44";
+    public static final String FAKE_DNS = "55.66.77.88";
+    public static final String FAKE_ADDRESS = "99.88.77.66";
 
     @Mock
     ISub mIsub;
@@ -111,6 +117,12 @@
     NetworkRequest mNetworkRequest;
     @Mock
     SubscriptionInfo mSubscriptionInfo;
+    @Mock
+    ApnContext mApnContext;
+    @Mock
+    ApnSetting mApnSetting;
+    @Mock
+    DcAsyncChannel mDcac;
 
     private DcTracker mDct;
     private DcTrackerTestHandler mDcTrackerTestHandler;
@@ -198,7 +210,7 @@
                             "IP",                   // protocol
                             "IP",                   // roaming_protocol
                             1,                      // carrier_enabled
-                            0,                      // bearer
+                            ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer
                             0,                      // bearer_bitmask
                             0,                      // profile_id
                             0,                      // modem_cognitive
@@ -227,7 +239,7 @@
                             "IP",                   // protocol
                             "IP",                   // roaming_protocol
                             1,                      // carrier_enabled
-                            0,                      // bearer
+                            ServiceState.RIL_RADIO_TECHNOLOGY_LTE, // bearer,
                             0,                      // bearer_bitmask
                             0,                      // profile_id
                             0,                      // modem_cognitive
@@ -251,7 +263,7 @@
                             "",                     // mmsport
                             "",                     // user
                             "",                     // password
-                            3,                      // authtype
+                            -1,                     // authtype
                             "ims",                  // types
                             "IP",                   // protocol
                             "IP",                   // roaming_protocol
@@ -268,6 +280,63 @@
                             ""                      // mnvo_match_data
                     });
 
+                    mc.addRow(new Object[]{
+                            2166,                   // id
+                            plmn,                   // numeric
+                            "sp-mode ehrpd",        // name
+                            FAKE_APN4,              // apn
+                            "",                     // proxy
+                            "",                     // port
+                            "",                     // mmsc
+                            "",                     // mmsproxy
+                            "",                     // mmsport
+                            "",                     // user
+                            "",                     // password
+                            -1,                     // authtype
+                            "default,supl",         // types
+                            "IP",                   // protocol
+                            "IP",                   // roaming_protocol
+                            1,                      // carrier_enabled
+                            ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD, // bearer
+                            0,                      // bearer_bitmask
+                            0,                      // profile_id
+                            0,                      // modem_cognitive
+                            0,                      // max_conns
+                            0,                      // wait_time
+                            0,                      // max_conns_time
+                            0,                      // mtu
+                            "",                     // mvno_type
+                            ""                      // mnvo_match_data
+                    });
+
+                    mc.addRow(new Object[]{
+                            2166,                   // id
+                            plmn,                   // numeric
+                            "b-mobile for Nexus",   // name
+                            FAKE_APN5,              // apn
+                            "",                     // proxy
+                            "",                     // port
+                            "",                     // mmsc
+                            "",                     // mmsproxy
+                            "",                     // mmsport
+                            "",                     // user
+                            "",                     // password
+                            -1,                     // authtype
+                            "dun",                  // types
+                            "IP",                   // protocol
+                            "IP",                   // roaming_protocol
+                            1,                      // carrier_enabled
+                            0,                      // bearer
+                            0,                      // bearer_bitmask
+                            0,                      // profile_id
+                            0,                      // modem_cognitive
+                            0,                      // max_conns
+                            0,                      // wait_time
+                            0,                      // max_conns_time
+                            0,                      // mtu
+                            "",                     // mvno_type
+                            ""                      // mnvo_match_data
+                    });
                     return mc;
                 }
             }
@@ -278,15 +347,13 @@
 
     @Before
     public void setUp() throws Exception {
-        // set the lazy cp to the real content provider in order to use the real settings
-        ContentResolver realContentResolver = TestApplication.getAppContext().getContentResolver();
-        Settings.Global.getInt(realContentResolver, Settings.Global.MOBILE_DATA, 1);
-
         logd("DcTrackerTest +Setup!");
         super.setUp(getClass().getSimpleName());
 
         doReturn("fake.action_detached").when(mPhone).getActionDetached();
         doReturn("fake.action_attached").when(mPhone).getActionAttached();
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
+                .getRilDataRadioTechnology();
         doReturn("44010").when(mSimRecords).getOperatorNumeric();
 
         mContextFixture.putStringArrayResource(com.android.internal.R.array.networkAttributes,
@@ -335,6 +402,8 @@
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mBundle = mContextFixture.getCarrierConfigBundle();
 
+        mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
+
         mDcTrackerTestHandler = new DcTrackerTestHandler(getClass().getSimpleName());
         mDcTrackerTestHandler.start();
         waitUntilReady();
@@ -347,40 +416,48 @@
         logd("DcTrackerTest -tearDown");
         mDct.removeCallbacksAndMessages(null);
         mDct = null;
-        mDcTrackerTestHandler.quitSafely();
+        mDcTrackerTestHandler.quit();
         super.tearDown();
     }
 
     // Create a successful data response
     public static DataCallResponse createDataCallResponse() {
 
-        DataCallResponse dcResponse = new DataCallResponse();
+        return new DataCallResponse(0, -1, 1, 2, "IP", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
+    }
 
-        dcResponse.version = 11;
-        dcResponse.status = 0;
-        dcResponse.suggestedRetryTime = -1; // No retry suggested by the modem
-        dcResponse.cid = 1;
-        dcResponse.active = 2;
-        dcResponse.type = "IP";
-        dcResponse.ifname = FAKE_IFNAME;
-        dcResponse.mtu = 1440;
-        dcResponse.addresses = new String[]{FAKE_ADDRESS};
-        dcResponse.dnses = new String[]{FAKE_DNS};
-        dcResponse.gateways = new String[]{FAKE_GATEWAY};
-        dcResponse.pcscf = new String[]{FAKE_PCSCF_ADDRESS};
-        return dcResponse;
+    private void verifyDataProfile(DataProfile dp, String apn, int profileId,
+                                   int supportedApnTypesBitmap, int type, int bearerBitmask) {
+        assertEquals(profileId, dp.profileId);
+        assertEquals(apn, dp.apn);
+        assertEquals("IP", dp.protocol);
+        assertEquals(0, dp.authType);
+        assertEquals("", dp.user);
+        assertEquals("", dp.password);
+        assertEquals(type, dp.type);
+        assertEquals(0, dp.maxConnsTime);
+        assertEquals(0, dp.maxConns);
+        assertEquals(0, dp.waitTime);
+        assertTrue(dp.enabled);
+        assertEquals(supportedApnTypesBitmap, dp.supportedApnTypesBitmap);
+        assertEquals("IP", dp.roamingProtocol);
+        assertEquals(bearerBitmask, dp.bearerBitmap);
+        assertEquals(0, dp.mtu);
+        assertEquals("", dp.mvnoType);
+        assertEquals("", dp.mvnoMatchData);
+        assertFalse(dp.modemCognitive);
     }
 
     private void verifyDataConnected(final String apnSetting) {
         verify(mPhone, times(1)).notifyDataConnection(eq(Phone.REASON_CONNECTED),
                 eq(PhoneConstants.APN_TYPE_DEFAULT));
 
-        verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(),
+        verify(mAlarmManager, times(1)).set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
                 any(PendingIntent.class));
 
         assertEquals(apnSetting, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
         assertArrayEquals(new String[]{PhoneConstants.APN_TYPE_DEFAULT}, mDct.getActiveApnTypes());
-        assertTrue(mDct.getAnyDataEnabled());
 
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
@@ -394,12 +471,12 @@
         assertEquals(FAKE_GATEWAY, linkProperties.getRoutes().get(0).getGateway().getHostAddress());
     }
 
-    private boolean isDataAllowed(DataAllowFailReason dataAllowFailReasons) {
+    private boolean isDataAllowed(DataConnectionReasons dataConnectionReasons) {
         try {
             Method method = DcTracker.class.getDeclaredMethod("isDataAllowed",
-                    DataAllowFailReason.class);
+                    DataConnectionReasons.class);
             method.setAccessible(true);
-            return (boolean) method.invoke(mDct, dataAllowFailReasons);
+            return (boolean) method.invoke(mDct, dataConnectionReasons);
         } catch (Exception e) {
             fail(e.toString());
             return false;
@@ -415,9 +492,9 @@
 
         mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
 
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        boolean allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -453,14 +530,16 @@
         mDct.setEnabled(0, true);
         waitForMs(200);
 
-        failureReason.clearAllReasons();
-        allowed = isDataAllowed(failureReason);
-        assertTrue(failureReason.getDataAllowFailReason(), allowed);
+        dataConnectionReasons = new DataConnectionReasons();
+        allowed = isDataAllowed(dataConnectionReasons);
+        assertTrue(dataConnectionReasons.toString(), allowed);
 
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN1),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         verifyDataConnected(FAKE_APN1);
     }
@@ -472,15 +551,15 @@
 
         mDct.setDataEnabled(true);
 
-        DataCallResponse dcResponse = createDataCallResponse();
         // LOST_CONNECTION(0x10004) is a non-permanent failure, so we'll retry data setup later.
-        dcResponse.status = 0x10004;
+        DataCallResponse dcResponse = new DataCallResponse(0x10004, -1, 1, 2, "IP", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
         // Simulate RIL fails the data call setup
         mSimulatedCommands.setDataCallResponse(false, dcResponse);
 
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        boolean allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -517,14 +596,16 @@
         waitForMs(200);
 
 
-        failureReason.clearAllReasons();
-        allowed = isDataAllowed(failureReason);
-        assertTrue(failureReason.getDataAllowFailReason(), allowed);
+        dataConnectionReasons = new DataConnectionReasons();
+        allowed = isDataAllowed(dataConnectionReasons);
+        assertTrue(dataConnectionReasons.toString(), allowed);
 
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN1),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         // Make sure we never notify connected because the data call setup is supposed to fail.
         verify(mPhone, never()).notifyDataConnection(eq(Phone.REASON_CONNECTED),
@@ -545,10 +626,12 @@
         mContext.sendBroadcast(intent);
         waitForMs(200);
 
+        dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN2),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 5, 1, LTE_BEARER_BITMASK);
 
         // Verify connected with APN2 setting.
         verifyDataConnected(FAKE_APN2);
@@ -556,6 +639,8 @@
 
     @Test
     @MediumTest
+    @Ignore
+    @FlakyTest
     public void testUserDisableData() throws Exception {
         //step 1: setup two DataCalls one for Metered: default, another one for Non-metered: IMS
         //set Default and MMS to be metered in the CarrierConfigManager
@@ -577,12 +662,11 @@
         mDct.setDataEnabled(true);
 
         waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN1),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(2), eq(FAKE_APN3),
-                eq(""), eq(""), eq(3), eq("IP"), any(Message.class));
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         logd("Sending DATA_DISABLED_CMD");
         mDct.setDataEnabled(false);
@@ -609,7 +693,7 @@
         //step 4: only tear down metered data connections.
 
         //set Default and MMS to be metered in the CarrierConfigManager
-        boolean roamingEnabled = mDct.getDataOnRoamingEnabled();
+        boolean roamingEnabled = mDct.getDataRoamingEnabled();
         boolean dataEnabled = mDct.getDataEnabled();
 
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
@@ -628,18 +712,17 @@
         logd("Sending DATA_ENABLED_CMD");
         mDct.setDataEnabled(true);
 
-        waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN1),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(2), eq(FAKE_APN3),
-                eq(""), eq(""), eq(3), eq("IP"), any(Message.class));
+        waitForMs(300);
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         //user is in roaming
         doReturn(true).when(mServiceState).getDataRoaming();
         logd("Sending DISABLE_ROAMING_CMD");
-        mDct.setDataOnRoamingEnabled(false);
+        mDct.setDataRoamingEnabledByUser(false);
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_ROAMING_ON));
         waitForMs(200);
 
@@ -651,12 +734,11 @@
         assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
 
         // reset roaming settings / data enabled settings at end of this test
-        mDct.setDataOnRoamingEnabled(roamingEnabled);
+        mDct.setDataRoamingEnabledByUser(roamingEnabled);
         mDct.setDataEnabled(dataEnabled);
         waitForMs(200);
     }
 
-    @Postsubmit
     @Test
     @MediumTest
     public void testDataCallOnUserDisableRoaming() throws Exception {
@@ -664,18 +746,21 @@
         //step 2: user toggled data settings on
         //step 3: only non-metered data call is established
 
-        boolean roamingEnabled = mDct.getDataOnRoamingEnabled();
+        boolean roamingEnabled = mDct.getDataRoamingEnabled();
         boolean dataEnabled = mDct.getDataEnabled();
+        doReturn(true).when(mServiceState).getDataRoaming();
 
         //set Default and MMS to be metered in the CarrierConfigManager
         mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
                 new String[]{PhoneConstants.APN_TYPE_DEFAULT, PhoneConstants.APN_TYPE_MMS});
         mDct.setEnabled(5, true);
         mDct.setEnabled(0, true);
-        doReturn(true).when(mServiceState).getDataRoaming();
+
+        logd("Sending DATA_ENABLED_CMD");
+        mDct.setDataEnabled(true);
 
         logd("Sending DISABLE_ROAMING_CMD");
-        mDct.setDataOnRoamingEnabled(false);
+        mDct.setDataRoamingEnabledByUser(false);
 
         logd("Sending EVENT_RECORDS_LOADED");
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
@@ -685,23 +770,19 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
         waitForMs(200);
 
-        logd("Sending DATA_ENABLED_CMD");
-        mDct.setDataEnabled(true);
-
         waitForMs(200);
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN1),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(2), eq(FAKE_APN3),
-                eq(""), eq(""), eq(3), eq("IP"), any(Message.class));
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
 
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
         assertEquals(DctConstants.State.CONNECTED, mDct.getState(PhoneConstants.APN_TYPE_IMS));
 
         // reset roaming settings / data enabled settings at end of this test
-        mDct.setDataOnRoamingEnabled(roamingEnabled);
+        mDct.setDataRoamingEnabledByUser(roamingEnabled);
         mDct.setDataEnabled(dataEnabled);
         waitForMs(200);
     }
@@ -718,9 +799,9 @@
 
         mSimulatedCommands.setDataCallResponse(true, createDataCallResponse());
 
-        DataAllowFailReason failureReason = new DataAllowFailReason();
-        boolean allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        DataConnectionReasons dataConnectionReasons = new DataConnectionReasons();
+        boolean allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
 
         ArgumentCaptor<Integer> intArgumentCaptor = ArgumentCaptor.forClass(Integer.class);
         verify(mUiccController, times(1)).registerForIccChanged(eq(mDct),
@@ -761,13 +842,13 @@
         waitForMs(200);
 
         // Data should not be allowed since auto attach flag has been reset.
-        failureReason.clearAllReasons();
-        allowed = isDataAllowed(failureReason);
-        assertFalse(failureReason.getDataAllowFailReason(), allowed);
+        dataConnectionReasons = new DataConnectionReasons();
+        allowed = isDataAllowed(dataConnectionReasons);
+        assertFalse(dataConnectionReasons.toString(), allowed);
     }
 
     // Test for API carrierActionSetMeteredApnsEnabled.
-    @Postsubmit
+    @FlakyTest
     @Test
     @MediumTest
     public void testCarrierActionSetMeteredApnsEnabled() throws Exception {
@@ -792,15 +873,17 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
         waitForMs(200);
 
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(2), eq(FAKE_APN3),
-                eq(""), eq(""), eq(3), eq("IP"), any(Message.class));
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), eq(0), eq(FAKE_APN1),
-                eq(""), eq(""), eq(0), eq("IP"), any(Message.class));
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
 
-        mDct.setApnsEnabledByCarrier(false);
+        Message msg = mDct.obtainMessage(DctConstants.EVENT_SET_CARRIER_DATA_ENABLED);
+        AsyncResult.forMessage(msg).result = false;
+        mDct.sendMessage(msg);
+
         waitForMs(100);
 
         // Validate all metered data connections have been torn down
@@ -813,4 +896,320 @@
         mDct.setDataEnabled(dataEnabled);
         waitForMs(200);
     }
+
+    private void initApns(String targetApn, String[] canHandleTypes) {
+        doReturn(targetApn).when(mApnContext).getApnType();
+        doReturn(true).when(mApnContext).isConnectable();
+        ApnSetting apnSetting = createApnSetting(canHandleTypes);
+        doReturn(apnSetting).when(mApnContext).getNextApnSetting();
+        doReturn(apnSetting).when(mApnContext).getApnSetting();
+        doReturn(mDcac).when(mApnContext).getDcAc();
+        doReturn(true).when(mApnContext).isEnabled();
+        doReturn(true).when(mApnContext).getDependencyMet();
+        doReturn(true).when(mApnContext).isReady();
+        doReturn(true).when(mApnContext).hasNoRestrictedRequests(eq(true));
+    }
+
+    // Test the emergency APN setup.
+    @Test
+    @SmallTest
+    public void testTrySetupDataEmergencyApn() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_EMERGENCY, new String[]{PhoneConstants.APN_TYPE_ALL});
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
+                eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the unmetered APN setup when data is disabled.
+    @Test
+    @SmallTest
+    public void testTrySetupDataUnmeteredDataDisabled() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_FOTA, new String[]{PhoneConstants.APN_TYPE_ALL});
+        mDct.setDataEnabled(false);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
+                eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the metered APN setup when data is disabled.
+    @Test
+    @SmallTest
+    public void testTrySetupMeteredDataDisabled() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        mDct.setDataEnabled(false);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the restricted data request when data is disabled.
+    @Test
+    @SmallTest
+    public void testTrySetupRestrictedDataDisabled() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(false).when(mApnContext).hasNoRestrictedRequests(eq(true));
+
+        mDct.setDataEnabled(false);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the default data when data is not connectable.
+    @Test
+    @SmallTest
+    public void testTrySetupNotConnectable() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(false).when(mApnContext).isConnectable();
+        mDct.setDataEnabled(true);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the default data on IWLAN.
+    @Test
+    @SmallTest
+    public void testTrySetupDefaultOnIWLAN() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.setDataEnabled(true);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test the default data when the phone is in ECBM.
+    @Test
+    @SmallTest
+    public void testTrySetupDefaultInECBM() throws Exception {
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+        doReturn(true).when(mPhone).isInEcm();
+        mDct.setDataEnabled(true);
+
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
+        waitForMs(200);
+
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
+                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+    }
+
+    // Test update waiting apn list when on data rat change
+    @Test
+    @SmallTest
+    public void testUpdateWaitingApnListOnDataRatChange() throws Exception {
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+        mDct.setEnabled(0, true);
+        mDct.setDataEnabled(true);
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        // Verify if RIL command was sent properly.
+        verify(mSimulatedCommandsVerifier).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+
+        //data rat change from ehrpd to lte
+        logd("Sending EVENT_DATA_RAT_CHANGED");
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_LTE).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
+        waitForMs(200);
+
+        // Verify the disconnected data call due to rat change and retry manger schedule another
+        // data call setup
+        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+                any(Message.class));
+        verify(mAlarmManager, times(1)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                anyLong(), any(PendingIntent.class));
+
+        // Simulate the timer expires.
+        Intent intent = new Intent("com.android.internal.telephony.data-reconnect.default");
+        intent.putExtra("reconnect_alarm_extra_type", PhoneConstants.APN_TYPE_DEFAULT);
+        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcast(intent);
+        waitForMs(200);
+
+        // Verify if RIL command was sent properly.
+        verify(mSimulatedCommandsVerifier).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+    }
+
+    // Test for fetchDunApn()
+    @Test
+    @SmallTest
+    public void testFetchDunApn() {
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        String dunApnString = "[ApnSettingV3]HOT mobile PC,pc.hotm,,,,,,,,,440,10,,DUN,,,true,"
+                + "0,,,,,,,,";
+        ApnSetting dunApnExpected = ApnSetting.fromString(dunApnString);
+
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.TETHER_DUN_APN, dunApnString);
+        // should return APN from Setting
+        ApnSetting dunApn = mDct.fetchDunApn();
+        assertTrue(dunApnExpected.equals(dunApn));
+
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.TETHER_DUN_APN, null);
+        // should return APN from db
+        dunApn = mDct.fetchDunApn();
+        assertEquals(FAKE_APN5, dunApn.apn);
+    }
+
+    // Test oos
+    @Test
+    @SmallTest
+    public void testDataRatChangeOOS() throws Exception {
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mBundle.putStringArray(CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{PhoneConstants.APN_TYPE_DEFAULT});
+        mDct.setEnabled(0, true);
+        mDct.setDataEnabled(true);
+        initApns(PhoneConstants.APN_TYPE_DEFAULT, new String[]{PhoneConstants.APN_TYPE_ALL});
+
+        logd("Sending EVENT_RECORDS_LOADED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_RECORDS_LOADED, null));
+        waitForMs(200);
+
+        logd("Sending EVENT_DATA_CONNECTION_ATTACHED");
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_CONNECTION_ATTACHED, null));
+        waitForMs(200);
+
+        ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
+        // Verify if RIL command was sent properly.
+        verify(mSimulatedCommandsVerifier).setupDataCall(
+                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
+                eq(false), eq(false), any(Message.class));
+        verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+
+        // Data rat change from ehrpd to unknown due to OOS
+        logd("Sending EVENT_DATA_RAT_CHANGED");
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
+        waitForMs(200);
+
+        // Verify data connection is on
+        verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(anyInt(), anyInt(),
+                any(Message.class));
+
+        // Data rat resume from unknown to ehrpd
+        doReturn(ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD).when(mServiceState)
+                .getRilDataRadioTechnology();
+        mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_DATA_RAT_CHANGED, null));
+        waitForMs(200);
+
+        // Verify the same data connection
+        assertEquals(FAKE_APN4, mDct.getActiveApnString(PhoneConstants.APN_TYPE_DEFAULT));
+        assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
index 60c8a0e..a60b502 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/TelephonyNetworkFactoryTest.java
@@ -16,33 +16,28 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.StringNetworkSpecifier;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.support.test.filters.FlakyTest;
+import android.telephony.Rlog;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
 import com.android.internal.telephony.ContextFixture;
-import com.android.internal.telephony.dataconnection.TelephonyNetworkFactory;
 import com.android.internal.telephony.mocks.ConnectivityServiceMock;
 import com.android.internal.telephony.mocks.DcTrackerMock;
 import com.android.internal.telephony.mocks.PhoneSwitcherMock;
 import com.android.internal.telephony.mocks.SubscriptionControllerMock;
 import com.android.internal.telephony.mocks.SubscriptionMonitorMock;
 import com.android.internal.telephony.mocks.TelephonyRegistryMock;
-import com.android.internal.telephony.test.SimulatedCommands;
-
-import android.content.Context;
-import android.os.AsyncResult;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.platform.test.annotations.Postsubmit;
-import android.net.ConnectivityManager;
-import android.net.IConnectivityManager;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import android.telephony.Rlog;
 
 
 public class TelephonyNetworkFactoryTest extends AndroidTestCase {
@@ -103,6 +98,7 @@
         }
 
         void die() {
+            connectivityServiceMock.die();
             looper.quit();
             handlerThread.quit();
         }
@@ -118,7 +114,7 @@
                 addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
                 addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        netCap.setNetworkSpecifier(Integer.toString(subId));
+        netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
         return ts.connectivityServiceMock.requestNetwork(netCap, null, 0, new Binder(), -1);
     }
     private NetworkRequest makeSubSpecificMmsRequest(TestSetup ts, int subId) {
@@ -126,7 +122,7 @@
                 addCapability(NetworkCapabilities.NET_CAPABILITY_MMS).
                 addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED).
                 addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        netCap.setNetworkSpecifier(Integer.toString(subId));
+        netCap.setNetworkSpecifier(new StringNetworkSpecifier(Integer.toString(subId)));
         return ts.connectivityServiceMock.requestNetwork(netCap, null, 0, new Binder(), -1);
     }
 
@@ -134,7 +130,7 @@
     /**
      * Test that phone active changes cause the DcTracker to get poked.
      */
-    @Postsubmit
+    @FlakyTest
     @SmallTest
     public void testActive() throws Exception {
         mTestName = "testActive";
@@ -144,67 +140,77 @@
 
         TestSetup ts = new TestSetup(numberOfPhones);
 
-        TelephonyNetworkFactory tnf = makeTnf(phoneId, ts);
+        makeTnf(phoneId, ts);
 
         ts.subscriptionControllerMock.setDefaultDataSubId(subId);
         ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
         ts.subscriptionMonitorMock.notifySubscriptionChanged(phoneId);
         ts.subscriptionMonitorMock.notifyDefaultSubscriptionChanged(phoneId);
 
+        log("addDefaultRequest");
         ts.connectivityServiceMock.addDefaultRequest();
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("pretest of LiveRequests != 0");
         }
 
+        log("setPhoneActive true: phoneId = " + phoneId);
         ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
             fail("post-active test of LiveRequests != 1");
         }
 
+        log("makeSubSpecificDefaultRequest: subId = " + subId);
         NetworkRequest subSpecificDefault = makeSubSpecificDefaultRequest(ts, subId);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 2) {
             fail("post-second-request test of LiveRequests != 2");
         }
 
+        log("setPhoneActive false: phoneId = " + phoneId);
         ts.phoneSwitcherMock.setPhoneActive(phoneId, false);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("post-inactive test of LiveRequests != 0");
         }
 
+        log("makeSubSpecificDefaultRequest: subId = " + subId);
         NetworkRequest subSpecificMms = makeSubSpecificMmsRequest(ts, subId);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("post-mms-add test of LiveRequests != 0");
         }
 
+        log("setPhoneActive true: phoneId = " + phoneId);
         ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 3) {
             fail("post-active-mms-add test of LiveRequests != 3");
         }
 
+        log("releaseNetworkRequest: subSpecificDefault = " + subSpecificDefault);
         ts.connectivityServiceMock.releaseNetworkRequest(subSpecificDefault);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 2) {
             fail("post-remove-default test of LiveRequests != 2");
         }
 
+        log("setPhoneActive false: phoneId = " + phoneId);
         ts.phoneSwitcherMock.setPhoneActive(phoneId, false);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("test 8, LiveRequests != 0");
         }
 
+        log("releaseNetworkRequest: subSpecificMms = " + subSpecificMms);
         ts.connectivityServiceMock.releaseNetworkRequest(subSpecificMms);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("test 9, LiveRequests != 0");
         }
 
+        log("setPhoneActive true: phoneId = " + phoneId);
         ts.phoneSwitcherMock.setPhoneActive(phoneId, true);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
@@ -229,7 +235,7 @@
 
         TestSetup ts = new TestSetup(numberOfPhones);
 
-        TelephonyNetworkFactory tnf = makeTnf(phoneId, ts);
+        makeTnf(phoneId, ts);
 
         ts.subscriptionControllerMock.setDefaultDataSubId(subId);
         ts.subscriptionControllerMock.setSlotSubId(phoneId, subId);
@@ -267,7 +273,7 @@
             fail("test 5, LiveRequests != 0");
         }
 
-        NetworkRequest subSpecificMms = makeSubSpecificMmsRequest(ts, subId);
+        makeSubSpecificMmsRequest(ts, subId);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 1) {
             fail("test 6,  LiveRequests != 1");
@@ -280,7 +286,7 @@
             fail("test 7,  LiveRequests != 0");
         }
 
-        NetworkRequest subSpecificDefault = makeSubSpecificDefaultRequest(ts, subId);
+        makeSubSpecificDefaultRequest(ts, subId);
         waitABit();
         if (ts.dcTrackerMock.getNumberOfLiveRequests() != 0) {
             fail("test 8, LiveRequests != 0");
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
index 2cddd8a..b117eb1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmCellBroadcastHandlerTest.java
@@ -16,10 +16,29 @@
 
 package com.android.internal.telephony.gsm;
 
+import static android.provider.Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
 import android.content.Intent;
+import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.provider.Telephony;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.support.test.filters.FlakyTest;
 
 import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.TelephonyTest;
@@ -27,16 +46,13 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Test;
+import org.junit.Ignore;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
+import java.util.List;
 
+@Ignore
 public class GsmCellBroadcastHandlerTest extends TelephonyTest {
     @Mock
     private SmsStorageMonitor mSmsStorageMonitor;
@@ -79,12 +95,18 @@
     @After
     public void tearDown() throws Exception {
         mGsmCellBroadcastHandler = null;
-        mGsmCellBroadcastHandlerTestHandler.quitSafely();
+        mGsmCellBroadcastHandlerTestHandler.quit();
         super.tearDown();
     }
 
-    @Test @SmallTest
+    @FlakyTest
     public void testBroadcastSms() {
+        mContextFixture.putResource(
+                com.android.internal.R.string.config_defaultCellBroadcastReceiverPkg,
+                "fake.cellbroadcastreceiver");
+
+        Settings.Secure.putString(mContext.getContentResolver(),
+                CMAS_ADDITIONAL_BROADCAST_PKG, "another.fake.pkg");
         mSimulatedCommands.notifyGsmBroadcastSms(new byte[] {
                 (byte)0xc0, //geographical scope
                 (byte)0x01, //serial number
@@ -93,12 +115,22 @@
                 (byte)0x01, //message identifier
                 (byte)0x01
         });
-        TelephonyTestUtils.waitForMs(50);
+        TelephonyTestUtils.waitForMs(100);
         ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mContextFixture.getTestDouble()).sendBroadcast(intentArgumentCaptor.capture());
-        assertTrue(intentArgumentCaptor.getValue().getAction().equals(
-                Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION) ||
-                intentArgumentCaptor.getValue().getAction().equals(
-                        Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION));
+        verify(mContextFixture.getTestDouble(), times(2)).sendOrderedBroadcastAsUser(
+                intentArgumentCaptor.capture(), eq(UserHandle.ALL),
+                eq(Manifest.permission.RECEIVE_SMS), eq(AppOpsManager.OP_RECEIVE_SMS),
+                nullable(BroadcastReceiver.class), any(Handler.class), eq(Activity.RESULT_OK),
+                eq(null), eq(null));
+
+        List<Intent> intentList = intentArgumentCaptor.getAllValues();
+
+        assertEquals(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION,
+                intentList.get(0).getAction());
+        assertEquals("another.fake.pkg", intentList.get(0).getPackage());
+
+        assertEquals(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION,
+                intentList.get(1).getAction());
+        assertEquals("fake.cellbroadcastreceiver", intentList.get(1).getPackage());
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index ddabe90..e9a16bf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -16,6 +16,22 @@
 
 package com.android.internal.telephony.gsm;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -29,8 +45,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.platform.test.annotations.Postsubmit;
 import android.provider.Telephony;
+import android.support.test.filters.FlakyTest;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
 
@@ -48,16 +64,13 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.lang.reflect.Method;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
-
 public class GsmInboundSmsHandlerTest extends TelephonyTest {
     @Mock
     private SmsStorageMonitor mSmsStorageMonitor;
@@ -134,7 +147,7 @@
         }
 
         mSmsMessage.mWrappedSmsMessage = mGsmSmsMessage;
-        mInboundSmsTrackerCV.put("destination_port", 1 << 16);
+        mInboundSmsTrackerCV.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
         mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu));
         mInboundSmsTrackerCV.put("address", "1234567890");
         mInboundSmsTrackerCV.put("reference_number", 1);
@@ -142,6 +155,7 @@
         mInboundSmsTrackerCV.put("count", 1);
         mInboundSmsTrackerCV.put("date", System.currentTimeMillis());
         mInboundSmsTrackerCV.put("message_body", mMessageBody);
+        mInboundSmsTrackerCV.put("display_originating_addr", "1234567890");
 
         doReturn(1).when(mInboundSmsTracker).getMessageCount();
         doReturn(1).when(mInboundSmsTracker).getReferenceNumber();
@@ -174,7 +188,7 @@
         assertFalse(mGsmInboundSmsHandler.getWakeLock().isHeld());
         mGsmInboundSmsHandler = null;
         mContentProvider.shutdown();
-        mGsmInboundSmsHandlerTestHandler.quitSafely();
+        mGsmInboundSmsHandlerTestHandler.quit();
         super.tearDown();
     }
 
@@ -212,6 +226,8 @@
         assertEquals("IdleState", getCurrentState().getName());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @MediumTest
     public void testNewSms() {
@@ -236,8 +252,8 @@
     @Test
     @MediumTest
     public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
-        String blockedNumber = "123456789";
-        doReturn(blockedNumber).when(mInboundSmsTracker).getAddress();
+        String blockedNumber = "1234567890";
+        doReturn(blockedNumber).when(mInboundSmsTracker).getDisplayAddress();
         mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
 
         transitionFromStartupToIdle();
@@ -291,7 +307,7 @@
 
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null,
                 mSmsMessage, null));
-        waitForMs(100);
+        waitForMs(200);
 
         verifySmsIntentBroadcasts(0);
 
@@ -304,10 +320,9 @@
         assertEquals("IdleState", getCurrentState().getName());
     }
 
-    private void prepareMultiPartSms() {
+    private void prepareMultiPartSms(boolean isWapPush) {
         // Part 1
         mInboundSmsTrackerCVPart1 = new ContentValues();
-        mInboundSmsTrackerCVPart1.put("destination_port", 1 << 16);
         mInboundSmsTrackerCVPart1.put("pdu", HexDump.toHexString(mSmsPdu));
         mInboundSmsTrackerCVPart1.put("address", "1234567890");
         mInboundSmsTrackerCVPart1.put("reference_number", 1);
@@ -315,6 +330,7 @@
         mInboundSmsTrackerCVPart1.put("count", 2);
         mInboundSmsTrackerCVPart1.put("date", System.currentTimeMillis());
         mInboundSmsTrackerCVPart1.put("message_body", mMessageBodyPart1);
+        mInboundSmsTrackerCVPart1.put("display_originating_addr", "1234567890");
 
         doReturn(2).when(mInboundSmsTrackerPart1).getMessageCount();
         doReturn(1).when(mInboundSmsTrackerPart1).getReferenceNumber();
@@ -324,13 +340,40 @@
         doReturn(-1).when(mInboundSmsTrackerPart1).getDestPort();
         doReturn(mMessageBodyPart1).when(mInboundSmsTrackerPart1).getMessageBody();
         doReturn(mSmsPdu).when(mInboundSmsTrackerPart1).getPdu();
+        doReturn(new String[]{mInboundSmsTrackerPart1.getAddress(),
+                Integer.toString(mInboundSmsTrackerPart1.getReferenceNumber()),
+                Integer.toString(mInboundSmsTrackerPart1.getMessageCount())})
+                .when(mInboundSmsTrackerPart1).getDeleteWhereArgs();
         doReturn(mInboundSmsTrackerCVPart1.get("date")).when(mInboundSmsTrackerPart1).
                 getTimestamp();
         doReturn(mInboundSmsTrackerCVPart1).when(mInboundSmsTrackerPart1).getContentValues();
+        if (isWapPush) {
+            mInboundSmsTrackerCVPart1.put("destination_port",
+                    (InboundSmsTracker.DEST_PORT_FLAG_3GPP2 |
+                            InboundSmsTracker.DEST_PORT_FLAG_3GPP2_WAP_PDU |
+                            SmsHeader.PORT_WAP_PUSH));
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart1)
+                    .getQueryForSegments();
+            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP)
+                    .when(mInboundSmsTrackerPart1).getQueryForMultiPartDuplicates();
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart1)
+                    .getDeleteWhere();
+            doReturn(SmsHeader.PORT_WAP_PUSH).when(mInboundSmsTrackerPart1).getDestPort();
+            doReturn(true).when(mInboundSmsTrackerPart1).is3gpp2();
+
+        } else {
+            mInboundSmsTrackerCVPart1.put("destination_port",
+                    InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart1)
+                    .getQueryForSegments();
+            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE)
+                    .when(mInboundSmsTrackerPart1).getQueryForMultiPartDuplicates();
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart1)
+                    .getDeleteWhere();
+        }
 
         // Part 2
         mInboundSmsTrackerCVPart2 = new ContentValues();
-        mInboundSmsTrackerCVPart2.put("destination_port", 1 << 16);
         mInboundSmsTrackerCVPart2.put("pdu", HexDump.toHexString(mSmsPdu));
         mInboundSmsTrackerCVPart2.put("address", "1234567890");
         mInboundSmsTrackerCVPart2.put("reference_number", 1);
@@ -338,6 +381,7 @@
         mInboundSmsTrackerCVPart2.put("count", 2);
         mInboundSmsTrackerCVPart2.put("date", System.currentTimeMillis());
         mInboundSmsTrackerCVPart2.put("message_body", mMessageBodyPart2);
+        mInboundSmsTrackerCVPart2.put("display_originating_addr", "1234567890");
 
         doReturn(2).when(mInboundSmsTrackerPart2).getMessageCount();
         doReturn(1).when(mInboundSmsTrackerPart2).getReferenceNumber();
@@ -347,26 +391,116 @@
         doReturn(-1).when(mInboundSmsTrackerPart2).getDestPort();
         doReturn(mMessageBodyPart2).when(mInboundSmsTrackerPart2).getMessageBody();
         doReturn(mSmsPdu).when(mInboundSmsTrackerPart2).getPdu();
+        doReturn(new String[]{mInboundSmsTrackerPart2.getAddress(),
+                Integer.toString(mInboundSmsTrackerPart2.getReferenceNumber()),
+                Integer.toString(mInboundSmsTrackerPart2.getMessageCount())})
+                .when(mInboundSmsTrackerPart2).getDeleteWhereArgs();
         doReturn(mInboundSmsTrackerCVPart2.get("date")).when(mInboundSmsTrackerPart2).
                 getTimestamp();
         doReturn(mInboundSmsTrackerCVPart2).when(mInboundSmsTrackerPart2).getContentValues();
+        if (isWapPush) {
+            mInboundSmsTrackerCVPart2.put("destination_port",
+                    (InboundSmsTracker.DEST_PORT_FLAG_3GPP2 |
+                            InboundSmsTracker.DEST_PORT_FLAG_3GPP2_WAP_PDU |
+                            SmsHeader.PORT_WAP_PUSH));
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart2)
+                    .getQueryForSegments();
+            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP)
+                    .when(mInboundSmsTrackerPart2).getQueryForMultiPartDuplicates();
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart2)
+                    .getDeleteWhere();
+            doReturn(SmsHeader.PORT_WAP_PUSH).when(mInboundSmsTrackerPart2).getDestPort();
+            doReturn(true).when(mInboundSmsTrackerPart2).is3gpp2();
+
+        } else {
+            mInboundSmsTrackerCVPart2.put("destination_port",
+                    InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart2)
+                    .getQueryForSegments();
+            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE)
+                    .when(mInboundSmsTrackerPart2).getQueryForMultiPartDuplicates();
+            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart2)
+                    .getDeleteWhere();
+        }
     }
 
-    @Postsubmit
+    @Test
+    @MediumTest
+    public void testMultiPartSmsWithIncompleteWAP() {
+        /**
+         * Test scenario: 3 messages are received with same address, ref number, count. two of the
+         * messages are belonging to the same multi-part SMS and the other one is a 3GPP2WAP.
+         * we should not try to merge 3gpp2wap with the multi-part SMS.
+         */
+        transitionFromStartupToIdle();
+
+        // prepare SMS part 1 and part 2
+        prepareMultiPartSms(false);
+
+        mSmsHeader.concatRef = new SmsHeader.ConcatRef();
+        doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
+
+        // part 2 of non-3gpp2wap arrives first
+        doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
+                mSmsMessage, null));
+        waitForMs(200);
+
+        // State machine should go back to idle and wait for second part
+        assertEquals("IdleState", getCurrentState().getName());
+
+        // mock a 3gpp2wap push
+        prepareMultiPartSms(true);
+        doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
+                mSmsMessage, null));
+        waitForMs(200);
+        // State machine should go back to idle and wait for second part
+        assertEquals("IdleState", getCurrentState().getName());
+
+        // verify no broadcast sent.
+        verify(mContext, times(0)).sendBroadcast(any(Intent.class));
+
+        // additional copy of part 1 of non-3gpp2wap
+        prepareMultiPartSms(false);
+        doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
+                mSmsMessage, null));
+        waitForMs(200);
+
+        // verify broadcast intents
+        verifySmsIntentBroadcasts(0);
+        assertEquals("IdleState", getCurrentState().getName());
+        // verify there are three segments in the db and only one of them is not marked as deleted.
+        assertEquals(3, mContentProvider.getNumRows());
+        assertEquals(1, mContentProvider.query(sRawUri, null, "deleted=0", null, null).getCount());
+    }
+
+    @FlakyTest
     @Test
     @MediumTest
     public void testMultiPartSms() {
         transitionFromStartupToIdle();
 
         // prepare SMS part 1 and part 2
-        prepareMultiPartSms();
+        prepareMultiPartSms(false);
 
         mSmsHeader.concatRef = new SmsHeader.ConcatRef();
         doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
 
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -375,8 +509,9 @@
         assertEquals("IdleState", getCurrentState().getName());
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -390,8 +525,9 @@
 
         // additional copy of part 2 of message
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -403,12 +539,13 @@
         // timestamps, should not be combined with the additional part 2 received above
 
         // call prepareMultiPartSms() to update timestamps
-        prepareMultiPartSms();
+        prepareMultiPartSms(false);
 
         // part 1 of new sms
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -429,7 +566,7 @@
         transitionFromStartupToIdle();
 
         // prepare SMS part 1 and part 2
-        prepareMultiPartSms();
+        prepareMultiPartSms(false);
         // change seqNumber in part 2 to 1
         mInboundSmsTrackerCVPart2.put("sequence", 1);
         doReturn(1).when(mInboundSmsTrackerPart2).getSequenceNumber();
@@ -438,8 +575,9 @@
         doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
 
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -448,8 +586,9 @@
         assertEquals("IdleState", getCurrentState().getName());
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -470,13 +609,14 @@
         transitionFromStartupToIdle();
 
         // prepare SMS part 1 and part 2
-        prepareMultiPartSms();
+        prepareMultiPartSms(false);
 
         mSmsHeader.concatRef = new SmsHeader.ConcatRef();
         doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
 
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
@@ -486,8 +626,47 @@
         assertEquals("IdleState", getCurrentState().getName());
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
+                mSmsMessage, null));
+        waitForMs(100);
+
+        verify(mContext, never()).sendBroadcast(any(Intent.class));
+        assertEquals("IdleState", getCurrentState().getName());
+    }
+
+    @Test
+    @MediumTest
+    public void testMultipartSmsFromBlockedEmail_noBroadcastsSent() {
+        mFakeBlockedNumberContentProvider.mBlockedNumbers.add("1234567890@test.com");
+
+        transitionFromStartupToIdle();
+
+        // prepare SMS part 1 and part 2
+        prepareMultiPartSms(false);
+        // only the first SMS is configured with the display originating email address
+        mInboundSmsTrackerCVPart1.put("display_originating_addr", "1234567890@test.com");
+
+        mSmsHeader.concatRef = new SmsHeader.ConcatRef();
+        doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
+        doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
+
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
+                mSmsMessage, null));
+        waitForMs(100);
+
+        // State machine should go back to idle and wait for second part
+        assertEquals("IdleState", getCurrentState().getName());
+
+        doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                        anyInt(), anyBoolean(), nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -575,14 +754,14 @@
 
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @MediumTest
     public void testBroadcastUndeliveredMultiPart() throws Exception {
         replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
 
         // prepare SMS part 1 and part 2
-        prepareMultiPartSms();
+        prepareMultiPartSms(false);
 
         //add the 2 SMS parts to db
         mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart1);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java
index 129a1ec..a4b7ae7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java
@@ -38,18 +38,18 @@
 
     private static final SmsCbLocation sTestLocation = new SmsCbLocation("94040", 1234, 5678);
 
-    private static SmsCbMessage createFromPdu(byte[] pdu) {
+    private SmsCbMessage createFromPdu(byte[] pdu) {
         try {
             SmsCbHeader header = new SmsCbHeader(pdu);
             byte[][] pdus = new byte[1][];
             pdus[0] = pdu;
-            return GsmSmsCbMessage.createSmsCbMessage(header, sTestLocation, pdus);
+            return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus);
         } catch (IllegalArgumentException e) {
             return null;
         }
     }
 
-    private static void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
+    private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
         pdu[0] = b;
         SmsCbMessage msg = createFromPdu(pdu);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 2b7f33b..335634d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -16,32 +16,54 @@
 
 package com.android.internal.telephony.gsm;
 
+import static android.telephony.SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED;
+
+import static com.android.internal.telephony.SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE;
+import static com.android.internal.telephony.SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.location.Country;
 import android.location.CountryDetector;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.provider.Telephony;
+import android.support.test.filters.FlakyTest;
+import android.telephony.SmsManager;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Singleton;
 
+import com.android.internal.telephony.ContextFixture;
 import com.android.internal.telephony.ISub;
 import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TelephonyTestUtils;
+import com.android.internal.telephony.TestApplication;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.HashMap;
+
 public class GsmSmsDispatcherTest extends TelephonyTest {
     @Mock
     private android.telephony.SmsMessage mSmsMessage;
@@ -54,7 +76,21 @@
     @Mock
     private CountryDetector mCountryDetector;
     @Mock
+    private SMSDispatcher.SmsTracker mSmsTracker;
+    @Mock
     private ISub.Stub mISubStub;
+    private Object mLock = new Object();
+    private boolean mReceivedTestIntent = false;
+    private static final String TEST_INTENT = "com.android.internal.telephony.TEST_INTENT";
+    private BroadcastReceiver mTestReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            logd("onReceive");
+            synchronized (mLock) {
+                mReceivedTestIntent = true;
+            }
+        }
+    };
 
     private GsmSMSDispatcher mGsmSmsDispatcher;
     private GsmSmsDispatcherTestHandler mGsmSmsDispatcherTestHandler;
@@ -90,13 +126,13 @@
     @After
     public void tearDown() throws Exception {
         mGsmSmsDispatcher = null;
-        mGsmSmsDispatcherTestHandler.quitSafely();
+        mGsmSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
     @Test @SmallTest
     public void testSmsStatus() {
-        mSimulatedCommands.notifySmsStatus("0123056789ABCDEF");
+        mSimulatedCommands.notifySmsStatus(new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF});
         TelephonyTestUtils.waitForMs(50);
         verify(mSimulatedCommandsVerifier).acknowledgeLastIncomingGsmSms(true,
                 Telephony.Sms.Intents.RESULT_SMS_HANDLED, null);
@@ -119,6 +155,7 @@
         assertEquals(0, mFakeBlockedNumberContentProvider.mNumEmergencyContactNotifications);
     }
 
+    @FlakyTest
     @Test @MediumTest
     public void testSendSmsToEmergencyNumber_notifiesBlockedNumberProvider() throws Exception {
         setupMockPackagePermissionChecks();
@@ -145,4 +182,57 @@
             return systemEmergencyNumbers.split(",")[0];
         }
     }
+
+    @Test @SmallTest
+    public void testSendTextWithInvalidDestAddr() throws Exception {
+        // unmock ActivityManager to be able to register receiver, create real PendingIntent and
+        // receive TEST_INTENT
+        restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
+        restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
+        Context realContext = TestApplication.getAppContext();
+        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(realContext, 0,
+                new Intent(TEST_INTENT), 0);
+        // send invalid dest address: +
+        mGsmSmsDispatcher.sendText("+", "222" /*scAddr*/, TAG,
+                pendingIntent, null, null, null, false);
+        waitForMs(500);
+        verify(mSimulatedCommandsVerifier, times(0)).sendSMS(anyString(), anyString(),
+                any(Message.class));
+        synchronized (mLock) {
+            assertEquals(true, mReceivedTestIntent);
+            assertEquals(SmsManager.RESULT_ERROR_NULL_PDU, mTestReceiver.getResultCode());
+        }
+    }
+
+    @Test
+    public void testSendRawPduWithEventStopSending() throws Exception {
+        setupMockPackagePermissionChecks();
+        mContextFixture.removeCallingOrSelfPermission(ContextFixture.PERMISSION_ENABLE_ALL);
+
+        // return a fake value to pass getData()
+        HashMap data = new HashMap<String, String>();
+        data.put("pdu", new byte[1]);
+        when(mSmsTracker.getData()).thenReturn(data);
+
+        // Set values to return to simulate EVENT_STOP_SENDING
+        when(mSmsUsageMonitor.checkDestination(any(), any()))
+                .thenReturn(CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE);
+        when(mSmsUsageMonitor.getPremiumSmsPermission(any()))
+                .thenReturn(PREMIUM_SMS_PERMISSION_NEVER_ALLOW);
+        when(mSmsTracker.getAppPackageName()).thenReturn("");
+
+        // Settings.Global.DEVICE_PROVISIONED to 1
+        Settings.Global.putInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 1);
+
+        mGsmSmsDispatcher.sendRawPdu(mSmsTracker);
+
+        verify(mSmsUsageMonitor, times(1)).checkDestination(any(), any());
+        verify(mSmsUsageMonitor, times(1)).getPremiumSmsPermission(any());
+        ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor
+                .forClass(Integer.class);
+        verify(mSmsTracker, times(1)).onFailed(any(), argumentCaptor.capture(), anyInt());
+        assertEquals(RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED, (int) argumentCaptor.getValue());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
new file mode 100644
index 0000000..5576227
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsResolverTest.java
@@ -0,0 +1,910 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import com.android.ims.internal.IImsServiceController;
+import com.android.internal.telephony.PhoneConstants;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Unit tests for ImsResolver
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsResolverTest extends ImsTestBase {
+
+    private static final int TEST_TIMEOUT = 200; //ms
+    private static final ComponentName TEST_DEVICE_DEFAULT_NAME = new ComponentName("TestDevicePkg",
+            "DeviceImsService");
+    private static final ComponentName TEST_CARRIER_DEFAULT_NAME = new ComponentName(
+            "TestCarrierPkg", "CarrierImsService");
+    private static final ComponentName TEST_CARRIER_2_DEFAULT_NAME = new ComponentName(
+            "TestCarrier2Pkg", "Carrier2ImsService");
+
+    @Mock Context mMockContext;
+    @Mock PackageManager mMockPM;
+    @Mock ImsResolver.SubscriptionManagerProxy mTestSubscriptionManagerProxy;
+    @Mock CarrierConfigManager mMockCarrierConfigManager;
+    private ImsResolver mTestImsResolver;
+    private BroadcastReceiver mTestPackageBroadcastReceiver;
+    private BroadcastReceiver mTestCarrierConfigReceiver;
+    private PersistableBundle[] mCarrierConfigs;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        mTestImsResolver = null;
+        super.tearDown();
+    }
+
+    /**
+     * Add a package to the package manager and make sure it is added to the cache of available
+     * ImsServices in the ImsResolver
+     */
+    @Test
+    @SmallTest
+    public void testAddPackageToCache() {
+        setupResolver(1/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> features = new HashSet<>();
+        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        ImsResolver.ImsServiceInfo testCachedService =
+                mTestImsResolver.getImsServiceInfoFromCache(
+                        TEST_DEVICE_DEFAULT_NAME.getPackageName());
+        assertNotNull(testCachedService);
+        assertTrue(isImsServiceInfoEqual(TEST_DEVICE_DEFAULT_NAME, features, testCachedService));
+    }
+
+    /**
+     * Set the carrier config override value and ensure that ImsResolver calls .bind on that
+     * package name with the correct ImsFeatures.
+     */
+    @Test
+    @SmallTest
+    public void testCarrierPackageBind() throws RemoteException {
+        setupResolver(1/*numSlots*/);
+        // Set CarrierConfig default package name and make it available to the package manager
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> features = new HashSet<>();
+        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController controller = mock(ImsServiceController.class);
+        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
+            when(controller.getComponentName()).thenReturn(componentName);
+            return controller;
+        });
+
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        verify(controller).bind(convertToHashSet(features, 0));
+        verify(controller, never()).unbind();
+        assertEquals(TEST_CARRIER_DEFAULT_NAME, controller.getComponentName());
+    }
+
+    /**
+     * Ensure that no ImsService is bound if there is no carrier or device package explictly set.
+     */
+    @Test
+    @SmallTest
+    public void testDontBindWhenNullCarrierPackage() throws RemoteException {
+        setupResolver(1/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> features = new HashSet<>();
+        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController controller = mock(ImsServiceController.class);
+        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
+            when(controller.getComponentName()).thenReturn(componentName);
+            return controller;
+        });
+
+        // Set the CarrierConfig string to null so that ImsResolver will not bind to the available
+        // Services
+        setConfigCarrierString(0, null);
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        verify(controller, never()).bind(any());
+        verify(controller, never()).unbind();
+    }
+
+    /**
+     * Test that the ImsService corresponding to the default device ImsService package name is
+     * bound.
+     */
+    @Test
+    @SmallTest
+    public void testDevicePackageBind() throws RemoteException {
+        setupResolver(1/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> features = new HashSet<>();
+        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, features, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController controller = mock(ImsServiceController.class);
+        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
+            when(controller.getComponentName()).thenReturn(componentName);
+            return controller;
+        });
+
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        // There is no carrier override set, so make sure that the ImsServiceController binds
+        // to all SIMs.
+        HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
+        verify(controller).bind(featureSet);
+        verify(controller, never()).unbind();
+        assertEquals(TEST_DEVICE_DEFAULT_NAME, controller.getComponentName());
+    }
+
+    /**
+     * Test that when a device and carrier override package are set, both ImsServices are bound.
+     * Verify that the carrier ImsService features are created and the device default features
+     * are created for all features that are not covered by the carrier ImsService.
+     */
+    @Test
+    @SmallTest
+    public void testDeviceAndCarrierPackageBind() throws RemoteException {
+        setupResolver(1/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        // Verify that all features that have been defined for the carrier override are bound
+        HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
+        carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
+        verify(carrierController).bind(carrierFeatureSet);
+        verify(carrierController, never()).unbind();
+        assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
+        // Verify that all features that are not defined in the carrier override are bound in the
+        // device controller (including emergency voice for slot 0)
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 0);
+        deviceFeatureSet.removeAll(carrierFeatureSet);
+        verify(deviceController).bind(deviceFeatureSet);
+        verify(deviceController, never()).unbind();
+        assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
+    }
+
+    /**
+     * Verify that the ImsServiceController is available for the feature specified
+     * (carrier for VOICE/RCS and device for emergency).
+     */
+    @Test
+    @SmallTest
+    public void testGetDeviceCarrierFeatures() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        IImsServiceController iDeviceController = mock(IImsServiceController.class);
+        when(deviceController.getImsServiceController()).thenReturn(iDeviceController);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        IImsServiceController iCarrierController = mock(IImsServiceController.class);
+        when(carrierController.getImsServiceController()).thenReturn(iCarrierController);
+        mTestImsResolver.populateCacheAndStartBind();
+
+        // Callback from mock ImsServiceControllers
+        // All features on slot 1 should be the device default
+        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.MMTEL, deviceController);
+        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.RCS, deviceController);
+        // The carrier override does not support emergency voice
+        mTestImsResolver.imsServiceFeatureCreated(1, ImsFeature.EMERGENCY_MMTEL, deviceController);
+        // The carrier override contains these features
+        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.MMTEL, carrierController);
+        mTestImsResolver.imsServiceFeatureCreated(0, ImsFeature.RCS, carrierController);
+        // Get the IImsServiceControllers for each feature on each slot and verify they are correct.
+        assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
+                1/*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
+        assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
+                1 /*Slot id*/, ImsFeature.MMTEL, null));
+        assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
+                1 /*Slot id*/, ImsFeature.RCS, null));
+        assertEquals(iDeviceController, mTestImsResolver.getImsServiceControllerAndListen(
+                1 /*Slot id*/, ImsFeature.EMERGENCY_MMTEL, null));
+        assertEquals(iCarrierController, mTestImsResolver.getImsServiceControllerAndListen(
+                0 /*Slot id*/, ImsFeature.MMTEL, null));
+        assertEquals(iCarrierController, mTestImsResolver.getImsServiceControllerAndListen(
+                0 /*Slot id*/, ImsFeature.RCS, null));
+    }
+
+    /**
+     * Bind to device ImsService and change the feature set. Verify that changeImsServiceFeature
+     * is called with the new feature set.
+     */
+    @Test
+    @SmallTest
+    public void testAddDeviceFeatureNoCarrier() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> features = new HashSet<>();
+        features.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        features.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Doesn't include RCS feature by default
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, features, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController controller = mock(ImsServiceController.class);
+        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
+            when(controller.getComponentName()).thenReturn(componentName);
+            return controller;
+        });
+
+        // Bind using default features
+        mTestImsResolver.populateCacheAndStartBind();
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        HashSet<Pair<Integer, Integer>> featureSet = convertToHashSet(features, 0);
+        featureSet.addAll(convertToHashSet(features, 1));
+        verify(controller).bind(featureSet);
+
+        // add RCS to features list
+        Set<String> newFeatures = new HashSet<>(features);
+        newFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.clear();
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newFeatures, true));
+
+        // Tell the package manager that a new device feature is installed
+        Intent addPackageIntent = new Intent();
+        addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
+        addPackageIntent.setData(new Uri.Builder().scheme("package")
+                .opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
+        mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        //Verify new feature is added to the device default.
+        HashSet<Pair<Integer, Integer>> newFeatureSet = convertToHashSet(newFeatures, 0);
+        newFeatureSet.addAll(convertToHashSet(newFeatures, 1));
+        verify(controller).changeImsServiceFeatures(newFeatureSet);
+    }
+
+    /**
+     * Bind to device ImsService and change the feature set. Verify that changeImsServiceFeature
+     * is called with the new feature set on the sub that doesn't include the carrier override.
+     */
+    @Test
+    @SmallTest
+    public void testAddDeviceFeatureWithCarrier() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        // Verify that all features that have been defined for the carrier override are bound
+        HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
+        carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
+        verify(carrierController).bind(carrierFeatureSet);
+        verify(carrierController, never()).unbind();
+        assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
+        // Verify that all features that are not defined in the carrier override are bound in the
+        // device controller (including emergency voice for slot 0)
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatures.removeAll(carrierFeatures);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        verify(deviceController).bind(deviceFeatureSet);
+        verify(deviceController, never()).unbind();
+        assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
+
+        // add RCS to features list
+        Set<String> newDeviceFeatures = new HashSet<>();
+        newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.clear();
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, newDeviceFeatures, true));
+
+        // Tell the package manager that a new device feature is installed
+        Intent addPackageIntent = new Intent();
+        addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
+        addPackageIntent.setData(new Uri.Builder().scheme("package")
+                .opaquePart(TEST_DEVICE_DEFAULT_NAME.getPackageName()).build());
+        mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        //Verify new feature is added to the device default.
+        // add all features for slot 1
+        HashSet<Pair<Integer, Integer>> newDeviceFeatureSet =
+                convertToHashSet(newDeviceFeatures, 1);
+        // remove carrier overrides for slot 0
+        newDeviceFeatures.removeAll(carrierFeatures);
+        newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
+        verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
+        verify(carrierController, never()).changeImsServiceFeatures(any());
+    }
+
+    /**
+     * Bind to device ImsService and change the feature set of the carrier overridden ImsService.
+     * Verify that the device and carrier ImsServices are changed.
+     */
+    @Test
+    @SmallTest
+    public void testAddCarrierFeature() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        // Verify that all features that have been defined for the carrier override are bound
+        HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
+        carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
+        verify(carrierController).bind(carrierFeatureSet);
+        verify(carrierController, never()).unbind();
+        assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
+        // Verify that all features that are not defined in the carrier override are bound in the
+        // device controller (including emergency voice for slot 0)
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatures.removeAll(carrierFeatures);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        verify(deviceController).bind(deviceFeatureSet);
+        verify(deviceController, never()).unbind();
+        assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
+
+        // add RCS to carrier features list
+        Set<String> newCarrierFeatures = new HashSet<>();
+        newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        newCarrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.clear();
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
+
+        // Tell the package manager that a new device feature is installed
+        Intent addPackageIntent = new Intent();
+        addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
+        addPackageIntent.setData(new Uri.Builder().scheme("package")
+                .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
+        mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        //Verify new feature is added to the carrier override.
+        // add all features for slot 0
+        HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
+                convertToHashSet(newCarrierFeatures, 0);
+        verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
+        deviceFeatureSet.removeAll(newCarrierFeatureSet);
+        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
+    }
+
+    /**
+     * Bind to device ImsService and change the feature set of the carrier overridden ImsService by
+     * removing a feature.
+     * Verify that the device and carrier ImsServices are changed.
+     */
+    @Test
+    @SmallTest
+    public void testRemoveCarrierFeature() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        mTestImsResolver.populateCacheAndStartBind();
+
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+        // Verify that all features that have been defined for the carrier override are bound
+        HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
+        carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
+        verify(carrierController).bind(carrierFeatureSet);
+        verify(carrierController, never()).unbind();
+        assertEquals(TEST_CARRIER_DEFAULT_NAME, carrierController.getComponentName());
+        // Verify that all features that are not defined in the carrier override are bound in the
+        // device controller (including emergency voice for slot 0)
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatures.removeAll(carrierFeatures);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        verify(deviceController).bind(deviceFeatureSet);
+        verify(deviceController, never()).unbind();
+        assertEquals(TEST_DEVICE_DEFAULT_NAME, deviceController.getComponentName());
+
+        // remove RCS from carrier features list
+        Set<String> newCarrierFeatures = new HashSet<>();
+        newCarrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        info.clear();
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, newCarrierFeatures, true));
+
+        // Tell the package manager that a new device feature is installed
+        Intent addPackageIntent = new Intent();
+        addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
+        addPackageIntent.setData(new Uri.Builder().scheme("package")
+                .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
+        mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        //Verify new feature is added to the carrier override.
+        // add all features for slot 0
+        HashSet<Pair<Integer, Integer>> newCarrierFeatureSet =
+                convertToHashSet(newCarrierFeatures, 0);
+        verify(carrierController).changeImsServiceFeatures(newCarrierFeatureSet);
+        Set<String> newDeviceFeatures = new HashSet<>();
+        newDeviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        newDeviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        newDeviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        HashSet<Pair<Integer, Integer>> newDeviceFeatureSet = convertToHashSet(newDeviceFeatures,
+                1);
+        newDeviceFeatures.removeAll(newCarrierFeatures);
+        newDeviceFeatureSet.addAll(convertToHashSet(newDeviceFeatures, 0));
+        verify(deviceController).changeImsServiceFeatures(newDeviceFeatureSet);
+    }
+
+    /**
+     * Inform the ImsResolver that a Carrier ImsService has been installed and must be bound.
+     */
+    @Test
+    @SmallTest
+    public void testInstallCarrierImsService() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        mTestImsResolver.populateCacheAndStartBind();
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+
+        // Tell the package manager that a new carrier app is installed
+        Intent addPackageIntent = new Intent();
+        addPackageIntent.setAction(Intent.ACTION_PACKAGE_ADDED);
+        addPackageIntent.setData(new Uri.Builder().scheme("package")
+                .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
+        mTestPackageBroadcastReceiver.onReceive(null, addPackageIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        // Verify that all features that have been defined for the carrier override are bound
+        HashSet<Pair<Integer, Integer>> carrierFeatureSet = convertToHashSet(carrierFeatures, 0);
+        carrierFeatureSet.addAll(convertToHashSet(carrierFeatures, 0));
+        verify(carrierController).bind(carrierFeatureSet);
+        // device features change
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        deviceFeatureSet.removeAll(carrierFeatureSet);
+        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
+    }
+
+    /**
+     * Inform the ImsResolver that a carrier ImsService has been uninstalled and the device default
+     * must now use those features.
+     */
+    @Test
+    @SmallTest
+    public void testUninstallCarrierImsService() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        mTestImsResolver.populateCacheAndStartBind();
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        // Tell the package manager that carrier app is uninstalled
+        Intent removePackageIntent = new Intent();
+        removePackageIntent.setAction(Intent.ACTION_PACKAGE_REMOVED);
+        removePackageIntent.setData(new Uri.Builder().scheme("package")
+                .opaquePart(TEST_CARRIER_DEFAULT_NAME.getPackageName()).build());
+        info.clear();
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        mTestPackageBroadcastReceiver.onReceive(null, removePackageIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        // Verify that the carrier controller is unbound
+        verify(carrierController).unbind();
+        assertNull(mTestImsResolver.getImsServiceInfoFromCache(
+                TEST_CARRIER_DEFAULT_NAME.getPackageName()));
+        // device features change to include all supported functionality
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
+    }
+
+    /**
+     * Inform ImsResolver that the carrier config has changed to none, requiring the device
+     * ImsService to be bound/set up and the previous carrier ImsService to be unbound.
+     */
+    @Test
+    @SmallTest
+    public void testCarrierConfigChangedToNone() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, true));
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController);
+
+        mTestImsResolver.populateCacheAndStartBind();
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        setConfigCarrierString(0, null);
+        Intent carrierConfigIntent = new Intent();
+        carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+        mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        // Verify that the carrier controller is unbound
+        verify(carrierController).unbind();
+        assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
+                TEST_CARRIER_DEFAULT_NAME.getPackageName()));
+        // device features change
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
+    }
+
+    /**
+     * Inform ImsResolver that the carrier config has changed to another, requiring the new carrier
+     * ImsService to be bound/set up and the previous carrier ImsService to be unbound.
+     */
+    @Test
+    @SmallTest
+    public void testCarrierConfigChangedToAnotherService() throws RemoteException {
+        setupResolver(2/*numSlots*/);
+        List<ResolveInfo> info = new ArrayList<>();
+        Set<String> deviceFeatures = new HashSet<>();
+        deviceFeatures.add(ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        deviceFeatures.add(ImsResolver.METADATA_RCS_FEATURE);
+        // Set the carrier override package for slot 0
+        setConfigCarrierString(0, TEST_CARRIER_DEFAULT_NAME.getPackageName());
+        Set<String> carrierFeatures1 = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures1.add(ImsResolver.METADATA_MMTEL_FEATURE);
+        carrierFeatures1.add(ImsResolver.METADATA_RCS_FEATURE);
+        Set<String> carrierFeatures2 = new HashSet<>();
+        // Carrier service doesn't support the emergency voice feature.
+        carrierFeatures2.add(ImsResolver.METADATA_RCS_FEATURE);
+        info.add(getResolveInfo(TEST_CARRIER_2_DEFAULT_NAME, carrierFeatures2, true));
+        info.add(getResolveInfo(TEST_CARRIER_DEFAULT_NAME, carrierFeatures1, true));
+        // Use device default package, which will load the ImsService that the device provides
+        info.add(getResolveInfo(TEST_DEVICE_DEFAULT_NAME, deviceFeatures, true));
+        when(mMockPM.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(info);
+        ImsServiceController deviceController = mock(ImsServiceController.class);
+        ImsServiceController carrierController1 = mock(ImsServiceController.class);
+        ImsServiceController carrierController2 = mock(ImsServiceController.class);
+        setImsServiceControllerFactory(deviceController, carrierController1, carrierController2);
+
+        mTestImsResolver.populateCacheAndStartBind();
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        setConfigCarrierString(0, TEST_CARRIER_2_DEFAULT_NAME.getPackageName());
+        Intent carrierConfigIntent = new Intent();
+        carrierConfigIntent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, 0);
+        mTestCarrierConfigReceiver.onReceive(null, carrierConfigIntent);
+        waitForHandlerAction(mTestImsResolver.getHandler(), TEST_TIMEOUT);
+
+        // Verify that carrier 1 is unbound
+        verify(carrierController1).unbind();
+        assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
+                TEST_CARRIER_DEFAULT_NAME.getPackageName()));
+        // Verify that carrier 2 is bound
+        HashSet<Pair<Integer, Integer>> carrier2FeatureSet = convertToHashSet(carrierFeatures2, 0);
+        verify(carrierController2).bind(carrier2FeatureSet);
+        assertNotNull(mTestImsResolver.getImsServiceInfoFromCache(
+                TEST_CARRIER_DEFAULT_NAME.getPackageName()));
+        // device features change to accommodate for the features carrier 2 lacks
+        HashSet<Pair<Integer, Integer>> deviceFeatureSet = convertToHashSet(deviceFeatures, 1);
+        deviceFeatures.removeAll(carrierFeatures2);
+        deviceFeatureSet.addAll(convertToHashSet(deviceFeatures, 0));
+        verify(deviceController).changeImsServiceFeatures(deviceFeatureSet);
+    }
+
+    private void setupResolver(int numSlots) {
+        when(mMockContext.getSystemService(eq(Context.CARRIER_CONFIG_SERVICE))).thenReturn(
+                mMockCarrierConfigManager);
+        when(mMockContext.getPackageManager()).thenReturn(mMockPM);
+        mCarrierConfigs = new PersistableBundle[numSlots];
+        for (int i = 0; i < numSlots; i++) {
+            mCarrierConfigs[i] = new PersistableBundle();
+            when(mMockCarrierConfigManager.getConfigForSubId(eq(i))).thenReturn(
+                    mCarrierConfigs[i]);
+            when(mTestSubscriptionManagerProxy.getSlotIndex(eq(i))).thenReturn(i);
+            when(mTestSubscriptionManagerProxy.getSubId(eq(i))).thenReturn(i);
+        }
+
+        mTestImsResolver = new ImsResolver(mMockContext, TEST_DEVICE_DEFAULT_NAME.getPackageName(),
+                numSlots);
+
+        ArgumentCaptor<BroadcastReceiver> packageBroadcastCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        ArgumentCaptor<BroadcastReceiver> carrierConfigCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mMockContext).registerReceiverAsUser(packageBroadcastCaptor.capture(), any(),
+                any(), any(), any());
+        verify(mMockContext).registerReceiver(carrierConfigCaptor.capture(), any());
+        mTestCarrierConfigReceiver = carrierConfigCaptor.getValue();
+        mTestPackageBroadcastReceiver = packageBroadcastCaptor.getValue();
+        mTestImsResolver.setSubscriptionManagerProxy(mTestSubscriptionManagerProxy);
+    }
+
+    private void setImsServiceControllerFactory(ImsServiceController deviceController,
+            ImsServiceController carrierController) {
+        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
+            if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
+                when(deviceController.getComponentName()).thenReturn(componentName);
+                return deviceController;
+            } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
+                    componentName.getPackageName())) {
+                when(carrierController.getComponentName()).thenReturn(componentName);
+                return carrierController;
+            }
+            return null;
+        });
+    }
+
+    private void setImsServiceControllerFactory(ImsServiceController deviceController,
+            ImsServiceController carrierController1, ImsServiceController carrierController2) {
+        mTestImsResolver.setImsServiceControllerFactory((context, componentName) -> {
+            if (TEST_DEVICE_DEFAULT_NAME.getPackageName().equals(componentName.getPackageName())) {
+                when(deviceController.getComponentName()).thenReturn(componentName);
+                return deviceController;
+            } else if (TEST_CARRIER_DEFAULT_NAME.getPackageName().equals(
+                    componentName.getPackageName())) {
+                when(carrierController1.getComponentName()).thenReturn(componentName);
+                return carrierController1;
+            } else if (TEST_CARRIER_2_DEFAULT_NAME.getPackageName().equals(
+                    componentName.getPackageName())) {
+                when(carrierController2.getComponentName()).thenReturn(componentName);
+                return carrierController2;
+            }
+            return null;
+        });
+    }
+
+
+    private void setConfigCarrierString(int subId, String packageName) {
+        mCarrierConfigs[subId].putString(
+                CarrierConfigManager.KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, packageName);
+    }
+
+    private HashSet<Pair<Integer, Integer>> convertToHashSet(Set<String> features, int subId) {
+        HashSet<Pair<Integer, Integer>> featureSet = features.stream()
+                .map(f -> new Pair<>(subId, metadataStringToFeature(f)))
+                .collect(Collectors.toCollection(HashSet::new));
+        return featureSet;
+    }
+
+    private int metadataStringToFeature(String f) {
+        switch (f) {
+            case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
+                return ImsFeature.EMERGENCY_MMTEL;
+            case ImsResolver.METADATA_MMTEL_FEATURE:
+                return ImsFeature.MMTEL;
+            case ImsResolver.METADATA_RCS_FEATURE:
+                return ImsFeature.RCS;
+        }
+        return -1;
+    }
+
+    private boolean isImsServiceInfoEqual(ComponentName name, Set<String> features,
+            ImsResolver.ImsServiceInfo sInfo) {
+        if (!Objects.equals(sInfo.name, name)) {
+            return false;
+        }
+        for (String f : features) {
+            switch (f) {
+                case ImsResolver.METADATA_EMERGENCY_MMTEL_FEATURE:
+                    if (!sInfo.supportedFeatures.contains(ImsFeature.EMERGENCY_MMTEL)) {
+                        return false;
+                    }
+                    break;
+                case ImsResolver.METADATA_MMTEL_FEATURE:
+                    if (!sInfo.supportedFeatures.contains(ImsFeature.MMTEL)) {
+                        return false;
+                    }
+                    break;
+                case ImsResolver.METADATA_RCS_FEATURE:
+                    if (!sInfo.supportedFeatures.contains(ImsFeature.RCS)) {
+                        return false;
+                    }
+                    break;
+            }
+        }
+        return true;
+    }
+
+    private ResolveInfo getResolveInfo(ComponentName name, Set<String> features,
+            boolean isPermissionGranted) {
+        ResolveInfo info = new ResolveInfo();
+        info.serviceInfo = new ServiceInfo();
+        info.serviceInfo.packageName = name.getPackageName();
+        info.serviceInfo.name = name.getClassName();
+        info.serviceInfo.metaData = new Bundle();
+        for (String s : features) {
+            info.serviceInfo.metaData.putBoolean(s, true);
+        }
+        if (isPermissionGranted) {
+            info.serviceInfo.permission = Manifest.permission.BIND_IMS_SERVICE;
+        }
+        return info;
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
new file mode 100644
index 0000000..7b831a0
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerTest.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import com.android.ims.internal.IImsServiceFeatureListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import java.util.HashSet;
+
+/**
+ * Unit tests for ImsServiceController
+ */
+@RunWith(AndroidJUnit4.class)
+@Ignore
+public class ImsServiceControllerTest extends ImsTestBase {
+
+    private static final int RETRY_TIMEOUT = 50; // ms
+
+    @Spy TestImsServiceControllerAdapter mMockServiceControllerBinder;
+    @Mock IBinder mMockBinder;
+    @Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
+    @Mock IImsServiceFeatureListener mMockProxyCallbacks;
+    @Mock Context mMockContext;
+    private final ComponentName mTestComponentName = new ComponentName("TestPkg",
+            "ImsServiceControllerTest");
+    private ImsServiceController mTestImsServiceController;
+    private final Handler mTestHandler = new Handler(Looper.getMainLooper());
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mTestImsServiceController = new ImsServiceController(mMockContext, mTestComponentName,
+                mMockCallbacks, mTestHandler);
+        mTestImsServiceController.addImsServiceFeatureListener(mMockProxyCallbacks);
+        when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+    }
+
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        mTestHandler.removeCallbacksAndMessages(null);
+        mTestImsServiceController = null;
+        super.tearDown();
+    }
+
+    /**
+     * Tests that Context.bindService is called with the correct parameters when we call bind.
+     */
+    @FlakyTest
+    @Test
+    public void testBindService() {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        ArgumentCaptor<Intent> intentCaptor =
+                ArgumentCaptor.forClass(Intent.class);
+
+        assertTrue(mTestImsServiceController.bind(testFeatures));
+
+        int expectedFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+                | Context.BIND_IMPORTANT;
+        verify(mMockContext).bindService(intentCaptor.capture(), any(), eq(expectedFlags));
+        Intent testIntent = intentCaptor.getValue();
+        assertEquals(ImsResolver.SERVICE_INTERFACE, testIntent.getAction());
+        assertEquals(mTestComponentName, testIntent.getComponent());
+    }
+
+    /**
+     * Verify that if bind is called multiple times, we only call bindService once.
+     */
+    @FlakyTest
+    @Test
+    public void testBindFailureWhenBound() {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        bindAndConnectService(testFeatures);
+
+        // already bound, should return false
+        assertFalse(mTestImsServiceController.bind(testFeatures));
+
+        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+    }
+
+    /**
+     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
+     * connected.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceAndConnected() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+
+        bindAndConnectService(testFeatures);
+
+        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+        verify(binder).linkToDeath(any(), anyInt());
+        verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+        verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(2));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(2),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(2));
+        assertEquals(mMockServiceControllerBinder.getBinder(),
+                mTestImsServiceController.getImsServiceControllerBinder());
+    }
+
+    /**
+     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
+     * connected.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceAndConnectedDisconnected() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        ServiceConnection conn = bindAndConnectService(testFeatures);
+
+        conn.onServiceDisconnected(mTestComponentName);
+
+        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+        verify(binder).unlinkToDeath(any(), anyInt());
+        // binder already disconnected, removeImsFeatures shouldn't be called.
+        verify(mMockServiceControllerBinder, never()).removeImsFeature(anyInt(), anyInt());
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
+    }
+
+    /**
+     * Tests ImsServiceController callbacks are properly called when an ImsService is bound and
+     * connected.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceBindUnbind() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        ServiceConnection conn = bindAndConnectService(testFeatures);
+
+        mTestImsServiceController.unbind();
+
+        verify(mMockContext).unbindService(eq(conn));
+        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+        verify(binder).unlinkToDeath(any(), anyInt());
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(1));
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(2));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
+    }
+
+    /**
+     * Ensures that imsServiceFeatureRemoved is called when the binder dies in another process.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceAndBinderDied() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        bindAndConnectService(testFeatures);
+        ArgumentCaptor<IBinder.DeathRecipient> deathCaptor =
+                ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
+        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+        verify(binder).linkToDeath(deathCaptor.capture(), anyInt());
+
+        deathCaptor.getValue().binderDied();
+
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(2),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(2));
+    }
+
+    /**
+     * Ensures ImsService and ImsResolver are notified when a feature is added.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceAndAddFeature() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        bindAndConnectService(testFeatures);
+        verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        // Create a new list with an additional item
+        HashSet<Pair<Integer, Integer>> testFeaturesWithAddition = new HashSet<>(testFeatures);
+        testFeaturesWithAddition.add(new Pair<>(2, 1));
+
+        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
+
+        verify(mMockServiceControllerBinder).createImsFeature(eq(2), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
+    }
+
+    /**
+     * Ensures ImsService and ImsResolver are notified when a feature is added.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceAndRemoveFeature() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(2, 1));
+        bindAndConnectService(testFeatures);
+        verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        verify(mMockServiceControllerBinder).createImsFeature(eq(2), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
+        // Create a new list with one less item
+        HashSet<Pair<Integer, Integer>> testFeaturesWithSubtraction = new HashSet<>(testFeatures);
+        testFeaturesWithSubtraction.remove(new Pair<>(2, 1));
+
+        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithSubtraction);
+
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(2), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(2), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(2), eq(1));
+    }
+
+    /**
+     * Ensures ImsService and ImsResolver are notified when all features are removed.
+     */
+    @FlakyTest
+    @Test
+    public void testBindServiceAndRemoveAllFeatures() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(2, 1));
+        bindAndConnectService(testFeatures);
+        verify(mMockServiceControllerBinder).createImsFeature(eq(1), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(1), eq(1));
+        verify(mMockServiceControllerBinder).createImsFeature(eq(2), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureCreated(eq(2), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureCreated(eq(2), eq(1));
+
+        // Create a new empty list
+        mTestImsServiceController.changeImsServiceFeatures(new HashSet<>());
+
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(1), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(1), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(1), eq(1));
+        verify(mMockServiceControllerBinder).removeImsFeature(eq(2), eq(1));
+        verify(mMockCallbacks).imsServiceFeatureRemoved(eq(2), eq(1),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks).imsFeatureRemoved(eq(2), eq(1));
+    }
+
+    /**
+     * Verifies that nothing is notified of a feature change if the service is not bound.
+     */
+    @FlakyTest
+    @Test
+    public void testBindUnbindServiceAndAddFeature() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        bindAndConnectService(testFeatures);
+        mTestImsServiceController.unbind();
+        // Create a new list with an additional item
+        HashSet<Pair<Integer, Integer>> testFeaturesWithAddition = new HashSet<>(testFeatures);
+        testFeaturesWithAddition.add(new Pair<>(1, 2));
+
+        mTestImsServiceController.changeImsServiceFeatures(testFeaturesWithAddition);
+
+        verify(mMockServiceControllerBinder, never()).createImsFeature(eq(1), eq(2));
+        verify(mMockCallbacks, never()).imsServiceFeatureCreated(eq(1), eq(2),
+                eq(mTestImsServiceController));
+        verify(mMockProxyCallbacks, never()).imsFeatureCreated(eq(1), eq(2));
+    }
+
+    /**
+     * Verifies that the ImsServiceController automatically tries to bind again after an untimely
+     * binder death.
+     */
+    @FlakyTest
+    @Test
+    public void testAutoBindAfterBinderDied() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        bindAndConnectService(testFeatures);
+        mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
+
+        getDeathRecipient().binderDied();
+
+        waitForHandlerActionDelayed(mTestImsServiceController.getHandler(), RETRY_TIMEOUT,
+                2 * RETRY_TIMEOUT);
+        // The service should autobind after RETRY_TIMEOUT occurs
+        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+    }
+
+    /**
+     * Ensure that bindService has only been called once before automatic rebind occurs.
+     */
+    @FlakyTest
+    @Test
+    public void testNoAutoBindBeforeTimeout() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        bindAndConnectService(testFeatures);
+        mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
+
+        getDeathRecipient().binderDied();
+
+        // Be sure that there are no binds before the RETRY_TIMEOUT expires
+        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+    }
+
+    /**
+     * Ensure that calling unbind stops automatic rebind of the ImsService from occuring.
+     */
+    @FlakyTest
+    @Test
+    public void testUnbindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        bindAndConnectService(testFeatures);
+        mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
+
+        getDeathRecipient().binderDied();
+        mTestImsServiceController.unbind();
+
+        waitForHandlerActionDelayed(mTestImsServiceController.getHandler(), RETRY_TIMEOUT,
+                2 * RETRY_TIMEOUT);
+        // Unbind should stop the autobind from occurring.
+        verify(mMockContext, times(1)).bindService(any(), any(), anyInt());
+    }
+
+    /**
+     * Ensure that calling bind causes the automatic rebinding to be cancelled or not cause another
+     * call to bindService.
+     */
+    @FlakyTest
+    @Test
+    public void testBindCauseAutoBindCancelAfterBinderDied() throws RemoteException {
+        HashSet<Pair<Integer, Integer>> testFeatures = new HashSet<>();
+        testFeatures.add(new Pair<>(1, 1));
+        testFeatures.add(new Pair<>(1, 2));
+        bindAndConnectService(testFeatures);
+        mTestImsServiceController.setRebindRetryTime(() -> RETRY_TIMEOUT);
+        getDeathRecipient().binderDied();
+        mTestImsServiceController.bind(testFeatures);
+
+        waitForHandlerActionDelayed(mTestImsServiceController.getHandler(), RETRY_TIMEOUT,
+                2 * RETRY_TIMEOUT);
+        // Should only see two binds, not three from the auto rebind that occurs.
+        verify(mMockContext, times(2)).bindService(any(), any(), anyInt());
+    }
+
+    private ServiceConnection bindAndConnectService(HashSet<Pair<Integer, Integer>> testFeatures) {
+        ArgumentCaptor<ServiceConnection> serviceCaptor =
+                ArgumentCaptor.forClass(ServiceConnection.class);
+        assertTrue(mTestImsServiceController.bind(testFeatures));
+        verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
+        serviceCaptor.getValue().onServiceConnected(mTestComponentName,
+                mMockServiceControllerBinder.getBinder().asBinder());
+        return serviceCaptor.getValue();
+    }
+
+    private IBinder.DeathRecipient getDeathRecipient() throws RemoteException {
+        ArgumentCaptor<IBinder.DeathRecipient> deathCaptor =
+                ArgumentCaptor.forClass(IBinder.DeathRecipient.class);
+        IBinder binder = mMockServiceControllerBinder.getBinder().asBinder();
+        verify(binder).linkToDeath(deathCaptor.capture(), anyInt());
+        return deathCaptor.getValue();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
new file mode 100644
index 0000000..e9fba8e
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsTestBase.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.test.InstrumentationRegistry;
+
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Helper class to load Mockito Resources into a test.
+ */
+public class ImsTestBase {
+
+    protected Context mContext;
+
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        MockitoAnnotations.initMocks(this);
+        // Set up the looper if it does not exist on the test thread.
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
+    }
+
+    public void tearDown() throws Exception {
+    }
+
+    protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
+        waitForHandlerActionDelayed(h, timeoutMillis, 0 /*delayMs*/);
+    }
+
+    protected final void waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs) {
+        final CountDownLatch lock = new CountDownLatch(1);
+        h.postDelayed(lock::countDown, delayMs);
+        while (lock.getCount() > 0) {
+            try {
+                lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsVideoProviderWrapperTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsVideoProviderWrapperTest.java
new file mode 100644
index 0000000..338e8a4
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsVideoProviderWrapperTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.when;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telecom.VideoProfile;
+
+import com.android.ims.internal.ImsVideoCallProviderWrapper;
+import com.android.ims.internal.VideoPauseTracker;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for the {@link com.android.ims.internal.VideoPauseTracker} class.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ImsVideoProviderWrapperTest extends TestCase {
+    private ImsVideoCallProviderWrapper mImsVideoCallProviderWrapper;
+    @Mock
+    VideoPauseTracker mVideoPauseTracker;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        mImsVideoCallProviderWrapper = new ImsVideoCallProviderWrapper(null, mVideoPauseTracker);
+        when(mVideoPauseTracker.shouldPauseVideoFor(anyInt())).thenReturn(true);
+        when(mVideoPauseTracker.shouldResumeVideoFor(anyInt())).thenReturn(true);
+    }
+
+    @SmallTest
+    @Test
+    public void testIsPause() {
+        assertTrue(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_BIDIRECTIONAL,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertTrue(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_BIDIRECTIONAL,
+                VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isPauseRequest(VideoProfile.STATE_AUDIO_ONLY,
+                VideoProfile.STATE_AUDIO_ONLY));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsResume() {
+        assertTrue(ImsVideoCallProviderWrapper.isResumeRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(ImsVideoCallProviderWrapper.isResumeRequest(VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_AUDIO_ONLY));
+        assertFalse(ImsVideoCallProviderWrapper.isResumeRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isResumeRequest(VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_AUDIO_ONLY | VideoProfile.STATE_PAUSED));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsTurnOffCameraRequest() {
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOffCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL, VideoProfile.STATE_RX_ENABLED));
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOffCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_RX_ENABLED));
+        assertFalse(ImsVideoCallProviderWrapper.isTurnOffCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED,
+                VideoProfile.STATE_BIDIRECTIONAL));
+    }
+
+    @SmallTest
+    @Test
+    public void testIsTurnOnCameraRequest() {
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOnCameraRequest(
+                VideoProfile.STATE_RX_ENABLED, VideoProfile.STATE_BIDIRECTIONAL));
+        assertTrue(ImsVideoCallProviderWrapper.isTurnOnCameraRequest(
+                VideoProfile.STATE_RX_ENABLED,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+        assertFalse(ImsVideoCallProviderWrapper.isTurnOnCameraRequest(
+                VideoProfile.STATE_BIDIRECTIONAL,
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED));
+    }
+
+    /**
+     * Verifies that the to profile is not changed when a request to turn off the camera is sent
+     * using the broken vendor-format request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilterWhenDisablingCamera() {
+        mImsVideoCallProviderWrapper.setUseVideoPauseWorkaround(true);
+        VideoProfile fromProfile = new VideoProfile(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED);
+        VideoProfile toProfile = new VideoProfile(VideoProfile.STATE_RX_ENABLED);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+
+    /**
+     * Verifies that the to profile is not changed when a request to turn on the camera is sent
+     * using the broken vendor-format request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilterWhenEnablingCamera() {
+        mImsVideoCallProviderWrapper.setUseVideoPauseWorkaround(true);
+        VideoProfile fromProfile = new VideoProfile(
+                VideoProfile.STATE_RX_ENABLED | VideoProfile.STATE_PAUSED);
+        VideoProfile toProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+
+    /**
+     * Verifies normal operation of filtering of pause request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilteringOnPause() {
+        VideoProfile fromProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+        VideoProfile toProfile = new VideoProfile(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+
+    /**
+     * Verifies normal operation of filtering of pause request.
+     */
+    @SmallTest
+    @Test
+    public void testNoFilteringOnResume() {
+        VideoProfile fromProfile = new VideoProfile(
+                VideoProfile.STATE_BIDIRECTIONAL | VideoProfile.STATE_PAUSED);
+        VideoProfile toProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+
+        VideoProfile filteredTo = mImsVideoCallProviderWrapper.maybeFilterPauseResume(fromProfile,
+                toProfile, VideoPauseTracker.SOURCE_INCALL);
+        assertEquals(filteredTo.getVideoState(), toProfile.getVideoState());
+    }
+}
+
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
new file mode 100644
index 0000000..0e22be1
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/TestImsServiceControllerAdapter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.ims;
+
+import android.app.PendingIntent;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.feature.ImsFeature;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsUt;
+
+import static org.mockito.Mockito.spy;
+
+/**
+ * Test base implementation of the ImsServiceController, which is used as a mockito spy.
+ */
+
+public class TestImsServiceControllerAdapter {
+
+    public IImsFeatureStatusCallback mStatusCallback;
+
+    public class ImsServiceControllerBinder extends IImsServiceController.Stub {
+
+        @Override
+        public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+                throws RemoteException {
+            TestImsServiceControllerAdapter.this.createImsFeature(slotId, feature);
+            mStatusCallback = c;
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+                throws RemoteException {
+            TestImsServiceControllerAdapter.this.removeImsFeature(slotId, feature);
+        }
+
+        @Override
+        public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
+                IImsRegistrationListener listener) throws RemoteException {
+            return 0;
+        }
+
+        @Override
+        public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
+
+        }
+
+        @Override
+        public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
+                throws RemoteException {
+            return false;
+        }
+
+        @Override
+        public boolean isOpened(int slotId, int featureType) throws RemoteException {
+            return false;
+        }
+
+        @Override
+        public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
+            return ImsFeature.STATE_NOT_AVAILABLE;
+        }
+
+        @Override
+        public void addRegistrationListener(int slotId, int featureType,
+                IImsRegistrationListener listener) throws RemoteException {
+
+        }
+
+        @Override
+        public void removeRegistrationListener(int slotId, int featureType,
+                IImsRegistrationListener listener) throws RemoteException {
+
+        }
+
+        @Override
+        public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
+                int callSessionType, int callType) throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
+                ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
+                String callId) throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public IImsUt getUtInterface(int slotId, int featureType)
+                throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public IImsConfig getConfigInterface(int slotId, int featureType)
+                throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public void turnOnIms(int slotId, int featureType)
+                throws RemoteException {
+
+        }
+
+        @Override
+        public void turnOffIms(int slotId, int featureType) throws RemoteException {
+
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface(int slotId, int featureType)
+                throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
+                throws RemoteException {
+
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
+                throws RemoteException {
+            return null;
+        }
+    }
+
+    private ImsServiceControllerBinder mBinder;
+
+    public IImsServiceController getBinder() {
+        if (mBinder == null) {
+            mBinder = spy(new ImsServiceControllerBinder());
+        }
+
+        return mBinder;
+    }
+
+    // Used by Mockito for verification that this method is being called in spy
+    public void createImsFeature(int subId, int feature) throws RemoteException {
+    }
+
+    // Used by Mockito for verification that this method is being called in spy
+    public void removeImsFeature(int subId, int feature) throws RemoteException {
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
index bc12cf0..b01ae4a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsExternalCallTrackerTest.java
@@ -31,7 +31,7 @@
 import org.mockito.stubbing.Answer;
 
 import android.net.Uri;
-import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -69,7 +69,7 @@
         mTracker = new ImsExternalCallTracker(mImsPhone, mImsPullCall, mCallNotifier);
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     public void testAddExternalCall() {
         List<ImsExternalCallState> dep = new ArrayList<>();
@@ -92,7 +92,7 @@
         assert(connection instanceof ImsExternalConnection);
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     public void testRemoveExternalCall() {
         testAddExternalCall();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
index e6c1761..4fc9411 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTest.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.imsphone;
 
+import android.support.test.filters.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.ImsStreamMediaProfile;
@@ -59,6 +60,7 @@
         super.tearDown();
     }
 
+    @FlakyTest
     @Test
     @SmallTest
     public void testAttachDetach() {
@@ -81,6 +83,7 @@
         assertEquals(Call.State.IDLE, mImsCallUT.getState());
     }
 
+    @FlakyTest
     @Test
     @SmallTest
     public void testConnectionDisconnected() {
@@ -98,6 +101,7 @@
         assertEquals(Call.State.DISCONNECTED, mImsCallUT.getState());
     }
 
+    @FlakyTest
     @Test
     @SmallTest
     public void testHangup() {
@@ -109,6 +113,7 @@
         }
     }
 
+    @FlakyTest
     @Test
     @SmallTest
     public void testUpdateRingBackTone() {
@@ -140,6 +145,7 @@
         assertEquals(mConnection2, mImsCallUT.getConnections().get(0));
     }
 
+    @FlakyTest
     @Test
     @SmallTest
     public void testMultiParty() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 116dcdd..be8d6f6 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -15,23 +15,52 @@
  */
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isNull;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.PendingIntent;
+import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.support.test.filters.FlakyTest;
+import android.telephony.DisconnectCause;
+import android.telecom.VideoProfile;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.ims.feature.ImsFeature;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.ims.ImsCall;
 import com.android.ims.ImsCallProfile;
 import com.android.ims.ImsConfig;
 import com.android.ims.ImsConnectionStateListener;
+import com.android.ims.ImsException;
 import com.android.ims.ImsManager;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsServiceClass;
 import com.android.ims.internal.ImsCallSession;
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
@@ -39,26 +68,13 @@
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.isNull;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class ImsPhoneCallTrackerTest extends TelephonyTest {
     private ImsPhoneCallTracker mCTUT;
     private ImsCTHandlerThread mImsCTHandlerThread;
@@ -70,6 +86,9 @@
     private int mServiceId;
     @Mock
     private ImsCallSession mImsCallSession;
+    @Mock
+    private SharedPreferences mSharedPreferences;
+    private Handler mCTHander;
 
     private class ImsCTHandlerThread extends HandlerThread {
 
@@ -79,6 +98,12 @@
         @Override
         public void onLooperPrepared() {
             mCTUT = new ImsPhoneCallTracker(mImsPhone);
+            mCTUT.addReasonCodeRemapping(null, "Wifi signal lost.", ImsReasonInfo.CODE_WIFI_LOST);
+            mCTUT.addReasonCodeRemapping(501, "Call answered elsewhere.",
+                    ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
+            mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.",
+                    ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
+            mCTHander = new Handler(mCTUT.getLooper());
             setReady(true);
         }
     }
@@ -144,7 +169,7 @@
         mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
         imsCallMocking(mImsCall);
         imsCallMocking(mSecondImsCall);
-
+        doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceStatus();
         doReturn(mImsCallProfile).when(mImsManager).createCallProfile(eq(mServiceId),
                 anyInt(), anyInt());
 
@@ -183,14 +208,14 @@
 
         waitUntilReady();
         logd("ImsPhoneCallTracker initiated");
-        /* Make sure getImsService is triggered on a separate thread */
-        waitForMs(100);
+        /* Make sure getImsService is triggered on handler */
+        waitForHandlerAction(mCTHander, 100);
     }
 
     @After
     public void tearDown() throws Exception {
         mCTUT = null;
-        mImsCTHandlerThread.quitSafely();
+        mImsCTHandlerThread.quit();
         super.tearDown();
     }
 
@@ -207,6 +232,7 @@
         mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
                 featureEnableArray,
                 featureDisableArray);
+        waitForHandlerAction(mCTHander, 1000);
         assertTrue(mCTUT.isVolteEnabled());
         assertFalse(mCTUT.isVideoCallEnabled());
         // video call not enabled
@@ -218,6 +244,7 @@
         mImsConnectionStateListener.onFeatureCapabilityChanged(ImsServiceClass.MMTEL,
                 featureEnableArray,
                 featureDisableArray);
+        waitForHandlerAction(mCTHander, 1000);
         assertTrue(mCTUT.isVideoCallEnabled());
         verify(mImsPhone, times(1)).notifyForVideoCapabilityChanged(eq(true));
     }
@@ -328,6 +355,32 @@
         assertEquals(Call.State.HOLDING, mCTUT.mBackgroundCall.getState());
     }
 
+    /**
+     * Ensures that the dial method will perform a shared preferences lookup using the correct
+     * shared preference key to determine the CLIR mode.
+     */
+    @Test
+    @SmallTest
+    public void testDialClirMode() {
+        mCTUT.setSharedPreferenceProxy((Context context) -> {
+            return mSharedPreferences;
+        });
+        ArgumentCaptor<String> mStringCaptor = ArgumentCaptor.forClass(String.class);
+        doReturn(CommandsInterface.CLIR_INVOCATION).when(mSharedPreferences).getInt(
+                mStringCaptor.capture(), anyInt());
+
+        try {
+            mCTUT.dial("+17005554141", VideoProfile.STATE_AUDIO_ONLY, null);
+        } catch (CallStateException cse) {
+            cse.printStackTrace();
+            Assert.fail("unexpected exception thrown" + cse.getMessage());
+        }
+
+        // Ensure that the correct key was queried from the shared prefs.
+        assertEquals("clir_key0", mStringCaptor.getValue());
+    }
+
+    @FlakyTest
     @Test
     @SmallTest
     public void testImsMOCallDial() {
@@ -348,6 +401,8 @@
         assertEquals(Call.State.ALERTING, mCTUT.mForegroundCall.getState());
     }
 
+    @FlakyTest
+    @Ignore
     @Test
     @SmallTest
     public void testImsMTActiveMODial() {
@@ -412,4 +467,84 @@
         //verify trigger sendDtmf to mImsSecondCall
         verify(mSecondImsCall, times(1)).sendDtmf(eq(PhoneNumberUtils.WAIT), (Message) isNull());
     }
+
+    @Test
+    @SmallTest
+    public void testReasonCodeRemap() {
+        assertEquals(ImsReasonInfo.CODE_WIFI_LOST, mCTUT.maybeRemapReasonCode(
+                new ImsReasonInfo(1, 1, "Wifi signal lost.")));
+        assertEquals(ImsReasonInfo.CODE_WIFI_LOST, mCTUT.maybeRemapReasonCode(
+                new ImsReasonInfo(200, 1, "Wifi signal lost.")));
+        assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
+                mCTUT.maybeRemapReasonCode(new ImsReasonInfo(501, 1, "Call answered elsewhere.")));
+        assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
+                mCTUT.maybeRemapReasonCode(new ImsReasonInfo(510, 1, "Call answered elsewhere.")));
+        assertEquals(90210, mCTUT.maybeRemapReasonCode(new ImsReasonInfo(90210, 1,
+                "Call answered elsewhere.")));
+    }
+
+
+    @Test
+    @SmallTest
+    public void testDialImsServiceUnavailable() throws ImsException {
+        doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
+                mImsManager).createCallProfile(anyInt(), anyInt(), anyInt());
+        mCTUT.mRetryTimeout = () -> 0; //ms
+        assertEquals(Call.State.IDLE, mCTUT.mForegroundCall.getState());
+        assertEquals(PhoneConstants.State.IDLE, mCTUT.getState());
+
+        try {
+            mCTUT.dial("+17005554141", ImsCallProfile.CALL_TYPE_VOICE, null);
+        } catch (Exception e) {
+            Assert.fail();
+        }
+
+        // wait for handler to process ImsService connection retry
+        waitForHandlerAction(mCTHander, 1000); // 1 second timeout
+        verify(mImsManager, never()).makeCall(anyInt(), nullable(ImsCallProfile.class),
+                eq(new String[]{"+17005554141"}), nullable(ImsCall.Listener.class));
+        // Make sure that open is called in ImsPhoneCallTracker when it was first connected and
+        // again after retry.
+        verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
+                nullable(ImsConnectionStateListener.class));
+    }
+
+    @FlakyTest
+    @Ignore
+    @Test
+    @SmallTest
+    public void testTTYImsServiceUnavailable() throws ImsException {
+        doThrow(new ImsException("Test Exception", ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN)).when(
+                mImsManager).setUiTTYMode(nullable(Context.class), anyInt(),
+                nullable(Message.class));
+        // Remove retry timeout delay
+        mCTUT.mRetryTimeout = () -> 0; //ms
+
+        mCTUT.setUiTTYMode(0, new Message());
+
+        // wait for handler to process ImsService connection retry
+        waitForHandlerAction(mCTHander, 100);
+        // Make sure that open is called in ImsPhoneCallTracker to re-establish connection to
+        // ImsService
+        verify(mImsManager, times(2)).open(anyInt(), nullable(PendingIntent.class),
+                nullable(ImsConnectionStateListener.class));
+    }
+
+    @Test
+    @SmallTest
+    public void testLowBatteryDisconnectMidCall() {
+        assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.ACTIVE));
+        assertEquals(DisconnectCause.LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.ACTIVE));
+    }
+
+    @Test
+    @SmallTest
+    public void testLowBatteryDisconnectDialing() {
+        assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, 0), Call.State.DIALING));
+        assertEquals(DisconnectCause.DIAL_LOW_BATTERY, mCTUT.getDisconnectCauseFromReasonInfo(
+                new ImsReasonInfo(ImsReasonInfo.CODE_LOW_BATTERY, 0), Call.State.DIALING));
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
index e6990fd..a84dd2c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -133,7 +133,7 @@
         // MT background Connection dialing -> active
         mConnectionUT = new ImsPhoneConnection(mImsPhone, mImsCall, mImsCT, mBackGroundCall, false);
         doReturn(Call.State.HOLDING).when(mBackGroundCall).getState();
-        assertTrue(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
+        assertFalse(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
         verify(mBackGroundCall, times(1)).detach(eq(mConnectionUT));
         verify(mForeGroundCall, times(1)).attach(eq(mConnectionUT));
         verify(mForeGroundCall, times(1)).update(eq(mConnectionUT), eq(mImsCall),
@@ -248,8 +248,6 @@
                 ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN + "");
         assertTrue(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
         assertTrue(mConnectionUT.isWifi());
-        //keep using the wifi state from extra, not update
-        assertFalse(mConnectionUT.updateWifiState());
     }
 
     @Test
@@ -265,7 +263,30 @@
                 ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN + "");
         assertTrue(mConnectionUT.update(mImsCall, Call.State.ACTIVE));
         assertTrue(mConnectionUT.isWifi());
-        //keep using the wifi state from extra, not update
-        assertFalse(mConnectionUT.updateWifiState());
+    }
+
+    @Test
+    @SmallTest
+    public void testAddressUpdate() {
+        String[] testAddressMappingSet[] = {
+                /* {"inputAddress", "updateAddress", "ExpectResult"} */
+                {"12345", "12345", "12345"},
+                {"12345", "67890", "67890"},
+                {"12345*00000", "12345", "12345*00000"},
+                {"12345*00000", "67890", "67890"},
+                {"12345*00000", "12345*00000", "12345*00000"},
+                {"12345;11111*00000", "12345", "12345"},
+                {"12345*00000;11111", "12345", "12345*00000"},
+                {"18412345*00000", "18412345", "18412345*00000"},
+                {"+8112345*00000", "+8112345", "+8112345*00000"},
+                {"12345*00000", "12346", "12346"}};
+        for (String[] testAddress : testAddressMappingSet) {
+            mConnectionUT = new ImsPhoneConnection(mImsPhone, testAddress[0], mImsCT,
+                    mForeGroundCall, false);
+            doReturn(testAddress[1]).when(mImsCallProfile)
+                    .getCallExtra(eq(ImsCallProfile.EXTRA_OI));
+            mConnectionUT.updateAddressDisplay(mImsCall);
+            assertEquals(testAddress[2], mConnectionUT.getAddress());
+        }
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java
index 8a7c53a..c31541e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneFactoryTest.java
@@ -60,7 +60,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mImsPhoneFactoryHandler.quitSafely();
+        mImsPhoneFactoryHandler.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
index 934f87a..3b357d7 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneTest.java
@@ -16,6 +16,23 @@
 
 package com.android.internal.telephony.imsphone;
 
+import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
+import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyChar;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.nullable;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
 import android.app.Activity;
 import android.app.IApplicationThread;
 import android.content.BroadcastReceiver;
@@ -28,7 +45,7 @@
 import android.os.Message;
 import android.os.PersistableBundle;
 import android.os.SystemProperties;
-import android.platform.test.annotations.Postsubmit;
+import android.support.test.filters.FlakyTest;
 import android.telephony.CarrierConfigManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -36,14 +53,12 @@
 import com.android.ims.ImsEcbmStateListener;
 import com.android.ims.ImsManager;
 import com.android.ims.ImsReasonInfo;
-import com.android.ims.ImsStreamMediaProfile;
 import com.android.ims.ImsUtInterface;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.PhoneInternalInterface;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.TelephonyTest;
@@ -51,31 +66,13 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 import java.util.List;
 
-import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
-import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyChar;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
 public class ImsPhoneTest extends TelephonyTest {
     @Mock
     private ImsPhoneCall mForegroundCall;
@@ -145,7 +142,7 @@
     @After
     public void tearDown() throws Exception {
         mImsPhoneUT = null;
-        mImsPhoneTestHandler.quitSafely();
+        mImsPhoneTestHandler.quit();
         super.tearDown();
     }
 
@@ -399,20 +396,20 @@
         // case 1
         doReturn(PhoneConstants.State.IDLE).when(mImsCT).getState();
         mImsPhoneUT.sendDtmf('-');
-        verify(mImsCT, times(0)).sendDtmf(anyChar(), any(Message.class));
+        verify(mImsCT, times(0)).sendDtmf(anyChar(), nullable(Message.class));
 
         // case 2
         mImsPhoneUT.sendDtmf('0');
-        verify(mImsCT, times(0)).sendDtmf(eq('0'), any(Message.class));
+        verify(mImsCT, times(0)).sendDtmf(eq('0'), nullable(Message.class));
 
         // case 3
         doReturn(PhoneConstants.State.OFFHOOK).when(mImsCT).getState();
         mImsPhoneUT.sendDtmf('-');
-        verify(mImsCT, times(0)).sendDtmf(eq('0'), any(Message.class));
+        verify(mImsCT, times(0)).sendDtmf(eq('0'), nullable(Message.class));
 
         // case 4
         mImsPhoneUT.sendDtmf('0');
-        verify(mImsCT, times(1)).sendDtmf(anyChar(), any(Message.class));
+        verify(mImsCT, times(1)).sendDtmf(anyChar(), nullable(Message.class));
 
         mImsPhoneUT.startDtmf('-');
         verify(mImsCT, times(0)).startDtmf(anyChar());
@@ -452,9 +449,9 @@
         assertEquals(msg, messageArgumentCaptor.getValue().obj);
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
-    @SmallTest
+    @Ignore
     public void testCallForwardingOption() throws Exception {
         Message msg = mTestHandler.obtainMessage();
         mImsPhoneUT.getCallForwardingOption(CF_REASON_UNCONDITIONAL, msg);
@@ -511,8 +508,9 @@
         assertEquals(msg, messageArgumentCaptor.getValue().obj);
     }
 
+    @FlakyTest
     @Test
-    @SmallTest
+    @Ignore
     public void testEcbm() throws Exception {
         ImsEcbmStateListener imsEcbmStateListener = mImsPhoneUT.getImsEcbmStateListener();
 
@@ -578,9 +576,10 @@
         assertEquals(false, mImsPhoneUT.getWakeLock().isHeld());
     }
 
-    @Postsubmit
+    @FlakyTest
     @Test
     @SmallTest
+    @Ignore
     public void testProcessDisconnectReason() throws Exception {
         // set up CarrierConfig
         PersistableBundle bundle = mContextFixture.getCarrierConfigBundle();
@@ -608,8 +607,9 @@
 
         ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
         verify(mContext).sendOrderedBroadcast(
-                intent.capture(), anyString(), any(BroadcastReceiver.class), any(),
-                eq(Activity.RESULT_OK), anyString(), any());
+                intent.capture(), nullable(String.class), any(BroadcastReceiver.class),
+                nullable(Handler.class), eq(Activity.RESULT_OK), nullable(String.class),
+                nullable(Bundle.class));
         assertEquals(ImsManager.ACTION_IMS_REGISTRATION_ERROR, intent.getValue().getAction());
         assertEquals(title, intent.getValue().getStringExtra(Phone.EXTRA_KEY_ALERT_TITLE));
         assertEquals(messageAlert, intent.getValue().getStringExtra(Phone.EXTRA_KEY_ALERT_MESSAGE));
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
new file mode 100644
index 0000000..7ace8cb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsRttTextHandlerTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.imsphone;
+
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.telecom.Connection;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+
+public class ImsRttTextHandlerTest extends TelephonyTest {
+    private static final int TEST_TIMEOUT = 1000;
+    private static final int READ_BUFFER_SIZE = 1000;
+    private static final String LONG_TEXT = "No Soldier shall, in time of peace be quartered in " +
+            "any house, without the consent of the Owner, nor in time of war, but in a manner to " +
+            "be prescribed by law.";
+
+    char[] buffer = new char[READ_BUFFER_SIZE];
+
+    public class MockNetworkWriter implements ImsRttTextHandler.NetworkWriter {
+        private String totalWritten = "";
+        private int numWrites = 0;
+
+        @Override
+        public synchronized void write(String s) {
+            totalWritten += s;
+            numWrites += 1;
+        }
+
+        public synchronized void reset() {
+            totalWritten = "";
+            numWrites = 0;
+        }
+
+        public synchronized String getContents() {
+            return totalWritten;
+        }
+
+        public synchronized int getNumWrites() {
+            return numWrites;
+        }
+    }
+
+    Connection.RttTextStream mRttTextStream;
+    MockNetworkWriter mNetworkWriter = new MockNetworkWriter();
+    ImsRttTextHandler mRttTextHandler;
+    HandlerThread mHandlerThread;
+
+    OutputStreamWriter mPipeToHandler;
+    InputStreamReader mPipeFromHandler;
+    InputStreamReader mHandlerSideOfPipeToHandler;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mNetworkWriter.reset();
+        mHandlerThread = new HandlerThread("TestImsRttTextHandler");
+        mHandlerThread.start();
+        mRttTextHandler = new ImsRttTextHandler(mHandlerThread.getLooper(), mNetworkWriter);
+
+        // Construct some pipes to use
+        ParcelFileDescriptor[] toTextHandler = ParcelFileDescriptor.createReliablePipe();
+        ParcelFileDescriptor[] fromTextHandler = ParcelFileDescriptor.createReliablePipe();
+        mRttTextStream = new Connection.RttTextStream(fromTextHandler[1],toTextHandler[0]);
+
+        mRttTextHandler.initialize(mRttTextStream);
+
+        mPipeFromHandler = new InputStreamReader(
+                new ParcelFileDescriptor.AutoCloseInputStream(fromTextHandler[0]));
+        mPipeToHandler = new OutputStreamWriter(
+                new ParcelFileDescriptor.AutoCloseOutputStream(toTextHandler[1]));
+        mHandlerSideOfPipeToHandler = new InputStreamReader(
+                new ParcelFileDescriptor.AutoCloseInputStream(toTextHandler[1]));
+    }
+
+    /**
+     * Test that the text handler won't send characters before a timeout or enough characters
+     * have accumulated.
+     */
+    @Test
+    public void testProperCharacterBuffering() throws Exception {
+        // Send four characters
+        mPipeToHandler.write("abcd");
+        mPipeToHandler.flush();
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        // make sure at it hasn't been sent.
+        Assert.assertEquals("", mNetworkWriter.getContents());
+        // Wait for 300ms
+        waitForHandlerActionDelayed(mRttTextHandler, TEST_TIMEOUT,
+                ImsRttTextHandler.MAX_BUFFERING_DELAY_MILLIS + 100);
+        // make sure that it has been sent and check that it's correct
+        Assert.assertEquals("abcd", mNetworkWriter.getContents());
+    }
+
+    /**
+     * Test that the text handler sends after enough characters have been sent from in-call
+     * @throws Exception
+     */
+    @Test
+    public void testSendAfterEnoughChars() throws Exception {
+        // Send four characters
+        mPipeToHandler.write("abcd");
+        mPipeToHandler.flush();
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        // make sure at it hasn't been sent.
+        Assert.assertEquals("", mNetworkWriter.getContents());
+        Thread.sleep(10);
+        // Send four more characters
+        mPipeToHandler.write("efgh");
+        mPipeToHandler.flush();
+        // Wait for the stream to consume the characters
+        int count = 0;
+        while (mHandlerSideOfPipeToHandler.ready()) {
+            Thread.sleep(10);
+            count += 1;
+            if (count >= 5) {
+                break;
+            }
+        }
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        // make sure that all characters were sent.
+        Assert.assertEquals("abcdefgh", mNetworkWriter.getContents());
+    }
+
+    /**
+     * Test that the text handler sends its characters as a batch after enough of them have been
+     * buffered.
+     * @throws Exception
+     */
+    @Test
+    public void testBufferedCharactersSentAsBatch() throws Exception {
+        // Send 5 characters, one at a time, pausing for 10ms between each one.
+        char[] characters = new char[] {'a', 'b', 'c', 'd', 'e'};
+        for (char c : characters) {
+            mPipeToHandler.write(String.valueOf(c));
+            mPipeToHandler.flush();
+            Thread.sleep(10);
+        }
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+
+        // Make sure that the ordering is correct and that there was only one write
+        Assert.assertEquals("abcde", mNetworkWriter.getContents());
+        Assert.assertEquals(1, mNetworkWriter.getNumWrites());
+    }
+
+    @Test
+    public void testProperThrottling() throws Exception {
+        // Send a lot of characters in rapid succession, 3 at a time
+        char[] characters = LONG_TEXT.toCharArray();
+        for (int i = 0; i < characters.length; i += 3) {
+            String toSend = new String(characters, i, Math.min(3, characters.length - i));
+            mPipeToHandler.write(toSend);
+            mPipeToHandler.flush();
+            Thread.sleep(10);
+        }
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+
+        // Wait one second and see how many characters are sent in that time.
+        int numCharsSoFar = mNetworkWriter.getContents().length();
+        Thread.sleep(1000);
+        int numCharsInOneSec = mNetworkWriter.getContents().length() - numCharsSoFar;
+        Assert.assertTrue(numCharsInOneSec <= ImsRttTextHandler.MAX_CODEPOINTS_PER_SECOND);
+
+        // Wait 5 seconds for all the chars to make it through
+        Thread.sleep(5000);
+        Assert.assertEquals(LONG_TEXT, mNetworkWriter.getContents());
+    }
+
+    @Test
+    public void testProperTransmissionFromNetworkToInCall() throws Exception {
+        // Make sure that nothing is in the pipe from the network to incall (us)
+        Assert.assertFalse(mPipeFromHandler.ready());
+        // Send a chunk of text
+        mRttTextHandler.sendToInCall(LONG_TEXT);
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        // Make sure we get it immediately
+        Assert.assertEquals(LONG_TEXT, readAll(mPipeFromHandler));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mPipeFromHandler.close();
+        mPipeToHandler.close();
+        mRttTextHandler.tearDown();
+        waitForHandlerAction(mRttTextHandler, TEST_TIMEOUT);
+        mHandlerThread.quit();
+        super.tearDown();
+    }
+
+    private String readAll(InputStreamReader inputStreamReader) throws IOException {
+        if (!inputStreamReader.ready()) {
+            return null;
+        }
+        int len = inputStreamReader.read(buffer, 0, READ_BUFFER_SIZE);
+        return new String(buffer, 0, len);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java
index 612cae7..0e04da8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressCallSessionTest.java
@@ -18,8 +18,8 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.telephony.TelephonyProto;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.TelephonyProto;
 
 import org.junit.After;
 import org.junit.Before;
@@ -72,6 +72,6 @@
         }
 
         assertTrue(mCallSession.isEventsDropped());
-        assertEquals(2, mCallSession.events.getFirst().getRilRequestId());
+        assertEquals(2, mCallSession.events.getFirst().rilRequestId);
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java
index 710f003..b2b9ca5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/InProgressSmsSessionTest.java
@@ -18,8 +18,8 @@
 
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.telephony.TelephonyProto;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.nano.TelephonyProto;
 
 import org.junit.After;
 import org.junit.Before;
@@ -68,7 +68,7 @@
         }
 
         assertTrue(mSmsSession.isEventsDropped());
-        assertEquals(6, mSmsSession.events.getFirst().getRilRequestId());
+        assertEquals(6, mSmsSession.events.getFirst().rilRequestId);
     }
 
     // Test dropped event scenario
@@ -92,4 +92,4 @@
 
         assertEquals(50, mSmsSession.getNumExpectedResponses());
     }
-}
\ No newline at end of file
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 8105335..bd5785d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -16,6 +16,26 @@
 
 package com.android.internal.telephony.metrics;
 
+import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
+import static android.telephony.ServiceState.ROAMING_TYPE_DOMESTIC;
+
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
+import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
+import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
+import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+
+import android.support.test.filters.FlakyTest;
 import android.telephony.ServiceState;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
@@ -24,22 +44,24 @@
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.internal.ImsCallSession;
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SmsResponse;
-import com.android.internal.telephony.TelephonyProto;
-import com.android.internal.telephony.TelephonyProto.ImsConnectionState;
-import com.android.internal.telephony.TelephonyProto.SmsSession;
-import com.android.internal.telephony.TelephonyProto.RadioAccessTechnology;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.CallState;
-import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
-import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
-import com.android.internal.telephony.TelephonyProto.TelephonyLog;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
-import com.android.internal.telephony.TelephonyProto.TelephonyServiceState.RoamingType;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.dataconnection.DataCallResponse;
+import com.android.internal.telephony.nano.TelephonyProto;
+import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.RadioAccessTechnology;
+import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.CallState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState.RoamingType;
 
 import org.junit.After;
 import org.junit.Before;
@@ -48,18 +70,6 @@
 
 import java.lang.reflect.Method;
 
-import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
-import static android.telephony.ServiceState.ROAMING_TYPE_DOMESTIC;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
-import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
-import static com.android.internal.telephony.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-
 public class TelephonyMetricsTest extends TelephonyTest {
 
     @Mock
@@ -71,6 +81,9 @@
     @Mock
     private ServiceState mServiceState;
 
+    @Mock
+    private GsmCdmaConnection mConnection;
+
     private TelephonyMetrics mMetrics;
 
     private UUSInfo mUusInfo;
@@ -133,9 +146,8 @@
         assertEquals(1000, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertTrue(log.getEventsDropped());
-        assertEquals(1, log.events[0].getDataStallAction());
+        assertTrue(log.eventsDropped);
+        assertEquals(1, log.events[0].dataStallAction);
     }
 
     // Test write data stall event
@@ -148,9 +160,23 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.events[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.events[0].getPhoneId());
-        assertEquals(3, log.events[0].getDataStallAction());
+        assertEquals(mPhone.getPhoneId(), log.events[0].phoneId);
+        assertEquals(3, log.events[0].dataStallAction);
+    }
+
+    // Test write modem restart event
+    @Test
+    @SmallTest
+    public void testModemRestartEvent() throws Exception {
+        mMetrics.writeModemRestartEvent(mPhone.getPhoneId(), "Test");
+        TelephonyLog log = buildProto();
+
+        assertEquals(1, log.events.length);
+        assertEquals(0, log.callSessions.length);
+        assertEquals(0, log.smsSessions.length);
+
+        assertEquals(mPhone.getPhoneId(), log.events[0].phoneId);
+        assertEquals("Test", log.events[0].modemRestart.reason);
     }
 
     // Test write on IMS call start
@@ -164,16 +190,36 @@
         assertEquals(0, log.events.length);
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.callSessions[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.callSessions[0].getPhoneId());
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].hasStartTimeMinutes());
+
+        assertEquals(mPhone.getPhoneId(), log.callSessions[0].phoneId);
+
+        assertFalse(log.callSessions[0].eventsDropped);
+
         assertEquals(1, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].events[0].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[0].getCallIndex());
-        assertTrue(log.callSessions[0].events[0].hasImsCommand());
-        assertEquals(ImsCommand.IMS_CMD_START, log.callSessions[0].events[0].getImsCommand());
+
+        assertEquals(123, log.callSessions[0].events[0].callIndex);
+
+        assertEquals(ImsCommand.IMS_CMD_START, log.callSessions[0].events[0].imsCommand);
+    }
+
+    // Test write on IMS call received
+    @Test
+    @SmallTest
+    public void testWriteOnImsCallReceive() throws Exception {
+        mMetrics.writeOnImsCallReceive(mPhone.getPhoneId(), mImsCallSession);
+        mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
+        TelephonyLog log = buildProto();
+
+        assertEquals(0, log.events.length);
+        assertEquals(1, log.callSessions.length);
+        assertEquals(0, log.smsSessions.length);
+        assertEquals(mPhone.getPhoneId(), log.callSessions[0].phoneId);
+
+        assertFalse(log.callSessions[0].eventsDropped);
+
+        assertEquals(1, log.callSessions[0].events.length);
+
+        assertEquals(123, log.callSessions[0].events[0].callIndex);
     }
 
     // Test write ims call state
@@ -189,12 +235,11 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[1].getCallIndex());
-        assertTrue(log.callSessions[0].events[1].hasCallState());
-        assertEquals(CallState.CALL_ACTIVE, log.callSessions[0].events[1].getCallState());
+        assertFalse(log.callSessions[0].eventsDropped);
+
+        assertEquals(123, log.callSessions[0].events[1].callIndex);
+
+        assertEquals(CallState.CALL_ACTIVE, log.callSessions[0].events[1].callState);
     }
 
     // Test write ims set feature value
@@ -204,6 +249,8 @@
         mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), mImsCallSession);
         mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
                 ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
+        mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(),
+                ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, 0, 1, 0);
         mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
         TelephonyLog log = buildProto();
 
@@ -211,10 +258,8 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].settings.hasIsEnhanced4GLteModeEnabled());
-        assertTrue(log.callSessions[0].events[1].settings.getIsEnhanced4GLteModeEnabled());
+        assertFalse(log.callSessions[0].eventsDropped);
+        assertTrue(log.callSessions[0].events[1].settings.isEnhanced4GLteModeEnabled);
     }
 
     // Test write on ims call handover event
@@ -232,24 +277,16 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasType());
+        assertFalse(log.callSessions[0].eventsDropped);
         assertEquals(TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[1].getCallIndex());
-        assertTrue(log.callSessions[0].events[1].hasSrcAccessTech());
-        assertEquals(5, log.callSessions[0].events[1].getSrcAccessTech());
-        assertTrue(log.callSessions[0].events[1].hasTargetAccessTech());
-        assertEquals(6, log.callSessions[0].events[1].getTargetAccessTech());
+                log.callSessions[0].events[1].type);
+        assertEquals(123, log.callSessions[0].events[1].callIndex);
+        assertEquals(5, log.callSessions[0].events[1].srcAccessTech);
+        assertEquals(6, log.callSessions[0].events[1].targetAccessTech);
 
-        assertTrue(log.callSessions[0].events[1].reasonInfo.hasExtraMessage());
-        assertEquals("extramessage", log.callSessions[0].events[1].reasonInfo.getExtraMessage());
-        assertTrue(log.callSessions[0].events[1].reasonInfo.hasExtraCode());
-        assertEquals(456, log.callSessions[0].events[1].reasonInfo.getExtraCode());
-        assertTrue(log.callSessions[0].events[1].reasonInfo.hasReasonCode());
-        assertEquals(123, log.callSessions[0].events[1].reasonInfo.getReasonCode());
+        assertEquals("extramessage", log.callSessions[0].events[1].reasonInfo.extraMessage);
+        assertEquals(456, log.callSessions[0].events[1].reasonInfo.extraCode);
+        assertEquals(123, log.callSessions[0].events[1].reasonInfo.reasonCode);
     }
 
     // Test write on ims command
@@ -265,15 +302,15 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasType());
+
+        assertFalse(log.callSessions[0].eventsDropped);
+
         assertEquals(TelephonyCallSession.Event.Type.IMS_COMMAND,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].hasImsCommand());
-        assertEquals(123, log.callSessions[0].events[1].getImsCommand());
-        assertTrue(log.callSessions[0].events[1].hasCallIndex());
-        assertEquals(123, log.callSessions[0].events[1].getCallIndex());
+                log.callSessions[0].events[1].type);
+
+        assertEquals(123, log.callSessions[0].events[1].imsCommand);
+
+        assertEquals(123, log.callSessions[0].events[1].callIndex);
     }
 
     // Test write on ims connection state
@@ -283,6 +320,8 @@
         mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), mImsCallSession);
         mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
                 ImsConnectionState.State.CONNECTED, mImsReasonInfo);
+        mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(),
+                ImsConnectionState.State.CONNECTED, mImsReasonInfo);
         mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
         TelephonyLog log = buildProto();
 
@@ -290,40 +329,26 @@
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
-        assertTrue(log.events[0].hasType());
-        assertEquals(TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED, log.events[0].getType());
-        assertTrue(log.events[0].imsConnectionState.hasState());
+        assertFalse(log.eventsDropped);
+        assertEquals(TelephonyEvent.Type.IMS_CONNECTION_STATE_CHANGED, log.events[0].type);
         assertEquals(ImsConnectionState.State.CONNECTED,
-                log.events[0].imsConnectionState.getState());
-        assertTrue(log.events[0].imsConnectionState.reasonInfo.hasReasonCode());
-        assertEquals(123, log.events[0].imsConnectionState.reasonInfo.getReasonCode());
-        assertTrue(log.events[0].imsConnectionState.reasonInfo.hasExtraCode());
-        assertEquals(456, log.events[0].imsConnectionState.reasonInfo.getExtraCode());
-        assertTrue(log.events[0].imsConnectionState.reasonInfo.hasExtraMessage());
-        assertEquals("extramessage", log.events[0].imsConnectionState.reasonInfo.getExtraMessage());
-        assertTrue(log.callSessions[0].hasEventsDropped());
-        assertFalse(log.callSessions[0].getEventsDropped());
-        assertTrue(log.callSessions[0].events[1].hasType());
+                log.events[0].imsConnectionState.state);
+        assertEquals(123, log.events[0].imsConnectionState.reasonInfo.reasonCode);
+        assertEquals(456, log.events[0].imsConnectionState.reasonInfo.extraCode);
+        assertEquals("extramessage", log.events[0].imsConnectionState.reasonInfo.extraMessage);
+        assertFalse(log.callSessions[0].eventsDropped);
         assertEquals(TelephonyCallSession.Event.Type.IMS_CONNECTION_STATE_CHANGED,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].imsConnectionState.hasState());
+                log.callSessions[0].events[1].type);
         assertEquals(ImsConnectionState.State.CONNECTED,
-                log.callSessions[0].events[1].imsConnectionState.getState());
+                log.callSessions[0].events[1].imsConnectionState.state);
     }
 
     // Test write on setup data call response
     @Test
     @SmallTest
     public void testWriteOnSetupDataCallResponse() throws Exception {
-        DataCallResponse response = new DataCallResponse();
-        response.status = 5;
-        response.suggestedRetryTime = 6;
-        response.cid = 7;
-        response.active = 8;
-        response.type = "IPV4V6";
-        response.ifname = "ifname";
+        DataCallResponse response = new DataCallResponse(5, 6, 7, 8, "IPV4V6", FAKE_IFNAME,
+                FAKE_ADDRESS, FAKE_DNS, FAKE_GATEWAY, FAKE_PCSCF_ADDRESS, 1440);
 
         mMetrics.writeOnRilSolicitedResponse(mPhone.getPhoneId(), 1, 2,
                 RIL_REQUEST_SETUP_DATA_CALL, response);
@@ -332,21 +357,15 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
         TelephonyEvent.RilSetupDataCallResponse respProto = log.events[0].setupDataCallResponse;
 
-        assertTrue(respProto.hasStatus());
-        assertEquals(5, respProto.getStatus());
-        assertTrue(respProto.hasSuggestedRetryTimeMillis());
-        assertEquals(6, respProto.getSuggestedRetryTimeMillis());
-        assertTrue(respProto.call.hasCid());
-        assertEquals(7, respProto.call.getCid());
-        assertTrue(respProto.call.hasType());
-        assertEquals(PDP_TYPE_IPV4V6, respProto.call.getType());
-        assertTrue(respProto.call.hasIframe());
-        assertEquals("ifname", respProto.call.getIframe());
+        assertEquals(5, respProto.status);
+        assertEquals(6, respProto.suggestedRetryTimeMillis);
+        assertEquals(7, respProto.call.cid);
+        assertEquals(PDP_TYPE_IPV4V6, respProto.call.type);
+        assertEquals(FAKE_IFNAME, respProto.call.iframe);
     }
 
     // Test write on deactivate data call response
@@ -360,13 +379,10 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
-        assertTrue(log.events[0].hasType());
-        assertEquals(TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE, log.events[0].getType());
-        assertTrue(log.events[0].hasError());
-        assertEquals(4, log.events[0].getError());
+        assertEquals(TelephonyEvent.Type.DATA_CALL_DEACTIVATE_RESPONSE, log.events[0].type);
+        assertEquals(4, log.events[0].error);
     }
 
     // Test write RIL send SMS
@@ -388,46 +404,29 @@
         assertEquals(0, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(1, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
         SmsSession.Event[] events = log.smsSessions[0].events;
         assertEquals(4, events.length);
-        assertTrue(events[0].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND, events[0].getType());
-        assertTrue(events[0].hasRilRequestId());
-        assertEquals(1, events[0].getRilRequestId());
-        assertTrue(events[0].hasTech());
-        assertEquals(2, events[0].getTech());
-        assertTrue(events[0].hasFormat());
-        assertEquals(1, events[0].getFormat());
+        assertEquals(SmsSession.Event.Type.SMS_SEND, events[0].type);
+        assertEquals(1, events[0].rilRequestId);
+        assertEquals(2, events[0].tech);
+        assertEquals(1, events[0].format);
 
-        assertTrue(events[1].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND, events[1].getType());
-        assertTrue(events[1].hasRilRequestId());
-        assertEquals(4, events[1].getRilRequestId());
-        assertTrue(events[1].hasTech());
-        assertEquals(5, events[1].getTech());
-        assertTrue(events[1].hasFormat());
-        assertEquals(2, events[1].getFormat());
+        assertEquals(SmsSession.Event.Type.SMS_SEND, events[1].type);
+        assertEquals(4, events[1].rilRequestId);
+        assertEquals(5, events[1].tech);
+        assertEquals(2, events[1].format);
 
-        assertTrue(events[2].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[2].getType());
-        assertTrue(events[2].hasRilRequestId());
-        assertEquals(1, events[2].getRilRequestId());
-        assertTrue(events[2].hasError());
-        assertEquals(0, events[2].getError());
-        assertTrue(events[2].hasErrorCode());
-        assertEquals(123, events[2].getErrorCode());
+        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[2].type);
+        assertEquals(1, events[2].rilRequestId);
+        assertEquals(1, events[2].error);
+        assertEquals(123, events[2].errorCode);
 
-        assertTrue(events[3].hasType());
-        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[3].getType());
-        assertTrue(events[3].hasRilRequestId());
-        assertEquals(4, events[3].getRilRequestId());
-        assertTrue(events[3].hasError());
-        assertEquals(0, events[3].getError());
-        assertTrue(events[3].hasErrorCode());
-        assertEquals(456, events[3].getErrorCode());
+        assertEquals(SmsSession.Event.Type.SMS_SEND_RESULT, events[3].type);
+        assertEquals(4, events[3].rilRequestId);
+        assertEquals(1, events[3].error);
+        assertEquals(456, events[3].errorCode);
     }
 
     // Test write phone state
@@ -442,53 +441,47 @@
         assertEquals(0, log.events.length);
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
-        assertTrue(log.callSessions[0].hasPhoneId());
-        assertEquals(mPhone.getPhoneId(), log.callSessions[0].getPhoneId());
+        assertEquals(mPhone.getPhoneId(), log.callSessions[0].phoneId);
         assertEquals(2, log.callSessions[0].events.length);
-        assertTrue(log.callSessions[0].events[1].hasType());
         assertEquals(TelephonyCallSession.Event.Type.PHONE_STATE_CHANGED,
-                log.callSessions[0].events[1].getType());
-        assertTrue(log.callSessions[0].events[1].hasPhoneState());
+                log.callSessions[0].events[1].type);
         assertEquals(TelephonyCallSession.Event.PhoneState.STATE_OFFHOOK,
-                log.callSessions[0].events[1].getPhoneState());
+                log.callSessions[0].events[1].phoneState);
     }
 
     // Test write RIL dial and hangup
     @Test
     @SmallTest
     public void testWriteRilDialHangup() throws Exception {
-        mMetrics.writeRilDial(mPhone.getPhoneId(), 1, 2, mUusInfo);
-        mMetrics.writeRilHangup(mPhone.getPhoneId(), 2, 3);
+        doReturn(Call.State.DIALING).when(mConnection).getState();
+        mMetrics.writeRilDial(mPhone.getPhoneId(), mConnection, 2, mUusInfo);
+        doReturn(Call.State.DISCONNECTED).when(mConnection).getState();
+        mMetrics.writeRilHangup(mPhone.getPhoneId(), mConnection, 3);
         mMetrics.writePhoneState(mPhone.getPhoneId(), PhoneConstants.State.IDLE);
         TelephonyLog log = buildProto();
 
         assertEquals(0, log.events.length);
         assertEquals(1, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+        assertFalse(log.eventsDropped);
 
         TelephonyCallSession.Event[] events = log.callSessions[0].events;
 
         assertEquals(2, events.length);
-        assertTrue(events[0].hasType());
-        assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[0].getType());
-        assertTrue(events[0].hasRilRequest());
-        assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL,
-                events[0].getRilRequest());
-        assertTrue(events[0].hasRilRequestId());
-        assertEquals(1, events[0].getRilRequestId());
+        assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[0].type);
 
-        assertTrue(events[1].hasType());
-        assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[1].getType());
-        assertTrue(events[1].hasRilRequest());
+        assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL,
+                events[0].rilRequest);
+        RilCall[] calls = events[0].calls;
+        assertEquals(CallState.CALL_DIALING, calls[0].state);
+
         assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP,
-                events[1].getRilRequest());
-        assertTrue(events[1].hasCallIndex());
-        assertEquals(3, events[1].getCallIndex());
+                events[1].rilRequest);
+        calls = events[1].calls;
+        assertEquals(3, calls[0].index);
+        assertEquals(CallState.CALL_DISCONNECTED, calls[0].state);
     }
 
     // Test write RIL setup data call
@@ -503,21 +496,21 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
 
-        assertTrue(log.events[0].hasType());
-        assertEquals(TelephonyEvent.Type.DATA_CALL_SETUP, log.events[0].getType());
+        assertFalse(log.eventsDropped);
+
+
+        assertEquals(TelephonyEvent.Type.DATA_CALL_SETUP, log.events[0].type);
 
         TelephonyEvent.RilSetupDataCall setupDataCall = log.events[0].setupDataCall;
-        assertTrue(setupDataCall.hasApn());
-        assertEquals("apn", setupDataCall.getApn());
-        assertTrue(setupDataCall.hasRat());
-        assertEquals(14, setupDataCall.getRat());
-        assertTrue(setupDataCall.hasDataProfile());
-        assertEquals(4, setupDataCall.getDataProfile());
-        assertTrue(setupDataCall.hasType());
-        assertEquals(PDP_TYPE_IPV4V6, setupDataCall.getType());
+
+        assertEquals("apn", setupDataCall.apn);
+
+        assertEquals(14, setupDataCall.rat);
+
+        assertEquals(4, setupDataCall.dataProfile);
+
+        assertEquals(PDP_TYPE_IPV4V6, setupDataCall.type);
     }
 
     // Test write service state changed
@@ -525,39 +518,40 @@
     @SmallTest
     public void testWriteServiceStateChanged() throws Exception {
         mMetrics.writeServiceStateChanged(mPhone.getPhoneId(), mServiceState);
+        mMetrics.writeServiceStateChanged(mPhone.getPhoneId(), mServiceState);
         TelephonyLog log = buildProto();
 
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+
+        assertFalse(log.eventsDropped);
 
         TelephonyEvent event = log.events[0];
-        assertTrue(event.hasType());
-        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.getType());
+
+        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.type);
 
         TelephonyServiceState state = event.serviceState;
-        assertTrue(state.hasVoiceRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getVoiceRat());
-        assertTrue(state.hasDataRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getDataRat());
-        assertTrue(state.hasVoiceRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getVoiceRoamingType());
-        assertTrue(state.hasDataRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getDataRoamingType());
-        assertTrue(state.voiceOperator.hasAlphaLong());
-        assertEquals("voicelong", state.voiceOperator.getAlphaLong());
-        assertTrue(state.voiceOperator.hasAlphaShort());
-        assertEquals("voiceshort", state.voiceOperator.getAlphaShort());
-        assertTrue(state.voiceOperator.hasNumeric());
-        assertEquals("123456", state.voiceOperator.getNumeric());
-        assertTrue(state.dataOperator.hasAlphaLong());
-        assertEquals("datalong", state.dataOperator.getAlphaLong());
-        assertTrue(state.dataOperator.hasAlphaShort());
-        assertEquals("datashort", state.dataOperator.getAlphaShort());
-        assertTrue(state.dataOperator.hasNumeric());
-        assertEquals("123456", state.dataOperator.getNumeric());
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.voiceRat);
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.dataRat);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.voiceRoamingType);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.dataRoamingType);
+
+        assertEquals("voicelong", state.voiceOperator.alphaLong);
+
+        assertEquals("voiceshort", state.voiceOperator.alphaShort);
+
+        assertEquals("123456", state.voiceOperator.numeric);
+
+        assertEquals("datalong", state.dataOperator.alphaLong);
+
+        assertEquals("datashort", state.dataOperator.alphaShort);
+
+        assertEquals("123456", state.dataOperator.numeric);
     }
 
     // Test reset scenario
@@ -571,34 +565,34 @@
         assertEquals(1, log.events.length);
         assertEquals(0, log.callSessions.length);
         assertEquals(0, log.smsSessions.length);
-        assertTrue(log.hasEventsDropped());
-        assertFalse(log.getEventsDropped());
+
+        assertFalse(log.eventsDropped);
 
         TelephonyEvent event = log.events[0];
-        assertTrue(event.hasType());
-        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.getType());
+
+        assertEquals(TelephonyEvent.Type.RIL_SERVICE_STATE_CHANGED, event.type);
 
         TelephonyServiceState state = event.serviceState;
-        assertTrue(state.hasVoiceRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getVoiceRat());
-        assertTrue(state.hasDataRat());
-        assertEquals(RadioAccessTechnology.RAT_LTE, state.getDataRat());
-        assertTrue(state.hasVoiceRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getVoiceRoamingType());
-        assertTrue(state.hasDataRoamingType());
-        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.getDataRoamingType());
-        assertTrue(state.voiceOperator.hasAlphaLong());
-        assertEquals("voicelong", state.voiceOperator.getAlphaLong());
-        assertTrue(state.voiceOperator.hasAlphaShort());
-        assertEquals("voiceshort", state.voiceOperator.getAlphaShort());
-        assertTrue(state.voiceOperator.hasNumeric());
-        assertEquals("123456", state.voiceOperator.getNumeric());
-        assertTrue(state.dataOperator.hasAlphaLong());
-        assertEquals("datalong", state.dataOperator.getAlphaLong());
-        assertTrue(state.dataOperator.hasAlphaShort());
-        assertEquals("datashort", state.dataOperator.getAlphaShort());
-        assertTrue(state.dataOperator.hasNumeric());
-        assertEquals("123456", state.dataOperator.getNumeric());
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.voiceRat);
+
+        assertEquals(RadioAccessTechnology.RAT_LTE, state.dataRat);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.voiceRoamingType);
+
+        assertEquals(RoamingType.ROAMING_TYPE_DOMESTIC, state.dataRoamingType);
+
+        assertEquals("voicelong", state.voiceOperator.alphaLong);
+
+        assertEquals("voiceshort", state.voiceOperator.alphaShort);
+
+        assertEquals("123456", state.voiceOperator.numeric);
+
+        assertEquals("datalong", state.dataOperator.alphaLong);
+
+        assertEquals("datashort", state.dataOperator.alphaShort);
+
+        assertEquals("123456", state.dataOperator.numeric);
     }
 
     // Test Proto Encoding/Decoding
@@ -612,4 +606,42 @@
         byte[] decodedString = Base64.decode(encodedString, Base64.DEFAULT);
         assertArrayEquals(TelephonyProto.TelephonyLog.toByteArray(log), decodedString);
     }
-}
\ No newline at end of file
+
+    // Test write ims capabilities changed
+    @Test
+    @SmallTest
+    public void testWriteOnImsCapabilities() throws Exception {
+        boolean[] caps1 = new boolean[]{true, false, true, false, true, false};
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps1);
+        boolean[] caps2 = new boolean[]{true, false, true, false, true, false};
+        // The duplicate one should be filtered out.
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps2);
+        boolean[] caps3 = new boolean[]{false, true, false, true, false, true};
+        mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), caps3);
+        TelephonyLog log = buildProto();
+
+        assertEquals(2, log.events.length);
+        assertEquals(0, log.callSessions.length);
+        assertEquals(0, log.smsSessions.length);
+
+        TelephonyEvent event = log.events[0];
+
+        assertEquals(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED, event.type);
+        assertEquals(caps1[0], event.imsCapabilities.voiceOverLte);
+        assertEquals(caps1[1], event.imsCapabilities.videoOverLte);
+        assertEquals(caps1[2], event.imsCapabilities.voiceOverWifi);
+        assertEquals(caps1[3], event.imsCapabilities.videoOverWifi);
+        assertEquals(caps1[4], event.imsCapabilities.utOverLte);
+        assertEquals(caps1[5], event.imsCapabilities.utOverWifi);
+
+        event = log.events[1];
+
+        assertEquals(TelephonyEvent.Type.IMS_CAPABILITIES_CHANGED, event.type);
+        assertEquals(caps3[0], event.imsCapabilities.voiceOverLte);
+        assertEquals(caps3[1], event.imsCapabilities.videoOverLte);
+        assertEquals(caps3[2], event.imsCapabilities.voiceOverWifi);
+        assertEquals(caps3[3], event.imsCapabilities.videoOverWifi);
+        assertEquals(caps3[4], event.imsCapabilities.utOverLte);
+        assertEquals(caps3[5], event.imsCapabilities.utOverWifi);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
index 47fc31d..9210a08 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
@@ -16,98 +16,37 @@
 
 package com.android.internal.telephony.mocks;
 
-import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
-import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
-import static android.net.ConnectivityManager.NETID_UNSET;
-import static android.net.ConnectivityManager.TYPE_NONE;
-import static android.net.ConnectivityManager.TYPE_VPN;
-import static android.net.ConnectivityManager.getNetworkTypeName;
-import static android.net.ConnectivityManager.isNetworkTypeValid;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
 import android.annotation.Nullable;
-import android.app.AlarmManager;
-import android.app.BroadcastOptions;
-import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
 import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
-import android.net.INetworkManagementEventObserver;
-import android.net.INetworkPolicyListener;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
 import android.net.LinkProperties;
-import android.net.LinkProperties.CompareResult;
 import android.net.Network;
-import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
 import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkMisc;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
-import android.net.NetworkUtils;
-import android.net.Proxy;
 import android.net.ProxyInfo;
-import android.net.RouteInfo;
-import android.net.UidRange;
-import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
-import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
-import android.os.PowerManager;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.security.Credentials;
-import android.security.KeyStore;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.LocalLog;
-import android.util.LocalLog.ReadOnlyLocalLog;
-import android.util.Pair;
 import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
-import android.util.Xml;
 
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
@@ -117,28 +56,9 @@
 import com.android.server.connectivity.NetworkAgentInfo;
 import com.android.server.connectivity.NetworkMonitor;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
 import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * @hide
@@ -207,6 +127,9 @@
 
     public void die() {
         // clean up threads/handlers
+        if (mHandlerThread != null) {
+            mHandlerThread.quit();
+        }
     }
 
     private class InternalHandler extends Handler {
@@ -557,21 +480,11 @@
     }
 
     @Override
-    public void requestLinkProperties(NetworkRequest networkRequest) {
-        throw new RuntimeException("not implemented");
-    }
-
-    @Override
     public NetworkCapabilities getNetworkCapabilities(Network network) {
         throw new RuntimeException("not implemented");
     }
 
     @Override
-    public void requestNetworkCapabilities(NetworkRequest networkRequest) {
-        throw new RuntimeException("not implemented");
-    }
-
-    @Override
     public NetworkState[] getAllNetworkState() {
         throw new RuntimeException("not implemented");
     }
@@ -608,11 +521,19 @@
         throw new RuntimeException("not implemented");
     }
 
-    public int tether(String iface) {
+    public void startCaptivePortalApp(Network network) {
         throw new RuntimeException("not implemented");
     }
 
-    public int untether(String iface) {
+    public int getMultipathPreference(Network network) {
+        throw new RuntimeException("not implemented");
+    }
+
+    public int tether(String iface, String callerPkg) {
+        throw new RuntimeException("not implemented");
+    }
+
+    public int untether(String iface, String callerPkg) {
         throw new RuntimeException("not implemented");
     }
 
@@ -632,7 +553,7 @@
         throw new RuntimeException("not implemented");
     }
 
-    public int setUsbTethering(boolean enable) {
+    public int setUsbTethering(boolean enable, String callerPkg) {
         throw new RuntimeException("not implemented");
     }
 
@@ -653,17 +574,18 @@
     }
 
     @Override
-    public boolean isTetheringSupported() {
+    public boolean isTetheringSupported(String callerPkg) {
         throw new RuntimeException("not implemented");
     }
 
     @Override
-    public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
+    public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
+            String callerPkg) {
         throw new RuntimeException("not implemented");
     }
 
     @Override
-    public void stopTethering(int type) {
+    public void stopTethering(int type, String callerPkg) {
         throw new RuntimeException("not implemented");
     }
 
@@ -729,6 +651,11 @@
     }
 
     @Override
+    public boolean isAlwaysOnVpnPackageSupported(int userId, String packageName) {
+        throw new RuntimeException("not implemented");
+    }
+
+    @Override
     public boolean setAlwaysOnVpnPackage(int userId, String packageName, boolean lockdownEnabled) {
         throw new RuntimeException("not implemented");
     }
@@ -764,7 +691,7 @@
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
         networkCapabilities = new NetworkCapabilities(networkCapabilities);
 
-        if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
+        if (timeoutMs < 0) {
             throw new IllegalArgumentException("Bad timeout specified");
         }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
index 7cc2870..504b4e1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/DcTrackerMock.java
@@ -20,7 +20,6 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
 import android.util.LocalLog;
 
@@ -62,10 +61,6 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public boolean isDataPossible(String apnType) {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
     public LinkProperties getLinkProperties(String apnType) {
         throw new RuntimeException("Not Implemented");
     }
@@ -90,10 +85,6 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public boolean getAnyDataEnabled() {
-        throw new RuntimeException("Not Implemented");
-    }
-    @Override
     public boolean hasMatchedTetherApnSetting() {
         throw new RuntimeException("Not Implemented");
     }
@@ -106,11 +97,11 @@
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public void setDataOnRoamingEnabled(boolean enabled) {
+    public void setDataRoamingEnabledByUser(boolean enabled) {
         throw new RuntimeException("Not Implemented");
     }
     @Override
-    public boolean getDataOnRoamingEnabled() {
+    public boolean getDataRoamingEnabled() {
         throw new RuntimeException("Not Implemented");
     }
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/OWNERS b/tests/telephonytests/src/com/android/internal/telephony/mocks/OWNERS
new file mode 100644
index 0000000..909c9bb
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/OWNERS
@@ -0,0 +1,13 @@
+# Re-list all owners appearing in the root OWNERS file for this project
+# This allows adding new owners for specific files using the per-file rule.
+per-file *=amitmahajan@google.com
+per-file *=breadley@google.com
+per-file *=fionaxu@google.com
+per-file *=jackyu@google.com
+per-file *=jsh@google.com
+per-file *=rgreenwalt@google.com
+per-file *=tgunn@google.com
+
+per-file ConnectivityServiceMock.java=ek@google.com
+per-file ConnectivityServiceMock.java=hugobenichi@google.com
+per-file ConnectivityServiceMock.java=lorenzo@google.com
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index 2f9d581..0d19f47 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -17,19 +17,21 @@
 package com.android.internal.telephony;
 
 import android.content.Context;
+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.Messenger;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.net.LinkProperties;
-import android.net.NetworkCapabilities;
+import android.os.ResultReceiver;
+import android.os.WorkSource;
 import android.service.carrier.CarrierIdentifier;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.NetworkScanRequest;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.VoLteServiceState;
@@ -158,6 +160,11 @@
         throw new RuntimeException("not implemented");
     }
 
+    @Override
+    public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback) {
+        throw new RuntimeException("not implemented");
+    }
+
     public ArrayList<Connection> getHandoverConnection() {
         throw new RuntimeException("not implemented");
     }
@@ -795,11 +802,7 @@
         throw new RuntimeException("not implemented");
     }
 
-    public boolean isDataConnectivityPossible() {
-        throw new RuntimeException("not implemented");
-    }
-
-    public boolean isDataConnectivityPossible(String apnType) {
+    public boolean isDataAllowed() {
         throw new RuntimeException("not implemented");
     }
 
@@ -1071,7 +1074,7 @@
         throw new RuntimeException("not implemented");
     }
 
-    public CellLocation getCellLocation() {
+    public CellLocation getCellLocation(WorkSource workSource) {
         throw new RuntimeException("not implemented");
     }
 
@@ -1156,6 +1159,10 @@
         throw new RuntimeException("not implemented");
     }
 
+    public boolean handleUssdServiceCall(String dialString, Callback wrappedCallback) {
+        throw new RuntimeException("not implemented");
+    }
+
     public boolean handleInCallMmiCommands(String command) throws CallStateException {
         throw new RuntimeException("not implemented");
     }
@@ -1229,6 +1236,14 @@
         throw new RuntimeException("not implemented");
     }
 
+    public void startNetworkScan(NetworkScanRequest nsr, Message response) {
+        throw new RuntimeException("not implemented");
+    }
+
+    public void stopNetworkScan(Message response) {
+        throw new RuntimeException("not implemented");
+    }
+
     public void getNeighboringCids(Message response) {
         throw new RuntimeException("not implemented");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
index ef3708b..60995a1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/SubscriptionControllerMock.java
@@ -44,7 +44,7 @@
 public class SubscriptionControllerMock extends SubscriptionController {
     final AtomicInteger mDefaultDataSubId = new AtomicInteger(INVALID_SUBSCRIPTION_ID);
     final ITelephonyRegistry.Stub mTelephonyRegistry;
-    final int[][] mSlotIdxToSubId;
+    final int[][] mSlotIndexToSubId;
 
     public static SubscriptionController init(Phone phone) {
         throw new RuntimeException("not implemented");
@@ -59,10 +59,10 @@
     public SubscriptionControllerMock(Context c, ITelephonyRegistry.Stub tr, int phoneCount) {
         super(c);
         mTelephonyRegistry = tr;
-        mSlotIdxToSubId = new int[phoneCount][];
+        mSlotIndexToSubId = new int[phoneCount][];
         for (int i = 0; i < phoneCount; i++) {
-            mSlotIdxToSubId[i] = new int[1];
-            mSlotIdxToSubId[i][0] = INVALID_SUBSCRIPTION_ID;
+            mSlotIndexToSubId[i] = new int[1];
+            mSlotIndexToSubId[i][0] = INVALID_SUBSCRIPTION_ID;
         }
     }
 
@@ -117,7 +117,7 @@
         throw new RuntimeException("not implemented");
     }
     @Override
-    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx, String cp){
+    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIndex, String cp){
         throw new RuntimeException("not implemented");
     }
     @Override
@@ -141,11 +141,11 @@
         throw new RuntimeException("not implemented");
     }
     @Override
-    public int addSubInfoRecord(String iccId, int slotId) {
+    public int addSubInfoRecord(String iccId, int slotIndex) {
         throw new RuntimeException("not implemented");
     }
     @Override
-    public boolean setPlmnSpn(int slotId, boolean showPlmn, String plmn, boolean showSpn,
+    public boolean setPlmnSpn(int slotIndex, boolean showPlmn, String plmn, boolean showSpn,
             String spn) {
         throw new RuntimeException("not implemented");
     }
@@ -174,28 +174,28 @@
         throw new RuntimeException("not implemented");
     }
     @Override
-    public int getSlotId(int subId) {
+    public int getSlotIndex(int subId) {
         throw new RuntimeException("not implemented");
     }
 
-    private boolean isInvalidSlotId(int slotIdx) {
-        if (slotIdx < 0 || slotIdx >= mSlotIdxToSubId.length) return true;
+    private boolean isInvalidslotIndex(int slotIndex) {
+        if (slotIndex < 0 || slotIndex >= mSlotIndexToSubId.length) return true;
         return false;
     }
 
     @Override
-    public int[] getSubId(int slotIdx) {
-        if (isInvalidSlotId(slotIdx)) {
+    public int[] getSubId(int slotIndex) {
+        if (isInvalidslotIndex(slotIndex)) {
             return null;
         }
-        return mSlotIdxToSubId[slotIdx];
+        return mSlotIndexToSubId[slotIndex];
     }
-    public void setSlotSubId(int slotIdx, int subId) {
-        if (isInvalidSlotId(slotIdx)) {
-            throw new RuntimeException("invalid slot specified" + slotIdx);
+    public void setSlotSubId(int slotIndex, int subId) {
+        if (isInvalidslotIndex(slotIndex)) {
+            throw new RuntimeException("invalid slot specified" + slotIndex);
         }
-        if (mSlotIdxToSubId[slotIdx][0] != subId) {
-            mSlotIdxToSubId[slotIdx][0] = subId;
+        if (mSlotIndexToSubId[slotIndex][0] != subId) {
+            mSlotIndexToSubId[slotIndex][0] = subId;
             try {
                 mTelephonyRegistry.notifySubscriptionInfoChanged();
             } catch (RemoteException ex) {}
@@ -209,8 +209,8 @@
 
         if (subId <= INVALID_SUBSCRIPTION_ID) return INVALID_PHONE_INDEX;
 
-        for (int i = 0; i < mSlotIdxToSubId.length; i++) {
-            if (mSlotIdxToSubId[i][0] == subId) return i;
+        for (int i = 0; i < mSlotIndexToSubId.length; i++) {
+            if (mSlotIndexToSubId[i][0] == subId) return i;
         }
         return INVALID_PHONE_INDEX;
     }
@@ -243,12 +243,8 @@
         throw new RuntimeException("not implemented");
     }
     @Override
-    public int[] getSubIdUsingSlotId(int slotId) {
-        return getSubId(slotId);
-    }
-    @Override
-    public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck,
-            String callingPackage) {
+    public List<SubscriptionInfo> getSubInfoUsingSlotIndexWithCheck(int slotId, boolean needCheck,
+                                                                    String callingPackage) {
         throw new RuntimeException("not implemented");
     }
     @Override
@@ -264,7 +260,7 @@
         throw new RuntimeException("not implemented");
     }
     @Override
-    public int getSimStateForSlotIdx(int slotIdx) {
+    public int getSimStateForSlotIndex(int slotIndex) {
         throw new RuntimeException("not implemented");
     }
     @Override
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
index b6ac0e7..b0b604c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/TelephonyRegistryMock.java
@@ -311,4 +311,10 @@
     public void notifyCarrierNetworkChange(boolean active) {
         throw new RuntimeException("Not implemented");
     }
+
+    @Override
+    public void notifySimActivationStateChangedForPhoneId(int phoneId, int subId,
+                                                          int activationType, int state) {
+        throw new RuntimeException("Not implemented");
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
new file mode 100644
index 0000000..e4795fa
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccCardProxyTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.uicc;
+
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.test.filters.FlakyTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
+import com.android.internal.telephony.uicc.IccCardStatus.CardState;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class IccCardProxyTest extends TelephonyTest {
+    private IccCardProxy mIccCardProxyUT;
+    // private UiccCard mUiccCard;
+    private IccCardProxyHandlerThread mIccCardProxyHandlerThread;
+    private static final int PHONE_ID = 0;
+    private static final int PHONE_COUNT = 1;
+
+    private static final int SCARY_SLEEP_MS = 200;
+    // Must match IccCardProxy.EVENT_ICC_CHANGED
+    private static final int EVENT_ICC_CHANGED = 3;
+
+    @Mock private Handler mMockedHandler;
+    @Mock private IccCardStatus mIccCardStatus;
+    @Mock private UiccCard mUiccCard;
+    @Mock private UiccCardApplication mUiccCardApplication;
+
+    private class IccCardProxyHandlerThread extends HandlerThread {
+
+        private IccCardProxyHandlerThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            /* create a new UICC Controller associated with the simulated Commands */
+            mIccCardProxyUT = new IccCardProxy(mContext, mSimulatedCommands, PHONE_ID);
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(this.getClass().getSimpleName());
+        doReturn(PHONE_COUNT).when(mTelephonyManager).getPhoneCount();
+        doReturn(PHONE_COUNT).when(mTelephonyManager).getSimCount();
+        mSimulatedCommands.setIccCardStatus(mIccCardStatus);
+        mIccCardProxyHandlerThread = new IccCardProxyHandlerThread(TAG);
+        mIccCardProxyHandlerThread.start();
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mIccCardProxyHandlerThread.quitSafely();
+        super.tearDown();
+    }
+
+    @Test
+    @SmallTest
+    public void testInitialCardState() {
+        assertEquals(mIccCardProxyUT.getState(), State.UNKNOWN);
+    }
+
+    @Test
+    @SmallTest
+    public void testPowerOn() {
+        mSimulatedCommands.setRadioPower(true, null);
+        mSimulatedCommands.notifyRadioOn();
+        when(mUiccController.getUiccCard(anyInt())).thenReturn(mUiccCard);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(CommandsInterface.RadioState.RADIO_ON, mSimulatedCommands.getRadioState());
+        assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
+        logd("IccCardProxy state = " + mIccCardProxyUT.getState());
+    }
+
+    @Test
+    @SmallTest
+    public void testCardLoaded() {
+        testPowerOn();
+        when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
+    }
+
+    @Test
+    @SmallTest
+    public void testAppNotLoaded() {
+        testPowerOn();
+        when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_UNKNOWN);
+        when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication);
+
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(mIccCardProxyUT.getState(), State.NOT_READY);
+    }
+
+    @Test
+    @Ignore
+    @FlakyTest
+    @SmallTest
+    public void testAppReady() {
+        testPowerOn();
+        when(mUiccCard.getCardState()).thenReturn(CardState.CARDSTATE_PRESENT);
+        mIccCardProxyUT.sendMessage(mIccCardProxyUT.obtainMessage(EVENT_ICC_CHANGED));
+        when(mUiccCardApplication.getState()).thenReturn(AppState.APPSTATE_READY);
+        when(mUiccCard.getApplication(anyInt())).thenReturn(mUiccCardApplication);
+
+        waitForMs(SCARY_SLEEP_MS);
+        assertEquals(mIccCardProxyUT.getState(), State.READY);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
index c99bd75..9510619 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccPhoneBookInterfaceManagerTest.java
@@ -89,7 +89,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mIccPhoneBookInterfaceManagerHandler.quitSafely();
+        mIccPhoneBookInterfaceManagerHandler.quit();
         super.tearDown();
     }
     @Test
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
index 1a5daeb..60f5f6f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardApplicationTest.java
@@ -110,7 +110,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mTestHandlerThread.quitSafely();
+        mTestHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
index 0436ff4..a7c3c48 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCardTest.java
@@ -15,26 +15,33 @@
  */
 package com.android.internal.telephony.uicc;
 
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.android.internal.telephony.TelephonyTest;
-import com.android.internal.telephony.cat.CatService;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import static org.mockito.Mockito.*;
+import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.isA;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+import com.android.internal.telephony.cat.CatService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
 
 public class UiccCardTest extends TelephonyTest {
     private UiccCard mUicccard;
@@ -69,7 +76,7 @@
         @Override
         public void onLooperPrepared() {
             mUicccard = new UiccCard(mContextFixture.getTestDouble(),
-                                     mSimulatedCommands, mIccCardStatus);
+                                     mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
             /* create a custom handler for the Handler Thread */
             mHandler = new Handler(mTestHandlerThread.getLooper()) {
                 @Override
@@ -134,7 +141,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mTestHandlerThread.quitSafely();
+        mTestHandlerThread.quit();
         super.tearDown();
     }
 
@@ -201,9 +208,9 @@
         waitForMs(50);
 
         assertTrue(mUicccard.areCarrierPriviligeRulesLoaded());
-        verify(mSimulatedCommandsVerifier, times(1)).iccOpenLogicalChannel(isA(String.class),
-                isA(Message.class));
-        verify(mSimulatedCommandsVerifier, times(1)).iccTransmitApduLogicalChannel(
+        verify(mSimulatedCommandsVerifier, times(2)).iccOpenLogicalChannel(isA(String.class),
+                anyInt(), isA(Message.class));
+        verify(mSimulatedCommandsVerifier, times(2)).iccTransmitApduLogicalChannel(
                 eq(mChannelId), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyString(),
                 isA(Message.class)
         );
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
new file mode 100644
index 0000000..8bb5d8c
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRulesTest.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
+
+import android.content.pm.Signature;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.telephony.TelephonyTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class UiccCarrierPrivilegeRulesTest extends TelephonyTest {
+    private UiccCarrierPrivilegeRules mUiccCarrierPrivilegeRules;
+    public UiccCarrierPrivilegeRulesTest() {
+        super();
+    }
+    private UiccCarrierPrivilegeRulesHandlerThread mTestHandlerThread;
+    private Handler mHandler;
+
+    private static final int EVENT_OPEN_LOGICAL_CHANNEL_DONE = 1;
+    private static final int EVENT_TEST_DONE = 2;
+
+    @Mock
+    private UiccCard mUiccCard;
+
+    private class UiccCarrierPrivilegeRulesHandlerThread extends HandlerThread {
+
+        private UiccCarrierPrivilegeRulesHandlerThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            /* create a custom handler for the Handler Thread */
+            mHandler = new Handler(mTestHandlerThread.getLooper()) {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
+                            /* Upon handling this event, new CarrierPrivilegeRule
+                            will be created with the looper of HandlerThread */
+                            mUiccCarrierPrivilegeRules = new UiccCarrierPrivilegeRules(
+                                    mUiccCard, mHandler.obtainMessage(EVENT_TEST_DONE));
+                            break;
+                        case EVENT_TEST_DONE:
+                            setReady(true);
+                            break;
+                        default:
+                            logd("Unknown Event " + msg.what);
+                    }
+                }
+            };
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mTestHandlerThread = new UiccCarrierPrivilegeRulesHandlerThread(TAG);
+        mTestHandlerThread.start();
+
+        waitUntilReady();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTestHandlerThread.quit();
+        super.tearDown();
+        mUiccCarrierPrivilegeRules = null;
+    }
+
+    private void testHelper(String hexString) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_Normal() {
+        /**
+         * FF40 45
+         *   E2 43
+         *      E1 35
+         *         C1 14 ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 0A
+         *         DB 08 0000000000000001
+         */
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(2, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_With4FD0D1() {
+        /**
+         * FF40 34
+         *   E2 32
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 10
+         *         D0 01 01
+         *         D1 01 01
+         *         DB 08 00 00 00 00 00 00 00 01
+         */
+        final String hexString = "FF4034E232E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED84"
+                + "93C313A137F89FA2765E310D00101D10101DB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_With4FD0() {
+        /**
+         * FF40 31
+         *   E2 2F
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 0D
+         *         D0 01 01
+         *         DB 08 00 00 00 00 00 00 00 01
+         */
+        final String hexString = "FF4031E22FE11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+                + "137F89FA2765E30DD00101DB080000000000000001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_TwoMessages() {
+        /**
+         * FF40 68
+         *   E2 39
+         *      E1 2B
+         *         4F 06 FFFFFFFFFFFF
+         *         C1 02 B61B
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 0A
+         *         D0 01 01
+         *         D1 01 01
+         *         DB 02 0001
+         *   E2 2B
+         *      E1 23
+         *         C1 02 ABCD
+         *         CA 1D 636F6D2E676F6F676C652E616E64726F69642E617070732E6D79617070
+         *      E3 04
+         *         DB 02 0001
+         */
+        final String hexString =
+                "FF4068E239E12B4F06FFFFFFFFFFFFC102B61BCA1D636F6D2E676F6F676C652E616E64726F69642"
+                        + "E617070732E6D79617070E30AD00101D10101DB020001E22BE123C102ABCDCA1D636F"
+                        + "6D2E676F6F676C652E616E64726F69642E617070732E6D79617070E304DB020001";
+
+        testHelper(hexString);
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(4, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature1 = new Signature("b61b");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature1,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1));
+        Signature signature2 = new Signature("abcd");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature2,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_InvalidRulesWith4F00() {
+        /**
+         * FF40 24
+         *   E2 22
+         *      E1 18
+         *         4F 00
+         *         C1 14 75C073AFD219AEB221948E828F066E778ADFDF23
+         *      E3 06
+         *         D0 01 01
+         *         D1 01 01
+         */
+        final String hexString = "FF4024E222E1184F00C11475C073AFD219AEB221948E828F066E778ADFDF23"
+                + "E306D00101D10101";
+
+        testHelper(hexString);
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    @Test
+    @SmallTest
+    public void testParseRule_InvalidRulesWithoutDB() {
+        /**
+         * FF40 2A
+         *   E2 28
+         *      E1 1E
+         *         4F 06 FF FF FF FF FF FF
+         *         C1 14 B6 1B E3 4A D2 C2 0D 7A FE D8 49 3C 31 3A 13 7F 89 FA 27 65
+         *      E3 06
+         *         D0 01 01
+         *         D1 01 01
+         */
+        final String hexString = "FF402AE228E11E4F06FFFFFFFFFFFFC114B61BE34AD2C20D7AFED8493C313A"
+                + "137F89FA2765E306D00101D10101";
+
+        testHelper(hexString);
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(0, mUiccCarrierPrivilegeRules.getPackageNames().size());
+    }
+
+    private static final String ARAM = "A00000015141434C00";
+    private static final String ARAD = "A00000015144414300";
+    private static final String PKCS15_AID = "A000000063504B43532D3135";
+
+    @Test
+    @SmallTest
+    public void testAID_OnlyARAM() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                if (aid.equals(ARAM)) {
+                    AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                    message.obj = ar;
+                    message.arg2 = 1;
+                    message.sendToTarget();
+                } else {
+                    AsyncResult ar = new AsyncResult(null, null, null);
+                    message.obj = ar;
+                    message.sendToTarget();
+                }
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testAID_OnlyARAD() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                if (aid.equals(ARAD)) {
+                    AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                    message.obj = ar;
+                    message.arg2 = 0;
+                    message.sendToTarget();
+                } else {
+                    AsyncResult ar = new AsyncResult(null, null, null);
+                    message.obj = ar;
+                    message.arg2 = 1;
+                    message.sendToTarget();
+                }
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+
+    @Test
+    @SmallTest
+    public void testAID_BothARAMandARAD() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                message.obj = ar;
+                if (aid.equals(ARAD)) {
+                    message.arg2 = 0;
+                } else {
+                    message.arg2 = 1;
+                }
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00, IccUtils.hexStringToBytes(hexString));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                anyInt(), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(2, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1));
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(1)));
+    }
+
+    @Test
+    @SmallTest
+    public void testAID_NeitherARAMorARAD() {
+        final String hexString =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30ADB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                AsyncResult ar = new AsyncResult(null, null, null);
+                if (aid.equals(ARAM)) {
+                    message.arg2 = 1;
+                } else if (aid.equals(ARAD)) {
+                    message.arg2 = 0;
+                } else {
+                    // PKCS15
+                    ar = new AsyncResult(null, null, new Throwable());
+                }
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(!mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+    }
+
+    private static final int P2 = 0x40;
+    private static final int P2_EXTENDED_DATA = 0x60;
+    @Test
+    @SmallTest
+    public void testAID_RetransmitLogicalChannel() {
+        final String hexString1 =
+                "FF4045E243E135C114ABCD92CBB156B280FA4E1429A6ECEEB6E5C1BFE4CA1D636F6D2E676F6F676"
+                        + "C652E616E64726F69642E617070732E6D79617070E30A";
+
+        final String hexString2 =
+                "DB080000000000000001";
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                String aid = (String) invocation.getArguments()[0];
+                Message message = (Message) invocation.getArguments()[2];
+                if (aid.equals(ARAD)) {
+                    AsyncResult ar = new AsyncResult(null, new int[]{0}, null);
+                    message.obj = ar;
+                    message.arg2 = 0;
+                    message.sendToTarget();
+                } else {
+                    AsyncResult ar = new AsyncResult(null, null, null);
+                    message.obj = ar;
+                    message.arg2 = 1;
+                    message.sendToTarget();
+                }
+                return null;
+            }
+        }).when(mUiccCard).iccOpenLogicalChannel(anyString(), anyInt(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00,
+                        IccUtils.hexStringToBytes(hexString1));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                eq(P2), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[7];
+                IccIoResult iir = new IccIoResult(0x90, 0x00,
+                        IccUtils.hexStringToBytes(hexString2));
+                AsyncResult ar = new AsyncResult(null, iir, null);
+                message.obj = ar;
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccTransmitApduLogicalChannel(anyInt(), anyInt(), anyInt(), anyInt(),
+                eq(P2_EXTENDED_DATA), anyInt(), anyString(), any(Message.class));
+
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Message message = (Message) invocation.getArguments()[1];
+                message.sendToTarget();
+                return null;
+            }
+        }).when(mUiccCard).iccCloseLogicalChannel(anyInt(), any(Message.class));
+
+
+        Message mCardOpenLogicalChannel = mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE);
+        setReady(false);
+        mCardOpenLogicalChannel.sendToTarget();
+        waitUntilReady();
+
+        assertTrue(mUiccCarrierPrivilegeRules.hasCarrierPrivilegeRules());
+        assertEquals(1, mUiccCarrierPrivilegeRules.getPackageNames().size());
+        assertEquals("com.google.android.apps.myapp",
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0));
+        Signature signature = new Signature("abcd92cbb156b280fa4e1429a6eceeb6e5c1bfe4");
+        assertEquals(0, mUiccCarrierPrivilegeRules.getCarrierPrivilegeStatus(signature,
+                mUiccCarrierPrivilegeRules.getPackageNames().get(0)));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
index 12c2690..e62700a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccControllerTest.java
@@ -96,7 +96,7 @@
 
     @After
     public void tearDown() throws Exception {
-        mUiccControllerHandlerThread.quitSafely();
+        mUiccControllerHandlerThread.quit();
         super.tearDown();
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
index fb48619..2f44b0a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccStateChangedLauncherTest.java
@@ -16,6 +16,16 @@
 
 package com.android.internal.telephony.uicc;
 
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyObject;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -35,15 +45,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.anyObject;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 public class UiccStateChangedLauncherTest extends TelephonyTest {
     private static final String TAG = UiccStateChangedLauncherTest.class.getName();
@@ -101,7 +102,7 @@
         // The first broadcast should be sent after initialization.
         UiccCard[] cards = new UiccCard[CARD_COUNT];
         cards[0] = new UiccCard(mContext, mSimulatedCommands,
-                makeCardStatus(CardState.CARDSTATE_PRESENT));
+                makeCardStatus(CardState.CARDSTATE_PRESENT), 0 /* phoneId */);
         when(UiccController.getInstance().getUiccCards()).thenReturn(cards);
         uiccLauncher.handleMessage(msg);