Merge "Update ImsPhoneCallTracker to detect situations where call will drop." into nyc-mr1-dev
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index c5b57b3..a23cddb 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -2987,7 +2987,7 @@
         logd("phoneObjectUpdater: newVoiceRadioTech=" + newVoiceRadioTech);
 
         // Check for a voice over lte replacement
-        if ((newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_LTE)
+        if (ServiceState.isLte(newVoiceRadioTech)
                 || (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
             CarrierConfigManager configMgr = (CarrierConfigManager)
                     getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
new file mode 100644
index 0000000..b782bc8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.PersistableBundle;
+import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+
+/**
+ * This class loads configuration from CarrierConfig and uses it to determine
+ * what RATs are within a ratcheting family.  For example all the HSPA/HSDPA/HSUPA RATs.
+ * Then, until reset the class will only ratchet upwards within the family (order
+ * determined by the CarrierConfig data).  The ServiceStateTracker will reset this
+ * on cell-change.
+ */
+public class RatRatcheter {
+    private final static String LOG_TAG = "RilRatcheter";
+
+    /**
+     * This is a map of RAT types -> RAT families for rapid lookup.
+     * The RAT families are defined by RAT type -> RAT Rank SparseIntArrays, so
+     * we can compare the priorities of two RAT types by comparing the values
+     * stored in the SparseIntArrays, higher values are higher priority.
+     */
+    private final SparseArray<SparseIntArray> mRatFamilyMap = new SparseArray<>();
+
+    private final Phone mPhone;
+
+    /** Constructor */
+    public RatRatcheter(Phone phone) {
+        mPhone = phone;
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+        phone.getContext().registerReceiverAsUser(mConfigChangedReceiver, UserHandle.ALL,
+                intentFilter, null, null);
+        resetRatFamilyMap();
+    }
+
+    public int ratchetRat(int oldRat, int newRat) {
+        synchronized (mRatFamilyMap) {
+            final SparseIntArray oldFamily = mRatFamilyMap.get(oldRat);
+            if (oldFamily == null) return newRat;
+
+            final SparseIntArray newFamily = mRatFamilyMap.get(newRat);
+            if (newFamily != oldFamily) return newRat;
+
+            // now go with the higher of the two
+            final int oldRatRank = newFamily.get(oldRat, -1);
+            final int newRatRank = newFamily.get(newRat, -1);
+            return (oldRatRank > newRatRank ? oldRat : newRat);
+        }
+    }
+
+    private BroadcastReceiver mConfigChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action)) {
+                resetRatFamilyMap();
+            }
+        }
+    };
+
+    private void resetRatFamilyMap() {
+        synchronized(mRatFamilyMap) {
+            mRatFamilyMap.clear();
+
+            final CarrierConfigManager configManager = (CarrierConfigManager)
+                    mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (configManager == null) return;
+            PersistableBundle b = configManager.getConfig();
+            if (b == null) return;
+
+            // Reads an array of strings, eg:
+            // ["GPRS, EDGE", "EVDO, EVDO_A, EVDO_B", "HSPA, HSDPA, HSUPA, HSPAP"]
+            // Each string defines a family and the order of rats within the string express
+            // the priority of the RAT within the family (ie, we'd move up to later-listed RATs, but
+            // not down).
+            String[] ratFamilies = b.getStringArray(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES);
+            if (ratFamilies == null) return;
+            for (String ratFamily : ratFamilies) {
+                String[] rats = ratFamily.split(",");
+                if (rats.length < 2) continue;
+                SparseIntArray currentFamily = new SparseIntArray(rats.length);
+                int pos = 0;
+                for (String ratString : rats) {
+                    int ratInt;
+                    try {
+                        ratInt = Integer.parseInt(ratString.trim());
+                    } catch (NumberFormatException e) {
+                        Rlog.e(LOG_TAG, "NumberFormatException on " + ratString);
+                        break;
+                    }
+                    if (mRatFamilyMap.get(ratInt) != null) {
+                        Rlog.e(LOG_TAG, "RAT listed twice: " + ratString);
+                        break;
+                    }
+                    currentFamily.put(ratInt, pos++);
+                    mRatFamilyMap.put(ratInt, currentFamily);
+                }
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index 1470364..0196bdd 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -274,6 +274,9 @@
     private final SstSubscriptionsChangedListener mOnSubscriptionsChangedListener =
         new SstSubscriptionsChangedListener();
 
+
+    private final RatRatcheter mRatRatcheter;
+
     private class SstSubscriptionsChangedListener extends OnSubscriptionsChangedListener {
         public final AtomicInteger mPreviousSubId =
                 new AtomicInteger(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
@@ -484,13 +487,10 @@
     private String mCurrentCarrier = null;
 
     public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci) {
-        initOnce(phone, ci);
-        updatePhoneType();
-    }
-
-    private void initOnce(GsmCdmaPhone phone, CommandsInterface ci) {
         mPhone = phone;
         mCi = ci;
+
+        mRatRatcheter = new RatRatcheter(mPhone);
         mVoiceCapable = mPhone.getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_voice_capable);
         mUiccController = UiccController.getInstance();
@@ -540,6 +540,8 @@
 
         mEventLog = new TelephonyEventLog(mPhone.getPhoneId());
         mPhone.notifyOtaspChanged(OTASP_UNINITIALIZED);
+
+        updatePhoneType();
     }
 
     @VisibleForTesting
@@ -1534,8 +1536,7 @@
                         mNewSS.setCdmaRoamingIndicator(mDefaultRoamingIndicator);
                     } else if (namMatch && !mIsInPrl) {
                         // TODO this will be removed when we handle roaming on LTE on CDMA+LTE phones
-                        if (mNewSS.getRilVoiceRadioTechnology()
-                                == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
+                        if (ServiceState.isLte(mNewSS.getRilVoiceRadioTechnology())) {
                             log("Turn off roaming indicator as voice is LTE");
                             mNewSS.setCdmaRoamingIndicator(EriInfo.ROAMING_INDICATOR_OFF);
                         } else {
@@ -1846,20 +1847,17 @@
                         }
                     }
 
-                    // If the unsolicited signal strength comes just before data RAT family changes (i.e.
-                    // from UNKNOWN to LTE, CDMA to LTE, LTE to CDMA), the signal bar might display
-                    // the wrong information until the next unsolicited signal strength information coming
-                    // from the modem, which might take a long time to come or even not come at all.
-                    // In order to provide the best user experience, we query the latest signal
-                    // information so it will show up on the UI on time.
-
+                    // If the unsolicited signal strength comes just before data RAT family changes
+                    // (i.e. from UNKNOWN to LTE, CDMA to LTE, LTE to CDMA), the signal bar might
+                    // display the wrong information until the next unsolicited signal strength
+                    // information coming from the modem, which might take a long time to come or
+                    // even not come at all.  In order to provide the best user experience, we
+                    // query the latest signal information so it will show up on the UI on time.
                     int oldDataRAT = mSS.getRilDataRadioTechnology();
                     if ((oldDataRAT == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN &&
                             newDataRAT != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) ||
-                            (ServiceState.isCdma(oldDataRAT) &&
-                                    newDataRAT == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) ||
-                            (oldDataRAT == ServiceState.RIL_RADIO_TECHNOLOGY_LTE &&
-                                    ServiceState.isCdma(newDataRAT))) {
+                            (ServiceState.isCdma(oldDataRAT) && ServiceState.isLte(newDataRAT)) ||
+                            (ServiceState.isLte(oldDataRAT) && ServiceState.isCdma(newDataRAT))) {
                         mCi.getSignalStrength(obtainMessage(EVENT_GET_SIGNAL_STRENGTH));
                     }
 
@@ -2541,6 +2539,17 @@
         boolean hasVoiceRegStateChanged =
                 mSS.getVoiceRegState() != mNewSS.getVoiceRegState();
 
+        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
+
+        // ratchet the new tech up through it's rat family but don't drop back down
+        // until cell change
+        if (hasLocationChanged == false) {
+            mNewSS.setRilVoiceRadioTechnology(mRatRatcheter.ratchetRat(
+                    mSS.getRilVoiceRadioTechnology(), mNewSS.getRilVoiceRadioTechnology()));
+            mNewSS.setRilDataRadioTechnology(mRatRatcheter.ratchetRat(
+                    mSS.getRilDataRadioTechnology(), mNewSS.getRilDataRadioTechnology()));
+        }
+
         boolean hasRilVoiceRadioTechnologyChanged =
                 mSS.getRilVoiceRadioTechnology() != mNewSS.getRilVoiceRadioTechnology();
 
@@ -2557,7 +2566,6 @@
 
         boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
 
-        boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
         TelephonyManager tm =
                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
 
@@ -2997,20 +3005,20 @@
 
         boolean has4gHandoff =
                 mNewSS.getDataRegState() == ServiceState.STATE_IN_SERVICE &&
-                        (((mSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) &&
-                                (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) ||
-                                ((mSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD) &&
-                                        (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_LTE)));
+                ((ServiceState.isLte(mSS.getRilDataRadioTechnology()) &&
+                (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) ||
+                ((mSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD) &&
+                ServiceState.isLte(mNewSS.getRilDataRadioTechnology())));
 
         boolean hasMultiApnSupport =
-                (((mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) ||
-                        (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) &&
-                        ((mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_LTE) &&
-                                (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)));
+                ((ServiceState.isLte(mNewSS.getRilDataRadioTechnology()) ||
+                (mNewSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) &&
+                (!ServiceState.isLte(mSS.getRilDataRadioTechnology()) &&
+                (mSS.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)));
 
         boolean hasLostMultiApnSupport =
                 ((mNewSS.getRilDataRadioTechnology() >= ServiceState.RIL_RADIO_TECHNOLOGY_IS95A) &&
-                        (mNewSS.getRilDataRadioTechnology() <= ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A));
+                (mNewSS.getRilDataRadioTechnology() <= ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A));
 
         TelephonyManager tm =
                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
@@ -3075,7 +3083,7 @@
             boolean hasBrandOverride = mUiccController.getUiccCard(getPhoneId()) == null ? false :
                     (mUiccController.getUiccCard(getPhoneId()).getOperatorBrandOverride() != null);
             if (!hasBrandOverride && (mCi.getRadioState().isOn()) && (mPhone.isEriFileLoaded()) &&
-                    (mSS.getRilVoiceRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_LTE ||
+                    (!ServiceState.isLte(mSS.getRilVoiceRadioTechnology()) ||
                             mPhone.getContext().getResources().getBoolean(com.android.internal.R.
                                     bool.config_LTE_eri_for_network_name))) {
                 // Only when CDMA is in service, ERI will take effect
@@ -3101,7 +3109,7 @@
 
             if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY &&
                     mIccRecords != null && (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)
-                    && mSS.getRilVoiceRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
+                    && !ServiceState.isLte(mSS.getRilVoiceRadioTechnology())) {
                 // SIM is found on the device. If ERI roaming is OFF, and SID/NID matches
                 // one configured in SIM, use operator name from CSIM record. Note that ERI, SID,
                 // and NID are CDMA only, not applicable to LTE.
@@ -4313,7 +4321,7 @@
         //override isGsm for CDMA LTE
         if (mPhone.isPhoneTypeGsm() ||
                 (mPhone.isPhoneTypeCdmaLte() &&
-                        mSS.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_LTE)) {
+                        ServiceState.isLte(mSS.getRilDataRadioTechnology()))) {
             isGsm = true;
         }
 
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index d3934f9..2e9ab44 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -255,7 +255,7 @@
 
         int currentDataNetwork = mPhone.getServiceState().getDataNetworkType();
         boolean imsSmsDisabled = (currentDataNetwork == TelephonyManager.NETWORK_TYPE_EHRPD
-                    || (currentDataNetwork == TelephonyManager.NETWORK_TYPE_LTE
+                    || (ServiceState.isLte(currentDataNetwork)
                     && !mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()))
                     && mPhone.getServiceState().getVoiceNetworkType()
                     == TelephonyManager.NETWORK_TYPE_1xRTT
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index f5600ad..b19b6025 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -813,6 +813,7 @@
                     sizes = TCP_BUFFER_SIZES_HSPA;
                     break;
                 case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+                case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
                     sizes = TCP_BUFFER_SIZES_LTE;
                     break;
                 case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
@@ -965,6 +966,7 @@
             case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA: up = 5898; down = 14336; break;
             case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B: up = 1843; down = 5017; break;
             case ServiceState.RIL_RADIO_TECHNOLOGY_LTE: up = 51200; down = 102400; break;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA: up = 51200; down = 102400; break;
             case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD: up = 153; down = 2516; break;
             case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP: up = 11264; down = 43008; break;
             default:
diff --git a/src/java/com/android/internal/telephony/uicc/UiccController.java b/src/java/com/android/internal/telephony/uicc/UiccController.java
index 4f3b58b..18957d1 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccController.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccController.java
@@ -23,6 +23,7 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.os.SystemProperties;
+import android.os.storage.StorageManager;
 import android.telephony.TelephonyManager;
 import android.telephony.Rlog;
 import android.text.format.Time;
@@ -122,7 +123,12 @@
             Integer index = new Integer(i);
             mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index);
             // TODO remove this once modem correctly notifies the unsols
-            if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt"))) {
+            // If the device has been decrypted or FBE is supported, read SIM when radio state is
+            // available.
+            // Else wait for radio to be on. This is needed for the scenario when SIM is locked --
+            // to avoid overlap of CryptKeeper and SIM unlock screen.
+            if (DECRYPT_STATE.equals(SystemProperties.get("vold.decrypt")) ||
+                    StorageManager.isFileEncryptedNativeOrEmulated()) {
                 mCis[i].registerForAvailable(this, EVENT_ICC_STATUS_CHANGED, index);
             } else {
                 mCis[i].registerForOn(this, EVENT_ICC_STATUS_CHANGED, index);