Merge "When vendor ImsService changed provisioning data, which should be updated in AOSP"
diff --git a/src/com/android/phone/ImsProvisioningController.java b/src/com/android/phone/ImsProvisioningController.java
index 696f567..6a6b155 100644
--- a/src/com/android/phone/ImsProvisioningController.java
+++ b/src/com/android/phone/ImsProvisioningController.java
@@ -41,6 +41,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.AsyncResult;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -55,6 +56,7 @@
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.aidl.IFeatureProvisioningCallback;
 import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
 import android.telephony.ims.stub.ImsConfigImplBase;
@@ -89,6 +91,7 @@
     private static final int EVENT_PROVISIONING_CAPABILITY_CHANGED = 2;
     @VisibleForTesting
     protected static final int EVENT_MULTI_SIM_CONFIGURATION_CHANGE = 3;
+    private static final int EVENT_PROVISIONING_VALUE_CHANGED = 4;
 
     // Provisioning Keys that are handled via AOSP cache and not sent to the ImsService
     private static final int[] LOCAL_IMS_CONFIG_KEYS = {
@@ -245,6 +248,11 @@
                     int activeModemCount = (int) ((AsyncResult) msg.obj).result;
                     onMultiSimConfigChanged(activeModemCount);
                     break;
+                case EVENT_PROVISIONING_VALUE_CHANGED:
+                    log("subId " + msg.arg1 + " changed provisioning value item : " + msg.arg2
+                            + " value : " + (int) msg.obj);
+                    updateCapabilityTechFromKey(msg.arg1, msg.arg2, (int) msg.obj);
+                    break;
                 default:
                     log("unknown message " + msg);
                     break;
@@ -366,12 +374,15 @@
         private boolean mRequiredNotify = false;
         private int mSubId;
         private int mSlotId;
+        private ConfigCallback mConfigCallback;
 
         MmTelFeatureListener(int slotId) {
             log(LOG_PREFIX, slotId, "created");
 
             mSlotId = slotId;
             mSubId = getSubId(slotId);
+            mConfigCallback = new ConfigCallback(mSubId);
+
             mConnector = mMmTelFeatureConnector.create(
                     mApp, slotId, TAG, this, new HandlerExecutor(mHandler));
             mConnector.connect();
@@ -389,10 +400,22 @@
 
             mSubId = subId;
             mSlotId = getSlotId(subId);
+            mConfigCallback.setSubId(subId);
         }
 
         public void destroy() {
             log("destroy");
+            if (mImsManager != null) {
+                try {
+                    ImsConfig imsConfig = getImsConfig(mImsManager);
+                    if (imsConfig != null) {
+                        imsConfig.removeConfigCallback(mConfigCallback);
+                    }
+                } catch (ImsException e) {
+                    logw(LOG_PREFIX, mSlotId, "destroy : " + e.getMessage());
+                }
+            }
+            mConfigCallback = null;
             mConnector.disconnect();
             mConnector = null;
             mReady = false;
@@ -409,6 +432,17 @@
             mReady = true;
             mImsManager = manager;
 
+            if (mImsManager != null) {
+                try {
+                    ImsConfig imsConfig = getImsConfig(mImsManager);
+                    if (imsConfig != null) {
+                        imsConfig.addConfigCallback(mConfigCallback);
+                    }
+                } catch (ImsException e) {
+                    logw(LOG_PREFIX, mSlotId, "addConfigCallback : " + e.getMessage());
+                }
+            }
+
             onMmTelAvailable();
         }
 
@@ -572,12 +606,15 @@
         private boolean mRequiredNotify = false;
         private int mSubId;
         private int mSlotId;
+        private ConfigCallback mConfigCallback;
 
         RcsFeatureListener(int slotId) {
             log(LOG_PREFIX, slotId, "created");
 
             mSlotId = slotId;
             mSubId = getSubId(slotId);
+            mConfigCallback = new ConfigCallback(mSubId);
+
             mConnector = mRcsFeatureConnector.create(
                     mApp, slotId, this, new HandlerExecutor(mHandler), TAG);
             mConnector.connect();
@@ -595,10 +632,22 @@
 
             mSubId = subId;
             mSlotId = getSlotId(subId);
+            mConfigCallback.setSubId(subId);
         }
 
         public void destroy() {
             log(LOG_PREFIX, mSlotId, "destroy");
+            if (mRcsFeatureManager != null) {
+                try {
+                    ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
+                    if (imsConfig != null) {
+                        imsConfig.removeConfigCallback(mConfigCallback);
+                    }
+                } catch (ImsException e) {
+                    logw(LOG_PREFIX, mSlotId, "destroy :" + e.getMessage());
+                }
+            }
+            mConfigCallback = null;
             mConnector.disconnect();
             mConnector = null;
             mReady = false;
@@ -611,6 +660,17 @@
             mReady = true;
             mRcsFeatureManager = manager;
 
+            if (mRcsFeatureManager != null) {
+                try {
+                    ImsConfig imsConfig = getImsConfig(mRcsFeatureManager.getConfig());
+                    if (imsConfig != null) {
+                        imsConfig.addConfigCallback(mConfigCallback);
+                    }
+                } catch (ImsException e) {
+                    logw(LOG_PREFIX, mSlotId, "addConfigCallback :" + e.getMessage());
+                }
+            }
+
             onRcsAvailable();
         }
 
@@ -726,6 +786,42 @@
         }
     }
 
+    // When vendor ImsService changed provisioning data, which should be updated in AOSP.
+    // Catch the event using IImsConfigCallback.
+    private final class ConfigCallback extends IImsConfigCallback.Stub {
+        private int mSubId;
+
+        ConfigCallback(int subId) {
+            mSubId = subId;
+        }
+
+        public void setSubId(int subId) {
+            mSubId = subId;
+        }
+
+        @Override
+        public void onIntConfigChanged(int item, int value) throws RemoteException {
+            if (!Arrays.stream(LOCAL_IMS_CONFIG_KEYS).anyMatch(keyValue -> keyValue == item)) {
+                return;
+            }
+
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                if (mHandler != null) {
+                    mHandler.sendMessage(mHandler.obtainMessage(
+                            EVENT_PROVISIONING_VALUE_CHANGED, mSubId, item, (Object) value));
+                }
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
+        public void onStringConfigChanged(int item, String value) throws RemoteException {
+            // Ignore this callback.
+        }
+    }
+
     /**
      * Do NOT use this directly, instead use {@link #getInstance()}.
      */
diff --git a/tests/src/com/android/phone/ImsProvisioningControllerTest.java b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
index 2094e20..db83cca 100644
--- a/tests/src/com/android/phone/ImsProvisioningControllerTest.java
+++ b/tests/src/com/android/phone/ImsProvisioningControllerTest.java
@@ -63,6 +63,7 @@
 import android.telephony.ims.ProvisioningManager;
 import android.telephony.ims.aidl.IFeatureProvisioningCallback;
 import android.telephony.ims.aidl.IImsConfig;
+import android.telephony.ims.aidl.IImsConfigCallback;
 import android.telephony.ims.feature.MmTelFeature.MmTelCapabilities;
 import android.telephony.ims.feature.RcsFeature.RcsImsCapabilities;
 import android.telephony.ims.stub.ImsConfigImplBase;
@@ -159,6 +160,9 @@
     @Mock
     IFeatureProvisioningCallback mIFeatureProvisioningCallback1;
 
+    @Captor
+    ArgumentCaptor<IImsConfigCallback> mIImsConfigCallback;
+
     @Mock
     IBinder mIbinder0;
     @Mock
@@ -347,6 +351,8 @@
         mSubChangedListener.onSubscriptionsChanged();
         processAllMessages();
 
+        verify(mImsConfig, times(1)).addConfigCallback((IImsConfigCallback) any());
+
         int[] keys = {
                 ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE,
                 ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS,
@@ -390,6 +396,8 @@
         mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
         processAllMessages();
 
+        verify(mImsConfig, times(1)).addConfigCallback((IImsConfigCallback) any());
+
         // verify # of read data times from storage : # of Rcs storage length
         verify(mImsProvisioningLoader, times(1))
                 .getProvisioningStatus(eq(mSubId0), eq(FEATURE_RCS), anyInt(), anyInt());
@@ -1736,6 +1744,110 @@
         verifyNoMoreInteractions(mImsProvisioningLoader);
     }
 
+    @Test
+    @SmallTest
+    public void changedProvisioningValue_withMmTel() throws Exception {
+        createImsProvisioningController();
+
+        // provisioning required capability
+        // voice, all tech
+        // video, all tech
+        setCarrierConfig(mSubId0, CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VOICE_INT_ARRAY,
+                RADIO_TECHS);
+        setCarrierConfig(mSubId0, CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_VIDEO_INT_ARRAY,
+                RADIO_TECHS);
+
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+
+        mMmTelConnectorListener0.getValue().connectionReady(mImsManager, mSubId0);
+
+        // clear interactions
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        // MmTel valid
+        int[] keys = {
+                ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS,
+                ProvisioningManager.KEY_VT_PROVISIONING_STATUS,
+                ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE
+        };
+        int[] capas = {
+                MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+                MmTelCapabilities.CAPABILITY_TYPE_VIDEO,
+                MmTelCapabilities.CAPABILITY_TYPE_VOICE
+        };
+        int[] techs = {
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
+                ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN
+        };
+
+        for (int index = 0; index < keys.length; index++) {
+            mIImsConfigCallback.getValue().onIntConfigChanged(keys[index],
+                    PROVISIONING_VALUE_DISABLED);
+            processAllMessages();
+
+            // verify # of read data times from storage : # of MmTel storage length
+            verify(mImsProvisioningLoader, times(1))
+                    .setProvisioningStatus(eq(mSubId0), eq(FEATURE_MMTEL), eq(capas[index]),
+                            eq(techs[index]), eq(false));
+
+            verify(mIFeatureProvisioningCallback0, times(1))
+                    .onFeatureProvisioningChanged(eq(capas[index]), eq(techs[index]), eq(false));
+        }
+
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mImsConfig);
+    }
+
+    @Test
+    @SmallTest
+    public void changedProvisioningValue_withRcs() throws Exception {
+        createImsProvisioningController();
+
+        // provisioning required capability : PRESENCE, tech : all
+        setCarrierConfig(mSubId0,
+                CarrierConfigManager.Ims.KEY_CAPABILITY_TYPE_PRESENCE_UCE_INT_ARRAY, RADIO_TECHS);
+
+        try {
+            mTestImsProvisioningController.addFeatureProvisioningChangedCallback(
+                    mSubId0, mIFeatureProvisioningCallback0);
+        } catch (Exception e) {
+            throw new AssertionError("not expected exception", e);
+        }
+
+        mRcsConnectorListener0.getValue().connectionReady(mRcsFeatureManager, mSubId0);
+
+        // clear interactions
+        clearInvocations(mIFeatureProvisioningCallback0);
+        clearInvocations(mImsConfig);
+        clearInvocations(mImsProvisioningLoader);
+
+        mIImsConfigCallback.getValue().onIntConfigChanged(KEY_EAB_PROVISIONING_STATUS,
+                PROVISIONING_VALUE_DISABLED);
+        processAllMessages();
+
+        // verify # of read data times from storage : # of MmTel storage length
+        verify(mImsProvisioningLoader, times(RADIO_TECHS.length))
+                .setProvisioningStatus(eq(mSubId0), eq(FEATURE_RCS),
+                        eq(CAPABILITY_TYPE_PRESENCE_UCE), anyInt(), eq(false));
+
+        verify(mIFeatureProvisioningCallback0, times(RADIO_TECHS.length))
+                .onRcsFeatureProvisioningChanged(eq(CAPABILITY_TYPE_PRESENCE_UCE), anyInt(),
+                        eq(false));
+
+        verifyNoMoreInteractions(mImsProvisioningLoader);
+        verifyNoMoreInteractions(mIFeatureProvisioningCallback0);
+        verifyNoMoreInteractions(mImsConfig);
+    }
+
     private void createImsProvisioningController() throws Exception {
         if (Looper.myLooper() == null) {
             Looper.prepare();
@@ -1755,6 +1867,9 @@
                 .create(any(), eq(1), mRcsConnectorListener1.capture(), any(), any()))
                 .thenReturn(mRcsFeatureConnector1);
 
+        doNothing().when(mImsConfig).addConfigCallback(mIImsConfigCallback.capture());
+        doNothing().when(mImsConfig).removeConfigCallback(any());
+
         when(mImsConfig.getConfigInt(anyInt()))
                 .thenAnswer(invocation -> {
                     int i = (Integer) (invocation.getArguments()[0]);