Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fbcbce28 authored by Jack Yu's avatar Jack Yu
Browse files

Support get/set subscription property

1. Support getSubscriptionProperty.
2. Added group update support. Setting some fields in the
   subscription will automatically apply to the other
   subscriptions in the group.

Bug: 239607619
Test: atest SubscriptionManagerServiceTest
      atest SubscriptionDatabaseManagerTest
Merged-In: I6f617aad7e2776e320eaf157b3a71446143ef401
Change-Id: I6f617aad7e2776e320eaf157b3a71446143ef401
parent 00dc8a15
Loading
Loading
Loading
Loading
+160 −118
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.os.UserHandle;
import android.provider.Telephony.SimInfo;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.ImsMmTelManager;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
@@ -2591,17 +2592,27 @@ public class SubscriptionManager {
    }

    /**
     * Store properties associated with SubscriptionInfo in database
     * @param subId Subscription Id of Subscription
     * @param propKey Column name in database associated with SubscriptionInfo
     * @param propValue Value to store in DB for particular subId & column name
     * Set a field in the subscription database. Note not all fields are supported.
     *
     * @param subscriptionId Subscription Id of Subscription.
     * @param columnName Column name in the database. Note not all fields are supported.
     * @param value Value to store in the database.
     *
     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
     * exposed.
     * @throws SecurityException if callers do not hold the required permission.
     *
     * @see android.provider.Telephony.SimInfo for all the columns.
     *
     * @hide
     */
    public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
    public static void setSubscriptionProperty(int subscriptionId, @NonNull String columnName,
            @NonNull String value) {
        try {
            ISub iSub = TelephonyManager.getSubscriptionService();
            if (iSub != null) {
                iSub.setSubscriptionProperty(subId, propKey, propValue);
                iSub.setSubscriptionProperty(subscriptionId, columnName, value);
            }
        } catch (RemoteException ex) {
            // ignore it
@@ -2630,118 +2641,149 @@ public class SubscriptionManager {
    }

    /**
     * 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
     * @return Value associated with subId and propKey column in database
     * Get specific field in string format from the subscription info database.
     *
     * @param context The calling context.
     * @param subscriptionId Subscription id of the subscription.
     * @param columnName Column name in subscription database.
     *
     * @return Value in string format associated with {@code subscriptionId} and {@code columnName}
     * from the database.
     *
     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
     * exposed.
     *
     * @see android.provider.Telephony.SimInfo for all the columns.
     *
     * @hide
     */
    private static String getSubscriptionProperty(int subId, String propKey,
            Context context) {
    @NonNull
    @RequiresPermission(anyOf = {
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
            "carrier privileges",
    })
    private static String getStringSubscriptionProperty(@NonNull Context context,
            int subscriptionId, @NonNull String columnName) {
        String resultValue = null;
        try {
            ISub iSub = TelephonyManager.getSubscriptionService();
            if (iSub != null) {
                resultValue = iSub.getSubscriptionProperty(subId, propKey,
                resultValue = iSub.getSubscriptionProperty(subscriptionId, columnName,
                        context.getOpPackageName(), context.getAttributionTag());
            }
        } catch (RemoteException ex) {
            // ignore it
        }
        return resultValue;
        return TextUtils.emptyIfNull(resultValue);
    }

    /**
     * Returns boolean value corresponding to query result.
     * @param subId Subscription Id of Subscription
     * @param propKey Column name in SubscriptionInfo database
     * @param defValue Default boolean value to be returned
     * @return boolean result value to be returned
     * Get specific field in {@code boolean} format from the subscription info database.
     *
     * @param subscriptionId Subscription id of the subscription.
     * @param columnName Column name in subscription database.
     * @param defaultValue Default value in case not found or error.
     * @param context The calling context.
     *
     * @return Value in {@code boolean} format associated with {@code subscriptionId} and
     * {@code columnName} from the database, or {@code defaultValue} if not found or error.
     *
     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
     * exposed.
     *
     * @see android.provider.Telephony.SimInfo for all the columns.
     *
     * @hide
     */
    public static boolean getBooleanSubscriptionProperty(int subId, String propKey,
            boolean defValue, Context context) {
        String result = getSubscriptionProperty(subId, propKey, context);
        if (result != null) {
    @RequiresPermission(anyOf = {
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
            "carrier privileges",
    })
    public static boolean getBooleanSubscriptionProperty(int subscriptionId,
            @NonNull String columnName, boolean defaultValue, @NonNull Context context) {
        String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
        if (!result.isEmpty()) {
            try {
                return Integer.parseInt(result) == 1;
            } catch (NumberFormatException err) {
                logd("getBooleanSubscriptionProperty NumberFormat exception");
            }
        }
        return defValue;
        return defaultValue;
    }

    /**
     * Returns integer value corresponding to query result.
     * @param subId Subscription Id of Subscription
     * @param propKey Column name in SubscriptionInfo database
     * @param defValue Default integer value to be returned
     * @return integer result value to be returned
     * Get specific field in {@code integer} format from the subscription info database.
     *
     * @param subscriptionId Subscription id of the subscription.
     * @param columnName Column name in subscription database.
     * @param defaultValue Default value in case not found or error.
     * @param context The calling context.
     *
     * @return Value in {@code integer} format associated with {@code subscriptionId} and
     * {@code columnName} from the database, or {@code defaultValue} if not found or error.
     *
     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
     * exposed.
     *
     * @see android.provider.Telephony.SimInfo for all the columns.
     *
     * @hide
     */
    public static int getIntegerSubscriptionProperty(int subId, String propKey, int defValue,
            Context context) {
        String result = getSubscriptionProperty(subId, propKey, context);
        if (result != null) {
    @RequiresPermission(anyOf = {
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
            "carrier privileges",
    })
    public static int getIntegerSubscriptionProperty(int subscriptionId, @NonNull String columnName,
            int defaultValue, @NonNull Context context) {
        String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
        if (!result.isEmpty()) {
            try {
                return Integer.parseInt(result);
            } catch (NumberFormatException err) {
                logd("getIntegerSubscriptionProperty NumberFormat exception");
            }
        }
        return defValue;
        return defaultValue;
    }

    /**
     * Returns long value corresponding to query result.
     * @param subId Subscription Id of Subscription
     * @param propKey Column name in SubscriptionInfo database
     * @param defValue Default long value to be returned
     * @return long result value to be returned
     * Get specific field in {@code long} format from the subscription info database.
     *
     * @param subscriptionId Subscription id of the subscription.
     * @param columnName Column name in subscription database.
     * @param defaultValue Default value in case not found or error.
     * @param context The calling context.
     *
     * @return Value in {@code long} format associated with {@code subscriptionId} and
     * {@code columnName} from the database, or {@code defaultValue} if not found or error.
     *
     * @throws IllegalArgumentException if {@code subscriptionId} is invalid, or the field is not
     * exposed.
     *
     * @see android.provider.Telephony.SimInfo for all the columns.
     *
     * @hide
     */
    public static long getLongSubscriptionProperty(int subId, String propKey, long defValue,
                                                     Context context) {
        String result = getSubscriptionProperty(subId, propKey, context);
        if (result != null) {
    @RequiresPermission(anyOf = {
            Manifest.permission.READ_PHONE_STATE,
            Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
            "carrier privileges",
    })
    public static long getLongSubscriptionProperty(int subscriptionId, @NonNull String columnName,
            long defaultValue, @NonNull Context context) {
        String result = getStringSubscriptionProperty(context, subscriptionId, columnName);
        if (!result.isEmpty()) {
            try {
                return Long.parseLong(result);
            } catch (NumberFormatException err) {
                logd("getLongSubscriptionProperty NumberFormat exception");
            }
        }
        return defValue;
        return defaultValue;
    }

    /**
@@ -3469,7 +3511,6 @@ public class SubscriptionManager {
     * @param groupUuid of which list of subInfo will be returned.
     * @return list of subscriptionInfo that belong to the same group, including the given
     * subscription itself. It will return an empty list if no subscription belongs to the group.
     *
     */
    @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
    @RequiresPermission(Manifest.permission.READ_PHONE_STATE)
@@ -3509,7 +3550,8 @@ public class SubscriptionManager {
     * want to see their own hidden subscriptions.
     *
     * @param info the subscriptionInfo to check against.
     * @return true if this subscription should be visible to the API caller.
     *
     * @return {@code true} if this subscription should be visible to the API caller.
     *
     * @hide
     */
@@ -3582,9 +3624,9 @@ public class SubscriptionManager {
     * <p>
     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
     *
     * @param subscriptionId Subscription to be enabled or disabled. It could be a eSIM or pSIM
     * subscription.
     * @param enable whether user is turning it on or off.
     * @param subscriptionId Subscription to be enabled or disabled.
     *                       It could be a eSIM or pSIM subscription.
     *
     * @return whether the operation is successful.
     *
@@ -3617,8 +3659,6 @@ public class SubscriptionManager {
     * available from SubscriptionInfo.areUiccApplicationsEnabled() will be updated
     * immediately.)
     *
     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
     *
     * @param subscriptionId which subscription to operate on.
     * @param enabled whether uicc applications are enabled or disabled.
     * @hide
@@ -3651,8 +3691,6 @@ public class SubscriptionManager {
     * It provides whether a physical SIM card can be disabled without taking it out, which is done
     * via {@link #setSubscriptionEnabled(int, boolean)} API.
     *
     * Requires Permission: READ_PRIVILEGED_PHONE_STATE.
     *
     * @return whether can disable subscriptions on physical SIMs.
     *
     * @hide
@@ -3680,13 +3718,9 @@ public class SubscriptionManager {
    }

    /**
     * Check if a subscription is active.
     *
     * @param subscriptionId The subscription id to check.
     *
     * @return {@code true} if the subscription is active.
     * Check if the subscription is currently active in any slot.
     *
     * @throws IllegalArgumentException if the provided slot index is invalid.
     * @param subscriptionId The subscription id.
     *
     * @hide
     */
@@ -3706,15 +3740,14 @@ public class SubscriptionManager {
    }

    /**
     * Set the device to device status sharing user preference for a subscription ID. The setting
     * Set the device to device status sharing user preference for a subscription id. The setting
     * app uses this method to indicate with whom they wish to share device to device status
     * information.
     *
     * @param subscriptionId the unique Subscription ID in database.
     * @param sharing the status sharing preference.
     * @param subscriptionId The subscription id.
     * @param sharing The status sharing preference.
     *
     * @throws IllegalArgumentException if the subscription does not exist, or the sharing
     * preference is invalid.
     * @throws SecurityException if the caller doesn't have permissions required.
     */
    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
    public void setDeviceToDeviceStatusSharingPreference(int subscriptionId,
@@ -3731,6 +3764,8 @@ public class SubscriptionManager {
     * Returns the user-chosen device to device status sharing preference
     * @param subscriptionId Subscription id of subscription
     * @return The device to device status sharing preference
     *
     * @throws SecurityException if the caller doesn't have permissions required.
     */
    public @DeviceToDeviceStatusSharingPreference int getDeviceToDeviceStatusSharingPreference(
            int subscriptionId) {
@@ -3742,15 +3777,14 @@ public class SubscriptionManager {
    }

    /**
     * Set the list of contacts that allow device to device status sharing for a subscription ID.
     * 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 subscriptionId The unique Subscription ID in database.
     * @param subscriptionId The subscription id.
     * @param contacts The list of contacts that allow device to device status sharing.
     *
     * @throws IllegalArgumentException if the subscription does not exist, or contacts is
     * {@code null}.
     * @throws SecurityException if the caller doesn't have permissions required.
     */
    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
    public void setDeviceToDeviceStatusSharingContacts(int subscriptionId,
@@ -3766,38 +3800,46 @@ public class SubscriptionManager {
    }

    /**
     * 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
     * Get the list of contacts that allow device to device status sharing.
     *
     * @param subscriptionId Subscription id.
     *
     * @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);
    public @NonNull List<Uri> getDeviceToDeviceStatusSharingContacts(int subscriptionId) {
        String result = getStringSubscriptionProperty(mContext, subscriptionId,
                D2D_STATUS_SHARING_SELECTED_CONTACTS);
        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 getContactsFromSubscriptionProperty(subscriptionId,
                D2D_STATUS_SHARING_SELECTED_CONTACTS, mContext);
                return uris;
            } catch (IOException e) {
                logd("getDeviceToDeviceStatusSharingContacts IO exception");
            } catch (ClassNotFoundException e) {
                logd("getDeviceToDeviceStatusSharingContacts ClassNotFound exception");
            }
        }
        return new ArrayList<>();
    }

    /**
     * Get the active subscription id by logical SIM slot index.
     *
     * @param slotIndex The logical SIM slot index.
     * @return The active subscription id.
     *
     * @throws IllegalArgumentException if the provided slot index is invalid.
     *
     * DO NOT USE.
     * This API is designed for features that are not finished at this point. Do not call this API.
     * @hide
     * TODO b/135547512: further clean up
     */
    @SystemApi
    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    public int getEnabledSubscriptionId(int slotIndex) {
        int subId = INVALID_SUBSCRIPTION_ID;

        if (!isValidSlotIndex(slotIndex)) {
            throw new IllegalArgumentException("Invalid slot index " + slotIndex);
        }

        try {
            ISub iSub = TelephonyManager.getSubscriptionService();
            if (iSub != null) {
@@ -3839,7 +3881,7 @@ public class SubscriptionManager {
    /**
     * Get active data subscription id. Active data subscription refers to the subscription
     * currently chosen to provide cellular internet connection to the user. This may be
     * different from getDefaultDataSubscriptionId().
     * different from {@link #getDefaultDataSubscriptionId()}.
     *
     * @return Active data subscription id if any is chosen, or {@link #INVALID_SUBSCRIPTION_ID} if
     * not.