Add support for selected contacts device to device sharing.

Bug: 163085177
Test: CTS tests
Change-Id: I4f6da463a29ba9cf067d36941a9b4e584012c817
Merged-In: I4f6da463a29ba9cf067d36941a9b4e584012c817
diff --git a/core/api/current.txt b/core/api/current.txt
index 2e78975..7fd3bca 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40797,6 +40797,7 @@
     method public static int getDefaultSubscriptionId();
     method public static int getDefaultVoiceSubscriptionId();
     method public int getDeviceToDeviceStatusSharing(int);
+    method @NonNull public java.util.List<android.net.Uri> getDeviceToDeviceStatusSharingContacts(int);
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions();
     method public static int getSlotIndex(int);
     method @Nullable public int[] getSubscriptionIds(int);
@@ -40810,6 +40811,7 @@
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingContacts(@NonNull java.util.List<android.net.Uri>, int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int);
     method public void setSubscriptionOverrideCongested(int, boolean, long);
     method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
@@ -40824,8 +40826,9 @@
     field public static final int D2D_SHARING_ALL = 3; // 0x3
     field public static final int D2D_SHARING_ALL_CONTACTS = 1; // 0x1
     field public static final int D2D_SHARING_DISABLED = 0; // 0x0
-    field public static final int D2D_SHARING_STARRED_CONTACTS = 2; // 0x2
+    field public static final int D2D_SHARING_SELECTED_CONTACTS = 2; // 0x2
     field public static final String D2D_STATUS_SHARING = "d2d_sharing_status";
+    field public static final String D2D_STATUS_SHARING_SELECTED_CONTACTS = "d2d_sharing_contacts";
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
     field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9c230d5..d22f7e3 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5342,5 +5342,14 @@
          * @hide
          */
         public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status";
+
+        /**
+         * TelephonyProvider column name for information selected contacts that allow device to
+         * device sharing.
+         *
+         * @hide
+         */
+        public static final String COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS =
+                "d2d_sharing_contacts";
     }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 417c8aa..06a2648 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -56,6 +56,7 @@
 import android.provider.Telephony.SimInfo;
 import android.telephony.euicc.EuiccManager;
 import android.telephony.ims.ImsMmTelManager;
+import android.util.Base64;
 import android.util.Log;
 import android.util.Pair;
 
@@ -67,6 +68,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.telephony.Rlog;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -592,9 +598,9 @@
     public static final int D2D_SHARING_ALL_CONTACTS = 1;
 
     /**
-     * Device status is shared with all starred contacts.
+     * Device status is shared with all selected contacts.
      */
-    public static final int D2D_SHARING_STARRED_CONTACTS = 2;
+    public static final int D2D_SHARING_SELECTED_CONTACTS = 2;
 
     /**
      * Device status is shared whenever possible.
@@ -607,7 +613,7 @@
             value = {
                     D2D_SHARING_DISABLED,
                     D2D_SHARING_ALL_CONTACTS,
-                    D2D_SHARING_STARRED_CONTACTS,
+                    D2D_SHARING_SELECTED_CONTACTS,
                     D2D_SHARING_ALL
             })
     public @interface DeviceToDeviceStatusSharing {}
@@ -619,6 +625,13 @@
     public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING;
 
     /**
+     * TelephonyProvider column name for contacts information that allow device to device sharing.
+     * <P>Type: TEXT (String)</P>
+     */
+    public static final String D2D_STATUS_SHARING_SELECTED_CONTACTS =
+            SimInfo.COLUMN_D2D_STATUS_SHARING_SELECTED_CONTACTS;
+
+    /**
      * TelephonyProvider column name for the color of a SIM.
      * <P>Type: INTEGER (int)</P>
      */
@@ -2419,6 +2432,57 @@
     }
 
     /**
+     * Serialize list of contacts uri to string
+     * @hide
+     */
+    public static String serializeUriLists(List<Uri> uris) {
+        List<String> contacts = new ArrayList<>();
+        for (Uri uri : uris) {
+            contacts.add(uri.toString());
+        }
+        try {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream();
+            ObjectOutputStream oos = new ObjectOutputStream(bos);
+            oos.writeObject(contacts);
+            oos.flush();
+            return Base64.encodeToString(bos.toByteArray(), Base64.DEFAULT);
+        } catch (IOException e) {
+            logd("serializeUriLists IO exception");
+        }
+        return "";
+    }
+
+    /**
+     * Return list of contacts uri corresponding to query result.
+     * @param subId Subscription Id of Subscription
+     * @param propKey Column name in SubscriptionInfo database
+     * @return list of contacts uri to be returned
+     * @hide
+     */
+    private static List<Uri> getContactsFromSubscriptionProperty(int subId, String propKey,
+            Context context) {
+        String result = getSubscriptionProperty(subId, propKey, context);
+        if (result != null) {
+            try {
+                byte[] b = Base64.decode(result, Base64.DEFAULT);
+                ByteArrayInputStream bis = new ByteArrayInputStream(b);
+                ObjectInputStream ois = new ObjectInputStream(bis);
+                List<String> contacts = ArrayList.class.cast(ois.readObject());
+                List<Uri> uris = new ArrayList<>();
+                for (String contact : contacts) {
+                    uris.add(Uri.parse(contact));
+                }
+                return uris;
+            } catch (IOException e) {
+                logd("getContactsFromSubscriptionProperty IO exception");
+            } catch (ClassNotFoundException e) {
+                logd("getContactsFromSubscriptionProperty ClassNotFound exception");
+            }
+        }
+        return new ArrayList<>();
+    }
+
+    /**
      * Store properties associated with SubscriptionInfo in database
      * @param subId Subscription Id of Subscription
      * @param propKey Column name in SubscriptionInfo database
@@ -3432,6 +3496,40 @@
     }
 
     /**
+     * Set the list of contacts that allow device to device status sharing for a subscription ID.
+     * The setting app uses this method to indicate with whom they wish to share device to device
+     * status information.
+     * @param contacts The list of contacts that allow device to device status sharing
+     * @param subscriptionId The unique Subscription ID in database
+     */
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void setDeviceToDeviceStatusSharingContacts(@NonNull List<Uri> contacts,
+            int subscriptionId) {
+        String contactString = serializeUriLists(contacts);
+        if (VDBG) {
+            logd("[setDeviceToDeviceStatusSharingContacts] + contacts: " + contactString
+                    + " subId: " + subscriptionId);
+        }
+        setSubscriptionPropertyHelper(subscriptionId, "setDeviceToDeviceSharingStatus",
+                (iSub)->iSub.setDeviceToDeviceStatusSharingContacts(serializeUriLists(contacts),
+                        subscriptionId));
+    }
+
+    /**
+     * Returns the list of contacts that allow device to device status sharing.
+     * @param subscriptionId Subscription id of subscription
+     * @return The list of contacts that allow device to device status sharing
+     */
+    public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(
+            int subscriptionId) {
+        if (VDBG) {
+            logd("[getDeviceToDeviceStatusSharingContacts] + subId: " + subscriptionId);
+        }
+        return getContactsFromSubscriptionProperty(subscriptionId,
+                D2D_STATUS_SHARING_SELECTED_CONTACTS, mContext);
+    }
+
+    /**
      * DO NOT USE.
      * This API is designed for features that are not finished at this point. Do not call this API.
      * @hide
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 9493c76..6493772 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -302,4 +302,6 @@
     int setUiccApplicationsEnabled(boolean enabled, int subscriptionId);
 
     int setDeviceToDeviceStatusSharing(int sharing, int subId);
+
+    int setDeviceToDeviceStatusSharingContacts(String contacts, int subscriptionId);
 }