Remove duplicate postsubmit. am: 436d508b58 am: 6274f843cd

Original change: https://android-review.googlesource.com/c/platform/packages/apps/ImsServiceEntitlement/+/2101431

Change-Id: I3784b3526737d6896adcd34ade3afb55e9cd1347
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/coverage.sh b/coverage.sh
new file mode 100755
index 0000000..f6c284f
--- /dev/null
+++ b/coverage.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+
+#
+# Copyright (C) 2021 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.
+#
+
+##### App specific parameters #####
+
+PACKAGE_NAME='com.android.imsserviceentitlement'
+MODULE_NAME='ImsServiceEntitlement'
+MODULE_PATH='packages/apps/ImsServiceEntitlement'
+
+TEST_PACKAGE='com.android.imsserviceentitlement.tests'
+TEST_MODULE_NAME='ImsServiceEntitlementUnitTests'
+TEST_MODULE_PATH='packages/apps/ImsServiceEntitlement/tests/unittests'
+TEST_MODULE_INSTALL_PATH="testcases/$TEST_MODULE_NAME/arm64/$TEST_MODULE_NAME.apk"
+TEST_RUNNER="$TEST_PACKAGE/androidx.test.runner.AndroidJUnitRunner"
+
+##### End app specific parameters #####
+
+if [[ $# != 0 && ! ($# == 1 && ($1 == "html" || $1 == "xml" || $1 == "csv")) ]]; then
+  echo "$0: usage: coverage.sh [REPORT_TYPE]"
+  echo "REPORT_TYPE [html | xml | csv] : the type of the report (default is html)"
+  exit 1
+fi
+
+REPORT_TYPE=${1:-html}
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+  echo "You need to source and lunch before you can use this script"
+  exit 1
+fi
+
+REPORTER_JAR="$ANDROID_BUILD_TOP/out/soong/host/linux-x86/framework/jacoco-cli.jar"
+
+OUTPUT_DIR="$ANDROID_BUILD_TOP/out/coverage/$MODULE_NAME"
+
+echo "Running tests and generating coverage report"
+echo "Output dir: $OUTPUT_DIR"
+echo "Report type: $REPORT_TYPE"
+
+# location on the device to store coverage results, need to be accessible by the app
+REMOTE_COVERAGE_OUTPUT_FILE="/data/data/$PACKAGE_NAME/files/coverage.ec"
+
+COVERAGE_OUTPUT_FILE="$ANDROID_BUILD_TOP/out/$PACKAGE_NAME.ec"
+OUT_COMMON="$ANDROID_BUILD_TOP/out/target/common"
+COVERAGE_CLASS_FILE="$ANDROID_BUILD_TOP/out/soong/.intermediates/$MODULE_PATH/ImsServiceEntitlementLib/android_common/javac/ImsServiceEntitlementLib.jar"
+
+source $ANDROID_BUILD_TOP/build/envsetup.sh
+
+set -e # fail early
+
+echo ""
+echo "BUILDING TEST PACKAGE $TEST_PACKAGE_NAME"
+echo "============================================"
+(cd "$ANDROID_BUILD_TOP/$TEST_MODULE_PATH" && EMMA_INSTRUMENT=true EMMA_INSTRUMENT_STATIC=true mma -j32)
+echo "============================================"
+
+#set -x # print commands
+
+adb root
+adb wait-for-device
+
+adb shell rm -f "$REMOTE_COVERAGE_OUTPUT_FILE"
+
+adb install -r -g "$OUT/$TEST_MODULE_INSTALL_PATH"
+
+echo ""
+echo "RUNNING TESTS $TEST_RUNNER"
+echo "============================================"
+adb shell am instrument -e coverage true -e coverageFile "$REMOTE_COVERAGE_OUTPUT_FILE" -w "$TEST_RUNNER"
+echo "============================================"
+
+mkdir -p "$OUTPUT_DIR"
+
+adb pull "$REMOTE_COVERAGE_OUTPUT_FILE" "$COVERAGE_OUTPUT_FILE"
+
+java -jar "$REPORTER_JAR" \
+  report "$COVERAGE_OUTPUT_FILE" \
+  --$REPORT_TYPE "$OUTPUT_DIR" \
+  --classfiles "$COVERAGE_CLASS_FILE" \
+  --sourcefiles "$ANDROID_BUILD_TOP/$MODULE_PATH/src"
+
+#set +x
+
+# Echo the file as URI to quickly open the result using ctrl-click in terminal
+if [[ REPORT_TYPE == html ]] ; then
+  echo "COVERAGE RESULTS IN:"
+  echo "file://$OUTPUT_DIR/index.html"
+fi
diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementApi.java b/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
index 7a906fd..74943ac 100644
--- a/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
+++ b/src/com/android/imsserviceentitlement/ImsEntitlementApi.java
@@ -112,8 +112,8 @@
         requestBuilder.setTerminalSoftwareVersion("versionZ");
         requestBuilder.setAcceptContentType(ServiceEntitlementRequest.ACCEPT_CONTENT_TYPE_XML);
         if (mNeedsImsProvisioning) {
-            mLastEntitlementConfiguration.getVersion().ifPresent(
-                    version -> requestBuilder.setConfigurationVersion(Integer.parseInt(version)));
+            requestBuilder.setConfigurationVersion(
+                    Integer.parseInt(mLastEntitlementConfiguration.getVersion()));
         }
         ServiceEntitlementRequest request = requestBuilder.build();
 
diff --git a/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java b/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java
index bd9ab76..04a5750 100644
--- a/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java
+++ b/src/com/android/imsserviceentitlement/ImsEntitlementPollingService.java
@@ -16,7 +16,6 @@
 
 package com.android.imsserviceentitlement;
 
-import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__CANCELED;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__DISABLED;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__ENABLED;
@@ -47,6 +46,7 @@
 import com.android.imsserviceentitlement.entitlement.EntitlementResult;
 import com.android.imsserviceentitlement.job.JobManager;
 import com.android.imsserviceentitlement.utils.ImsUtils;
+import com.android.imsserviceentitlement.utils.MetricsLogger;
 import com.android.imsserviceentitlement.utils.TelephonyUtils;
 
 import java.time.Duration;
@@ -147,6 +147,7 @@
         private final ImsEntitlementApi mImsEntitlementApi;
         private final ImsUtils mImsUtils;
         private final TelephonyUtils mTelephonyUtils;
+        private final MetricsLogger mMetricsLogger;
         private final int mSubid;
         private final boolean mNeedsImsProvisioning;
 
@@ -168,15 +169,15 @@
             this.mImsEntitlementApi = ImsEntitlementPollingService.this.mImsEntitlementApi != null
                     ? ImsEntitlementPollingService.this.mImsEntitlementApi
                     : new ImsEntitlementApi(ImsEntitlementPollingService.this, subId);
+            this.mMetricsLogger = new MetricsLogger(mTelephonyUtils);
         }
 
         @Override
         protected Void doInBackground(Void... unused) {
-            mStartTime = mTelephonyUtils.getUptimeMillis();
             int jobId = JobManager.getPureJobId(mParams.getJobId());
             switch (jobId) {
                 case JobManager.QUERY_ENTITLEMENT_STATUS_JOB_ID:
-                    mPurpose = IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__POLLING;
+                    mMetricsLogger.start(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__POLLING);
                     doEntitlementCheck();
                     break;
                 default:
@@ -343,13 +344,11 @@
         }
 
         private void sendStatsLogToMetrics() {
-            mDurationMillis = mTelephonyUtils.getUptimeMillis() - mStartTime;
-
             // If no result set, it was cancelled for reasons.
             if (mVowifiResult == IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNKNOWN_RESULT) {
                 mVowifiResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__CANCELED;
             }
-            writeStateLogByApps(
+            mMetricsLogger.write(
                     IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI, mVowifiResult);
 
             if (mNeedsImsProvisioning) {
@@ -359,22 +358,11 @@
                 if (mSmsoipResult == IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNKNOWN_RESULT) {
                     mSmsoipResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__CANCELED;
                 }
-                writeStateLogByApps(
+                mMetricsLogger.write(
                         IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOLTE, mVolteResult);
-                writeStateLogByApps(
+                mMetricsLogger.write(
                         IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__SMSOIP, mSmsoipResult);
             }
         }
-
-        private void writeStateLogByApps(int appId, int appResult) {
-            ImsServiceEntitlementStatsLog.write(
-                    IMS_SERVICE_ENTITLEMENT_UPDATED,
-                    mTelephonyUtils.getCarrierId(),
-                    mTelephonyUtils.getSpecificCarrierId(),
-                    mPurpose,
-                    appId,
-                    appResult,
-                    mDurationMillis);
-        }
     }
 }
diff --git a/src/com/android/imsserviceentitlement/WfcActivationController.java b/src/com/android/imsserviceentitlement/WfcActivationController.java
index ed63bf9..6bcd9ec 100644
--- a/src/com/android/imsserviceentitlement/WfcActivationController.java
+++ b/src/com/android/imsserviceentitlement/WfcActivationController.java
@@ -16,7 +16,6 @@
 
 package com.android.imsserviceentitlement;
 
-import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__CANCELED;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__DISABLED;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__FAILED;
@@ -26,7 +25,6 @@
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNEXPECTED_RESULT;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNKNOWN_RESULT;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION;
-import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UNKNOWN_PURPOSE;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UPDATE;
 import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI;
 
@@ -44,8 +42,11 @@
 import com.android.imsserviceentitlement.entitlement.EntitlementResult;
 import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus;
 import com.android.imsserviceentitlement.utils.ImsUtils;
+import com.android.imsserviceentitlement.utils.MetricsLogger;
 import com.android.imsserviceentitlement.utils.TelephonyUtils;
 
+import com.google.common.annotations.VisibleForTesting;
+
 import java.time.Duration;
 
 /**
@@ -67,14 +68,10 @@
     private final ImsEntitlementApi mImsEntitlementApi;
     private final ImsUtils mImsUtils;
     private final Intent mStartIntent;
+    private final MetricsLogger mMetricsLogger;
 
     // States
     private int mEvaluateTimes = 0;
-
-    // States for metrics
-    private long mStartTime;
-    private long mDurationMillis;
-    private int mPurpose = IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UNKNOWN_PURPOSE;
     private int mAppResult = IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNKNOWN_RESULT;
 
     @MainThread
@@ -88,6 +85,23 @@
         this.mImsEntitlementApi = imsEntitlementApi;
         this.mTelephonyUtils = new TelephonyUtils(context, getSubId());
         this.mImsUtils = ImsUtils.getInstance(context, getSubId());
+        this.mMetricsLogger = new MetricsLogger(mTelephonyUtils);
+    }
+
+    @VisibleForTesting
+    WfcActivationController(
+            Context context,
+            WfcActivationUi wfcActivationUi,
+            ImsEntitlementApi imsEntitlementApi,
+            Intent intent,
+            ImsUtils imsUtils,
+            MetricsLogger metricsLogger) {
+        this.mStartIntent = intent;
+        this.mActivationUi = wfcActivationUi;
+        this.mImsEntitlementApi = imsEntitlementApi;
+        this.mTelephonyUtils = new TelephonyUtils(context, getSubId());
+        this.mImsUtils = imsUtils;
+        this.mMetricsLogger = metricsLogger;
     }
 
     /** Indicates the controller to start WFC activation or emergency address update flow. */
@@ -96,11 +110,10 @@
         showGeneralWaitingUi();
         evaluateEntitlementStatus();
         if (isActivationFlow()) {
-            mPurpose = IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION;
+            mMetricsLogger.start(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION);
         } else {
-            mPurpose = IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UPDATE;
+            mMetricsLogger.start(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UPDATE);
         }
-        mStartTime = mTelephonyUtils.getUptimeMillis();
     }
 
     /** Evaluates entitlement status for activation or update. */
@@ -141,22 +154,11 @@
     public void finish() {
         EntitlementUtils.cancelEntitlementCheck();
 
-        // If no duration set, set now.
-        if (mDurationMillis == 0L) {
-            mDurationMillis = mTelephonyUtils.getUptimeMillis() - mStartTime;
-        }
         // 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;
         }
-        ImsServiceEntitlementStatsLog.write(
-                IMS_SERVICE_ENTITLEMENT_UPDATED,
-                /* carrier_id= */ mTelephonyUtils.getCarrierId(),
-                /* actual_carrier_id= */ mTelephonyUtils.getSpecificCarrierId(),
-                mPurpose,
-                IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI,
-                mAppResult,
-                mDurationMillis);
+        mMetricsLogger.write(IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI, mAppResult);
     }
 
     /**
@@ -268,9 +270,10 @@
         } else {
             if (vowifiStatus.incompatible()) {
                 showErrorUi(R.string.failure_contact_carrier);
-                turnOffWfc(() -> {
-                    finishStatsLog(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__INCOMPATIBLE);
-                });
+                mImsUtils.turnOffWfc(
+                        () -> finishStatsLog(
+                                IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__INCOMPATIBLE)
+                );
             } else {
                 Log.e(TAG, "Unexpected status. Show error UI.");
                 finishStatsLog(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__UNEXPECTED_RESULT);
@@ -334,10 +337,11 @@
             finishStatsLog(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__SUCCESSFUL);
         } else if (vowifiStatus.serverDataMissing()) {
             // Some carrier allows de-activating in updating flow.
-            turnOffWfc(() -> {
-                finishStatsLog(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__DISABLED);
-                mActivationUi.setResultAndFinish(Activity.RESULT_OK);
-            });
+            mImsUtils.turnOffWfc(
+                    () -> {
+                        finishStatsLog(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__DISABLED);
+                        mActivationUi.setResultAndFinish(Activity.RESULT_OK);
+                    });
         } else {
             Log.e(TAG, "Unexpected status. Show error UI.");
             showGeneralErrorUi();
@@ -361,14 +365,7 @@
         }.start();
     }
 
-    /** Turns WFC off and then runs {@code action} on main thread. */
-    @MainThread
-    private void turnOffWfc(Runnable action) {
-        ImsUtils.turnOffWfc(mImsUtils, action);
-    }
-
     private void finishStatsLog(int result) {
         mAppResult = result;
-        mDurationMillis = mTelephonyUtils.getUptimeMillis() - mStartTime;
     }
 }
diff --git a/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java b/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java
index c2c8abb..1aa92dd 100644
--- a/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java
+++ b/src/com/android/imsserviceentitlement/entitlement/EntitlementConfiguration.java
@@ -131,9 +131,13 @@
                 .orElse(DEFAULT_VALIDITY);
     }
 
-    /** Returns version stored in the {@link EntitlementCharacteristicDataStore}. */
-    public Optional<String> getVersion() {
-        return mXmlDoc.get(ResponseXmlNode.VERS, ResponseXmlAttributes.VERSION, null);
+    /**
+     * Returns version stored in the {@link EntitlementCharacteristicDataStore}.
+     * If no data exists then return the default value {@link #DEFAULT_VERSION}.
+     */
+    public String getVersion() {
+        return mXmlDoc.get(ResponseXmlNode.VERS, ResponseXmlAttributes.VERSION, null)
+                .orElse(String.valueOf(DEFAULT_VERSION));
     }
 
     /**
@@ -229,7 +233,7 @@
         String rawXml =
                 "<wap-provisioningdoc version=\"1.1\">"
                 + "  <characteristic type=\"VERS\">"
-                + "    <parm name=\"version\" value=\"" + getVersion().get() + "\"/>"
+                + "    <parm name=\"version\" value=\"" + getVersion() + "\"/>"
                 + "    <parm name=\"validity\" value=\"" + getVersValidity() + "\"/>"
                 + "  </characteristic>"
                 + "  <characteristic type=\"TOKEN\">"
diff --git a/src/com/android/imsserviceentitlement/utils/ImsUtils.java b/src/com/android/imsserviceentitlement/utils/ImsUtils.java
index d03d8f8..5681afe 100644
--- a/src/com/android/imsserviceentitlement/utils/ImsUtils.java
+++ b/src/com/android/imsserviceentitlement/utils/ImsUtils.java
@@ -28,14 +28,15 @@
 import androidx.annotation.GuardedBy;
 import androidx.annotation.Nullable;
 
+import com.google.common.annotations.VisibleForTesting;
+
 /** A helper class for IMS relevant APIs with subscription id. */
 public class ImsUtils {
     private static final String TAG = "IMSSE-ImsUtils";
 
-    private final CarrierConfigManager mCarrierConfigManager;
+    @Nullable private final PersistableBundle mCarrierConfigs;
     private final ImsMmTelManager mImsMmTelManager;
     private final ProvisioningManager mProvisioningManager;
-    private final int mSubId;
 
     /**
      * Turns Volte provisioning status ON/OFF.
@@ -63,11 +64,20 @@
     private static SparseArray<ImsUtils> sInstances = new SparseArray<ImsUtils>();
 
     private ImsUtils(Context context, int subId) {
-        mCarrierConfigManager =
-                (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
-        mImsMmTelManager = getImsMmTelManager(context, subId);
-        mProvisioningManager = getProvisioningManager(subId);
-        this.mSubId = subId;
+        this(
+                context.getSystemService(CarrierConfigManager.class).getConfigForSubId(subId),
+                getImsMmTelManager(subId),
+                getProvisioningManager(subId));
+    }
+
+    @VisibleForTesting
+    ImsUtils(
+            PersistableBundle carrierConfigs,
+            ImsMmTelManager imsMmTelManager,
+            ProvisioningManager provisioningManager) {
+        mCarrierConfigs = carrierConfigs;
+        mImsMmTelManager = imsMmTelManager;
+        mProvisioningManager = provisioningManager;
     }
 
     /** Returns {@link ImsUtils} instance. */
@@ -83,7 +93,7 @@
     }
 
     /** Changes persistent WFC enabled setting. */
-    public void setWfcSetting(boolean enabled) {
+    private void setWfcSetting(boolean enabled) {
         try {
             mImsMmTelManager.setVoWiFiSettingEnabled(enabled);
         } catch (RuntimeException e) {
@@ -132,21 +142,19 @@
     }
 
     /** Disables WFC and reset WFC mode to carrier default value */
-    public void disableAndResetVoWiFiImsSettings() {
+    @VisibleForTesting
+    void disableAndResetVoWiFiImsSettings() {
         try {
             disableWfc();
 
             // Reset WFC mode to carrier default value
-            if (mCarrierConfigManager != null) {
-                PersistableBundle b = mCarrierConfigManager.getConfigForSubId(mSubId);
-                if (b != null) {
-                    mImsMmTelManager.setVoWiFiModeSetting(
-                            b.getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
-                    mImsMmTelManager.setVoWiFiRoamingModeSetting(
-                            b.getInt(
-                                    CarrierConfigManager
-                                            .KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
-                }
+            if (mCarrierConfigs != null) {
+                mImsMmTelManager.setVoWiFiModeSetting(
+                        mCarrierConfigs.getInt(
+                                CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT));
+                mImsMmTelManager.setVoWiFiRoamingModeSetting(
+                        mCarrierConfigs.getInt(
+                                CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT));
             }
         } catch (RuntimeException e) {
             // ignore this exception, possible exception should be NullPointerException or
@@ -159,7 +167,7 @@
      * Returns {@code null} if provided subscription id invalid.
      */
     @Nullable
-    public static ImsMmTelManager getImsMmTelManager(Context context, int subId) {
+    private static ImsMmTelManager getImsMmTelManager(int subId) {
         try {
             return ImsMmTelManager.createForSubscriptionId(subId);
         } catch (IllegalArgumentException e) {
@@ -174,7 +182,7 @@
      * Returns {@code null} if provided subscription id invalid.
      */
     @Nullable
-    public static ProvisioningManager getProvisioningManager(int subId) {
+    private static ProvisioningManager getProvisioningManager(int subId) {
         try {
             return ProvisioningManager.createForSubscriptionId(subId);
         } catch (IllegalArgumentException e) {
@@ -195,11 +203,11 @@
     }
 
     /** Calls {@link #disableAndResetVoWiFiImsSettings()} in background thread. */
-    public static void turnOffWfc(ImsUtils imsUtils, Runnable action) {
+    public void turnOffWfc(Runnable action) {
         new AsyncTask<Void, Void, Void>() {
             @Override
             protected Void doInBackground(Void... params) {
-                imsUtils.disableAndResetVoWiFiImsSettings();
+                disableAndResetVoWiFiImsSettings();
                 return null; // To satisfy compiler
             }
 
diff --git a/src/com/android/imsserviceentitlement/utils/MetricsLogger.java b/src/com/android/imsserviceentitlement/utils/MetricsLogger.java
new file mode 100644
index 0000000..8eec246
--- /dev/null
+++ b/src/com/android/imsserviceentitlement/utils/MetricsLogger.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2021 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.utils;
+
+import static android.os.SystemClock.uptimeMillis;
+
+import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED;
+import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UNKNOWN_PURPOSE;
+
+import com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog;
+
+/** Utility for writing the metrics logs. */
+public class MetricsLogger {
+    private final TelephonyUtils mTelephonyUtils;
+
+    private int mPurpose = IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__UNKNOWN_PURPOSE;
+    private long mStartTime = uptimeMillis();
+
+    public MetricsLogger(TelephonyUtils telephonyUtils) {
+        mTelephonyUtils = telephonyUtils;
+    }
+
+    /** Starts the log session for the purpose as well as record the start time. */
+    public void start(int purpose) {
+        mStartTime = uptimeMillis();
+        mPurpose = purpose;
+    }
+
+    /** Writes the metrics log. */
+    public void write(int appId, int appResult) {
+        ImsServiceEntitlementStatsLog.write(
+                IMS_SERVICE_ENTITLEMENT_UPDATED,
+                mTelephonyUtils.getCarrierId(),
+                mTelephonyUtils.getSpecificCarrierId(),
+                mPurpose,
+                appId,
+                appResult,
+                uptimeMillis() - mStartTime);
+    }
+}
diff --git a/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java b/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java
index e968c2f..62bc8d7 100644
--- a/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java
+++ b/src/com/android/imsserviceentitlement/utils/TelephonyUtils.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
-import android.os.Build;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -62,32 +61,12 @@
         return android.os.SystemClock.uptimeMillis();
     }
 
-    /** Returns device model name. */
-    public String getDeviceName() {
-        return Build.MODEL;
-    }
-
-    /** Returns device OS version. */
-    public String getDeviceOsVersion() {
-        return Build.VERSION.RELEASE;
-    }
-
     /** Returns {@code true} if network is connected (cellular or WiFi). */
     public boolean isNetworkConnected() {
         NetworkInfo activeNetwork = mConnectivityManager.getActiveNetworkInfo();
         return activeNetwork != null && activeNetwork.isConnected();
     }
 
-    /**
-     * Returns the response of EAP-AKA authetication {@code data} or {@code null} on failure.
-     *
-     * <p>Requires permission: READ_PRIVILEGED_PHONE_STATE
-     */
-    public String getEapAkaAuthentication(String data) {
-        return mTelephonyManager.getIccAuthentication(
-                TelephonyManager.APPTYPE_USIM, TelephonyManager.AUTHTYPE_EAP_AKA, data);
-    }
-
     /** Returns carrier ID. */
     public int getCarrierId() {
         return mTelephonyManager.getSimCarrierId();
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java b/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java
index 474e755..04477c9 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/EntitlementUtilsTest.java
@@ -44,10 +44,8 @@
     @Mock private EntitlementResult mEntitlementResult;
 
     @Before
-    public void setup() throws Exception {
-        Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest");
-        field.setAccessible(true);
-        field.set(null, true);
+    public void setUp() throws Exception {
+        useDirectExecutor(true);
     }
 
     @Test
@@ -67,4 +65,21 @@
 
         verify(mEntitlementResultCallback, never()).onEntitlementResult(mEntitlementResult);
     }
+
+    @Test
+    public void entitlementCheck_cancelEntitlementCheck_onFailure() throws Exception {
+        useDirectExecutor(false);
+        when(mMockImsEntitlementApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+
+        EntitlementUtils.entitlementCheck(mMockImsEntitlementApi, mEntitlementResultCallback);
+        EntitlementUtils.cancelEntitlementCheck();
+
+        verify(mEntitlementResultCallback, never()).onEntitlementResult(mEntitlementResult);
+    }
+
+    private void useDirectExecutor(boolean enable) throws Exception {
+        Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest");
+        field.setAccessible(true);
+        field.set(null, enable);
+    }
 }
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java
index d0ea3ee..217ac7b 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementApiTest.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.runner.AndroidJUnit4;
@@ -296,6 +297,16 @@
         assertThat(result.getRetryAfterSeconds()).isEqualTo(120);
     }
 
+    @Test
+    public void checkEntitlementStatus_invalidSubId_resultNull() {
+        ImsEntitlementApi imsEntitlementApi =
+                new ImsEntitlementApi(mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        EntitlementResult result = imsEntitlementApi.checkEntitlementStatus();
+
+        assertThat(result).isNull();
+    }
+
     private ServiceEntitlementRequest authenticationRequest(String token) {
         ServiceEntitlementRequest.Builder requestBuilder = ServiceEntitlementRequest.builder();
         if (token != null) {
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java
index 444db10..117745c 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/ImsEntitlementReceiverTest.java
@@ -32,6 +32,7 @@
 import android.content.SharedPreferences;
 import android.os.PersistableBundle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 
@@ -194,8 +195,6 @@
 
     @Test
     public void onReceive_isSystemUser_jobScheduled() {
-        when(mMockUserManager.isSystemUser()).thenReturn(true);
-
         mReceiver.onReceive(
                 mContext, getCarrierConfigChangedIntent(SUB_ID, /* slotId= */ 0));
 
@@ -204,9 +203,9 @@
 
     @Test
     public void onReceive_notSystemUser_noJobScheduled() {
-        when(mMockUserManager.isSystemUser()).thenReturn(false);
+        ImsEntitlementReceiver receiver = new ImsEntitlementReceiver();
 
-        mReceiver.onReceive(
+        receiver.onReceive(
                 mContext, getCarrierConfigChangedIntent(SUB_ID, /* slotId= */ 0));
 
         verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady();
@@ -232,6 +231,27 @@
         verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady();
     }
 
+    @Test
+    public void onReceive_invalidSubId_noJobScheduled() {
+        mReceiver.onReceive(mContext,
+                getCarrierConfigChangedIntent(SubscriptionManager.INVALID_SUBSCRIPTION_ID, 0));
+
+        verify(mMockJobManager, never()).queryEntitlementStatusOnceNetworkReady();
+    }
+
+    @Test
+    public void isBootUp_compareWithLastBootCount_returnResult() {
+        int currentBootCount =
+                Settings.Global.getInt(
+                        mContext.getContentResolver(), Settings.Global.BOOT_COUNT, /* def= */ -1);
+        setLastBootCount(/* slotId= */ 0, /* count=*/ currentBootCount);
+        setLastBootCount(/* slotId= */ 1, /* count=*/ currentBootCount - 1);
+        ImsEntitlementReceiver receiver = new ImsEntitlementReceiver();
+
+        assertThat(receiver.isBootUp(mContext, 0)).isFalse();
+        assertThat(receiver.isBootUp(mContext, 1)).isTrue();
+    }
+
     private Intent getCarrierConfigChangedIntent(int subId, int slotId) {
         Intent intent = new Intent(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
@@ -255,6 +275,12 @@
         preferences.edit().putInt("last_sub_id_" + slotId, subId).apply();
     }
 
+    private void setLastBootCount(int slotId, int count) {
+        SharedPreferences preferences =
+                mContext.getSharedPreferences("PREFERENCE_ACTIVATION_INFO", Context.MODE_PRIVATE);
+        preferences.edit().putInt("last_boot_count_" + slotId, count).apply();
+    }
+
     private void useDirectExecutor() throws Exception {
         Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest");
         field.setAccessible(true);
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
index 72b9341..cf72f1f 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/WfcActivationControllerTest.java
@@ -16,13 +16,19 @@
 
 package com.android.imsserviceentitlement;
 
+import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__FAILED;
+import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__SUCCESSFUL;
+import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION;
+import static com.android.imsserviceentitlement.ImsServiceEntitlementStatsLog.IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
-import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
 import android.net.ConnectivityManager;
@@ -40,6 +46,8 @@
 import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus.ProvStatus;
 import com.android.imsserviceentitlement.ts43.Ts43VowifiStatus.TcStatus;
 import com.android.imsserviceentitlement.utils.Executors;
+import com.android.imsserviceentitlement.utils.ImsUtils;
+import com.android.imsserviceentitlement.utils.MetricsLogger;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -52,32 +60,32 @@
 
 import java.lang.reflect.Field;
 
-// TODO(b/176127289) add tests
 @RunWith(AndroidJUnit4.class)
 public class WfcActivationControllerTest {
     @Rule public final MockitoRule rule = MockitoJUnit.rule();
-    @Mock private TelephonyManager mTelephonyManager;
-    @Mock private ImsEntitlementApi mActivationApi;
-    @Mock private WfcActivationUi mActivationUi;
-    @Mock private ConnectivityManager mConnectivityManager;
-    @Mock private NetworkInfo mNetworkInfo;
+    @Mock private TelephonyManager mMockTelephonyManager;
+    @Mock private ImsEntitlementApi mMockActivationApi;
+    @Mock private WfcActivationUi mMockActivationUi;
+    @Mock private ConnectivityManager mMockConnectivityManager;
+    @Mock private NetworkInfo mMockNetworkInfo;
+    @Mock private ImsUtils mMockImsUtils;
+    @Mock private MetricsLogger mMockMetricsLogger;
 
     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 TERMS_AND_CONDITION_WEB_URL = "tncUrl";
-    private static final String WEBVIEW_JS_CONTROLLER_NAME = "webviewJsControllerName";
 
     private WfcActivationController mWfcActivationController;
     private Context mContext;
-    private Instrumentation mInstrumentation;
 
     @Before
     public void setUp() throws Exception {
         mContext = spy(ApplicationProvider.getApplicationContext());
 
-        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
-        when(mTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(mTelephonyManager);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mMockTelephonyManager);
+        when(mMockTelephonyManager.createForSubscriptionId(SUB_ID)).thenReturn(
+                mMockTelephonyManager);
         setNetworkConnected(true);
 
         Field field = Executors.class.getDeclaredField("sUseDirectExecutorForTest");
@@ -87,7 +95,7 @@
 
     @Test
     public void startFlow_launchAppForActivation_setPurposeActivation() {
-        InOrder mOrderVerifier = inOrder(mActivationUi);
+        InOrder mOrderVerifier = inOrder(mMockActivationUi);
         setNetworkConnected(false);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
@@ -102,7 +110,7 @@
 
     @Test
     public void startFlow_launchAppForUpdate_setPurposeUpdate() {
-        InOrder mOrderVerifier = inOrder(mActivationUi);
+        InOrder mOrderVerifier = inOrder(mMockActivationUi);
         setNetworkConnected(false);
         buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
 
@@ -114,7 +122,7 @@
 
     @Test
     public void startFlow_launchAppForShowTc_setPurposeUpdate() {
-        InOrder mOrderVerifier = inOrder(mActivationUi);
+        InOrder mOrderVerifier = inOrder(mMockActivationUi);
         setNetworkConnected(false);
         buildActivity(ActivityConstants.LAUNCH_APP_SHOW_TC);
 
@@ -129,14 +137,14 @@
 
     @Test
     public void finishFlow_isFinishing_showGeneralWaitingUi() {
-        InOrder mOrderVerifier = inOrder(mActivationUi);
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(null);
+        InOrder mOrderVerifier = inOrder(mMockActivationUi);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(null);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.finishFlow();
 
         mOrderVerifier
-                .verify(mActivationUi)
+                .verify(mMockActivationUi)
                 .showActivationUi(
                         R.string.activate_title,
                         R.string.progress_text,
@@ -145,7 +153,7 @@
                         Activity.RESULT_CANCELED,
                         0);
         mOrderVerifier
-                .verify(mActivationUi)
+                .verify(mMockActivationUi)
                 .showActivationUi(
                         R.string.activate_title,
                         R.string.wfc_activation_error,
@@ -156,8 +164,105 @@
     }
 
     @Test
+    public void finishFlow_startForUpdate_showGeneralWaitingUi() {
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(
+                EntitlementResult
+                        .builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus
+                                        .builder()
+                                        .setEntitlementStatus(EntitlementStatus.ENABLED)
+                                        .setProvStatus(ProvStatus.PROVISIONED)
+                                        .setTcStatus(TcStatus.AVAILABLE)
+                                        .setAddrStatus(AddrStatus.AVAILABLE)
+                                        .build())
+                        .build());
+        setNetworkConnected(false);
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
+                ActivityConstants.LAUNCH_APP_UPDATE);
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        null,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.finishFlow();
+
+        verify(mMockActivationUi).setResultAndFinish(eq(Activity.RESULT_OK));
+    }
+
+    @Test
+    public void finish_startFlowForActivate_writeLoggerPurposeActivation() {
+        when(mMockTelephonyManager.getSimCarrierId()).thenReturn(CARRIER_ID);
+        when(mMockTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
+                ActivityConstants.LAUNCH_APP_ACTIVATE);
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        intent,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.startFlow();
+        mWfcActivationController.finish();
+
+        verify(mMockMetricsLogger).start(eq(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION));
+        verify(mMockMetricsLogger).write(
+                eq(IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI),
+                eq(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__FAILED));
+    }
+
+    @Test
+    public void finish_entitlementResultWfcEntitled_writeLoggerAppResultSuccessful() {
+        when(mMockTelephonyManager.getSimCarrierId()).thenReturn(CARRIER_ID);
+        when(mMockTelephonyManager.getSimSpecificCarrierId()).thenReturn(CARRIER_ID);
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
+                ActivityConstants.LAUNCH_APP_ACTIVATE);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(
+                EntitlementResult
+                        .builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus
+                                        .builder()
+                                        .setEntitlementStatus(EntitlementStatus.ENABLED)
+                                        .setProvStatus(ProvStatus.PROVISIONED)
+                                        .setTcStatus(TcStatus.AVAILABLE)
+                                        .setAddrStatus(AddrStatus.AVAILABLE)
+                                        .build())
+                        .build());
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        intent,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.startFlow();
+        mWfcActivationController.finish();
+
+        verify(mMockMetricsLogger).start(eq(IMS_SERVICE_ENTITLEMENT_UPDATED__PURPOSE__ACTIVATION));
+        verify(mMockMetricsLogger).write(
+                eq(IMS_SERVICE_ENTITLEMENT_UPDATED__SERVICE_TYPE__VOWIFI),
+                eq(IMS_SERVICE_ENTITLEMENT_UPDATED__APP_RESULT__SUCCESSFUL));
+    }
+
+    @Test
     public void handleEntitlementStatusForActivation_isVowifiEntitledTrue_setActivityResultOk() {
-        EntitlementResult mEntitlementResult =
+        EntitlementResult entitlementResult =
                 EntitlementResult.builder()
                         .setVowifiStatus(
                                 Ts43VowifiStatus.builder()
@@ -167,17 +272,17 @@
                                         .setProvStatus(ProvStatus.PROVISIONED)
                                         .build())
                         .build();
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
 
-        verify(mActivationUi).setResultAndFinish(Activity.RESULT_OK);
+        verify(mMockActivationUi).setResultAndFinish(Activity.RESULT_OK);
     }
 
     @Test
     public void handleEntitlementStatusForActivation_isServerDataMissingTrue_showWebview() {
-        EntitlementResult mEntitlementResult =
+        EntitlementResult entitlementResult =
                 EntitlementResult.builder()
                         .setVowifiStatus(
                                 Ts43VowifiStatus.builder()
@@ -188,24 +293,45 @@
                         .setEmergencyAddressWebUrl(EMERGENCY_ADDRESS_WEB_URL)
                         .setEmergencyAddressWebData(EMERGENCY_ADDRESS_WEB_DATA)
                         .build();
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
 
-        verify(mActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL, EMERGENCY_ADDRESS_WEB_DATA);
+        verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL,
+                EMERGENCY_ADDRESS_WEB_DATA);
+    }
+
+    @Test
+    public void handleEntitlementStatusForActivation_showTc_showWebview() {
+        EntitlementResult entitlementResult =
+                EntitlementResult.builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus.builder()
+                                        .setEntitlementStatus(EntitlementStatus.DISABLED)
+                                        .setTcStatus(TcStatus.NOT_AVAILABLE)
+                                        .setAddrStatus(AddrStatus.NOT_AVAILABLE)
+                                        .build())
+                        .setTermsAndConditionsWebUrl(EMERGENCY_ADDRESS_WEB_URL)
+                        .build();
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+        buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
+
+        mWfcActivationController.evaluateEntitlementStatus();
+
+        verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL, null);
     }
 
     @Test
     public void handleEntitlementStatusForActivation_isIncompatibleTrue_showErrorUi() {
-        EntitlementResult mEntitlementResult =
+        EntitlementResult entitlementResult =
                 EntitlementResult.builder()
                         .setVowifiStatus(
                                 Ts43VowifiStatus.builder()
                                         .setEntitlementStatus(EntitlementStatus.INCOMPATIBLE)
                                         .build())
                         .build();
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
@@ -215,7 +341,7 @@
 
     @Test
     public void handleEntitlementStatusForActivation_unexpectedStatus_showGeneralErrorUi() {
-        EntitlementResult mEntitlementResult =
+        EntitlementResult entitlementResult =
                 EntitlementResult.builder()
                         .setVowifiStatus(
                                 Ts43VowifiStatus.builder()
@@ -224,7 +350,7 @@
                                         .setAddrStatus(AddrStatus.IN_PROGRESS)
                                         .build())
                         .build();
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.evaluateEntitlementStatus();
@@ -234,7 +360,7 @@
 
     @Test
     public void handleEntitlementStatusAfterActivation_isVowifiEntitledTrue_setActivityResultOk() {
-        EntitlementResult mEntitlementResult =
+        EntitlementResult entitlementResult =
                 EntitlementResult.builder()
                         .setVowifiStatus(
                                 Ts43VowifiStatus.builder()
@@ -244,17 +370,17 @@
                                         .setProvStatus(ProvStatus.PROVISIONED)
                                         .build())
                         .build();
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
 
-        verify(mActivationUi).setResultAndFinish(Activity.RESULT_OK);
+        verify(mMockActivationUi).setResultAndFinish(Activity.RESULT_OK);
     }
 
     @Test
     public void handleEntitlementStatusAfterActivation_unexpectedStatus_showGeneralErrorUi() {
-        EntitlementResult mEntitlementResult =
+        EntitlementResult entitlementResult =
                 EntitlementResult.builder()
                         .setVowifiStatus(
                                 Ts43VowifiStatus.builder()
@@ -263,7 +389,7 @@
                                         .setAddrStatus(AddrStatus.IN_PROGRESS)
                                         .build())
                         .build();
-        when(mActivationApi.checkEntitlementStatus()).thenReturn(mEntitlementResult);
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
         buildActivity(ActivityConstants.LAUNCH_APP_ACTIVATE);
 
         mWfcActivationController.reevaluateEntitlementStatus();
@@ -271,24 +397,174 @@
         verifyErrorUi(R.string.activate_title, R.string.wfc_activation_error);
     }
 
+    @Test
+    public void handleEntitlementStatusAfterUpdating_entitlementStatusEnabled_setResultOk() {
+        EntitlementResult entitlementResult =
+                EntitlementResult.builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus.builder()
+                                        .setEntitlementStatus(EntitlementStatus.ENABLED)
+                                        .setTcStatus(TcStatus.AVAILABLE)
+                                        .setAddrStatus(AddrStatus.AVAILABLE)
+                                        .setProvStatus(ProvStatus.PROVISIONED)
+                                        .build())
+                        .build();
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
+
+        mWfcActivationController.reevaluateEntitlementStatus();
+
+        verify(mMockActivationUi).setResultAndFinish(eq(Activity.RESULT_OK));
+    }
+
+    @Test
+    public void handleEntitlementStatusAfterUpdating_entitlementStatusNoServerData_turnOffWfc() {
+        EntitlementResult entitlementResult =
+                EntitlementResult.builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus.builder()
+                                        .setEntitlementStatus(EntitlementStatus.DISABLED)
+                                        .setTcStatus(TcStatus.NOT_AVAILABLE)
+                                        .setAddrStatus(AddrStatus.NOT_AVAILABLE)
+                                        .build())
+                        .build();
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
+        intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP,
+                ActivityConstants.LAUNCH_APP_UPDATE);
+        mWfcActivationController =
+                new WfcActivationController(
+                        mContext,
+                        mMockActivationUi,
+                        mMockActivationApi,
+                        intent,
+                        mMockImsUtils,
+                        mMockMetricsLogger);
+
+        mWfcActivationController.reevaluateEntitlementStatus();
+
+        verify(mMockImsUtils).turnOffWfc(any());
+    }
+
+    @Test
+    public void handleEntitlementStatusAfterUpdating_unexpectedStatus_showGeneralErrorUi() {
+        EntitlementResult entitlementResult =
+                EntitlementResult.builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus.builder()
+                                        .setEntitlementStatus(EntitlementStatus.DISABLED)
+                                        .setTcStatus(TcStatus.IN_PROGRESS)
+                                        .setAddrStatus(AddrStatus.IN_PROGRESS)
+                                        .build())
+                        .build();
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
+
+        mWfcActivationController.reevaluateEntitlementStatus();
+
+        verifyErrorUi(R.string.e911_title, R.string.address_update_error);
+    }
+
+    @Test
+    public void handleEntitlementStatusForUpdate_serviceEntitled_showWebview() {
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(
+                EntitlementResult
+                        .builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus
+                                        .builder()
+                                        .setEntitlementStatus(EntitlementStatus.ENABLED)
+                                        .setProvStatus(ProvStatus.PROVISIONED)
+                                        .setTcStatus(TcStatus.AVAILABLE)
+                                        .setAddrStatus(AddrStatus.AVAILABLE)
+                                        .build())
+                        .setEmergencyAddressWebUrl(EMERGENCY_ADDRESS_WEB_URL)
+                        .setEmergencyAddressWebData(EMERGENCY_ADDRESS_WEB_DATA)
+                        .build());
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
+
+        mWfcActivationController.evaluateEntitlementStatus();
+
+        verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL,
+                EMERGENCY_ADDRESS_WEB_DATA);
+    }
+
+    @Test
+    public void handleEntitlementStatusForUpdate_showTc_showWebview() {
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(
+                EntitlementResult
+                        .builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus
+                                        .builder()
+                                        .setEntitlementStatus(EntitlementStatus.ENABLED)
+                                        .setProvStatus(ProvStatus.PROVISIONED)
+                                        .setTcStatus(TcStatus.AVAILABLE)
+                                        .setAddrStatus(AddrStatus.AVAILABLE)
+                                        .build())
+                        .setTermsAndConditionsWebUrl(EMERGENCY_ADDRESS_WEB_URL)
+                        .build());
+        buildActivity(ActivityConstants.LAUNCH_APP_SHOW_TC);
+
+        mWfcActivationController.evaluateEntitlementStatus();
+
+        verify(mMockActivationUi).showWebview(EMERGENCY_ADDRESS_WEB_URL, null);
+    }
+
+    @Test
+    public void handleEntitlementStatusForUpdate_entitlementStatusIncompatible_showErrorUi() {
+        EntitlementResult entitlementResult =
+                EntitlementResult.builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus.builder()
+                                        .setEntitlementStatus(EntitlementStatus.INCOMPATIBLE)
+                                        .build())
+                        .build();
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
+
+        mWfcActivationController.evaluateEntitlementStatus();
+
+        verifyErrorUi(R.string.e911_title, R.string.failure_contact_carrier);
+    }
+
+    @Test
+    public void handleEntitlementStatusForUpdate_entitlementStatusDisabled_showGenericErrorUi() {
+        EntitlementResult entitlementResult =
+                EntitlementResult.builder()
+                        .setVowifiStatus(
+                                Ts43VowifiStatus.builder()
+                                        .setEntitlementStatus(EntitlementStatus.DISABLED)
+                                        .build())
+                        .build();
+        when(mMockActivationApi.checkEntitlementStatus()).thenReturn(entitlementResult);
+        buildActivity(ActivityConstants.LAUNCH_APP_UPDATE);
+
+        mWfcActivationController.evaluateEntitlementStatus();
+
+        verifyErrorUi(R.string.e911_title, R.string.address_update_error);
+    }
+
     private void buildActivity(int extraLaunchCarrierApp) {
         Intent intent = new Intent(Intent.ACTION_MAIN);
         intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, SUB_ID);
         intent.putExtra(ActivityConstants.EXTRA_LAUNCH_CARRIER_APP, extraLaunchCarrierApp);
         mWfcActivationController =
-                new WfcActivationController(mContext, mActivationUi, mActivationApi, intent);
+                new WfcActivationController(mContext, mMockActivationUi, mMockActivationApi,
+                        intent);
     }
 
     private void setNetworkConnected(boolean isConnected) {
-        when(mNetworkInfo.isConnected()).thenReturn(isConnected);
+        when(mMockNetworkInfo.isConnected()).thenReturn(isConnected);
         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
-                mConnectivityManager);
-        when(mConnectivityManager.getActiveNetworkInfo()).thenReturn(mNetworkInfo);
-        when(mNetworkInfo.isConnected()).thenReturn(isConnected);
+                mMockConnectivityManager);
+        when(mMockConnectivityManager.getActiveNetworkInfo()).thenReturn(mMockNetworkInfo);
+        when(mMockNetworkInfo.isConnected()).thenReturn(isConnected);
     }
 
     private void verifyErrorUi(int title, int errorMesssage) {
-        verify(mActivationUi)
+        verify(mMockActivationUi)
                 .showActivationUi(
                         title,
                         errorMesssage,
@@ -298,7 +574,7 @@
     }
 
     private void verifyErrorUiInOrder(InOrder inOrder, int title, int errorMesssage) {
-        inOrder.verify(mActivationUi)
+        inOrder.verify(mMockActivationUi)
                 .showActivationUi(
                         title,
                         errorMesssage,
@@ -308,7 +584,7 @@
     }
 
     private void verifyGeneralWaitingUiInOrder(InOrder inOrder, int title) {
-        inOrder.verify(mActivationUi)
+        inOrder.verify(mMockActivationUi)
                 .showActivationUi(title, R.string.progress_text, true, 0, 0, 0);
     }
 }
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationTest.java b/tests/unittests/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationTest.java
index 4260e5e..eb32c49 100644
--- a/tests/unittests/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationTest.java
+++ b/tests/unittests/src/com/android/imsserviceentitlement/entitlement/EntitlementConfigurationTest.java
@@ -68,6 +68,25 @@
                     + "        <parm name=\"EntitlementStatus\" value=\"0\"/>\n"
                     + "    </characteristic>\n"
                     + "</wap-provisioningdoc>\n";
+    private static final String RAW_XML_NO_VERS_VERSION =
+            "<wap-provisioningdoc version=\"1.1\">\n"
+                    + "    <characteristic type=\"VERS\">\n"
+                    + "        <parm name=\"version\" value=\"\"/>\n"
+                    + "        <parm name=\"validity\" value=\"1728000\"/>\n"
+                    + "    </characteristic>\n"
+                    + "    <characteristic type=\"TOKEN\">\n"
+                    + "        <parm name=\"token\" value=\"kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX\"/>\n"
+                    + "        <parm name=\"validity\" value=\"3600\"/>\n"
+                    + "    </characteristic>\n"
+                    + "    <characteristic type=\"APPLICATION\">\n"
+                    + "        <parm name=\"AppID\" value=\"ap2004\"/>\n"
+                    + "        <parm name=\"EntitlementStatus\" value=\"1\"/>\n"
+                    + "    </characteristic>\n"
+                    + "    <characteristic type=\"APPLICATION\">\n"
+                    + "        <parm name=\"AppID\" value=\"ap2003\"/>\n"
+                    + "        <parm name=\"EntitlementStatus\" value=\"0\"/>\n"
+                    + "    </characteristic>\n"
+                    + "</wap-provisioningdoc>\n";
     private static final int SUB_ID = 1;
 
     private Context mContext;
@@ -89,6 +108,7 @@
         assertThat(mConfiguration.getSmsOverIpStatus()).isEqualTo(2);
         assertThat(mConfiguration.getToken().get()).isEqualTo("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX");
         assertThat(mConfiguration.getTokenValidity()).isEqualTo(3600);
+        assertThat(mConfiguration.getVersion()).isEqualTo("1");
         assertThat(mConfiguration.entitlementValidation()).isEqualTo(
                 ClientBehavior.VALID_DURING_VALIDITY);
     }
@@ -113,4 +133,11 @@
         assertThat(mConfiguration.getToken().get()).isEqualTo("kZYfCEpSsMr88KZVmab5UsZVzl+nWSsX");
         assertThat(mConfiguration.getTokenValidity()).isEqualTo(0);
     }
+
+    @Test
+    public void updateConfigurations_noVersVersion_getDefaultValue() {
+        mConfiguration.update(RAW_XML_NO_VERS_VERSION);
+
+        assertThat(mConfiguration.getVersion()).isEqualTo("0");
+    }
 }
diff --git a/tests/unittests/src/com/android/imsserviceentitlement/utils/ImsUtilsTest.java b/tests/unittests/src/com/android/imsserviceentitlement/utils/ImsUtilsTest.java
new file mode 100644
index 0000000..961e913
--- /dev/null
+++ b/tests/unittests/src/com/android/imsserviceentitlement/utils/ImsUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2021 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.utils;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ProvisioningManager;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsUtilsTest {
+    @Rule public final MockitoRule rule = MockitoJUnit.rule();
+
+    @Mock CarrierConfigManager mMockCarrierConfigManager;
+    @Mock ImsMmTelManager mMockImsMmTelManager;
+    @Mock ProvisioningManager mMockProvisioningManager;
+
+    private Context mContext = ApplicationProvider.getApplicationContext();
+
+    @Test
+    public void isWfcEnabledByUser_invalidSubId_defaultValues() {
+        ImsUtils imsUtils =
+                ImsUtils.getInstance(mContext, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+
+        assertThat(imsUtils.isWfcEnabledByUser()).isFalse();
+    }
+
+    @Test
+    public void disableAndResetVoWiFiImsSettings_hasCarrierConfig() {
+        PersistableBundle carrierConfig = new PersistableBundle();
+        carrierConfig.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT, 1);
+        carrierConfig.putInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_ROAMING_MODE_INT, 2);
+        ImsUtils imsUtils =
+                new ImsUtils(carrierConfig, mMockImsMmTelManager, mMockProvisioningManager);
+
+        imsUtils.disableAndResetVoWiFiImsSettings();
+
+        verify(mMockImsMmTelManager).setVoWiFiSettingEnabled(eq(false));
+        verify(mMockImsMmTelManager).setVoWiFiModeSetting(eq(1));
+        verify(mMockImsMmTelManager).setVoWiFiRoamingModeSetting(eq(2));
+    }
+
+    @Test
+    public void disableWfc() {
+        ImsUtils imsUtils = new ImsUtils(
+                new PersistableBundle(), mMockImsMmTelManager, mMockProvisioningManager);
+
+        imsUtils.disableWfc();
+
+        verify(mMockImsMmTelManager).setVoWiFiSettingEnabled(false);
+    }
+}