Merge "Require correct permission in carrier config loader." into mnc-dev
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ab074e4..1ce9e9c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1283,7 +1283,7 @@
     <string name="message_decode_error">There was an error while decoding the message.</string>
 
     <!-- Call failure reason: SIM card and roaming capabilities have already been activated. [CHAR LIMIT=NONE]-->
-    <string name="callFailed_cdma_activation_">
+    <string name="callFailed_cdma_activation">
         A SIM card has activated your service and updated your phone\'s roaming capabilities.
     </string>
 
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 6a4034e..11f81ff 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -200,8 +200,7 @@
                 (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
 
         Preference phoneAccountSettingsPreference = findPreference(PHONE_ACCOUNT_SETTINGS_KEY);
-        if (telephonyManager.isMultiSimEnabled() || (mTelecomManager.getSimCallManagers().isEmpty()
-                    && !SipUtil.isVoipSupported(mPhone.getContext()))) {
+        if (telephonyManager.isMultiSimEnabled() || !SipUtil.isVoipSupported(mPhone.getContext())) {
             getPreferenceScreen().removePreference(phoneAccountSettingsPreference);
         }
 
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index f1706be..d5db212 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -17,7 +17,6 @@
 package com.android.phone;
 
 import static android.Manifest.permission.READ_PHONE_STATE;
-import static com.android.internal.telephony.uicc.IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED;
 
 import android.annotation.NonNull;
 import android.app.ActivityManagerNative;
@@ -72,7 +71,6 @@
 
 /**
  * CarrierConfigLoader binds to privileged carrier apps to fetch carrier config overlays.
- * TODO: handle package install/uninstall events
  */
 
 public class CarrierConfigLoader extends ICarrierConfigLoader.Stub {
@@ -163,10 +161,13 @@
 
                 case EVENT_PACKAGE_CHANGED:
                     carrierPackageName = (String) msg.obj;
-                    deleteConfigForPackage(carrierPackageName);
-                    int numPhones = TelephonyManager.from(mContext).getPhoneCount();
-                    for (int i = 0; i < numPhones; ++i) {
-                        updateConfigForPhoneId(i);
+                    // Only update if there are cached config removed to avoid updating config
+                    // for unrelated packages.
+                    if (deleteConfigForPackage(carrierPackageName)) {
+                        int numPhones = TelephonyManager.from(mContext).getPhoneCount();
+                        for (int i = 0; i < numPhones; ++i) {
+                            updateConfigForPhoneId(i);
+                        }
                     }
                     break;
 
@@ -311,12 +312,14 @@
     private CarrierConfigLoader(Context context) {
         mContext = context;
 
-        // Register for package updates.
-        IntentFilter triggers = new IntentFilter();
-        triggers.addAction(Intent.ACTION_PACKAGE_ADDED);
-        triggers.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        triggers.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        mContext.registerReceiver(mReceiver, triggers);
+        // Register for package updates. Update app or uninstall app update will have all 3 intents,
+        // in the order or removed, added, replaced, all with extra_replace set to true.
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        pkgFilter.addDataScheme("package");
+        context.registerReceiverAsUser(mReceiver, UserHandle.ALL, pkgFilter, null, null);
 
         int numPhones = TelephonyManager.from(context).getPhoneCount();
         mConfigFromDefaultApp = new PersistableBundle[numPhones];
@@ -530,18 +533,23 @@
         return restoredBundle;
     }
 
-    /** Deletes all saved XML files associated with the given package name. */
-    private void deleteConfigForPackage(final String packageName) {
+    /**
+     * Deletes all saved XML files associated with the given package name.
+     * Return false if can't find matching XML files.
+     */
+    private boolean deleteConfigForPackage(final String packageName) {
         File dir = mContext.getFilesDir();
         File[] packageFiles = dir.listFiles(new FilenameFilter() {
             public boolean accept(File dir, String filename) {
                 return filename.startsWith("carrierconfig-" + packageName + "-");
             }
         });
+        if (packageFiles == null || packageFiles.length < 1) return false;
         for (File f : packageFiles) {
             log("deleting " + f.getName());
             f.delete();
         }
+        return true;
     }
 
     /** Builds a canonical file name for a config file. */
@@ -566,6 +574,12 @@
      * have a saved config file to use instead.
      */
     private void updateConfigForPhoneId(int phoneId) {
+        // Clear in-memory cache for carrier app config, so when carrier app gets uninstalled, no
+        // stale config is left.
+        if (mConfigFromCarrierApp[phoneId] != null &&
+                getCarrierPackageForPhoneId(phoneId) == null) {
+            mConfigFromCarrierApp[phoneId] = null;
+        }
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_FETCH_DEFAULT, phoneId, -1));
     }
 
@@ -675,18 +689,23 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            log("Receive action: " + action);
+            boolean replace = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+            // If replace is true, only care ACTION_PACKAGE_REPLACED.
+            if (replace && !Intent.ACTION_PACKAGE_REPLACED.equals(action))
+                return;
+
             switch (action) {
                 case Intent.ACTION_PACKAGE_ADDED:
-                case Intent.ACTION_PACKAGE_CHANGED:
                 case Intent.ACTION_PACKAGE_REMOVED:
+                case Intent.ACTION_PACKAGE_REPLACED:
                     int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
                     String packageName = mContext.getPackageManager().getNameForUid(uid);
-                    // We don't have a phoneId for arg1.
-                    mHandler.sendMessage(
-                            mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName));
+                    if (packageName != null) {
+                        // We don't have a phoneId for arg1.
+                        mHandler.sendMessage(
+                                mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName));
+                    }
                     break;
-
             }
         }
     }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ec8ce40..882a898 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -227,15 +227,18 @@
             IccAPDUArgument iccArgument;
 
             switch (msg.what) {
-                case CMD_HANDLE_PIN_MMI:
+                case CMD_HANDLE_PIN_MMI: {
                     request = (MainThreadRequest) msg.obj;
-                    request.result = getPhoneFromRequest(request).handlePinMmi(
-                            (String) request.argument);
+                    final Phone phone = getPhoneFromRequest(request);
+                    request.result = phone != null ?
+                            getPhoneFromRequest(request).handlePinMmi((String) request.argument)
+                            : false;
                     // Wake up the requesting thread
                     synchronized (request) {
                         request.notifyAll();
                     }
                     break;
+                }
 
                 case CMD_HANDLE_NEIGHBORING_CELL:
                     request = (MainThreadRequest) msg.obj;
@@ -2201,16 +2204,26 @@
     }
 
     @Override
-    public String[] getMergedSubscriberIds() {
+    public String[] getMergedSubscriberIds(String callingPackage) {
+        if (!canReadPhoneState(callingPackage, "getMergedSubscriberIds")) {
+            return null;
+        }
         final Context context = mPhone.getContext();
         final TelephonyManager tele = TelephonyManager.from(context);
         final SubscriptionManager sub = SubscriptionManager.from(context);
 
         // Figure out what subscribers are currently active
         final ArraySet<String> activeSubscriberIds = new ArraySet<>();
-        final int[] subIds = sub.getActiveSubscriptionIdList();
-        for (int subId : subIds) {
-            activeSubscriberIds.add(tele.getSubscriberId(subId));
+        // Clear calling identity, when calling TelephonyManager, because callerUid must be
+        // the process, where TelephonyManager was instantiated. Otherwise AppOps check will fail.
+        final long identity  = Binder.clearCallingIdentity();
+        try {
+            final int[] subIds = sub.getActiveSubscriptionIdList();
+            for (int subId : subIds) {
+                activeSubscriberIds.add(tele.getSubscriberId(subId));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
 
         // First pass, find a number override for an active subscriber
diff --git a/src/com/android/services/telephony/DisconnectCauseUtil.java b/src/com/android/services/telephony/DisconnectCauseUtil.java
index aaaf7db..4a92847 100644
--- a/src/com/android/services/telephony/DisconnectCauseUtil.java
+++ b/src/com/android/services/telephony/DisconnectCauseUtil.java
@@ -95,6 +95,7 @@
                 return DisconnectCause.RESTRICTED;
 
             case android.telephony.DisconnectCause.CDMA_ACCESS_FAILURE:
+            case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
             case android.telephony.DisconnectCause.CDMA_CALL_LOST:
             case android.telephony.DisconnectCause.CDMA_DROP:
             case android.telephony.DisconnectCause.CDMA_INTERCEPT:
@@ -230,6 +231,10 @@
                 resourceId = R.string.callFailed_cb_enabled;
                 break;
 
+            case android.telephony.DisconnectCause.CDMA_ALREADY_ACTIVATED:
+                resourceId = R.string.callFailed_cdma_activation;
+                break;
+
             case android.telephony.DisconnectCause.FDN_BLOCKED:
                 resourceId = R.string.callFailed_fdn_only;
                 break;
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index a52418c..8c09a95 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -724,7 +724,8 @@
                     break;
                 case DISCONNECTED:
                     setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
-                            mOriginalConnection.getDisconnectCause()));
+                            mOriginalConnection.getDisconnectCause(),
+                            mOriginalConnection.getVendorDisconnectCause()));
                     close();
                     break;
                 case DISCONNECTING:
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 7826ce0..f8a3b84 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -185,7 +185,8 @@
                         if (disableActivation) {
                             return Connection.createFailedConnection(
                                     DisconnectCauseUtil.toTelecomDisconnectCause(
-                                            android.telephony.DisconnectCause.INVALID_NUMBER,
+                                            android.telephony.DisconnectCause
+                                                    .CDMA_ALREADY_ACTIVATED,
                                             "Tried to dial *228"));
                         }
                     }
@@ -293,7 +294,8 @@
         if (phone == null) {
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
-                            android.telephony.DisconnectCause.ERROR_UNSPECIFIED));
+                            android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+                            "Phone is null"));
         }
 
         Call call = phone.getRingingCall();
@@ -332,7 +334,8 @@
         if (phone == null) {
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
-                            android.telephony.DisconnectCause.ERROR_UNSPECIFIED));
+                            android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
+                            "Phone is null"));
         }
 
         final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>();