Derive "merged" subscribers based on line numbers.

Adds an API to indicate a set of subscriber identities that should
be considered as "merged" together for data usage purposes.  The
internal implementation clusters identities using the carrier-provided
line number override as a key.

Currently only supports a single merge set, which is selected
arbitrarily based on the first active subscriber with a line number
override.

Bug: 18012787
Change-Id: I9a0b1d1850bf1cd1fe9fe90984b00600f7d1c689
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index d83f10d..30c3a39 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -41,10 +41,14 @@
 import android.telephony.RadioAccessFamily;
 import android.telephony.ServiceState;
 import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Slog;
 
 import com.android.ims.ImsManager;
 import com.android.internal.telephony.CallManager;
@@ -66,7 +70,10 @@
 import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 /**
  * Implementation of the ITelephony interface.
@@ -75,6 +82,7 @@
     private static final String LOG_TAG = "PhoneInterfaceManager";
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
     private static final boolean DBG_LOC = false;
+    private static final boolean DBG_MERGE = false;
 
     // Message codes used with mMainThreadHandler
     private static final int CMD_HANDLE_PIN_MMI = 1;
@@ -124,6 +132,7 @@
 
     private static final String PREF_CARRIERS_ALPHATAG_PREFIX = "carrier_alphtag_";
     private static final String PREF_CARRIERS_NUMBER_PREFIX = "carrier_number_";
+    private static final String PREF_CARRIERS_SUBSCRIBER_PREFIX = "carrier_subscriber_";
     private static final String PREF_ENABLE_VIDEO_CALLING = "enable_video_calling";
 
     /**
@@ -1981,28 +1990,43 @@
     }
 
     @Override
-    public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag, String number) {
+    public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag,
+            String number) {
         enforceCarrierPrivilege();
 
-        String iccId = getIccId(subId);
+        final String iccId = getIccId(subId);
+        final String subscriberId = getPhone(subId).getSubscriberId();
+
+        if (DBG_MERGE) {
+            Slog.d(LOG_TAG, "Setting line number for ICC=" + iccId + ", subscriberId="
+                    + subscriberId + " to " + number);
+        }
+
         if (TextUtils.isEmpty(iccId)) {
             return false;
         }
 
-        String alphaTagPrefKey = PREF_CARRIERS_ALPHATAG_PREFIX + iccId;
-        SharedPreferences.Editor editor = mTelephonySharedPreferences.edit();
+        final SharedPreferences.Editor editor = mTelephonySharedPreferences.edit();
+
+        final String alphaTagPrefKey = PREF_CARRIERS_ALPHATAG_PREFIX + iccId;
         if (alphaTag == null) {
             editor.remove(alphaTagPrefKey);
         } else {
             editor.putString(alphaTagPrefKey, alphaTag);
         }
 
-        String numberPrefKey = PREF_CARRIERS_NUMBER_PREFIX + iccId;
+        // Record both the line number and IMSI for this ICCID, since we need to
+        // track all merged IMSIs based on line number
+        final String numberPrefKey = PREF_CARRIERS_NUMBER_PREFIX + iccId;
+        final String subscriberPrefKey = PREF_CARRIERS_SUBSCRIBER_PREFIX + iccId;
         if (number == null) {
             editor.remove(numberPrefKey);
+            editor.remove(subscriberPrefKey);
         } else {
             editor.putString(numberPrefKey, number);
+            editor.putString(subscriberPrefKey, subscriberId);
         }
+
         editor.commit();
         return true;
     }
@@ -2032,6 +2056,69 @@
     }
 
     @Override
+    public String[] getMergedSubscriberIds() {
+        final Context context = mPhone.getContext();
+        final TelephonyManager tele = TelephonyManager.from(context);
+        final SubscriptionManager sub = SubscriptionManager.from(context);
+
+        // Figure out what subscribers are currently active
+        final ArraySet<String> activeSubscriberIds = new ArraySet<>();
+        final int[] subIds = sub.getActiveSubscriptionIdList();
+        for (int subId : subIds) {
+            activeSubscriberIds.add(tele.getSubscriberId(subId));
+        }
+
+        // First pass, find a number override for an active subscriber
+        String mergeNumber = null;
+        final Map<String, ?> prefs = mTelephonySharedPreferences.getAll();
+        for (String key : prefs.keySet()) {
+            if (key.startsWith(PREF_CARRIERS_SUBSCRIBER_PREFIX)) {
+                final String subscriberId = (String) prefs.get(key);
+                if (activeSubscriberIds.contains(subscriberId)) {
+                    final String iccId = key.substring(PREF_CARRIERS_SUBSCRIBER_PREFIX.length());
+                    final String numberKey = PREF_CARRIERS_NUMBER_PREFIX + iccId;
+                    mergeNumber = (String) prefs.get(numberKey);
+                    if (DBG_MERGE) {
+                        Slog.d(LOG_TAG, "Found line number " + mergeNumber
+                                + " for active subscriber " + subscriberId);
+                    }
+                    if (!TextUtils.isEmpty(mergeNumber)) {
+                        break;
+                    }
+                }
+            }
+        }
+
+        // Shortcut when no active merged subscribers
+        if (TextUtils.isEmpty(mergeNumber)) {
+            return null;
+        }
+
+        // Second pass, find all subscribers under that line override
+        final ArraySet<String> result = new ArraySet<>();
+        for (String key : prefs.keySet()) {
+            if (key.startsWith(PREF_CARRIERS_NUMBER_PREFIX)) {
+                final String number = (String) prefs.get(key);
+                if (mergeNumber.equals(number)) {
+                    final String iccId = key.substring(PREF_CARRIERS_NUMBER_PREFIX.length());
+                    final String subscriberKey = PREF_CARRIERS_SUBSCRIBER_PREFIX + iccId;
+                    final String subscriberId = (String) prefs.get(subscriberKey);
+                    if (!TextUtils.isEmpty(subscriberId)) {
+                        result.add(subscriberId);
+                    }
+                }
+            }
+        }
+
+        final String[] resultArray = result.toArray(new String[result.size()]);
+        Arrays.sort(resultArray);
+        if (DBG_MERGE) {
+            Slog.d(LOG_TAG, "Found subscribers " + Arrays.toString(resultArray) + " after merge");
+        }
+        return resultArray;
+    }
+
+    @Override
     public boolean setOperatorBrandOverride(String brand) {
         enforceCarrierPrivilege();
         return mPhone.setOperatorBrandOverride(brand);