Merge "Support reset IMS stack in Reset Mobile Network flow" into main
diff --git a/Android.bp b/Android.bp
index 79df743..8353634 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,6 +104,7 @@
         "settings-telephony-protos-lite",
         "statslog-settings",
         "androidx.test.rules",
+        "telephony_flags_core_java_lib",
     ],
 
     plugins: ["androidx.room_room-compiler-plugin"],
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index 80f5962..8e59a7f 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -44,6 +44,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.telephony.flags.Flags;
 import com.android.settings.core.InstrumentedFragment;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.network.ResetNetworkRestrictionViewBuilder;
@@ -121,16 +122,22 @@
     void showFinalConfirmation() {
         Bundle args = new Bundle();
 
-        ResetNetworkRequest request = new ResetNetworkRequest(
-                ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER |
-                ResetNetworkRequest.RESET_VPN_MANAGER
-        );
+        // TODO(b/317276437) Simplify the logic once flag is released
+        int resetOptions = ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER
+                        | ResetNetworkRequest.RESET_VPN_MANAGER;
+        if (Flags.resetMobileNetworkSettings()) {
+            resetOptions |= ResetNetworkRequest.RESET_IMS_STACK;
+        }
+        ResetNetworkRequest request = new ResetNetworkRequest(resetOptions);
         if (mSubscriptions != null && mSubscriptions.size() > 0) {
             int selectedIndex = mSubscriptionSpinner.getSelectedItemPosition();
             SubscriptionInfo subscription = mSubscriptions.get(selectedIndex);
             int subId = subscription.getSubscriptionId();
             request.setResetTelephonyAndNetworkPolicyManager(subId)
                    .setResetApn(subId);
+            if (Flags.resetMobileNetworkSettings()) {
+                request.setResetImsSubId(subId);
+            }
         }
         if (mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked()) {
             request.setResetEsim(getContext().getPackageName())
diff --git a/src/com/android/settings/ResetNetworkRequest.java b/src/com/android/settings/ResetNetworkRequest.java
index 40eebb0..71c12b1 100644
--- a/src/com/android/settings/ResetNetworkRequest.java
+++ b/src/com/android/settings/ResetNetworkRequest.java
@@ -48,11 +48,23 @@
     /* Reset option - reset BluetoothManager */
     public static final int RESET_BLUETOOTH_MANAGER = 0x10;
 
-    /* Subscription ID for not performing reset TelephonyAndNetworkPolicy or reset APN */
+    /* Reset option - reset IMS stack */
+    public static final int RESET_IMS_STACK = 0x20;
+
+    /**
+     *  Subscription ID indicates NOT resetting any of the components below:
+     *  - TelephonyAndNetworkPolicy
+     *  - APN
+     *  - IMS
+     */
     public static final int INVALID_SUBSCRIPTION_ID = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
-    /* Subscription ID for performing reset TelephonyAndNetworkPolicy or reset APN
-        on all subscriptions */
+    /**
+     *  Subscription ID indicates resetting components below for ALL subscriptions:
+     *  - TelephonyAndNetworkPolicy
+     *  - APN
+     *  - IMS
+     */
     public static final int ALL_SUBSCRIPTION_ID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
 
     /* Key within Bundle. To store some connectivity options for reset */
@@ -75,10 +87,14 @@
     @VisibleForTesting
     protected static final String KEY_APN_SUBID = "resetApnSubId";
 
+    /** Key within Bundle. To store subscription ID for resetting IMS. */
+    protected  static final String KEY_RESET_IMS_SUBID = "resetImsSubId";
+
     private int mResetOptions = RESET_NONE;
     private String mResetEsimPackageName;
     private int mResetTelephonyManager = INVALID_SUBSCRIPTION_ID;
     private int mResetApn = INVALID_SUBSCRIPTION_ID;
+    private int mSubscriptionIdToResetIms = INVALID_SUBSCRIPTION_ID;
 
     /**
      * Reconstruct based on keys stored within Bundle.
@@ -93,6 +109,8 @@
         mResetTelephonyManager = optionsFromBundle.getInt(
                 KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID, INVALID_SUBSCRIPTION_ID);
         mResetApn = optionsFromBundle.getInt(KEY_APN_SUBID, INVALID_SUBSCRIPTION_ID);
+        mSubscriptionIdToResetIms = optionsFromBundle.getInt(KEY_RESET_IMS_SUBID,
+                INVALID_SUBSCRIPTION_ID);
     }
 
     /**
@@ -173,6 +191,29 @@
     }
 
     /**
+     * Get the subscription ID applied for resetting IMS.
+     * @return subscription ID.
+     *         {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions.
+     *         {@code INVALID_SUBSCRIPTION_ID} means resetting IMS
+     *         is not part of the option within this request.
+     */
+    public int getResetImsSubId() {
+        return mSubscriptionIdToResetIms;
+    }
+
+    /**
+     * Set the subscription ID applied for resetting APN.
+     * @param subId is the subscription ID referenced from SubscriptionManager.
+     *         {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions.
+     *         {@code INVALID_SUBSCRIPTION_ID} means resetting IMS will not take place.
+     * @return this
+     */
+    public ResetNetworkRequest setResetImsSubId(int subId) {
+        mSubscriptionIdToResetIms = subId;
+        return this;
+    }
+
+    /**
      * Store a copy of this request into Bundle given.
      * @param writeToBundle is a Bundle for storing configurations of this request.
      * @return this request
@@ -182,6 +223,7 @@
         writeToBundle.putString(KEY_ESIM_PACKAGE, mResetEsimPackageName);
         writeToBundle.putInt(KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID, mResetTelephonyManager);
         writeToBundle.putInt(KEY_APN_SUBID, mResetApn);
+        writeToBundle.putInt(KEY_RESET_IMS_SUBID, mSubscriptionIdToResetIms);
         return this;
     }
 
@@ -219,6 +261,9 @@
         if (mResetApn != INVALID_SUBSCRIPTION_ID) {
             builder.resetApn(mResetApn);
         }
+        if ((mResetOptions & RESET_IMS_STACK) != 0) {
+            builder.resetIms(mSubscriptionIdToResetIms);
+        }
         return builder;
     }
 }
diff --git a/src/com/android/settings/ResetSubscriptionContract.java b/src/com/android/settings/ResetSubscriptionContract.java
index 580e907..528a16d 100644
--- a/src/com/android/settings/ResetSubscriptionContract.java
+++ b/src/com/android/settings/ResetSubscriptionContract.java
@@ -23,8 +23,8 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.IntStream;
 
@@ -53,8 +53,8 @@
         mContext = context;
         // Only keeps specific subscription ID required to perform reset operation
         IntStream subIdStream = IntStream.of(
-                resetRequest.getResetTelephonyAndNetworkPolicyManager()
-                , resetRequest.getResetApnSubId());
+                resetRequest.getResetTelephonyAndNetworkPolicyManager(),
+                resetRequest.getResetApnSubId(), resetRequest.getResetImsSubId());
         mResetSubscriptionIds = subIdStream.sorted().distinct()
                 .filter(id -> SubscriptionManager.isUsableSubscriptionId(id))
                 .toArray();
diff --git a/src/com/android/settings/network/ResetNetworkOperationBuilder.java b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
index 3583d06..61f57f9 100644
--- a/src/com/android/settings/network/ResetNetworkOperationBuilder.java
+++ b/src/com/android/settings/network/ResetNetworkOperationBuilder.java
@@ -33,6 +33,7 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
+import com.android.settings.ResetNetworkRequest;
 import com.android.settings.network.apn.ApnSettings;
 
 import java.util.ArrayList;
@@ -213,6 +214,32 @@
     }
 
     /**
+     * Append a step of resetting IMS stack.
+     *
+     * @return this
+     */
+    public ResetNetworkOperationBuilder resetIms(int subId) {
+        attachSystemServiceWork(Context.TELEPHONY_SERVICE,
+                (Consumer<TelephonyManager>) tm -> {
+                    if (subId == ResetNetworkRequest.INVALID_SUBSCRIPTION_ID) {
+                        // Do nothing
+                        return;
+                    }
+                    if (subId == ResetNetworkRequest.ALL_SUBSCRIPTION_ID) {
+                        // Reset IMS for all slots
+                        for (int slotIndex = 0; slotIndex < tm.getActiveModemCount(); slotIndex++) {
+                            tm.resetIms(slotIndex);
+                        }
+                    } else {
+                        // Reset IMS for the slot specified by the sucriptionId.
+                        final int slotIndex = SubscriptionManager.getSlotIndex(subId);
+                        tm.resetIms(slotIndex);
+                    }
+                });
+        return this;
+    }
+
+    /**
      * Construct a Runnable containing all operations appended.
      * @return Runnable
      */
diff --git a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
index 6f5440b..41b6b27 100644
--- a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
+++ b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java
@@ -16,12 +16,12 @@
 
 package com.android.settings.network;
 
-import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -34,6 +34,8 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
+import com.android.settings.ResetNetworkRequest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -129,4 +131,44 @@
         verify(mTelephonyManager).resetSettings();
         verify(mNetworkPolicyManager).factoryReset(imsi);
     }
+
+    @Test
+    public void resetIms_performReset_whenBuildAndRun_withSingleValidSubId() {
+        final int subId = 1;
+        doReturn(mTelephonyManager).when(mTelephonyManager)
+                .createForSubscriptionId(anyInt());
+        doReturn(mTelephonyManager).when(mContext)
+                .getSystemService(Context.TELEPHONY_SERVICE);
+
+        mBuilder.resetIms(subId).build().run();
+
+        verify(mTelephonyManager).resetIms(anyInt());
+    }
+
+    @Test
+    public void resetIms_performReset_whenBuildAndRun_withInvalidSubId() {
+        final int subId = ResetNetworkRequest.INVALID_SUBSCRIPTION_ID;
+        doReturn(mTelephonyManager).when(mTelephonyManager)
+                .createForSubscriptionId(anyInt());
+        doReturn(mTelephonyManager).when(mContext)
+                .getSystemService(Context.TELEPHONY_SERVICE);
+
+        mBuilder.resetIms(subId).build().run();
+
+        verify(mTelephonyManager, never()).resetIms(anyInt());
+    }
+
+    @Test
+    public void resetIms_performReset_whenBuildAndRun_withAllValidSubId() {
+        final int subId = ResetNetworkRequest.ALL_SUBSCRIPTION_ID;
+        doReturn(mTelephonyManager).when(mTelephonyManager)
+                .createForSubscriptionId(anyInt());
+        doReturn(mTelephonyManager).when(mContext)
+                .getSystemService(Context.TELEPHONY_SERVICE);
+        doReturn(2).when(mTelephonyManager).getActiveModemCount();
+
+        mBuilder.resetIms(subId).build().run();
+
+        verify(mTelephonyManager, times(2)).resetIms(anyInt());
+    }
 }