Post SUW Slot Change Receiver Migration

Implement the case when user inserts / removes pSIM during the SUW.
Bug: 153811431
Bug: 170508680
Test: Manually tested

Change-Id: Iccc3a2fd416ccfb57c3b4f9dc7b32c0b7681c90b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 81a5ea8..f3066ce 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3662,6 +3662,14 @@
         </receiver>
 
         <receiver
+            android:name=".sim.receivers.SuwFinishReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.google.android.setupwizard.SETUP_WIZARD_FINISHED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver
             android:name=".sim.receivers.SimCompleteBootReceiver"
             android:exported="true">
             <intent-filter>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 09aea0e..d497e4e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -12142,6 +12142,10 @@
     <string name="switch_to_removable_notification_no_carrier_name">Switched to another carrier</string>
     <!-- Message in a push notification indicating that the user's phone has connected to a different mobile network. [CHAR LIMIT=NONE] -->
     <string name="network_changed_notification_text">Your mobile network has changed</string>
+    <!-- Title on a push notification indicating that the user's device is capable of DSDS. [CHAR LIMIT=NONE] -->
+    <string name="dsds_notification_after_suw_title">Set up your other SIM</string>
+    <!-- Message in a push notification indicating that the user's device is capable of DSDS. [CHAR LIMIT=NONE] -->
+    <string name="dsds_notification_after_suw_text">Choose your active SIM or use 2 SIMs at once</string>
 
     <!-- Strings for choose SIM activity -->
     <!--  The title text of choose SIM activity. [CHAR LIMIT=NONE] -->
diff --git a/src/com/android/settings/sim/SimActivationNotifier.java b/src/com/android/settings/sim/SimActivationNotifier.java
index a38816a..735cb46 100644
--- a/src/com/android/settings/sim/SimActivationNotifier.java
+++ b/src/com/android/settings/sim/SimActivationNotifier.java
@@ -66,12 +66,15 @@
             value = {
                 NotificationType.NETWORK_CONFIG,
                 NotificationType.SWITCH_TO_REMOVABLE_SLOT,
+                NotificationType.ENABLE_DSDS,
             })
     public @interface NotificationType {
         // The notification to remind users to config network Settings.
         int NETWORK_CONFIG = 1;
         // The notification to notify users that the device is switched to the removable slot.
         int SWITCH_TO_REMOVABLE_SLOT = 2;
+        // The notification to notify users that the device is capable of DSDS.
+        int ENABLE_DSDS = 3;
     }
 
     private final Context mContext;
@@ -120,8 +123,8 @@
             return;
         }
 
-        CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
-                activeRemovableSub, mContext);
+        CharSequence displayName =
+                SubscriptionUtil.getUniqueSubscriptionDisplayName(activeRemovableSub, mContext);
         String carrierName =
                 TextUtils.isEmpty(displayName)
                         ? mContext.getString(R.string.sim_card_label)
@@ -135,7 +138,8 @@
                 TaskStackBuilder.create(mContext).addNextIntent(clickIntent);
         PendingIntent contentIntent =
                 stackBuilder.getPendingIntent(
-                        0 /* requestCode */, PendingIntent.FLAG_UPDATE_CURRENT);
+                        0 /* requestCode */,
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
 
         Notification.Builder builder =
                 new Notification.Builder(mContext, SIM_SETUP_CHANNEL_ID)
@@ -155,7 +159,8 @@
                 TaskStackBuilder.create(mContext).addNextIntent(clickIntent);
         PendingIntent contentIntent =
                 stackBuilder.getPendingIntent(
-                        0 /* requestCode */, PendingIntent.FLAG_UPDATE_CURRENT);
+                        0 /* requestCode */,
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
         String titleText =
                 TextUtils.isEmpty(carrierName)
                         ? mContext.getString(
@@ -178,6 +183,33 @@
         mNotificationManager.notify(SWITCH_TO_REMOVABLE_SLOT_NOTIFICATION_ID, builder.build());
     }
 
+    /** Sends a push notification for enabling DSDS. */
+    public void sendEnableDsdsNotification() {
+        Intent parentIntent = new Intent(mContext, Settings.MobileNetworkListActivity.class);
+
+        Intent clickIntent = new Intent(mContext, DsdsDialogActivity.class);
+
+        TaskStackBuilder stackBuilder =
+                TaskStackBuilder.create(mContext)
+                        .addNextIntentWithParentStack(parentIntent)
+                        .addNextIntent(clickIntent);
+        PendingIntent contentIntent =
+                stackBuilder.getPendingIntent(
+                        0 /* requestCode */,
+                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+        Notification.Builder builder =
+                new Notification.Builder(mContext, SIM_SETUP_CHANNEL_ID)
+                        .setContentTitle(
+                                mContext.getString(R.string.dsds_notification_after_suw_title))
+                        .setContentText(
+                                mContext.getString(R.string.dsds_notification_after_suw_text))
+                        .setContentIntent(contentIntent)
+                        .setSmallIcon(R.drawable.ic_sim_alert)
+                        .setAutoCancel(true);
+        mNotificationManager.notify(SIM_ACTIVATION_NOTIFICATION_ID, builder.build());
+    }
+
     @Nullable
     private SubscriptionInfo getActiveRemovableSub() {
         SubscriptionManager subscriptionManager =
diff --git a/src/com/android/settings/sim/SimNotificationService.java b/src/com/android/settings/sim/SimNotificationService.java
index 0f52c8b..42b5e58 100644
--- a/src/com/android/settings/sim/SimNotificationService.java
+++ b/src/com/android/settings/sim/SimNotificationService.java
@@ -71,6 +71,9 @@
             case SimActivationNotifier.NotificationType.SWITCH_TO_REMOVABLE_SLOT:
                 new SimActivationNotifier(this).sendSwitchedToRemovableSlotNotification();
                 break;
+            case SimActivationNotifier.NotificationType.ENABLE_DSDS:
+                new SimActivationNotifier(this).sendEnableDsdsNotification();
+                break;
             default:
                 Log.e(TAG, "Invalid notification type: " + notificationType);
                 break;
diff --git a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
index c092428..fe44389 100644
--- a/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
+++ b/src/com/android/settings/sim/receivers/SimSlotChangeHandler.java
@@ -50,7 +50,13 @@
     private static final String TAG = "SimSlotChangeHandler";
 
     private static final String EUICC_PREFS = "euicc_prefs";
+    // Shared preference keys
     private static final String KEY_REMOVABLE_SLOT_STATE = "removable_slot_state";
+    private static final String KEY_SUW_PSIM_ACTION = "suw_psim_action";
+    // User's last removable SIM insertion / removal action during SUW.
+    private static final int LAST_USER_ACTION_IN_SUW_NONE = 0;
+    private static final int LAST_USER_ACTION_IN_SUW_INSERT = 1;
+    private static final int LAST_USER_ACTION_IN_SUW_REMOVE = 2;
 
     private static volatile SimSlotChangeHandler sSlotChangeHandler;
 
@@ -107,6 +113,47 @@
         Log.i(TAG, "Do nothing on slot status changes.");
     }
 
+    void onSuwFinish(Context context) {
+        init(context);
+
+        if (Looper.myLooper() == Looper.getMainLooper()) {
+            throw new IllegalStateException("Cannot be called from main thread.");
+        }
+
+        if (mTelMgr.getActiveModemCount() > 1) {
+            Log.i(TAG, "The device is already in DSDS mode. Do nothing.");
+            return;
+        }
+
+        UiccSlotInfo removableSlotInfo = getRemovableUiccSlotInfo();
+        if (removableSlotInfo == null) {
+            Log.e(TAG, "Unable to find the removable slot. Do nothing.");
+            return;
+        }
+
+        boolean embeddedSimExist = getGroupedEmbeddedSubscriptions().size() != 0;
+        int removableSlotAction = getSuwRemovableSlotAction(mContext);
+        setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_NONE);
+
+        if (embeddedSimExist
+                && removableSlotInfo.getCardStateInfo() == UiccSlotInfo.CARD_STATE_INFO_PRESENT) {
+            if (mTelMgr.isMultiSimSupported() == TelephonyManager.MULTISIM_ALLOWED) {
+                Log.i(TAG, "DSDS condition satisfied. Show notification.");
+                SimNotificationService.scheduleSimNotification(
+                        mContext, SimActivationNotifier.NotificationType.ENABLE_DSDS);
+            } else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_INSERT) {
+                Log.i(
+                        TAG,
+                        "Both removable SIM and eSIM are present. DSDS condition doesn't"
+                            + " satisfied. User inserted pSIM during SUW. Show choose SIM"
+                            + " screen.");
+                startChooseSimActivity(true);
+            }
+        } else if (removableSlotAction == LAST_USER_ACTION_IN_SUW_REMOVE) {
+            handleSimRemove(removableSlotInfo);
+        }
+    }
+
     private void init(Context context) {
         mSubMgr =
                 (SubscriptionManager)
@@ -116,11 +163,11 @@
     }
 
     private void handleSimInsert(UiccSlotInfo removableSlotInfo) {
-        Log.i(TAG, "Detect SIM inserted.");
+        Log.i(TAG, "Handle SIM inserted.");
 
         if (!isSuwFinished(mContext)) {
-            // TODO(b/170508680): Store the action and handle it after SUW is finished.
             Log.i(TAG, "Still in SUW. Handle SIM insertion after SUW is finished");
+            setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_INSERT);
             return;
         }
 
@@ -156,11 +203,11 @@
     }
 
     private void handleSimRemove(UiccSlotInfo removableSlotInfo) {
-        Log.i(TAG, "Detect SIM removed.");
+        Log.i(TAG, "Handle SIM removed.");
 
         if (!isSuwFinished(mContext)) {
-            // TODO(b/170508680): Store the action and handle it after SUW is finished.
             Log.i(TAG, "Still in SUW. Handle SIM removal after SUW is finished");
+            setSuwRemovableSlotAction(mContext, LAST_USER_ACTION_IN_SUW_REMOVE);
             return;
         }
 
@@ -195,6 +242,16 @@
         prefs.edit().putInt(KEY_REMOVABLE_SLOT_STATE, state).apply();
     }
 
+    private int getSuwRemovableSlotAction(Context context) {
+        final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
+        return prefs.getInt(KEY_SUW_PSIM_ACTION, LAST_USER_ACTION_IN_SUW_NONE);
+    }
+
+    private void setSuwRemovableSlotAction(Context context, int action) {
+        final SharedPreferences prefs = context.getSharedPreferences(EUICC_PREFS, MODE_PRIVATE);
+        prefs.edit().putInt(KEY_SUW_PSIM_ACTION, action).apply();
+    }
+
     @Nullable
     private UiccSlotInfo getRemovableUiccSlotInfo() {
         UiccSlotInfo[] slotInfos = mTelMgr.getUiccSlotsInfo();
diff --git a/src/com/android/settings/sim/receivers/SuwFinishReceiver.java b/src/com/android/settings/sim/receivers/SuwFinishReceiver.java
new file mode 100644
index 0000000..7facbe2
--- /dev/null
+++ b/src/com/android/settings/sim/receivers/SuwFinishReceiver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.settings.sim.receivers;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.settings.R;
+import com.android.settingslib.utils.ThreadUtils;
+
+/** The receiver when SUW is finished. */
+public class SuwFinishReceiver extends BroadcastReceiver {
+    private static final String TAG = "SuwFinishReceiver";
+
+    private final SimSlotChangeHandler mSlotChangeHandler = SimSlotChangeHandler.get();
+    private final Object mLock = new Object();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (!context.getResources().getBoolean(R.bool.config_handle_sim_slot_change)) {
+            Log.i(TAG, "The flag is off. Ignore SUW finish event.");
+            return;
+        }
+
+        final BroadcastReceiver.PendingResult pendingResult = goAsync();
+        ThreadUtils.postOnBackgroundThread(
+                () -> {
+                    synchronized (mLock) {
+                        Log.i(TAG, "Detected SUW finished. Checking slot events.");
+                        mSlotChangeHandler.onSuwFinish(context.getApplicationContext());
+                    }
+                    ThreadUtils.postOnMainThread(pendingResult::finish);
+                });
+    }
+}