Create Device configuration for per feature binding in ImsResolver

Bug: 111305845
Test: atest FrameworksTelephonyTests; atest CtsTelephonyTestCases
Merged-In: I162011d7cf05e6e690fd8f65259f2463111517ba
Change-Id: I162011d7cf05e6e690fd8f65259f2463111517ba
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 5d4d9b3..1371674 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -26,6 +26,7 @@
 import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.res.XmlResourceParser;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
@@ -66,6 +67,7 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons;
 import com.android.internal.telephony.dataconnection.DataConnectionReasons.DataDisallowedReasonType;
+import com.android.internal.telephony.ims.ImsResolver;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.phone.settings.SettingsConstants;
 import com.android.phone.vvm.CarrierVvmPackageInstalledReceiver;
@@ -146,6 +148,7 @@
     CallNotifier notifier;
     CallerInfoCache callerInfoCache;
     NotificationMgr notificationMgr;
+    ImsResolver mImsResolver;
     public PhoneInterfaceManager phoneMgr;
     public ImsRcsController imsRcsController;
     CarrierConfigLoader configLoader;
@@ -317,6 +320,19 @@
             // Initialize the telephony framework
             PhoneFactory.makeDefaultPhones(this);
 
+            // Only bring up ImsResolver if the device supports having an IMS stack.
+            if (getPackageManager().hasSystemFeature(
+                    PackageManager.FEATURE_TELEPHONY_IMS)) {
+                // Get the package name of the default IMS implementation.
+                String defaultImsMmtelPackage = getResources().getString(
+                        R.string.config_ims_mmtel_package);
+                String defaultImsRcsPackage = getResources().getString(
+                        R.string.config_ims_rcs_package);
+                mImsResolver = new ImsResolver(this, defaultImsMmtelPackage,
+                        defaultImsRcsPackage, PhoneFactory.getPhones().length);
+                mImsResolver.initialize();
+            }
+
             // Start TelephonyDebugService After the default phone is created.
             Intent intent = new Intent(this, TelephonyDebugService.class);
             startService(intent);
@@ -438,6 +454,10 @@
         return PhoneFactory.getPhone(SubscriptionManager.getPhoneId(subId));
     }
 
+    public ImsResolver getImsResolver() {
+        return mImsResolver;
+    }
+
     /* package */ CallManager getCallManager() {
         return mCM;
     }
@@ -877,6 +897,14 @@
         pw.increaseIndent();
         mDataRoamingNotifLog.dump(fd, pw, args);
         pw.decreaseIndent();
+        pw.println("ImsResolver:");
+        pw.increaseIndent();
+        try {
+            if (mImsResolver != null) mImsResolver.dump(fd, pw, args);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.decreaseIndent();
         pw.decreaseIndent();
         pw.println("------- End PhoneGlobals -------");
     }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 296525d..183a50c 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -281,6 +281,7 @@
 
     private PhoneGlobals mApp;
     private CallManager mCM;
+    private ImsResolver mImsResolver;
     private UserManager mUserManager;
     private AppOpsManager mAppOps;
     private MainThreadHandler mMainThreadHandler;
@@ -1393,6 +1394,7 @@
     private PhoneInterfaceManager(PhoneGlobals app) {
         mApp = app;
         mCM = PhoneGlobals.getInstance().mCM;
+        mImsResolver = PhoneGlobals.getInstance().getImsResolver();
         mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
         mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
         mMainThreadHandler = new MainThreadHandler();
@@ -4462,12 +4464,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return;
             }
-            resolver.enableIms(slotId);
+            mImsResolver.enableIms(slotId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4482,12 +4483,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return;
             }
-            resolver.disableIms(slotId);
+            mImsResolver.disableIms(slotId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4504,12 +4504,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return null;
             }
-            return resolver.getMmTelFeatureAndListen(slotId, callback);
+            return mImsResolver.getMmTelFeatureAndListen(slotId, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4526,12 +4525,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return null;
             }
-            return resolver.getRcsFeatureAndListen(slotId, callback);
+            return mImsResolver.getRcsFeatureAndListen(slotId, callback);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4546,12 +4544,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return null;
             }
-            return resolver.getImsRegistration(slotId, feature);
+            return mImsResolver.getImsRegistration(slotId, feature);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4566,12 +4563,11 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return null;
             }
-            return resolver.getImsConfig(slotId, feature);
+            return mImsResolver.getImsConfig(slotId, feature);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -4580,58 +4576,65 @@
     /**
      * Sets the ImsService Package Name that Telephony will bind to.
      *
-     * @param slotId the slot ID that the ImsService should bind for.
-     * @param isCarrierImsService true if the ImsService is the carrier override, false if the
+     * @param slotIndex the slot ID that the ImsService should bind for.
+     * @param isCarrierService true if the ImsService is the carrier override, false if the
      *         ImsService is the device default ImsService.
-     * @param packageName The package name of the application that contains the ImsService to bind
-     *         to.
+     * @param featureTypes An integer array of feature types associated with a packageName.
+     * @param packageName The name of the package that the current configuration will be replaced
+     *                    with.
      * @return true if setting the ImsService to bind to succeeded, false if it did not.
-     * @hide
      */
-    public boolean setImsService(int slotId, boolean isCarrierImsService, String packageName) {
-        int[] subIds = SubscriptionManager.getSubId(slotId);
+    public boolean setBoundImsServiceOverride(int slotIndex, boolean isCarrierService,
+            int[] featureTypes, String packageName) {
+        int[] subIds = SubscriptionManager.getSubId(slotIndex);
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(), "setBoundImsServiceOverride");
         TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
                 (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
-                "setImsService");
+                "setBoundImsServiceOverride");
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return false;
             }
-            return resolver.overrideImsServiceConfiguration(slotId, isCarrierImsService,
-                    packageName);
+            Map<Integer, String> featureConfig = new HashMap<>();
+            for (int featureType : featureTypes) {
+                featureConfig.put(featureType, packageName);
+            }
+            return mImsResolver.overrideImsServiceConfiguration(slotIndex, isCarrierService,
+                    featureConfig);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     /**
-     * Return the ImsService configuration.
+     * Return the package name of the currently bound ImsService.
      *
      * @param slotId The slot that the ImsService is associated with.
      * @param isCarrierImsService true, if the ImsService is a carrier override, false if it is
      *         the device default.
+     * @param featureType The feature associated with the queried configuration.
      * @return the package name of the ImsService configuration.
      */
-    public String getImsService(int slotId, boolean isCarrierImsService) {
+    public String getBoundImsServicePackage(int slotId, boolean isCarrierImsService,
+            @ImsFeature.FeatureType int featureType) {
         int[] subIds = SubscriptionManager.getSubId(slotId);
-        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
-                (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
-                "getImsService");
+        TelephonyPermissions
+                .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+                mApp, (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
+                "getBoundImsServicePackage");
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            ImsResolver resolver = PhoneFactory.getImsResolver();
-            if (resolver == null) {
+            if (mImsResolver == null) {
                 // may happen if the device does not support IMS.
                 return "";
             }
             // TODO: change API to query RCS separately.
-            return resolver.getImsServiceConfiguration(slotId, isCarrierImsService,
-                    ImsFeature.FEATURE_MMTEL);
+            return mImsResolver.getImsServiceConfiguration(slotId, isCarrierImsService,
+                    featureType);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 0806fd1..53b3356 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -26,6 +26,7 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.emergency.EmergencyNumber;
+import android.telephony.ims.feature.ImsFeature;
 import android.util.Log;
 
 import com.android.internal.telephony.ITelephony;
@@ -35,6 +36,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.TreeSet;
 
@@ -180,13 +182,15 @@
     private void onHelpIms() {
         PrintWriter pw = getOutPrintWriter();
         pw.println("IMS Commands:");
-        pw.println("  ims set-ims-service [-s SLOT_ID] (-c | -d) PACKAGE_NAME");
+        pw.println("  ims set-ims-service [-s SLOT_ID] (-c | -d | -f) PACKAGE_NAME");
         pw.println("    Sets the ImsService defined in PACKAGE_NAME to to be the bound");
         pw.println("    ImsService. Options are:");
         pw.println("      -s: the slot ID that the ImsService should be bound for. If no option");
         pw.println("          is specified, it will choose the default voice SIM slot.");
         pw.println("      -c: Override the ImsService defined in the carrier configuration.");
         pw.println("      -d: Override the ImsService defined in the device overlay.");
+        pw.println("      -f: Set the feature that this override if for, if no option is");
+        pw.println("          specified, the new package name will be used for all features.");
         pw.println("  ims get-ims-service [-s SLOT_ID] [-c | -d]");
         pw.println("    Gets the package name of the currently defined ImsService.");
         pw.println("    Options are:");
@@ -194,6 +198,8 @@
         pw.println("          is specified, it will choose the default voice SIM slot.");
         pw.println("      -c: The ImsService defined as the carrier configured ImsService.");
         pw.println("      -c: The ImsService defined as the device default ImsService.");
+        pw.println("      -f: The feature type that the query will be requested for. If none is");
+        pw.println("          specified, the returned package name will correspond to MMTEL.");
         pw.println("  ims enable [-s SLOT_ID]");
         pw.println("    enables IMS for the SIM slot specified, or for the default voice SIM slot");
         pw.println("    if none is specified.");
@@ -444,6 +450,7 @@
         PrintWriter errPw = getErrPrintWriter();
         int slotId = getDefaultSlot();
         Boolean isCarrierService = null;
+        List<Integer> featuresList = new ArrayList<>();
 
         String opt;
         while ((opt = getNextOption()) != null) {
@@ -465,6 +472,26 @@
                     isCarrierService = false;
                     break;
                 }
+                case "-f": {
+                    String featureString = getNextArgRequired();
+                    String[] features = featureString.split(",");
+                    for (int i = 0; i < features.length; i++) {
+                        try {
+                            Integer result = Integer.parseInt(features[i]);
+                            if (result < ImsFeature.FEATURE_EMERGENCY_MMTEL
+                                    || result >= ImsFeature.FEATURE_MAX) {
+                                errPw.println("ims set-ims-service -f " + result
+                                        + " is an invalid feature.");
+                                return -1;
+                            }
+                            featuresList.add(result);
+                        } catch (NumberFormatException e) {
+                            errPw.println("ims set-ims-service -f tried to parse " + features[i]
+                                            + " as an integer.");
+                            return -1;
+                        }
+                    }
+                }
             }
         }
         // Mandatory param, either -c or -d
@@ -479,16 +506,24 @@
             if (packageName == null) {
                 packageName = "";
             }
-            boolean result = mInterface.setImsService(slotId, isCarrierService, packageName);
+            int[] featureArray = new int[featuresList.size()];
+            for (int i = 0; i < featuresList.size(); i++) {
+                featureArray[i] = featuresList.get(i);
+            }
+            boolean result = mInterface.setBoundImsServiceOverride(slotId, isCarrierService,
+                    featureArray, packageName);
             if (VDBG) {
                 Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " "
-                        + (isCarrierService ? "-c " : "-d ") + packageName + ", result=" + result);
+                        + (isCarrierService ? "-c " : "-d ")
+                        + "-f " + featuresList + " "
+                        + packageName + ", result=" + result);
             }
             getOutPrintWriter().println(result);
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " "
-                    + (isCarrierService ? "-c " : "-d ") + packageName + ", error"
-                    + e.getMessage());
+                    + (isCarrierService ? "-c " : "-d ")
+                    + "-f " + featuresList + " "
+                    + packageName + ", error" + e.getMessage());
             errPw.println("Exception: " + e.getMessage());
             return -1;
         }
@@ -500,6 +535,7 @@
         PrintWriter errPw = getErrPrintWriter();
         int slotId = getDefaultSlot();
         Boolean isCarrierService = null;
+        Integer featureType = ImsFeature.FEATURE_MMTEL;
 
         String opt;
         while ((opt = getNextOption()) != null) {
@@ -521,23 +557,38 @@
                     isCarrierService = false;
                     break;
                 }
+                case "-f": {
+                    try {
+                        featureType = Integer.parseInt(getNextArg());
+                    } catch (NumberFormatException e) {
+                        errPw.println("ims get-ims-service -f requires valid integer as feature.");
+                        return -1;
+                    }
+                    if (featureType < ImsFeature.FEATURE_EMERGENCY_MMTEL
+                            || featureType >= ImsFeature.FEATURE_MAX) {
+                        errPw.println("ims get-ims-service -f invalid feature.");
+                        return -1;
+                    }
+                }
             }
         }
         // Mandatory param, either -c or -d
         if (isCarrierService == null) {
-            errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
+            errPw.println("ims get-ims-service requires either \"-c\" or \"-d\" to be set.");
             return -1;
         }
 
         String result;
         try {
-            result = mInterface.getImsService(slotId, isCarrierService);
+            result = mInterface.getBoundImsServicePackage(slotId, isCarrierService, featureType);
         } catch (RemoteException e) {
             return -1;
         }
         if (VDBG) {
             Log.v(LOG_TAG, "ims get-ims-service -s " + slotId + " "
-                    + (isCarrierService ? "-c " : "-d ") + ", returned: " + result);
+                    + (isCarrierService ? "-c " : "-d ")
+                    + (featureType != null ? ("-f " + featureType) : "") + " , returned: "
+                    + result);
         }
         getOutPrintWriter().println(result);
         return 0;