From 00dc8a15e9cb98a36e369be389aea1979682c7cd Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 9 Dec 2022 02:20:59 -0800 Subject: [PATCH 1/2] Added 15 APIs support Added setDisplayNumber, setOpportunistic, createSubscriptionGroup setPreferredDataSubscriptionId, getPreferredDataSubscriptionId getOpportunisticSubscriptions, addSubscriptionsIntoGroup canDisablePhysicalSubscription, setUiccApplicationsEnabled setDeviceToDeviceStatusSharing, setDeviceToDeviceStatusSharingContacts getPhoneNumberFromFirstAvailableSource, setUsageSetting setSubscriptionUserHandle, getSubscriptionUserHandle Test: atest SubscriptionManagerServiceTest Bug: 239607619 Merged-In: I617e94d96ca29d277e0f217ebef5548b7aad1f80 Change-Id: I617e94d96ca29d277e0f217ebef5548b7aad1f80 --- .../telephony/TelephonyPermissions.java | 24 +++++++- .../telephony/SubscriptionManager.java | 58 ++++++++++++++----- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java index 1ba997f4c3347..fdf694303dbc5 100644 --- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java @@ -743,11 +743,23 @@ public final class TelephonyPermissions { /** * Given a list of permissions, check to see if the caller has at least one of them granted. If - * not, check to see if the caller has carrier privileges. If the caller does not have any of + * not, check to see if the caller has carrier privileges. If the caller does not have any of * these permissions, throw a SecurityException. */ public static void enforceAnyPermissionGrantedOrCarrierPrivileges(Context context, int subId, int uid, String message, String... permissions) { + enforceAnyPermissionGrantedOrCarrierPrivileges( + context, subId, uid, false, message, permissions); + } + + /** + * Given a list of permissions, check to see if the caller has at least one of them granted. If + * not, check to see if the caller has carrier privileges on the specified subscription (or any + * subscription if {@code allowCarrierPrivilegeOnAnySub} is {@code true}. If the caller does not + * have any of these permissions, throw a {@link SecurityException}. + */ + public static void enforceAnyPermissionGrantedOrCarrierPrivileges(Context context, int subId, + int uid, boolean allowCarrierPrivilegeOnAnySub, String message, String... permissions) { if (permissions.length == 0) return; boolean isGranted = false; for (String perm : permissions) { @@ -758,7 +770,12 @@ public final class TelephonyPermissions { } if (isGranted) return; - if (checkCarrierPrivilegeForSubId(context, subId)) return; + + if (allowCarrierPrivilegeOnAnySub) { + if (checkCarrierPrivilegeForAnySubId(context, Binder.getCallingUid())) return; + } else { + if (checkCarrierPrivilegeForSubId(context, subId)) return; + } StringBuilder b = new StringBuilder(message); b.append(": Neither user "); @@ -769,7 +786,8 @@ public final class TelephonyPermissions { b.append(" or "); b.append(permissions[i]); } - b.append(" or carrier privileges"); + b.append(" or carrier privileges. subId=" + subId + ", allowCarrierPrivilegeOnAnySub=" + + allowCarrierPrivilegeOnAnySub); throw new SecurityException(b.toString()); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index aedca33d734bc..74ae1bca92428 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -731,6 +731,15 @@ public class SubscriptionManager { /** Indicates that data roaming is disabled for a subscription */ public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"DATA_ROAMING_"}, + value = { + DATA_ROAMING_ENABLE, + DATA_ROAMING_DISABLE + }) + public @interface DataRoamingMode {} + /** * TelephonyProvider column name for subscription carrier id. * @see TelephonyManager#getSimCarrierId() @@ -3671,10 +3680,15 @@ public class SubscriptionManager { } /** - * DO NOT USE. - * This API is designed for features that are not finished at this point. Do not call this API. + * Check if a subscription is active. + * + * @param subscriptionId The subscription id to check. + * + * @return {@code true} if the subscription is active. + * + * @throws IllegalArgumentException if the provided slot index is invalid. + * * @hide - * TODO b/135547512: further clean up */ @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -3695,8 +3709,12 @@ public class SubscriptionManager { * 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 sharing the status sharing preference - * @param subscriptionId the unique Subscription ID in database + * + * @param subscriptionId the unique Subscription ID in database. + * @param sharing the status sharing preference. + * + * @throws IllegalArgumentException if the subscription does not exist, or the sharing + * preference is invalid. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingPreference(int subscriptionId, @@ -3727,8 +3745,12 @@ public class SubscriptionManager { * 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 + * + * @param subscriptionId The unique Subscription ID in database. + * @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}. */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharingContacts(int subscriptionId, @@ -3758,16 +3780,24 @@ public class SubscriptionManager { } /** - * DO NOT USE. - * This API is designed for features that are not finished at this point. Do not call this API. + * 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. + * * @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) { @@ -3809,12 +3839,12 @@ 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(). Eg. Opportunistics data + * different from getDefaultDataSubscriptionId(). * - * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details. + * @return Active data subscription id if any is chosen, or {@link #INVALID_SUBSCRIPTION_ID} if + * not. * - * @return Active data subscription id if any is chosen, or - * SubscriptionManager.INVALID_SUBSCRIPTION_ID if not. + * @see TelephonyCallback.ActiveDataSubscriptionIdListener */ public static int getActiveDataSubscriptionId() { if (isSubscriptionManagerServiceEnabled()) { From fbcbce286f6f74fefd970654fd4b7d4171ad67ee Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Sat, 10 Dec 2022 02:11:18 -0800 Subject: [PATCH 2/2] 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 --- .../telephony/SubscriptionManager.java | 278 ++++++++++-------- 1 file changed, 160 insertions(+), 118 deletions(-) diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 74ae1bca92428..b67e0b558b8b3 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -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 + * 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 List 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 contacts = ArrayList.class.cast(ois.readObject()); - List 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 - * @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 { *

* 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. + * Check if the subscription is currently active in any slot. * - * @param subscriptionId The subscription id to check. - * - * @return {@code true} if the subscription is active. - * - * @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 getDeviceToDeviceStatusSharingContacts( - int subscriptionId) { - if (VDBG) { - logd("[getDeviceToDeviceStatusSharingContacts] + subId: " + subscriptionId); + public @NonNull List 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 contacts = ArrayList.class.cast(ois.readObject()); + List uris = new ArrayList<>(); + for (String contact : contacts) { + uris.add(Uri.parse(contact)); + } + return uris; + } catch (IOException e) { + logd("getDeviceToDeviceStatusSharingContacts IO exception"); + } catch (ClassNotFoundException e) { + logd("getDeviceToDeviceStatusSharingContacts ClassNotFound exception"); + } } - return getContactsFromSubscriptionProperty(subscriptionId, - D2D_STATUS_SHARING_SELECTED_CONTACTS, mContext); + 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.