[automerger] Fixed Invalid Pdu Issue am: 4b938358de am: fc2a2d071a am: ca00d5d151 am: 23de93c197 am: 909c1606d3 am: bfb0076f03 skipped: c8ec5c3e94

Change-Id: I006349baf849bde28a904ef5d25419495b5b2470
diff --git a/proto/telephony.proto b/proto/telephony.proto
index 0663759..70d210b 100644
--- a/proto/telephony.proto
+++ b/proto/telephony.proto
@@ -1018,6 +1018,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
diff --git a/src/java/com/android/internal/telephony/CallFailCause.java b/src/java/com/android/internal/telephony/CallFailCause.java
index c597927..d532175 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;
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 57cbeb8..cac701a 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -27,6 +27,7 @@
     public static final int ERROR_INVALID = -1;
 
     public static final int ERROR_DISCONNECTED = 1;
+    public static final int ERROR_POWER_OFF = 2;
 
     public
     CallStateException()
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..735bfe2
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -0,0 +1,192 @@
+/*
+ * 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;
+
+/**
+ * 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.getConfig();
+            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)
+                .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);
+    }
+}
\ No newline at end of file
diff --git a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
index 4f99c9f..f340e80 100755
--- a/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaCallTracker.java
@@ -88,6 +88,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;
@@ -303,6 +305,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) {
@@ -715,7 +719,7 @@
         }
         if (mState != oldState) {
             mPhone.notifyPhoneStateChanged();
-            TelephonyMetrics.getInstance().writePhoneState(mPhone.getPhoneId(), mState);
+            mMetrics.writePhoneState(mPhone.getPhoneId(), mState);
         }
     }
 
@@ -863,9 +867,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 +893,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 +986,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 +1008,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 +1023,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 +1038,7 @@
             } else {
                 hoConnection.onDisconnect(DisconnectCause.NOT_VALID);
             }
+            // TODO: Do we need to update these hoConnections in Metrics ?
             it.remove();
         }
 
@@ -1071,6 +1077,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 +1090,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 +1168,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 +1217,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 +1231,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 +1252,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 +1284,7 @@
         for (int i = 0; i < count; i++) {
             GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
             if (cn.getGsmCdmaIndex() == index) {
+                mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex());
                 mCi.hangupConnection(index, obtainCompleteMessage());
                 return;
             }
@@ -1263,6 +1298,7 @@
             int count = call.mConnections.size();
             for (int i = 0; i < count; i++) {
                 GsmCdmaConnection cn = (GsmCdmaConnection)call.mConnections.get(i);
+                mMetrics.writeRilHangup(mPhone.getPhoneId(), cn, cn.getGsmCdmaIndex());
                 mCi.hangupConnection(cn.getGsmCdmaIndex(), obtainCompleteMessage());
             }
         } catch (CallStateException ex) {
@@ -1416,6 +1452,7 @@
                 updatePhoneState();
 
                 mPhone.notifyPreciseCallStateChanged();
+                mMetrics.writeRilCallList(mPhone.getPhoneId(), mDroppedDuringPoll);
                 mDroppedDuringPoll.clear();
             break;
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index 1530ada..868955c 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -441,6 +441,7 @@
             case CallFailCause.ACM_LIMIT_EXCEEDED:
                 return DisconnectCause.LIMIT_EXCEEDED;
 
+            case CallFailCause.OPERATOR_DETERMINED_BARRING:
             case CallFailCause.CALL_BARRED:
                 return DisconnectCause.CALL_BARRED;
 
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index ffcf6c7..07afe0c 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1121,6 +1121,14 @@
                 && 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");
+        }
         if (DBG) logd("Trying (non-IMS) CS call");
 
         if (isPhoneTypeGsm()) {
@@ -1857,6 +1865,19 @@
         }
     }
 
+    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) {
         mMmiCompleteRegistrants.notifyRegistrants(
             new AsyncResult(null, mmi, null));
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index d52e71b..b7eb32a 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -67,7 +67,6 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.EventLog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -80,7 +79,10 @@
 import java.io.ByteArrayOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import android.util.EventLog;
 
 /**
  * This class broadcasts incoming SMS messages to interested apps after storing them in
@@ -115,9 +117,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;
@@ -127,6 +139,7 @@
     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 " +
@@ -675,7 +688,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;
@@ -683,7 +697,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());
         }
@@ -729,6 +743,7 @@
         int messageCount = tracker.getMessageCount();
         byte[][] pdus;
         int destPort = tracker.getDestPort();
+        boolean block = false;
 
         // Do not process when the message count is invalid.
         if (messageCount <= 0) {
@@ -737,14 +752,16 @@
                     "72298611" /* buganizer id */,
                     -1 /* uid */,
                     String.format(
-                        "processMessagePart: invalid messageCount = %d",
-                        messageCount));
+                            "processMessagePart: invalid messageCount = %d",
+                            messageCount));
+
             return 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;
@@ -773,7 +790,8 @@
                 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();
 
                     // The invalid PDUs can be received and stored in the raw table. The range
                     // check ensures the process not crash even if the seqNumber in the
@@ -784,25 +802,39 @@
                                 "72298611" /* buganizer id */,
                                 -1 /* uid */,
                                 String.format(
-                                    "processMessagePart: invalid seqNumber = %d, "
-                                    + "messageCount = %d",
-                                    index + tracker.getIndexOffset(),
-                                    messageCount));
+                                        "processMessagePart: invalid seqNumber = %d, messageCount = %d",
+                                        index + tracker.getIndexOffset(),
+                                        messageCount));
                         continue;
                     }
 
-                    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);
@@ -856,7 +888,7 @@
             }
         }
 
-        if (BlockChecker.isBlocked(mContext, tracker.getAddress())) {
+        if (block) {
             deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs(),
                     DELETE_PERMANENTLY);
             return false;
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index 89ea681..9db83ab 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -38,10 +38,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,6 +49,16 @@
     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;
+
     /** Destination port flag bit for no destination port. */
     private static final int DEST_PORT_FLAG_NO_PORT = (1 << 16);
 
@@ -73,10 +82,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 +95,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 +114,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 +166,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
@@ -203,6 +220,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 +259,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);
@@ -287,6 +306,10 @@
         return mAddress;
     }
 
+    public String getDisplayAddress() {
+        return mDisplayAddress;
+    }
+
     public String getMessageBody() {
         return mMessageBody;
     }
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index c01be85..e7cfe88 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -50,10 +50,12 @@
 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.DcTracker;
+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;
@@ -2072,23 +2074,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;
     }
 
     /**
@@ -2828,6 +2847,9 @@
      * Return if UT capability of ImsPhone is enabled or not
      */
     public boolean isUtEnabled() {
+        if (mImsPhone != null) {
+            return mImsPhone.isUtEnabled();
+        }
         return false;
     }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index ae07e93..2e50b2f 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1076,8 +1076,6 @@
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        mMetrics.writeRilDial(mInstanceId, rr.mSerial, clirMode, uusInfo);
-
         send(rr);
     }
 
@@ -1133,8 +1131,6 @@
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " +
                 gsmIndex);
 
-        mMetrics.writeRilHangup(mInstanceId, rr.mSerial, gsmIndex);
-
         rr.mParcel.writeInt(1);
         rr.mParcel.writeInt(gsmIndex);
 
@@ -1149,8 +1145,6 @@
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        mMetrics.writeRilHangup(mInstanceId, rr.mSerial, -1);
-
         send(rr);
     }
 
@@ -1163,8 +1157,6 @@
                                         result);
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
-        mMetrics.writeRilHangup(mInstanceId, rr.mSerial, -1);
-
         send(rr);
     }
 
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index f42304e..58eec0c 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -155,6 +155,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();
 
@@ -283,6 +284,7 @@
     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 class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
         public final AtomicInteger mPreviousSubId =
@@ -422,6 +424,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.
      */
@@ -527,6 +531,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,
@@ -548,6 +555,17 @@
         mPhone.notifyOtaspChanged(OTASP_UNINITIALIZED);
 
         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
@@ -2311,12 +2329,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) {
@@ -2688,6 +2708,10 @@
             mNitzUpdatedTime = false;
         }
 
+        if (hasDeregistered) {
+            mNetworkDetachedRegistrants.notifyRegistrants();
+        }
+
         if (hasChanged) {
             String operatorNumeric;
 
@@ -2842,6 +2866,10 @@
                 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;
@@ -2916,6 +2944,10 @@
             mNetworkAttachedRegistrants.notifyRegistrants();
         }
 
+        if (hasDeregistered) {
+            mNetworkDetachedRegistrants.notifyRegistrants();
+        }
+
         if (hasChanged) {
             updateSpnDisplay();
 
@@ -3155,6 +3187,10 @@
             mNetworkAttachedRegistrants.notifyRegistrants();
         }
 
+        if (hasDeregistered) {
+            mNetworkDetachedRegistrants.notifyRegistrants();
+        }
+
         if (hasChanged) {
             updateSpnDisplay();
 
@@ -4268,11 +4304,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
@@ -4639,6 +4695,21 @@
         }
     }
 
+    private void dumpCellInfoList(PrintWriter pw) {
+        pw.print(" mLastCellInfoList={");
+        if(mLastCellInfoList != null) {
+            boolean first = true;
+            for(CellInfo info : mLastCellInfoList) {
+               if(first == false) {
+                   pw.print(",");
+               }
+               first = false;
+               pw.print(info.toString());
+            }
+        }
+        pw.println("}");
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("ServiceStateTracker:");
         pw.println(" mSubId=" + mSubId);
@@ -4658,6 +4729,8 @@
         pw.println(" mCellLoc=" + mCellLoc);
         pw.println(" mNewCellLoc=" + mNewCellLoc);
         pw.println(" mLastCellInfoListTime=" + mLastCellInfoListTime);
+        dumpCellInfoList(pw);
+        pw.flush();
         pw.println(" mPreferredNetworkType=" + mPreferredNetworkType);
         pw.println(" mMaxDataCalls=" + mMaxDataCalls);
         pw.println(" mNewMaxDataCalls=" + mNewMaxDataCalls);
@@ -4731,6 +4804,11 @@
         ipw.increaseIndent();
         mRatLog.dump(fd, ipw, args);
         ipw.decreaseIndent();
+
+        ipw.println(" Radio power Log:");
+        ipw.increaseIndent();
+        mRadioPowerLog.dump(fd, ipw, args);
+        ipw.decreaseIndent();
     }
 
     public boolean isImsRegistered() {
diff --git a/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java b/src/java/com/android/internal/telephony/SmsBroadcastUndelivered.java
index dde4d2d..7f87e43 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;
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index e12d02a..9134691 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -92,19 +92,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);
     }
 
     /**
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/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index bc318e6..ceb9eda 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -112,18 +112,6 @@
      * */
     public boolean permanentFailed = false;
 
-    /**
-     * Metered APN types which would be accounted for in data usage. This is a map of subId ->
-     * set of metered apn strings for the carrier.
-     */
-    private static HashMap<Integer, HashSet<String>> sMeteredApnTypes = new HashMap<>();
-
-    /**
-     * Metered Roaming APN types which would be accounted for in data usage. This is a map of
-     * subId -> set of metered roaming apn strings for the carrier.
-     */
-    private static HashMap<Integer, HashSet<String>> sMeteredRoamingApnTypes = new HashMap<>();
-
     public ApnSetting(int id, String numeric, String carrier, String apn,
             String proxy, String port,
             String mmsc, String mmsProxy, String mmsPort,
@@ -352,10 +340,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;
@@ -413,64 +403,55 @@
     public static boolean isMeteredApnType(String type, Context context, int subId,
                                            boolean isRoaming) {
 
-        HashMap<Integer, HashSet<String>> meteredApnTypesCache = (isRoaming) ?
-                sMeteredApnTypes : sMeteredRoamingApnTypes;
         String carrierConfig = (isRoaming) ?
                 CarrierConfigManager.KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS :
                 CarrierConfigManager.KEY_CARRIER_METERED_APN_TYPES_STRINGS;
 
-        synchronized (meteredApnTypesCache) {
-            HashSet<String> meteredApnSet = meteredApnTypesCache.get(subId);
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager == null) {
+            Rlog.e(LOG_TAG, "Carrier config service is not available");
+            return true;
+        }
 
-            // In case of cache miss, we need to look up the settings from carrier config.
-            if (meteredApnSet == null) {
-                // Retrieve the metered APN types from carrier config
-                CarrierConfigManager configManager = (CarrierConfigManager)
-                        context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-                if (configManager == null) {
-                    Rlog.e(LOG_TAG, "Carrier config service is not available");
-                    return true;
-                }
+        PersistableBundle b = configManager.getConfigForSubId(subId);
+        if (b == null) {
+            Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
+            return true;
+        }
 
-                PersistableBundle b = configManager.getConfigForSubId(subId);
-                if (b == null) {
-                    Rlog.e(LOG_TAG, "Can't get the config. subId = " + subId);
-                    return true;
-                }
+        String[] meteredApnTypes = b.getStringArray(carrierConfig);
+        if (meteredApnTypes == null) {
+            Rlog.e(LOG_TAG, carrierConfig +  " is not available. " + "subId = " + subId);
+            return true;
+        }
 
-                String[] meteredApnTypes = b.getStringArray(carrierConfig);
-                if (meteredApnTypes == null) {
-                    Rlog.e(LOG_TAG, carrierConfig +  " is not available. " + "subId = " + subId);
-                    return true;
-                }
+        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);
+        }
 
-                meteredApnSet = new HashSet<String>(Arrays.asList(meteredApnTypes));
-                meteredApnTypesCache.put(subId, meteredApnSet);
-                if (DBG) {
-                    Rlog.d(LOG_TAG, "For subId = " + subId + ", metered APN types are " +
-                            Arrays.toString(meteredApnSet.toArray()) +
-                            " isRoaming: " + isRoaming);
-                }
-            }
-            // 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 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);
+            return true;
+        }
+
+        if (meteredApnSet.contains(type)) {
+            if (DBG) Rlog.d(LOG_TAG, type + " is metered. isRoaming: " + isRoaming);
+            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);
                 return true;
             }
-
-            if (meteredApnSet.contains(type)) {
-                if (DBG) Rlog.d(LOG_TAG, type + " is metered. isRoaming: " + isRoaming);
-                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);
-                    return true;
-                }
-            }
         }
+
         if (DBG) Rlog.d(LOG_TAG, type + " is not metered. isRoaming: " + isRoaming);
         return false;
     }
@@ -505,7 +486,10 @@
                 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) &&
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index e42540e..fcccfef 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -47,6 +47,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -55,6 +56,7 @@
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
 import android.provider.Telephony;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellLocation;
 import android.telephony.PcoData;
 import android.telephony.Rlog;
@@ -310,6 +312,14 @@
                     log("WIFI_STATE_CHANGED_ACTION: enabled=" + enabled
                             + " mIsWifiConnected=" + mIsWifiConnected);
                 }
+            } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
+                CarrierConfigManager configMgr = (CarrierConfigManager)
+                        mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                if (configMgr != null) {
+                    PersistableBundle cfg = configMgr.getConfigForSubId(mPhone.getSubId());
+                    if (cfg != null) mAllowUserEditTetherApn =
+                            cfg.getBoolean(CarrierConfigManager.KEY_EDITABLE_TETHER_APN_BOOL);
+                }
             } else {
                 if (DBG) log("onReceive: Unknown action=" + action);
             }
@@ -612,6 +622,22 @@
     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;
+
+    /**
+     * Whether carrier allow user edited tether APN. Updated by carrier config
+     * KEY_EDITABLE_TETHER_APN_BOOL
+     * If true, APN with dun type from database will be used, see fetchDunApn for details.
+     */
+    private boolean mAllowUserEditTetherApn = false;
+
+    /**
      * Handles changes to the APN db.
      */
     private class ApnChangeObserver extends ContentObserver {
@@ -679,6 +705,7 @@
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(INTENT_DATA_STALL_ALARM);
         filter.addAction(INTENT_PROVISIONING_APN_ALARM);
+        filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
 
         // TODO - redundent with update call below?
         mDataEnabledSettings.setUserDataEnabled(getDataEnabled());
@@ -1029,6 +1056,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(
@@ -1818,50 +1846,56 @@
             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, APN database if
+        // carrier allows it, and config_tether_apndata resource.
+        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);
+        } else if (mAllowUserEditTetherApn) {
+            for (ApnSetting apn : mAllApnSettings) {
+                if (apn.canHandleType(PhoneConstants.APN_TYPE_DUN)) {
+                    dunCandidates.add(apn);
+                }
+            }
+            if (VDBG) log("fetchDunApn: dunCandidates from database: " + dunCandidates);
+        }
+        // If TETHER_DUN_APN isn't set or
+        // mAllowUserEditTetherApn is true but 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);
+            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);
+        }
+
+        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;
     }
 
@@ -2124,15 +2158,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;
@@ -2384,6 +2418,10 @@
                     // Tear down all metered apns
                     cleanUpAllConnections(true, Phone.REASON_CARRIER_ACTION_DISABLE_METERED_APN);
                 } else {
+                    // Re-evauluate Otasp state
+                    int otaspState = mPhone.getServiceStateTracker().getOtasp();
+                    mPhone.notifyOtaspChanged(otaspState);
+
                     teardownRestrictedMeteredConnections();
                     setupDataOnConnectableApns(Phone.REASON_DATA_ENABLED);
                 }
@@ -2617,6 +2655,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.
@@ -2737,6 +2785,10 @@
 
         if (!mDataEnabledSettings.isUserDataEnabled()) return;
 
+        // Note onRoamingOff will be called immediately when DcTracker registerForDataRoamingOff,
+        // and will be called again if SST calls mDataRoamingOffRegistrants.notify().
+        setDataProfilesAsNeeded();
+
         if (getDataOnRoamingEnabled() == false) {
             notifyOffApnsOfAvailability(Phone.REASON_ROAMING_OFF);
             setupDataOnConnectableApns(Phone.REASON_ROAMING_OFF);
@@ -3269,27 +3321,29 @@
     }
 
     private void setDataProfilesAsNeeded() {
-        if (DBG) log("setDataProfilesAsNeeded");
+        if (DBG) log("setDataProfilesAsNeeded mSetDataProfileStatus: " + mSetDataProfileStatus);
         if (mAllApnSettings != null && !mAllApnSettings.isEmpty()) {
             ArrayList<DataProfile> dps = new ArrayList<DataProfile>();
+            // Note getDataRoaming is also false if data not registered
+            boolean isRoaming = mPhone.getServiceState().getDataRoaming();
+            // If has set profile with home, and isRoaming is also false, no need to resend
+            // Also skip if has set profile with roaming and isRoaming is true.
+            if ((mSetDataProfileStatus == 1 && !isRoaming) ||
+                (mSetDataProfileStatus == 2 && isRoaming)) {
+                return;
+            }
             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, isRoaming);
+                    // ArrayList.contains will call object.equals
+                    if (!dps.contains(dp)) {
                         dps.add(dp);
                     }
                 }
             }
             if(dps.size() > 0) {
                 mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[0]), null);
+                mSetDataProfileStatus = isRoaming ? 2 : 1;
             }
         }
     }
@@ -4204,6 +4258,7 @@
         pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
         pw.println(" mIsScreenOn=" + mIsScreenOn);
         pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
+        pw.println(" mSetDataProfileStatus=" + mSetDataProfileStatus);
         pw.flush();
         pw.println(" ***************************************");
         DcController dcc = mDcc;
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 425a469..c63a9de 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;
@@ -1238,6 +1237,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));
                 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index edd2c9d..0e5fe5e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -802,7 +802,7 @@
                         dialingNumber,
                         serviceClass,
                         timerSeconds,
-                        onComplete);
+                        resp);
             } catch (ImsException e) {
                 sendErrorResponse(onComplete, e);
             }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index c5378d1..2a48d4e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -65,6 +65,7 @@
 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.telephony.Call;
 import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.CallTracker;
@@ -260,6 +261,7 @@
     private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
 
     private boolean mIsInEmergencyCall = false;
+    private boolean mIsDataEnabled = false;
 
     private int pendingCallClirMode;
     private int mPendingCallVideoState;
@@ -268,6 +270,7 @@
     private boolean mSwitchingFgAndBgCalls = false;
     private ImsCall mCallExpectedToResume = null;
     private boolean mAllowEmergencyVideoCalls = false;
+    private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
 
     /**
      * Listeners to changes in the phone state.  Intended for use by other interested IMS components
@@ -314,6 +317,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 +332,17 @@
      */
     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;
+
     //***** Events
 
 
@@ -571,6 +592,10 @@
                 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
         mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
+        mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
+        mSupportPauseVideo = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
 
         String[] mappings = carrierConfig
                 .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
@@ -1646,6 +1671,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
@@ -2170,16 +2202,10 @@
                     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();
 
@@ -2734,7 +2760,14 @@
     private void onDataEnabledChanged(boolean enabled, int reason) {
 
         log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
+
         ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()).setDataEnabled(enabled);
+        mIsDataEnabled = enabled;
+
+        if (mIgnoreDataEnabledChangedForVideoCalls) {
+            log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " due to carrier policy.");
+            return;
+        }
 
         if (!enabled) {
             int reasonCode;
@@ -2767,9 +2800,12 @@
                                     TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
                         }
                         modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
+                    } else if (mSupportPauseVideo) {
+                        // The carrier supports video pause signalling, so pause the video.
+                        mShouldUpdateImsConfigOnDisconnect = true;
+                        conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
                     } else {
-                        // If the carrier does not support downgrading to voice, the only choice we
-                        // have is to terminate the call.
+                        // At this point the only choice we have is to terminate the call.
                         try {
                             imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
                         } catch (ImsException ie) {
@@ -2778,11 +2814,27 @@
                     }
                 }
             }
+        } 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("onDataEnabledChanged - 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;
         }
 
-        // 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);
+        if (!mShouldUpdateImsConfigOnDisconnect) {
+            // 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);
+        }
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 96ac877..99526c4 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -100,15 +100,7 @@
      */
     private boolean mShouldIgnoreVideoStateChanges = false;
 
-    /**
-     * 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.
-     */
-    private boolean mIsWifiStateFromExtras = false;
+    private ImsVideoCallProviderWrapper mImsVideoCallProviderWrapper;
 
     //***** Event Constants
     private static final int EVENT_DTMF_DONE = 1;
@@ -179,8 +171,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).
@@ -674,13 +664,11 @@
         }
 
         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
@@ -879,28 +867,6 @@
     }
 
     /**
-     * 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;
-        }
-
-        Rlog.d(LOG_TAG, "updateWifiState: " + mOwner.isVowifiEnabled());
-        if (isWifi() != mOwner.isVowifiEnabled()) {
-            setWifi(mOwner.isVowifiEnabled());
-            return true;
-        }
-        return false;
-    }
-
-    /**
      * Updates the wifi state based on the {@link ImsCallProfile#EXTRA_CALL_RAT_TYPE}.
      * The call is considered to be a WIFI call if the extra value is
      * {@link ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}.
@@ -911,10 +877,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) {
@@ -1036,6 +998,15 @@
         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
@@ -1087,4 +1058,46 @@
             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);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
index 1cf0825..42d38a4 100644
--- a/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/CallSessionEventBuilder.java
@@ -21,6 +21,7 @@
 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.TelephonyCallSession.Event.RilCall;
 import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
 import com.android.internal.telephony.TelephonyProto.TelephonySettings;
 
@@ -124,4 +125,9 @@
         mEvent.setNitzTimestampMillis(timestamp);
         return this;
     }
+
+    public CallSessionEventBuilder setRilCalls(RilCall[] rilCalls) {
+        mEvent.calls = rilCalls;
+        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 92e3f0a..eddb1c0 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -21,11 +21,14 @@
 import android.telephony.ServiceState;
 import android.telephony.TelephonyHistogram;
 import android.util.Base64;
+import android.util.Log;
 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.Call;
+import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RIL;
 import com.android.internal.telephony.RILConstants;
@@ -36,6 +39,9 @@
 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.TelephonyCallSession.Event.RilCall;
+import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.RilCall.Type.*;
+import com.android.internal.telephony.TelephonyProto.TelephonyCallSession.Event.CallState.*;
 import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
 import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
 import com.android.internal.telephony.TelephonyProto.TelephonyEvent.RilSetupDataCall;
@@ -58,6 +64,7 @@
 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;
@@ -1012,22 +1019,106 @@
     }
 
     /**
+     * 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");
+        }
+    }
+
+    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].setIndex(i);
+            convertConnectionToRilCall(mConnections.get(i), calls[i]);
+        }
+        return calls;
+    }
+
+    private void convertConnectionToRilCall(GsmCdmaConnection conn, RilCall call) {
+        if (conn.isIncoming()) {
+            call.setType(TelephonyCallSession.Event.RilCall.Type.MT);
+        } else {
+            call.setType(TelephonyCallSession.Event.RilCall.Type.MO);
+        }
+        switch (conn.getState()) {
+            case IDLE:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_IDLE);
+                break;
+            case ACTIVE:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_ACTIVE);
+                break;
+            case HOLDING:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_HOLDING);
+                break;
+            case DIALING:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_DIALING);
+                break;
+            case ALERTING:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_ALERTING);
+                break;
+            case INCOMING:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_INCOMING);
+                break;
+            case WAITING:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_WAITING);
+                break;
+            case DISCONNECTED:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_DISCONNECTED);
+                break;
+            case DISCONNECTING:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_DISCONNECTING);
+                break;
+            default:
+                call.setState(TelephonyCallSession.Event.CallState.CALL_UNKNOWN);
+                break;
+        }
+        call.setCallEndReason(conn.getDisconnectCause());
+        call.setIsMultiparty(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].setIndex(-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");
+        }
     }
 
     /**
@@ -1047,19 +1138,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].setIndex(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");
         }
     }
 
@@ -1072,7 +1167,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)
@@ -1090,7 +1185,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)
@@ -1180,7 +1275,7 @@
                                               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)
@@ -1295,7 +1390,7 @@
 
         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) {
                 finishCallSession(callSession);
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 061b28f..970bf35 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -30,21 +30,21 @@
     static final int EF_EXT2 = 0x6F4B;
     static final int EF_EXT3 = 0x6F4C;
     static final int EF_EXT5 = 0x6F4E;
-    static final int EF_EXT6 = 0x6fc8;   // Ext record for EF[MBDN]
+    static final int EF_EXT6 = 0x6FC8;   // Ext record for EF[MBDN]
     static final int EF_MWIS = 0x6FCA;
-    static final int EF_MBDN = 0x6fc7;
-    static final int EF_PNN = 0x6fc5;
-    static final int EF_OPL = 0x6fc6;
+    static final int EF_MBDN = 0x6FC7;
+    static final int EF_PNN = 0x6FC5;
+    static final int EF_OPL = 0x6FC6;
     static final int EF_SPN = 0x6F46;
     static final int EF_SMS = 0x6F3C;
-    static final int EF_ICCID = 0x2fe2;
+    static final int EF_ICCID = 0x2FE2;
     static final int EF_AD = 0x6FAD;
-    static final int EF_MBI = 0x6fc9;
-    static final int EF_MSISDN = 0x6f40;
-    static final int EF_SPDI = 0x6fcd;
-    static final int EF_SST = 0x6f38;
+    static final int EF_MBI = 0x6FC9;
+    static final int EF_MSISDN = 0x6F40;
+    static final int EF_SPDI = 0x6FCD;
+    static final int EF_SST = 0x6F38;
     static final int EF_CFIS = 0x6FCB;
-    static final int EF_IMG = 0x4f20;
+    static final int EF_IMG = 0x4F20;
 
     // USIM SIM file ids from TS 131.102
     public static final int EF_PBR = 0x4F30;
@@ -54,13 +54,13 @@
     static final int EF_MAILBOX_CPHS = 0x6F17;
     static final int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11;
     static final int EF_CFF_CPHS = 0x6F13;
-    static final int EF_SPN_CPHS = 0x6f14;
-    static final int EF_SPN_SHORT_CPHS = 0x6f18;
-    static final int EF_INFO_CPHS = 0x6f16;
-    static final int EF_CSP_CPHS = 0x6f15;
+    static final int EF_SPN_CPHS = 0x6F14;
+    static final int EF_SPN_SHORT_CPHS = 0x6F18;
+    static final int EF_INFO_CPHS = 0x6F16;
+    static final int EF_CSP_CPHS = 0x6F15;
 
     // CDMA RUIM file ids from 3GPP2 C.S0023-0
-    static final int EF_CST = 0x6f32;
+    static final int EF_CST = 0x6F32;
     static final int EF_RUIM_SPN =0x6F41;
 
     // ETSI TS.102.221
@@ -75,12 +75,27 @@
     static final int EF_CSIM_MIPUPP = 0x6F4D;
 
     //ISIM access
-    static final int EF_IMPU = 0x6f04;
-    static final int EF_IMPI = 0x6f02;
-    static final int EF_DOMAIN = 0x6f03;
-    static final int EF_IST = 0x6f07;
-    static final int EF_PCSCF = 0x6f09;
-    static final int EF_PSI = 0x6fe5;
+    static final int EF_IMPU = 0x6F04;
+    static final int EF_IMPI = 0x6F02;
+    static final int EF_DOMAIN = 0x6F03;
+    static final int EF_IST = 0x6F07;
+    static final int EF_PCSCF = 0x6F09;
+    static final int EF_PSI = 0x6FE5;
+
+    //PLMN Selection Information w/ Access Technology TS 131.102
+    static final int EF_PLMN_W_ACT = 0x6F60;
+    static final int EF_OPLMN_W_ACT = 0x6F61;
+    static final int EF_HPLMN_W_ACT = 0x6F62;
+
+    //Equivalent Home and Forbidden PLMN Lists TS 131.102
+    static final int EF_EHPLMN = 0x6FD9;
+    static final int EF_FPLMN = 0x6F7B;
+
+    // Last Roaming Selection Indicator
+    static final int EF_LRPLMNSI = 0x6FDC;
+
+    //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;
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index 881b4c3..4750814 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.uicc;
 
 import android.os.*;
+
 import com.android.internal.telephony.CommandsInterface;
 
 import java.util.ArrayList;
@@ -25,6 +26,7 @@
  * {@hide}
  */
 public abstract class IccFileHandler extends Handler implements IccConstants {
+    private static final boolean VDBG = false;
 
     //from TS 11.11 9.1 or elsewhere
     static protected final int COMMAND_READ_BINARY = 0xb0;
@@ -516,6 +518,11 @@
 
                 fileid = msg.arg1;
 
+                if (VDBG) {
+                    logd(String.format("Contents of the Select Response for command %x: ", fileid)
+                            + IccUtils.bytesToHexString(data));
+                }
+
                 if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
                     throw new IccFileTypeMismatch();
                 }
@@ -628,7 +635,6 @@
 
     protected abstract String getEFPath(int efid);
     protected abstract void logd(String s);
-
     protected abstract void loge(String s);
 
 }
diff --git a/src/java/com/android/internal/telephony/uicc/IccIoResult.java b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
index 6f0b5c3..4a35e14 100644
--- a/src/java/com/android/internal/telephony/uicc/IccIoResult.java
+++ b/src/java/com/android/internal/telephony/uicc/IccIoResult.java
@@ -22,6 +22,147 @@
  */
 public class
 IccIoResult {
+
+    private static final String UNKNOWN_ERROR = "unknown";
+
+    private String getErrorString() {
+        // Errors from 3gpp 11.11 9.4.1
+        // Additional Errors from ETSI 102.221
+        //
+        // All error codes below are copied directly from their respective specification
+        // without modification except in cases where necessary string formatting has been omitted.
+        switch(sw1) {
+            case 0x62:
+                switch(sw2) {
+                    case 0x00: return "No information given,"
+                               + " state of non volatile memory unchanged";
+                    case 0x81: return "Part of returned data may be corrupted";
+                    case 0x82: return "End of file/record reached before reading Le bytes";
+                    case 0x83: return "Selected file invalidated";
+                    case 0x84: return "Selected file in termination state";
+                    case 0xF1: return "More data available";
+                    case 0xF2: return "More data available and proactive command pending";
+                    case 0xF3: return "Response data available";
+                }
+                break;
+            case 0x63:
+                if (sw2 >> 4 == 0x0C) {
+                    return "Command successful but after using an internal"
+                        + "update retry routine but Verification failed";
+                }
+                switch(sw2) {
+                    case 0xF1: return "More data expected";
+                    case 0xF2: return "More data expected and proactive command pending";
+                }
+                break;
+            case 0x64:
+                switch(sw2) {
+                    case 0x00: return "No information given,"
+                               + " state of non-volatile memory unchanged";
+                }
+                break;
+            case 0x65:
+                switch(sw2) {
+                    case 0x00: return "No information given, state of non-volatile memory changed";
+                    case 0x81: return "Memory problem";
+                }
+                break;
+            case 0x67:
+                switch(sw2) {
+                    case 0x00: return "incorrect parameter P3";
+                    default: return "The interpretation of this status word is command dependent";
+                }
+                // break;
+            case 0x6B: return "incorrect parameter P1 or P2";
+            case 0x6D: return "unknown instruction code given in the command";
+            case 0x6E: return "wrong instruction class given in the command";
+            case 0x6F:
+                switch(sw2) {
+                    case 0x00: return "technical problem with no diagnostic given";
+                    default: return "The interpretation of this status word is command dependent";
+                }
+                // break;
+            case 0x68:
+                switch(sw2) {
+                    case 0x00: return "No information given";
+                    case 0x81: return "Logical channel not supported";
+                    case 0x82: return "Secure messaging not supported";
+                }
+                break;
+            case 0x69:
+                switch(sw2) {
+                    case 0x00: return "No information given";
+                    case 0x81: return "Command incompatible with file structure";
+                    case 0x82: return "Security status not satisfied";
+                    case 0x83: return "Authentication/PIN method blocked";
+                    case 0x84: return "Referenced data invalidated";
+                    case 0x85: return "Conditions of use not satisfied";
+                    case 0x86: return "Command not allowed (no EF selected)";
+                    case 0x89: return "Command not allowed - secure channel -"
+                               + " security not satisfied";
+                }
+                break;
+            case 0x6A:
+                switch(sw2) {
+                    case 0x80: return "Incorrect parameters in the data field";
+                    case 0x81: return "Function not supported";
+                    case 0x82: return "File not found";
+                    case 0x83: return "Record not found";
+                    case 0x84: return "Not enough memory space";
+                    case 0x86: return "Incorrect parameters P1 to P2";
+                    case 0x87: return "Lc inconsistent with P1 to P2";
+                    case 0x88: return "Referenced data not found";
+                }
+                break;
+            case 0x90: return null; // success
+            case 0x91: return null; // success
+            //Status Code 0x92 has contradictory meanings from 11.11 and 102.221 10.2.1.1
+            case 0x92:
+                if (sw2 >> 4 == 0) {
+                    return "command successful but after using an internal update retry routine";
+                }
+                switch(sw2) {
+                    case 0x40: return "memory problem";
+                }
+                break;
+            case 0x93:
+                switch(sw2) {
+                    case 0x00:
+                        return "SIM Application Toolkit is busy. Command cannot be executed"
+                            + " at present, further normal commands are allowed.";
+                }
+                break;
+            case 0x94:
+                switch(sw2) {
+                    case 0x00: return "no EF selected";
+                    case 0x02: return "out f range (invalid address)";
+                    case 0x04: return "file ID not found/pattern not found";
+                    case 0x08: return "file is inconsistent with the command";
+                }
+                break;
+            case 0x98:
+                switch(sw2) {
+                    case 0x02: return "no CHV initialized";
+                    case 0x04: return "access condition not fulfilled/"
+                            + "unsuccessful CHV verification, at least one attempt left/"
+                            + "unsuccessful UNBLOCK CHV verification, at least one attempt left/"
+                            + "authentication failed";
+                    case 0x08: return "in contradiction with CHV status";
+                    case 0x10: return "in contradiction with invalidation status";
+                    case 0x40: return "unsuccessful CHV verification, no attempt left/"
+                            + "unsuccessful UNBLOCK CHV verification, no attempt left/"
+                            + "CHV blocked"
+                            + "UNBLOCK CHV blocked";
+                    case 0x50: return "increase cannot be performed, Max value reached";
+                }
+                break;
+            case 0x9E: return null; // success
+            case 0x9F: return null; // success
+        }
+        return UNKNOWN_ERROR;
+    }
+
+
     public int sw1;
     public int sw2;
 
@@ -39,8 +180,8 @@
 
     @Override
     public String toString() {
-        return "IccIoResponse sw1:0x" + Integer.toHexString(sw1) + " sw2:0x"
-                + Integer.toHexString(sw2);
+        return "IccIoResult sw1:0x" + Integer.toHexString(sw1) + " sw2:0x"
+                + Integer.toHexString(sw2) + ((!success()) ? " Error: " + getErrorString() : "");
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index af54e6b..6bbecfe 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -22,6 +22,7 @@
 import android.os.Message;
 import android.os.Registrant;
 import android.os.RegistrantList;
+
 import android.telephony.Rlog;
 import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
@@ -88,6 +89,13 @@
     protected String mGid2;
     protected String mPrefLang;
 
+    protected PlmnActRecord[] mHplmnActRecords;
+    protected PlmnActRecord[] mOplmnActRecords;
+    protected PlmnActRecord[] mPlmnActRecords;
+
+    protected String[] mEhplmns;
+    protected String[] mFplmns;
+
     private final Object mLock = new Object();
 
     // ***** Constants
@@ -101,7 +109,6 @@
     public static final int SPN_RULE_SHOW_PLMN = 0x02;
 
     // ***** Event Constants
-    protected static final int EVENT_SET_MSISDN_DONE = 30;
     public static final int EVENT_MWI = 0; // Message Waiting indication
     public static final int EVENT_CFI = 1; // Call Forwarding indication
     public static final int EVENT_SPN = 2; // Service Provider Name
@@ -320,34 +327,13 @@
         return null;
     }
 
-    /**
-     * Set subscriber number to SIM record
-     *
-     * The subscriber number is stored in EF_MSISDN (TS 51.011)
-     *
-     * 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)
-     *        if the number starts with '+', then set to international TOA
-     * @param onComplete
-     *        onComplete.obj will be an AsyncResult
-     *        ((AsyncResult)onComplete.obj).exception == null on success
-     *        ((AsyncResult)onComplete.obj).exception != null on fail
-     */
     public void setMsisdnNumber(String alphaTag, String number,
             Message onComplete) {
-
-        mMsisdn = number;
-        mMsisdnTag = alphaTag;
-
-        if (DBG) log("Set MSISDN: " + mMsisdnTag +" " + mMsisdn);
-
-
-        AdnRecord adn = new AdnRecord(mMsisdnTag, mMsisdn);
-
-        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
-                obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
+        loge("setMsisdn() should not be invoked on base IccRecords");
+        // synthesize a "File Not Found" exception and return it
+        AsyncResult.forMessage(onComplete).exception =
+            (new IccIoResult(0x6A, 0x82, (byte[]) null)).getException();
+        onComplete.sendToTarget();
     }
 
     public String getMsisdnAlphaTag() {
diff --git a/src/java/com/android/internal/telephony/uicc/IccUtils.java b/src/java/com/android/internal/telephony/uicc/IccUtils.java
index fdcc125..67de87f 100644
--- a/src/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/src/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -23,6 +23,7 @@
 import android.telephony.Rlog;
 
 import com.android.internal.telephony.GsmAlphabet;
+
 import java.io.UnsupportedEncodingException;
 
 /**
@@ -61,6 +62,28 @@
     }
 
     /**
+     * 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
diff --git a/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java b/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java
new file mode 100644
index 0000000..a4cf7cc
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/PlmnActRecord.java
@@ -0,0 +1,144 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.Rlog;
+
+import java.util.Arrays;
+
+/**
+ * {@hide}
+ */
+public class PlmnActRecord implements Parcelable {
+    private static final String LOG_TAG = "PlmnActRecord";
+
+    // Values specified in 3GPP 31.102 sec. 4.2.5
+    public static final int ACCESS_TECH_UTRAN = 0x8000;
+    public static final int ACCESS_TECH_EUTRAN = 0x4000;
+    public static final int ACCESS_TECH_GSM = 0x0080;
+    public static final int ACCESS_TECH_GSM_COMPACT = 0x0040;
+    public static final int ACCESS_TECH_CDMA2000_HRPD = 0x0020;
+    public static final int ACCESS_TECH_CDMA2000_1XRTT = 0x0010;
+    public static final int ACCESS_TECH_RESERVED = 0x3F0F;
+
+    public static final int ENCODED_LENGTH = 5;
+
+    public final String plmn;
+    public final int accessTechs;
+
+    private static final boolean VDBG = true;
+
+    public static final Parcelable.Creator<PlmnActRecord> CREATOR =
+            new Parcelable.Creator<PlmnActRecord>() {
+        @Override
+        public PlmnActRecord createFromParcel(Parcel source) {
+            return new PlmnActRecord(source.readString(), source.readInt());
+        }
+
+        @Override
+        public PlmnActRecord[] newArray(int size) {
+            return new PlmnActRecord[size];
+        }
+    };
+
+    /* From 3gpp 31.102 section 4.2.5
+     * Bytes 0-2 bcd-encoded PLMN-ID
+     * Bytes 3-4 bitfield of access technologies
+     */
+    public PlmnActRecord(byte[] bytes, int offset) {
+        if (VDBG) Rlog.v(LOG_TAG, "Creating PlmnActRecord " + offset);
+        this.plmn = IccUtils.bcdPlmnToString(bytes, offset);
+        this.accessTechs = ((int) bytes[offset + 3] << 8) | bytes[offset + 4];
+    }
+
+    private PlmnActRecord(String plmn, int accessTechs) {
+        this.plmn = plmn;
+        this.accessTechs = accessTechs;
+    }
+
+    private String accessTechString() {
+        if (accessTechs == 0) {
+            return "NONE";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        if ((accessTechs & ACCESS_TECH_UTRAN) != 0) {
+            sb.append("UTRAN|");
+        }
+        if ((accessTechs & ACCESS_TECH_EUTRAN) != 0) {
+            sb.append("EUTRAN|");
+        }
+        if ((accessTechs & ACCESS_TECH_GSM) != 0) {
+            sb.append("GSM|");
+        }
+        if ((accessTechs & ACCESS_TECH_GSM_COMPACT) != 0) {
+            sb.append("GSM_COMPACT|");
+        }
+        if ((accessTechs & ACCESS_TECH_CDMA2000_HRPD) != 0) {
+            sb.append("CDMA2000_HRPD|");
+        }
+        if ((accessTechs & ACCESS_TECH_CDMA2000_1XRTT) != 0) {
+            sb.append("CDMA2000_1XRTT|");
+        }
+        if ((accessTechs & ACCESS_TECH_RESERVED) != 0) {
+            sb.append(String.format("UNKNOWN:%x|", accessTechs & ACCESS_TECH_RESERVED));
+        }
+        // Trim the tailing pipe character
+        return sb.substring(0, sb.length() - 1);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{PLMN=%s,AccessTechs=%s}", plmn, accessTechString());
+    }
+
+    /**
+     * Convenience method for extracting all records from encoded bytes
+     */
+    public static PlmnActRecord[] getRecords(byte[] recordBytes) {
+        if (recordBytes == null || recordBytes.length == 0
+                || recordBytes.length % ENCODED_LENGTH != 0) {
+            Rlog.e(LOG_TAG, "Malformed PlmnActRecord, bytes: "
+                    + ((recordBytes != null) ? Arrays.toString(recordBytes) : null));
+            return null;
+        }
+        int numRecords = recordBytes.length / ENCODED_LENGTH;
+        if (VDBG) Rlog.v(LOG_TAG, "Extracting Logs, count=" + numRecords);
+
+        PlmnActRecord[] records = new PlmnActRecord[numRecords];
+
+        for(int i = 0; i < numRecords; i++) {
+            records[i] = new PlmnActRecord(recordBytes, i * ENCODED_LENGTH);
+        }
+        return records;
+    }
+
+    // Parcelable Implementation
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(plmn);
+        dest.writeInt(accessTechs);
+    }
+
+}
diff --git a/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java b/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
index 8412b4e..3be0c99 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
@@ -56,8 +56,6 @@
         case EF_CFIS:
         case EF_GID1:
         case EF_GID2:
-            return MF_SIM + DF_GSM;
-
         case EF_MAILBOX_CPHS:
         case EF_VOICE_MAIL_INDICATOR_CPHS:
         case EF_CFF_CPHS:
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index e936d85..d5985c8 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -20,15 +20,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.telephony.CarrierConfigManager;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionInfo;
 import android.text.TextUtils;
-import android.telephony.Rlog;
-import android.content.res.Resources;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.MccTable;
@@ -51,6 +51,8 @@
 
     private static final boolean CRASH_RIL = false;
 
+    private static final boolean VDBG = false;
+
     // ***** Instance Variables
 
     VoiceMailConstants mVmConfig;
@@ -138,36 +140,47 @@
     private static final int CFIS_ADN_EXTENSION_ID_OFFSET = 15;
 
     // ***** Event Constants
-    private static final int EVENT_GET_IMSI_DONE = 3;
-    private static final int EVENT_GET_ICCID_DONE = 4;
-    private static final int EVENT_GET_MBI_DONE = 5;
-    private static final int EVENT_GET_MBDN_DONE = 6;
-    private static final int EVENT_GET_MWIS_DONE = 7;
-    private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
-    protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
-    protected static final int EVENT_GET_MSISDN_DONE = 10;
-    private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
-    private static final int EVENT_GET_SPN_DONE = 12;
-    private static final int EVENT_GET_SPDI_DONE = 13;
-    private static final int EVENT_UPDATE_DONE = 14;
-    private static final int EVENT_GET_PNN_DONE = 15;
-    protected static final int EVENT_GET_SST_DONE = 17;
-    private static final int EVENT_GET_ALL_SMS_DONE = 18;
-    private static final int EVENT_MARK_SMS_READ_DONE = 19;
-    private static final int EVENT_SET_MBDN_DONE = 20;
-    private static final int EVENT_SMS_ON_SIM = 21;
-    private static final int EVENT_GET_SMS_DONE = 22;
-    private static final int EVENT_GET_CFF_DONE = 24;
-    private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25;
-    private static final int EVENT_GET_INFO_CPHS_DONE = 26;
-    // private static final int EVENT_SET_MSISDN_DONE = 30; Defined in IccRecords as 30
-    private static final int EVENT_SIM_REFRESH = 31;
-    private static final int EVENT_GET_CFIS_DONE = 32;
-    private static final int EVENT_GET_CSP_CPHS_DONE = 33;
-    private static final int EVENT_GET_GID1_DONE = 34;
-    private static final int EVENT_APP_LOCKED = 35;
-    private static final int EVENT_GET_GID2_DONE = 36;
-    private static final int EVENT_CARRIER_CONFIG_CHANGED = 37;
+    private static final int SIM_RECORD_EVENT_BASE = 0x00;
+    private static final int EVENT_GET_IMSI_DONE = 3 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_ICCID_DONE = 4 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_MBI_DONE = 5 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_MBDN_DONE = 6 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_MWIS_DONE = 7 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_AD_DONE = 9 + SIM_RECORD_EVENT_BASE; // Admin data on SIM
+    private static final int EVENT_GET_MSISDN_DONE = 10 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_SPN_DONE = 12 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_SPDI_DONE = 13 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_UPDATE_DONE = 14 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_PNN_DONE = 15 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_SST_DONE = 17 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_ALL_SMS_DONE = 18 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_MARK_SMS_READ_DONE = 19 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_SET_MBDN_DONE = 20 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_SMS_ON_SIM = 21 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_SMS_DONE = 22 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_CFF_DONE = 24 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_INFO_CPHS_DONE = 26 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_SET_MSISDN_DONE = 30 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_CFIS_DONE = 32 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_CSP_CPHS_DONE = 33 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_GID1_DONE = 34 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_GID2_DONE = 36 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_PLMN_W_ACT_DONE = 37 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_OPLMN_W_ACT_DONE = 38 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_HPLMN_W_ACT_DONE = 39 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_EHPLMN_DONE = 40 + SIM_RECORD_EVENT_BASE;
+    private static final int EVENT_GET_FPLMN_DONE = 41 + SIM_RECORD_EVENT_BASE;
+
+    // TODO: Possibly move these to IccRecords.java
+    private static final int SYSTEM_EVENT_BASE = 0x100;
+    private static final int EVENT_CARRIER_CONFIG_CHANGED = 1 + SYSTEM_EVENT_BASE;
+    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.
 
@@ -262,6 +275,11 @@
         mPnnHomeName = null;
         mGid1 = null;
         mGid2 = null;
+        mPlmnActRecords = null;
+        mOplmnActRecords = null;
+        mHplmnActRecords = null;
+        mFplmns = null;
+        mEhplmns = null;
 
         mAdnCache.reset();
 
@@ -1240,6 +1258,72 @@
 
                 break;
 
+            case EVENT_GET_PLMN_W_ACT_DONE:
+                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:
+                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:
+                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:
+                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");
+                }
+                break;
+
             case EVENT_CARRIER_CONFIG_CHANGED:
                 handleCarrierNameOverride();
                 break;
@@ -1634,6 +1718,17 @@
         mFh.loadEFTransparent(EF_GID2, obtainMessage(EVENT_GET_GID2_DONE));
         mRecordsToLoad++;
 
+        mFh.loadEFTransparent(EF_PLMN_W_ACT, obtainMessage(EVENT_GET_PLMN_W_ACT_DONE));
+
+        mFh.loadEFTransparent(EF_OPLMN_W_ACT, obtainMessage(EVENT_GET_OPLMN_W_ACT_DONE));
+
+        mFh.loadEFTransparent(EF_HPLMN_W_ACT, obtainMessage(EVENT_GET_HPLMN_W_ACT_DONE));
+
+        mFh.loadEFTransparent(EF_EHPLMN, obtainMessage(EVENT_GET_EHPLMN_DONE));
+
+        mFh.loadEFTransparent(EF_FPLMN, obtainMessage(EVENT_GET_FPLMN_DONE));
+        mRecordsToLoad++;
+
         loadEfLiAndEfPl();
 
         // XXX should seek instead of examining them all
@@ -1910,6 +2005,25 @@
     }
 
     /**
+     * convert a byte array of packed plmns to an array of strings
+     */
+    private String[] parseBcdPlmnList(byte[] data, String description) {
+        final int packedBcdPlmnLenBytes = 3;
+        log("Received " + description + " PLMNs, raw=" + IccUtils.bytesToHexString(data));
+        if (data.length == 0 || (data.length % packedBcdPlmnLenBytes) != 0) {
+            loge("Received invalid " + description + " PLMN list");
+            return null;
+        }
+        int numPlmns = data.length / packedBcdPlmnLenBytes;
+        String[] ret = new String[numPlmns];
+        for (int i = 0; i < numPlmns; i++) {
+            ret[i] = IccUtils.bcdPlmnToString(data, i * packedBcdPlmnLenBytes);
+        }
+        if (VDBG) logv(description + " PLMNs: " + Arrays.toString(ret));
+        return ret;
+    }
+
+    /**
      * check to see if Mailbox Number is allocated and activated in CPHS SST
      */
     private boolean isCphsMailboxEnabled() {
@@ -2006,6 +2120,11 @@
         pw.println(" mUsimServiceTable=" + mUsimServiceTable);
         pw.println(" mGid1=" + mGid1);
         pw.println(" mGid2=" + mGid2);
+        pw.println(" mPlmnActRecords[]=" + Arrays.toString(mPlmnActRecords));
+        pw.println(" mOplmnActRecords[]=" + Arrays.toString(mOplmnActRecords));
+        pw.println(" mHplmnActRecords[]=" + Arrays.toString(mHplmnActRecords));
+        pw.println(" mFplmns[]=" + Arrays.toString(mFplmns));
+        pw.println(" mEhplmns[]=" + Arrays.toString(mEhplmns));
         pw.flush();
     }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
index bd3357e..f2834f7 100644
--- a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
@@ -61,6 +61,13 @@
         case EF_GID1:
         case EF_GID2:
         case EF_LI:
+        case EF_PLMN_W_ACT:
+        case EF_OPLMN_W_ACT:
+        case EF_HPLMN_W_ACT:
+        case EF_EHPLMN:
+        case EF_FPLMN:
+        case EF_LRPLMNSI:
+        case EF_HPPLMN:
             return MF_SIM + DF_ADF;
 
         case EF_PBR:
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/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/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index 805ee93..30c8d03 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -329,10 +329,11 @@
                 .makeWspTypeDecoder(any(byte[].class));
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyBoolean(), anyString(), anyString());
+                        anyBoolean(), anyString(), anyString(), anyString());
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(Cursor.class), anyBoolean());
         doReturn(mImsCT).when(mTelephonyComponentFactory)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index e635793..aea5a89 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -210,7 +210,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/dataconnection/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
index 4e03553..92d8c34 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/ApnSettingTest.java
@@ -28,13 +28,23 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.List;
 
+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;
 
+
 public class ApnSettingTest extends TelephonyTest {
 
     private PersistableBundle mBundle;
@@ -52,6 +62,14 @@
     }
 
     private ApnSetting createApnSetting(String[] apnTypes) {
+        return createApnSettingInternal(apnTypes, true);
+    }
+
+    private ApnSetting createDisabledApnSetting(String[] apnTypes) {
+        return createApnSettingInternal(apnTypes, false);
+    }
+
+    private ApnSetting createApnSettingInternal(String[] apnTypes, boolean carrierEnabled) {
         return new ApnSetting(
                 2163,                   // id
                 "44010",                // numeric
@@ -68,7 +86,7 @@
                 apnTypes,               // types
                 "IP",                   // protocol
                 "IP",                   // roaming_protocol
-                true,                   // carrier_enabled
+                carrierEnabled,         // carrier_enabled
                 0,                      // bearer
                 0,                      // bearer_bitmask
                 0,                      // profile_id
@@ -243,7 +261,6 @@
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
                 isMetered(mContext, 1, isRoaming));
 
-        //reuse the cached result for subId 1
         assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_DEFAULT,
                 mContext, 1, isRoaming));
         assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_MMS,
@@ -260,6 +277,15 @@
                 mContext, 1, isRoaming));
         assertFalse(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_HIPRI,
                 mContext, 1, isRoaming));
+
+        // 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));
     }
 
     @Test
@@ -300,6 +326,17 @@
         assertFalse(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_CBS}).
                 isMetered(mContext, 1, isRoaming));
+
+        // 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));
     }
 
     @Test
@@ -380,7 +417,6 @@
                 new String[]{PhoneConstants.APN_TYPE_IMS}).
                 isMetered(mContext, 2, isRoaming));
 
-        //reuse the cached result for subId 2
         assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_SUPL,
                 mContext, 2, isRoaming));
         assertTrue(ApnSetting.isMeteredApnType(PhoneConstants.APN_TYPE_CBS,
@@ -493,6 +529,102 @@
         assertTrue(createApnSetting(
                 new String[]{PhoneConstants.APN_TYPE_IA, PhoneConstants.APN_TYPE_DUN}).
                 isMetered(mContext, 4, isRoaming));
-
     }
-}
\ No newline at end of file
+
+    @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
+    @SmallTest
+    public void testEquals() throws Exception {
+        final int dummyInt = 1;
+        final String dummyString = "dummy";
+        final String[] dummyStringArr = new String[] {"dummy"};
+        // base apn
+        ApnSetting baseApn = createApnSetting(new String[] {"mms", "default"});
+        Field[] fields = ApnSetting.class.getDeclaredFields();
+        for (Field f : fields) {
+            int modifiers = f.getModifiers();
+            if (Modifier.isStatic(modifiers) || !Modifier.isFinal(modifiers)) {
+                continue;
+            }
+            f.setAccessible(true);
+            ApnSetting testApn = null;
+            if (int.class.equals(f.getType())) {
+                testApn = new ApnSetting(baseApn);
+                f.setInt(testApn, dummyInt + f.getInt(testApn));
+            } else if (boolean.class.equals(f.getType())) {
+                testApn = new ApnSetting(baseApn);
+                f.setBoolean(testApn, !f.getBoolean(testApn));
+            } else if (String.class.equals(f.getType())) {
+                testApn = new ApnSetting(baseApn);
+                f.set(testApn, dummyString);
+            } else if (String[].class.equals(f.getType())) {
+                testApn = new ApnSetting(baseApn);
+                f.set(testApn, dummyStringArr);
+            } else {
+                fail("Unsupported field:" + f.getName());
+            }
+            if (testApn != null) {
+                assertFalse(f.getName() + " is NOT checked", testApn.equals(baseApn));
+            }
+        }
+    }
+}
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..8d17ddb 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -142,6 +142,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();
@@ -236,8 +237,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();
@@ -315,6 +316,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();
@@ -338,6 +340,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();
@@ -366,7 +369,8 @@
 
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -376,7 +380,8 @@
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -391,7 +396,8 @@
         // additional copy of part 2 of message
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -408,7 +414,8 @@
         // part 1 of new sms
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -439,7 +446,8 @@
 
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -449,7 +457,8 @@
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
@@ -476,7 +485,8 @@
         doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
         doReturn(mInboundSmsTrackerPart1).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
 
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
@@ -487,7 +497,46 @@
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyString());
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
+        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();
+        // 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(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
+
+        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(any(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                        anyString(), anyString(), anyInt(), anyInt(), anyInt(), anyBoolean(),
+                        anyString());
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
                 mSmsMessage, null));
         waitForMs(100);
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..197c94c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneConnectionTest.java
@@ -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,5 @@
                 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());
     }
 }
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..572cf41 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -24,6 +24,7 @@
 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;
@@ -33,6 +34,7 @@
 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.TelephonyCallSession.Event.RilCall;
 import com.android.internal.telephony.TelephonyProto.TelephonyEvent;
 import com.android.internal.telephony.TelephonyProto.TelephonyLog;
 import com.android.internal.telephony.TelephonyProto.TelephonyServiceState;
@@ -71,6 +73,9 @@
     @Mock
     private ServiceState mServiceState;
 
+    @Mock
+    private GsmCdmaConnection mConnection;
+
     private TelephonyMetrics mMetrics;
 
     private UUSInfo mUusInfo;
@@ -460,8 +465,10 @@
     @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();
 
@@ -479,16 +486,18 @@
         assertTrue(events[0].hasRilRequest());
         assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_DIAL,
                 events[0].getRilRequest());
-        assertTrue(events[0].hasRilRequestId());
-        assertEquals(1, events[0].getRilRequestId());
+        RilCall[] calls = events[0].calls;
+        assertEquals(CallState.CALL_DIALING, calls[0].getState());
 
         assertTrue(events[1].hasType());
         assertEquals(TelephonyCallSession.Event.Type.RIL_REQUEST, events[1].getType());
         assertTrue(events[1].hasRilRequest());
         assertEquals(TelephonyCallSession.Event.RilRequest.RIL_REQUEST_HANGUP,
                 events[1].getRilRequest());
-        assertTrue(events[1].hasCallIndex());
-        assertEquals(3, events[1].getCallIndex());
+        calls = events[1].calls;
+        assertTrue(calls[0].hasIndex());
+        assertEquals(3, calls[0].getIndex());
+        assertEquals(CallState.CALL_DISCONNECTED, calls[0].getState());
     }
 
     // Test write RIL setup data call
@@ -612,4 +621,4 @@
         byte[] decodedString = Base64.decode(encodedString, Base64.DEFAULT);
         assertArrayEquals(TelephonyProto.TelephonyLog.toByteArray(log), decodedString);
     }
-}
\ No newline at end of file
+}