create a new notification channel for high prio sim alert

create a high priority notification channel for sim releated alert
in some scenario, we want users to know that they cannot completely
rely on their ability to make a phone call and data call with the
SIM and this may apply to emergency situations.

Bug: 134790138
Test: Manual test
Change-Id: Ia3c6d568d424bf501d77336e3dcb940f4c8179a4
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index 942c1e2..3b9f996 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.READ_PHONE_STATE;
 
+import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -62,6 +63,7 @@
 import com.android.internal.telephony.util.NotificationChannelController;
 import com.android.phone.settings.VoicemailSettingsActivity;
 
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -92,6 +94,7 @@
     static final int CALL_FORWARD_NOTIFICATION = 4;
     static final int DATA_DISCONNECTED_ROAMING_NOTIFICATION = 5;
     static final int SELECTED_OPERATOR_FAIL_NOTIFICATION = 6;
+    static final int LIMITED_SIM_FUNCTION_NOTIFICATION = 7;
 
     // Event for network selection notification.
     private static final int EVENT_PENDING_NETWORK_SELECTION_NOTIFICATION = 1;
@@ -101,6 +104,8 @@
 
     private static final int STATE_UNKNOWN_SERVICE = -1;
 
+    private static final String ACTION_MOBILE_NETWORK_LIST = "android.settings.MOBILE_NETWORK_LIST";
+
     /** The singleton NotificationMgr instance. */
     private static NotificationMgr sInstance;
 
@@ -118,6 +123,9 @@
     // used to track the notification of selected network unavailable, per subscription id.
     private SparseArray<Boolean> mSelectedUnavailableNotify = new SparseArray<>();
 
+    // used to track the notification of limited sim function under dual sim, per subscription id.
+    private Set<Integer> mLimitedSimFunctionNotify = new HashSet<>();
+
     // used to track whether the message waiting indicator is visible, per subscription id.
     private ArrayMap<Integer, Boolean> mMwiVisible = new ArrayMap<Integer, Boolean>();
 
@@ -597,6 +605,88 @@
     }
 
     /**
+     * Shows the "Limited SIM functionality" warning notification, which appears when using a
+     * special carrier under dual sim. limited function applies for DSDS in general when two SIM
+     * cards share a single radio, thus the voice & data maybe impaired under certain scenarios.
+     */
+    public void showLimitedSimFunctionWarningNotification(int subId, @Nullable String carrierName) {
+        if (DBG) log("showLimitedSimFunctionWarningNotification carrier: " + carrierName
+                + " subId: " + subId);
+        if (mLimitedSimFunctionNotify.contains(subId)) {
+            // handle the case that user swipe the notification but condition triggers
+            // frequently which cause the same notification consistently displayed.
+            if (DBG) log("showLimitedSimFunctionWarningNotification, "
+                    + "not display again if already displayed");
+            return;
+        }
+        // Navigate to "Network Selection Settings" which list all subscriptions.
+        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0,
+                new Intent(ACTION_MOBILE_NETWORK_LIST), 0);
+        String line1Num = mTelephonyManager.getLine1Number(subId);
+
+        final CharSequence contentText = TextUtils.isEmpty(line1Num) ?
+            String.format(mContext.getText(
+                R.string.limited_sim_function_notification_message).toString(),
+                carrierName, line1Num) :
+            String.format(mContext.getText(
+                R.string.limited_sim_function_with_phone_num_notification_message).toString(),
+                carrierName);
+        final Notification.Builder builder = new Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.ic_sim_card)
+                .setContentTitle(mContext.getText(
+                        R.string.limited_sim_function_notification_title))
+                .setContentText(contentText)
+                .setOnlyAlertOnce(true)
+                .setOngoing(true)
+                .setChannel(NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY)
+                .setContentIntent(contentIntent);
+        final Notification notification = new Notification.BigTextStyle(builder).bigText(
+                contentText).build();
+
+        mNotificationManager.notifyAsUser(Integer.toString(subId),
+                LIMITED_SIM_FUNCTION_NOTIFICATION,
+                notification, UserHandle.ALL);
+        mLimitedSimFunctionNotify.add(subId);
+    }
+
+    /**
+     * Dismiss the "Limited SIM functionality" warning notification for the given subId.
+     */
+    public void dismissLimitedSimFunctionWarningNotification(int subId) {
+        if (DBG) log("dismissLimitedSimFunctionWarningNotification subId: " + subId);
+        if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+            // dismiss all notifications
+            for (int id : mLimitedSimFunctionNotify) {
+                mNotificationManager.cancelAsUser(Integer.toString(id),
+                        LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+            }
+            mLimitedSimFunctionNotify.clear();
+        } else if (mLimitedSimFunctionNotify.contains(subId)) {
+            mNotificationManager.cancelAsUser(Integer.toString(subId),
+                    LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+            mLimitedSimFunctionNotify.remove(subId);
+        }
+    }
+
+    /**
+     * Dismiss the "Limited SIM functionality" warning notification for all inactive subscriptions.
+     */
+    public void dismissLimitedSimFunctionWarningNotificationForInactiveSubs() {
+        if (DBG) log("dismissLimitedSimFunctionWarningNotificationForInactiveSubs");
+        // dismiss notification for inactive subscriptions.
+        // handle the corner case that SIM change by SIM refresh doesn't clear the notification
+        // from the old SIM if both old & new SIM configured to display the notification.
+        mLimitedSimFunctionNotify.removeIf(id -> {
+            if (!mSubscriptionManager.isActiveSubId(id)) {
+                mNotificationManager.cancelAsUser(Integer.toString(id),
+                        LIMITED_SIM_FUNCTION_NOTIFICATION, UserHandle.ALL);
+                return true;
+            }
+            return false;
+        });
+    }
+
+    /**
      * Display the network selection "no service" notification
      * @param operator is the numeric operator number
      * @param subId is the subscription ID
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 0f8f146..8d3027a 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -46,6 +46,7 @@
 import android.telephony.AnomalyReporter;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
@@ -73,6 +74,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 /**
  * Global state for the telephony subsystem when running in the primary
@@ -649,6 +651,7 @@
                 // Roaming status could be overridden by carrier config, so we need to update it.
                 if (VDBG) Log.v(LOG_TAG, "carrier config changed.");
                 updateDataRoamingStatus();
+                updateLimitedSimFunctionForDualSim();
             } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
                 // We also need to pay attention when default data subscription changes.
                 if (VDBG) Log.v(LOG_TAG, "default data sub changed.");
@@ -755,6 +758,38 @@
         }
     }
 
+    private void updateLimitedSimFunctionForDualSim() {
+        if (DBG) Log.d(LOG_TAG, "updateLimitedSimFunctionForDualSim");
+        // check conditions to display limited SIM function notification under dual SIM
+        SubscriptionManager subMgr = (SubscriptionManager) getSystemService(
+                Context.TELEPHONY_SUBSCRIPTION_SERVICE);
+        List<SubscriptionInfo> subList = subMgr.getActiveSubscriptionInfoList(false);
+        if (subList != null && subList.size() > 1) {
+            CarrierConfigManager configMgr = (CarrierConfigManager)
+                    getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            for (SubscriptionInfo info : subList) {
+                PersistableBundle b = configMgr.getConfigForSubId(info.getSubscriptionId());
+                if (b != null) {
+                    if (b.getBoolean(CarrierConfigManager
+                            .KEY_LIMITED_SIM_FUNCTION_NOTIFICATION_FOR_DSDS_BOOL)) {
+                        notificationMgr.showLimitedSimFunctionWarningNotification(
+                                info.getSubscriptionId(),
+                                info.getDisplayName().toString());
+                    } else {
+                        notificationMgr.dismissLimitedSimFunctionWarningNotification(
+                                info.getSubscriptionId());
+                    }
+                }
+            }
+        } else {
+            // cancel notifications for all subs
+            notificationMgr.dismissLimitedSimFunctionWarningNotification(
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        }
+        notificationMgr.dismissLimitedSimFunctionWarningNotificationForInactiveSubs();
+
+    }
+
     public Phone getPhoneInEcm() {
         return phoneInEcm;
     }