Integrate RCS provisioning updates into SipTransport

Bug: 173828167
Test: atest TeleServiceTests:com.android.phone.RcsProvisioningMonitorTest
Test: atest TeleServiceTests:com.android.services.telephony.rcs.SipTransportControllerTest
Merged-In: I4b7e9ee4017f78cb465723592d8dcfa618f69421
Change-Id: I4b7e9ee4017f78cb465723592d8dcfa618f69421
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index ebe6c4d..9a1e275 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -9884,6 +9884,30 @@
     }
 
     /**
+     * Overrides the ims feature validation result
+     */
+    @Override
+    public boolean setImsFeatureValidationOverride(int subId, String enabledStr) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "setImsFeatureValidationOverride");
+
+        Boolean enabled = "NULL".equalsIgnoreCase(enabledStr) ? null
+                : Boolean.parseBoolean(enabledStr);
+        return RcsProvisioningMonitor.getInstance().overrideImsFeatureValidation(
+                subId, enabled);
+    }
+
+    /**
+     * Gets the ims feature validation override value
+     */
+    @Override
+    public boolean getImsFeatureValidationOverride(int subId) {
+        TelephonyPermissions.enforceShellOnly(Binder.getCallingUid(),
+                "getImsFeatureValidationOverride");
+        return RcsProvisioningMonitor.getInstance().getImsFeatureValidationOverride(subId);
+    }
+
+    /**
      * Get the mobile provisioning url that is used to launch a browser to allow users to manage
      * their mobile plan.
      */
diff --git a/src/com/android/phone/RcsProvisioningMonitor.java b/src/com/android/phone/RcsProvisioningMonitor.java
index bcf1491..6fdde78 100644
--- a/src/com/android/phone/RcsProvisioningMonitor.java
+++ b/src/com/android/phone/RcsProvisioningMonitor.java
@@ -73,6 +73,7 @@
     private static final int EVENT_DEVICE_CONFIG_OVERRIDE = 6;
     private static final int EVENT_CARRIER_CONFIG_OVERRIDE = 7;
     private static final int EVENT_RESET = 8;
+    private static final int EVENT_FEATURE_ENABLED_OVERRIDE = 9;
 
     private final PhoneGlobals mPhone;
     private final Handler mHandler;
@@ -82,6 +83,8 @@
     private Boolean mDeviceSingleRegistrationEnabledOverride;
     private final HashMap<Integer, Boolean> mCarrierSingleRegistrationEnabledOverride =
             new HashMap<>();
+    private final ConcurrentHashMap<Integer, Boolean> mImsFeatureValidationOverride =
+            new ConcurrentHashMap<>();
     private String mDmaPackageName;
     private final SparseArray<RcsFeatureListener> mRcsFeatureListeners = new SparseArray<>();
     private volatile boolean mTestModeEnabled;
@@ -626,6 +629,18 @@
     }
 
     /**
+     * override the rcs feature validation result for a subscription
+     */
+    public boolean overrideImsFeatureValidation(int subId, Boolean enabled) {
+        if (enabled == null) {
+            mImsFeatureValidationOverride.remove(subId);
+        } else {
+            mImsFeatureValidationOverride.put(subId, enabled);
+        }
+        return true;
+    }
+
+    /**
      * Returns the device config whether single registration is enabled
      */
     public boolean getDeviceSingleRegistrationEnabled() {
@@ -647,6 +662,13 @@
         return false;
     }
 
+    /**
+     * Returns the rcs feature validation override value, null if it is not set.
+     */
+    public Boolean getImsFeatureValidationOverride(int subId) {
+        return mImsFeatureValidationOverride.get(subId);
+    }
+
     private void onDefaultMessagingApplicationChanged() {
         final String packageName = getDmaPackageName();
         if (!TextUtils.equals(mDmaPackageName, packageName)) {
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index b2e1a99..36d539a 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -113,6 +113,8 @@
     private static final String SRC_GET_CARRIER_ENABLED = "get-carrier-enabled";
     private static final String SRC_SET_TEST_ENABLED = "set-test-enabled";
     private static final String SRC_GET_TEST_ENABLED = "get-test-enabled";
+    private static final String SRC_SET_FEATURE_ENABLED = "set-feature-validation";
+    private static final String SRC_GET_FEATURE_ENABLED = "get-feature-validation";
 
     private static final String D2D_SUBCOMMAND = "d2d";
     private static final String D2D_SEND = "send";
@@ -532,6 +534,17 @@
         pw.println("    Options are:");
         pw.println("      -s: The SIM slot ID to read the config value for. If no option");
         pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("  src set-feature-validation [-s SLOT_ID] true|false|null");
+        pw.println("    Sets ims feature validation result.");
+        pw.println("    The value could be true, false, or null(undefined).");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to set the config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("  src get-feature-validation [-s SLOT_ID]");
+        pw.println("    Gets ims feature validation override value.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to read the config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
     }
 
     private int handleImsCommand() {
@@ -1765,6 +1778,12 @@
             case SRC_GET_CARRIER_ENABLED: {
                 return handleSrcGetCarrierEnabledCommand();
             }
+            case SRC_SET_FEATURE_ENABLED: {
+                return handleSrcSetFeatureValidationCommand();
+            }
+            case SRC_GET_FEATURE_ENABLED: {
+                return handleSrcGetFeatureValidationCommand();
+            }
         }
 
         return -1;
@@ -2088,6 +2107,55 @@
         return 0;
     }
 
+    private int handleSrcSetFeatureValidationCommand() {
+        //the release time value could be -1
+        int subId = getRemainingArgsCount() > 1 ? getSubId("src set-feature-validation")
+                : SubscriptionManager.getDefaultSubscriptionId();
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+
+        String enabledStr = getNextArg();
+        if (enabledStr == null) {
+            return -1;
+        }
+
+        try {
+            boolean result =
+                    mInterface.setImsFeatureValidationOverride(subId, enabledStr);
+            if (VDBG) {
+                Log.v(LOG_TAG, "src set-feature-validation -s " + subId + " "
+                        + enabledStr + ", result=" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (NumberFormatException | RemoteException e) {
+            Log.w(LOG_TAG, "src set-feature-validation -s " + subId + " "
+                    + enabledStr + ", error" + e.getMessage());
+            getErrPrintWriter().println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    private int handleSrcGetFeatureValidationCommand() {
+        int subId = getSubId("src get-feature-validation");
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            return -1;
+        }
+
+        Boolean result = false;
+        try {
+            result = mInterface.getImsFeatureValidationOverride(subId);
+        } catch (RemoteException e) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "src get-feature-validation -s " + subId + ", returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
     private int handleHasCarrierPrivilegesCommand() {
         String packageName = getNextArgRequired();
 
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index a948cdb..cecc8a2 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -19,8 +19,10 @@
 import android.app.role.OnRoleHoldersChangedListener;
 import android.app.role.RoleManager;
 import android.content.Context;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.ims.DelegateRequest;
 import android.telephony.ims.FeatureTagState;
 import android.telephony.ims.ImsException;
@@ -44,12 +46,14 @@
 import com.android.ims.RcsFeatureManager;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.phone.RcsProvisioningMonitor;
 
 import com.google.common.base.Objects;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Callable;
@@ -247,6 +251,10 @@
     private RcsFeatureManager mRcsManager;
     // Cached package name of the app that is considered the default SMS app.
     private String mCachedSmsRolePackageName = "";
+    // Callback to monitor rcs provisioning change
+    private CarrierConfigManager mCarrierConfigManager;
+    // Cached allowed feature tags from carrier config
+    ArraySet<String> mFeatureTagsAllowed = new ArraySet<>();
 
     /**
      * Create an instance of SipTransportController.
@@ -261,6 +269,7 @@
         mRoleManagerAdapter = new RoleManagerAdapterImpl(context);
         mTimerAdapter = new TimerAdapterImpl();
         mExecutorService = Executors.newSingleThreadScheduledExecutor();
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
     }
 
     /**
@@ -277,6 +286,7 @@
         mTimerAdapter = timerAdapter;
         mDelegateControllerFactory = delegateFactory;
         mExecutorService = executor;
+        mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class);
         logi("created");
     }
 
@@ -813,21 +823,14 @@
         }
 
         ArraySet<String> previouslyGrantedTags = new ArraySet<>(alreadyRequestedTags);
-        // deny tags already used by other delegates
-        Set<FeatureTagState> deniedTags = new ArraySet<>();
-        for (String s : requestedFeatureTags) {
-            if (previouslyGrantedTags.contains(s)) {
-                deniedTags.add(new FeatureTagState(s,
-                        SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
-            }
-        }
-        Set<String> nonDeniedTags = requestedFeatureTags.stream()
-                .filter(r -> !previouslyGrantedTags.contains(r))
-                .collect(Collectors.toSet());
+        ArraySet<String> candidateFeatureTags = new ArraySet<>(requestedFeatureTags);
+        Set<FeatureTagState> deniedTags =
+                updateSupportedTags(candidateFeatureTags, previouslyGrantedTags);
+
         // Add newly granted tags to the already requested tags list.
-        previouslyGrantedTags.addAll(nonDeniedTags);
+        previouslyGrantedTags.addAll(candidateFeatureTags);
         CompletableFuture<Boolean> pendingChange = controller.changeSupportedFeatureTags(
-                nonDeniedTags, deniedTags);
+                candidateFeatureTags, deniedTags);
         logi("changeSupportedFeatureTags pendingChange=" + pendingChange);
         // do not worry about executor used here, this stage used to interpret result + add log.
         return pendingChange.thenApply((completedSuccessfully) ->  {
@@ -838,6 +841,51 @@
     }
 
     /**
+     * Update candidate feature tags according to feature tags allowed by carrier config,
+     * and previously granted by other SipDelegates.
+     *
+     * @param candidateFeatureTags The candidate feature tags to be updated. It will be
+     * updated as needed per the carrier config and previously granted feature tags.
+     * @param previouslyGrantedTags The feature tags already granted by other SipDelegates.
+     * @return The set of denied feature tags.
+     */
+    private Set<FeatureTagState> updateSupportedTags(Set<String> candidateFeatureTags,
+            Set<String> previouslyGrantedTags) {
+        Boolean overrideRes = RcsProvisioningMonitor.getInstance()
+                .getImsFeatureValidationOverride(mSubId);
+        // deny tags already used by other delegates
+        Set<FeatureTagState> deniedTags = new ArraySet<>();
+
+        // match config if feature validation is not overridden
+        if (overrideRes == null) {
+            Iterator<String> it = candidateFeatureTags.iterator();
+            while (it.hasNext()) {
+                String tag = it.next();
+                if (previouslyGrantedTags.contains(tag)) {
+                    logi(tag + " has already been granted previously.");
+                    it.remove();
+                    deniedTags.add(new FeatureTagState(tag,
+                            SipDelegateManager.DENIED_REASON_IN_USE_BY_ANOTHER_DELEGATE));
+                } else if (!mFeatureTagsAllowed.contains(tag.trim().toLowerCase())) {
+                    logi(tag + " is not allowed per config.");
+                    it.remove();
+                    deniedTags.add(new FeatureTagState(tag,
+                              SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+                }
+            }
+        } else if (Boolean.FALSE.equals(overrideRes)) {
+            logi("all features are denied for test purpose.");
+            for (String s : candidateFeatureTags) {
+                deniedTags.add(new FeatureTagState(s,
+                        SipDelegateManager.DENIED_REASON_NOT_ALLOWED));
+            }
+            candidateFeatureTags.clear();
+        }
+
+        return deniedTags;
+    }
+
+    /**
      * Run a Callable on the ExecutorService Thread and wait for the result.
      * If an ImsException is thrown, catch it and rethrow it to caller.
      */
@@ -918,6 +966,8 @@
             scheduleDestroyDelegates(
                     SipDelegateManager.SIP_DELEGATE_DESTROY_REASON_SUBSCRIPTION_TORN_DOWN);
         }
+
+        onCarrierConfigChangedInternal();
     }
 
     /**
@@ -925,6 +975,15 @@
      */
     private void onCarrierConfigChangedInternal() {
         logi("Carrier Config changed for subId: " + mSubId);
+        mFeatureTagsAllowed.clear();
+        PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId);
+        String[] tagConfigs = carrierConfig.getStringArray(
+                CarrierConfigManager.Ims.KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY);
+        if (tagConfigs != null && tagConfigs.length > 0) {
+            for (String tag : tagConfigs) {
+                mFeatureTagsAllowed.add(tag.trim().toLowerCase());
+            }
+        }
     }
 
     /**