Support the feature to skip wfc activation and support emergency address update only

If imsserviceentitlement.skip_wfc_activation_bool is true, Imsserviceentitlement APP will skip wfc activation and support emergency address update only

Bug: 262332213
Test: testing with a real device PASS
Flag: NONE It can be disabled by this carrier config, imsserviceentitlement.skip_wfc_activation_bool

Change-Id: I87a006565caefe7287752fafd2375769949145ed
diff --git a/src/com/android/imsserviceentitlement/WfcActivationActivity.java b/src/com/android/imsserviceentitlement/WfcActivationActivity.java
index f384362..aadf999 100644
--- a/src/com/android/imsserviceentitlement/WfcActivationActivity.java
+++ b/src/com/android/imsserviceentitlement/WfcActivationActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.imsserviceentitlement;
 
+import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.SystemProperties;
@@ -34,6 +35,8 @@
     private static final String TAG = "IMSSE-WfcActivationActivity";
 
     // Dependencies
+    @SuppressWarnings("StaticFieldLeak")
+    private static WfcActivationController sWfcActivationController;
     private WfcActivationController mWfcActivationController;
     private WfcWebPortalFragment mWfcWebPortalFragment;
 
@@ -45,7 +48,13 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_wfc_activation);
 
-        int subId = ActivityConstants.getSubId(getIntent());
+        if (mWfcActivationController.isSkipWfcActivation()
+                && ActivityConstants.isActivationFlow(getIntent())) {
+            Log.d(TAG, "Skip wfc activation");
+            setResultAndFinish(Activity.RESULT_OK);
+            return;
+        }
+
         mWfcActivationController.startFlow();
     }
 
@@ -140,7 +149,7 @@
     private void createDependeny() {
         Log.d(TAG, "Loading dependencies...");
         // TODO(b/177495634) Use DependencyInjector
-        if (mWfcActivationController == null) {
+        if (sWfcActivationController == null) {
             // Default initialization
             Log.d(TAG, "Default WfcActivationController initialization");
             Intent startIntent = this.getIntent();
@@ -150,7 +159,9 @@
                             /* context = */ this,
                             /* wfcActivationUi = */ this,
                             new ImsEntitlementApi(this, subId),
-                            this.getIntent());
+                            startIntent);
+        } else {
+            mWfcActivationController = sWfcActivationController;
         }
     }
 }
diff --git a/src/com/android/imsserviceentitlement/WfcActivationController.java b/src/com/android/imsserviceentitlement/WfcActivationController.java
index 6bcd9ec..4b65e46 100644
--- a/src/com/android/imsserviceentitlement/WfcActivationController.java
+++ b/src/com/android/imsserviceentitlement/WfcActivationController.java
@@ -69,6 +69,7 @@
     private final ImsUtils mImsUtils;
     private final Intent mStartIntent;
     private final MetricsLogger mMetricsLogger;
+    private final Context mContext;
 
     // States
     private int mEvaluateTimes = 0;
@@ -80,6 +81,7 @@
             WfcActivationUi wfcActivationUi,
             ImsEntitlementApi imsEntitlementApi,
             Intent intent) {
+        this.mContext = context;
         this.mStartIntent = intent;
         this.mActivationUi = wfcActivationUi;
         this.mImsEntitlementApi = imsEntitlementApi;
@@ -96,6 +98,7 @@
             Intent intent,
             ImsUtils imsUtils,
             MetricsLogger metricsLogger) {
+        this.mContext = context;
         this.mStartIntent = intent;
         this.mActivationUi = wfcActivationUi;
         this.mImsEntitlementApi = imsEntitlementApi;
@@ -154,6 +157,9 @@
     public void finish() {
         EntitlementUtils.cancelEntitlementCheck();
 
+        if (isSkipWfcActivation() && isActivationFlow()) {
+            return;
+        }
         // If no result set, it must be cancelled by user pressing back button.
         if (mAppResult == IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNKNOWN_RESULT) {
             mAppResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__CANCELED;
@@ -329,6 +335,10 @@
         return ENTITLEMENT_STATUS_UPDATE_RETRY_INTERVAL_MS;
     }
 
+    public boolean isSkipWfcActivation() {
+        return TelephonyUtils.isSkipWfcActivation(mContext, getSubId());
+    }
+
     @MainThread
     private void handleEntitlementStatusAfterUpdating(EntitlementResult result) {
         Ts43VowifiStatus vowifiStatus = result.getVowifiStatus();
diff --git a/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java b/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java
index 5306b26..1a39c11 100644
--- a/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java
+++ b/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java
@@ -40,6 +40,8 @@
             "imsserviceentitlement.entitlement_version_int";
     private static final String KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL =
             "imsserviceentitlement.default_service_entitlement_status_bool";
+    private static final String KEY_SKIP_WFC_ACTIVATION_BOOL =
+            "imsserviceentitlement.skip_wfc_activation_bool";
 
     private final ConnectivityManager mConnectivityManager;
     private final TelephonyManager mTelephonyManager;
@@ -172,6 +174,14 @@
         );
     }
 
+    /** Returns true if app can skip wfc activation and support emergency address update only. */
+    public static boolean isSkipWfcActivation(Context context, int subId) {
+        return getConfigForSubId(context, subId).getBoolean(
+                KEY_SKIP_WFC_ACTIVATION_BOOL,
+                false
+        );
+    }
+
     /**
      * Returns default service entitlement status for the {@code subId} or false if it is not
      * available.
diff --git a/tests/unittests/AndroidManifest.xml b/tests/unittests/AndroidManifest.xml
index be8024f..6ef1fbf 100644
--- a/tests/unittests/AndroidManifest.xml
+++ b/tests/unittests/AndroidManifest.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.imsserviceentitlement.tests">
+    package="com.android.imsserviceentitlement">
+
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationActivityTest.java b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationActivityTest.java
new file mode 100644
index 0000000..3755c10
--- /dev/null
+++ b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationActivityTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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.imsserviceentitlement;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.SubscriptionManager;
+import android.testing.AndroidTestingRunner;
+import android.util.Log;
+
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidTestingRunner.class)
+public final class WfcActivationActivityTest {
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+    private static final String TAG = "IMSSE-WfcActivationActivityTest";
+    private static final int SUB_ID = 1;
+    private static final String EXTRA_LAUNCH_CARRIER_APP = "EXTRA_LAUNCH_CARRIER_APP";
+    private static final int LAUNCH_APP_ACTIVATE = 0;
+    private static final int LAUNCH_APP_UPDATE = 1;
+
+    @Mock private WfcActivationController mMockWfcActivationController;
+
+    private final Context mAppContext = ApplicationProvider.getApplicationContext();
+    private ActivityScenario<WfcActivationActivity> mActivityScenario;
+
+    @Before
+    public void setUp() {
+        when(mMockWfcActivationController.isSkipWfcActivation()).thenReturn(false);
+    }
+
+    @After
+    public void tearDown() {
+        if (mActivityScenario != null) {
+            mActivityScenario.close();
+        }
+    }
+
+    @Test
+    public void launchAppForActivate_isSkipWfcActivationTrue_notDoStartFlow() {
+        when(mMockWfcActivationController.isSkipWfcActivation()).thenReturn(true);
+        Intent launchIntent =
+                new Intent(mAppContext, WfcActivationActivity.class)
+                        .putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUNCH_APP_ACTIVATE)
+                        .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        mockWfcActivationController(mMockWfcActivationController);
+
+        mActivityScenario = ActivityScenario.launch(launchIntent);
+
+        verify(mMockWfcActivationController, never()).startFlow();
+    }
+
+    @Test
+    public void launchAppForActivate_isSkipWfcActivationFalse_doStartFlow() {
+        Intent launchIntent =
+                new Intent(mAppContext, WfcActivationActivity.class)
+                        .putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUNCH_APP_ACTIVATE)
+                        .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        mockWfcActivationController(mMockWfcActivationController);
+
+        mActivityScenario = ActivityScenario.launch(launchIntent);
+
+        verify(mMockWfcActivationController).startFlow();
+    }
+
+    @Test
+    public void launchAppForUpdate_isSkipWfcActivationTrue_doStartFlow() {
+        when(mMockWfcActivationController.isSkipWfcActivation()).thenReturn(true);
+        Intent launchIntent =
+                new Intent(mAppContext, WfcActivationActivity.class)
+                        .putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUNCH_APP_UPDATE)
+                        .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        mockWfcActivationController(mMockWfcActivationController);
+
+        mActivityScenario = ActivityScenario.launch(launchIntent);
+
+        verify(mMockWfcActivationController).startFlow();
+    }
+
+    @Test
+    public void launchAppForUpdate_isSkipWfcActivationFalse_doStartFlow() {
+        Intent launchIntent =
+                new Intent(mAppContext, WfcActivationActivity.class)
+                        .putExtra(EXTRA_LAUNCH_CARRIER_APP, LAUNCH_APP_UPDATE)
+                        .putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        mockWfcActivationController(mMockWfcActivationController);
+
+        mActivityScenario = ActivityScenario.launch(launchIntent);
+
+        verify(mMockWfcActivationController).startFlow();
+    }
+
+    private void mockWfcActivationController(WfcActivationController mockWfcActivationController) {
+        try {
+            Field field = WfcActivationActivity.class.getDeclaredField("sWfcActivationController");
+            field.setAccessible(true);
+            field.set(null, mockWfcActivationController);
+        } catch (Exception e) {
+            Log.d(TAG, "Mocking WfcActivationController failed.");
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
index b986f54..b69127c 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
@@ -22,8 +22,10 @@
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -33,6 +35,8 @@
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
@@ -70,23 +74,30 @@
     @Mock private NetworkInfo mMockNetworkInfo;
     @Mock private ImsUtils mMockImsUtils;
     @Mock private MetricsLogger mMockMetricsLogger;
+    @Mock private CarrierConfigManager mMockCarrierConfigManager;
 
     private static final int SUB_ID = 1;
     private static final int CARRIER_ID = 1234;
     private static final String EMERGENCY_ADDRESS_WEB_URL = "webUrl";
     private static final String EMERGENCY_ADDRESS_WEB_DATA = "webData";
+    private static final String KEY_SKIP_WFC_ACTIVATION_BOOL =
+            "imsserviceentitlement.skip_wfc_activation_bool";
 
     private WfcActivationController mWfcActivationController;
     private Context mContext;
+    private PersistableBundle mCarrierConfig;
 
     @Before
     public void setUp() throws Exception {
         mContext = spy(ApplicationProvider.getApplicationContext());
 
+        when(mContext.getSystemService(CarrierConfigManager.class))
+                .thenReturn(mMockCarrierConfigManager);
         when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mMockTelephonyManager);
         when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(
                 mMockTelephonyManager);
         setNetworkConnected(true);
+        setIsSkipWfcActivation(false);
 
         Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest");
         field.setAccessible(true);
@@ -136,6 +147,68 @@
     }
 
     @Test
+    public void finish_launchAppForActivateWithIsSkipWfcActivationTrue_notWriteMetrics() {
+        setIsSkipWfcActivation(true);
+        Intent startIntent = new Intent(Intent.ACTION_MAIN);
+        startIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        startIntent.putExtra(
+                ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, ActivityConstants.LAUNCH_APP_ACTIVATE);
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        startIntent,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.finish();
+
+        verify(mMockMetricsLogger, never()).write(anyInt(), anyInt());
+    }
+
+    @Test
+    public void finish_launchAppForUpdateWithIsSkipWfcActivationTrue_writeMetrics() {
+        setIsSkipWfcActivation(true);
+        Intent startIntent = new Intent(Intent.ACTION_MAIN);
+        startIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        startIntent.putExtra(
+                ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, ActivityConstants.LAUNCH_APP_UPDATE);
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        startIntent,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.finish();
+
+        verify(mMockMetricsLogger).write(anyInt(), anyInt());
+    }
+
+    @Test
+    public void finish_launchAppForUpdateAndIsSkipWfcActivationFalse_writeMetrics() {
+        Intent startIntent = new Intent(Intent.ACTION_MAIN);
+        startIntent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        startIntent.putExtra(
+                ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, ActivityConstants.LAUNCH_APP_UPDATE);
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        startIntent,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.finish();
+
+        verify(mMockMetricsLogger).write(anyInt(), anyInt());
+    }
+
+    @Test
     public void finishFlow_isFinishing_showGeneralWaitingUi() {
         InOrder mOrderVerifier = inOrder(mMockActivationUi);
         when(mMockActivationApi.checkEntitlementStatus()).thenReturn(null);
@@ -583,4 +656,16 @@
         inOrder.verify(mMockActivationUi)
                 .showActivationUi(title, R.string.progress_text, true, 0, 0, 0);
     }
+
+    private void setIsSkipWfcActivation(boolean isSkip) {
+        initializeCarrierConfig();
+        mCarrierConfig.putBoolean(KEY_SKIP_WFC_ACTIVATION_BOOL, isSkip);
+    }
+
+    private void initializeCarrierConfig() {
+        if (mCarrierConfig == null) {
+            mCarrierConfig = new PersistableBundle();
+            when(mMockCarrierConfigManager.getConfigForSubId(SUB_ID)).thenReturn(mCarrierConfig);
+        }
+    }
 }