diff options
Diffstat (limited to 'android/telephony/SubscriptionManager.java')
-rw-r--r-- | android/telephony/SubscriptionManager.java | 269 |
1 files changed, 249 insertions, 20 deletions
diff --git a/android/telephony/SubscriptionManager.java b/android/telephony/SubscriptionManager.java index 1e6abf22..debf43da 100644 --- a/android/telephony/SubscriptionManager.java +++ b/android/telephony/SubscriptionManager.java @@ -16,31 +16,44 @@ package android.telephony; +import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED; +import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED; + +import android.annotation.DurationMillisLong; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.annotation.SdkConstant; -import android.annotation.SystemApi; import android.annotation.SdkConstant.SdkConstantType; +import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.BroadcastOptions; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.net.INetworkPolicyManager; +import android.net.NetworkCapabilities; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.ServiceManager.ServiceNotFoundException; import android.util.DisplayMetrics; + import com.android.internal.telephony.IOnSubscriptionsChangedListener; import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; + import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; /** * SubscriptionManager is the application interface to SubscriptionController @@ -271,6 +284,14 @@ public class SubscriptionManager { public static final String IS_EMBEDDED = "is_embedded"; /** + * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the + * current enabled profile on the card, while for eUICC card it is the EID of the card. + * <P>Type: TEXT (String)</P> + * @hide + */ + public static final String CARD_ID = "card_id"; + + /** * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1. * <p>TYPE: BLOB @@ -430,6 +451,55 @@ public class SubscriptionManager { = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"; /** + * Activity Action: Display UI for managing the billing relationship plans + * between a carrier and a specific subscriber. + * <p> + * Carrier apps are encouraged to implement this activity, and the OS will + * provide an affordance to quickly enter this activity, typically via + * Settings. This affordance will only be shown when the carrier app is + * actively providing subscription plan information via + * {@link #setSubscriptionPlans(int, List)}. + * <p> + * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription + * the user is interested in. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + @SystemApi + public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS + = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; + + /** + * Broadcast Action: Request a refresh of the billing relationship plans + * between a carrier and a specific subscriber. + * <p> + * Carrier apps are encouraged to implement this receiver, and the OS will + * provide an affordance to request a refresh. This affordance will only be + * shown when the carrier app is actively providing subscription plan + * information via {@link #setSubscriptionPlans(int, List)}. + * <p> + * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription + * the user is interested in. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @SystemApi + public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS + = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS"; + + /** + * Broadcast Action: The billing relationship plans between a carrier and a + * specific subscriber has changed. + * <p> + * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription + * changed. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @RequiresPermission(android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS) + public static final String ACTION_SUBSCRIPTION_PLANS_CHANGED + = "android.telephony.action.SUBSCRIPTION_PLANS_CHANGED"; + + /** * Integer extra used with {@link #ACTION_DEFAULT_SUBSCRIPTION_CHANGED} and * {@link #ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED} to indicate the subscription * which has changed. @@ -437,6 +507,7 @@ public class SubscriptionManager { public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX"; private final Context mContext; + private INetworkPolicyManager mNetworkPolicy; /** * A listener class for monitoring changes to {@link SubscriptionInfo} records. @@ -515,16 +586,21 @@ public class SubscriptionManager { } /** - * Get an instance of the SubscriptionManager from the Context. - * This invokes {@link android.content.Context#getSystemService - * Context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)}. - * - * @param context to use. - * @return SubscriptionManager instance + * @deprecated developers should always obtain references directly from + * {@link Context#getSystemService(Class)}. */ + @Deprecated public static SubscriptionManager from(Context context) { - return (SubscriptionManager) context.getSystemService( - Context.TELEPHONY_SUBSCRIPTION_SERVICE); + return (SubscriptionManager) context + .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); + } + + private final INetworkPolicyManager getNetworkPolicy() { + if (mNetworkPolicy == null) { + mNetworkPolicy = INetworkPolicyManager.Stub + .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + } + return mNetworkPolicy; } /** @@ -1612,21 +1688,18 @@ public class SubscriptionManager { * This method is only accessible to the following narrow set of apps: * <ul> * <li>The carrier app for this subscriberId, as determined by - * {@link TelephonyManager#hasCarrierPrivileges(int)}. + * {@link TelephonyManager#hasCarrierPrivileges()}. * <li>The carrier app explicitly delegated access through * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. * </ul> * * @param subId the subscriber this relationship applies to - * @hide */ @SystemApi public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) { - final INetworkPolicyManager npm = INetworkPolicyManager.Stub - .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); try { SubscriptionPlan[] subscriptionPlans = - npm.getSubscriptionPlans(subId, mContext.getOpPackageName()); + getNetworkPolicy().getSubscriptionPlans(subId, mContext.getOpPackageName()); return subscriptionPlans == null ? Collections.emptyList() : Arrays.asList(subscriptionPlans); } catch (RemoteException e) { @@ -1641,7 +1714,7 @@ public class SubscriptionManager { * This method is only accessible to the following narrow set of apps: * <ul> * <li>The carrier app for this subscriberId, as determined by - * {@link TelephonyManager#hasCarrierPrivileges(int)}. + * {@link TelephonyManager#hasCarrierPrivileges()}. * <li>The carrier app explicitly delegated access through * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. * </ul> @@ -1650,17 +1723,173 @@ public class SubscriptionManager { * @param plans the list of plans. The first plan is always the primary and * most important plan. Any additional plans are secondary and * may not be displayed or used by decision making logic. - * @hide */ @SystemApi public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) { - final INetworkPolicyManager npm = INetworkPolicyManager.Stub - .asInterface(ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); try { - npm.setSubscriptionPlans(subId, plans.toArray(new SubscriptionPlan[plans.size()]), - mContext.getOpPackageName()); + getNetworkPolicy().setSubscriptionPlans(subId, + plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** @hide */ + private String getSubscriptionPlansOwner(int subId) { + try { + return getNetworkPolicy().getSubscriptionPlansOwner(subId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered unmetered. This will be reflected + * to apps via {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this override applies to. + * @param overrideUnmetered set if the billing relationship should be + * considered unmetered. + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + */ + @SystemApi + public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, + @DurationMillisLong long timeoutMillis) { + try { + final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0; + mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue, + timeoutMillis, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Temporarily override the billing relationship plan between a carrier and + * a specific subscriber to be considered congested. This will cause the + * device to delay certain network requests when possible, such as developer + * jobs that are willing to run in a flexible time window. + * <p> + * This method is only accessible to the following narrow set of apps: + * <ul> + * <li>The carrier app for this subscriberId, as determined by + * {@link TelephonyManager#hasCarrierPrivileges()}. + * <li>The carrier app explicitly delegated access through + * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. + * </ul> + * + * @param subId the subscriber this override applies to. + * @param overrideCongested set if the subscription should be considered + * congested. + * @param timeoutMillis the timeout after which the requested override will + * be automatically cleared, or {@code 0} to leave in the + * requested state until explicitly cleared, or the next reboot, + * whichever happens first. + */ + @SystemApi + public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, + @DurationMillisLong long timeoutMillis) { + try { + final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0; + mNetworkPolicy.setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue, + timeoutMillis, mContext.getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Create an {@link Intent} that can be launched towards the carrier app + * that is currently defining the billing relationship plan through + * {@link #setSubscriptionPlans(int, List)}. + * + * @return ready to launch Intent targeted towards the carrier app, or + * {@code null} if no carrier app is defined, or if the defined + * carrier app provides no management activity. + * @hide + */ + public @Nullable Intent createManageSubscriptionIntent(int subId) { + // Bail if no owner + final String owner = getSubscriptionPlansOwner(subId); + if (owner == null) return null; + + // Bail if no plans + final List<SubscriptionPlan> plans = getSubscriptionPlans(subId); + if (plans.isEmpty()) return null; + + final Intent intent = new Intent(ACTION_MANAGE_SUBSCRIPTION_PLANS); + intent.setPackage(owner); + intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId); + + // Bail if not implemented + if (mContext.getPackageManager().queryIntentActivities(intent, + PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) { + return null; + } + + return intent; + } + + /** @hide */ + private @Nullable Intent createRefreshSubscriptionIntent(int subId) { + // Bail if no owner + final String owner = getSubscriptionPlansOwner(subId); + if (owner == null) return null; + + // Bail if no plans + final List<SubscriptionPlan> plans = getSubscriptionPlans(subId); + if (plans.isEmpty()) return null; + + final Intent intent = new Intent(ACTION_REFRESH_SUBSCRIPTION_PLANS); + intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + intent.setPackage(owner); + intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, subId); + + // Bail if not implemented + if (mContext.getPackageManager().queryBroadcastReceivers(intent, 0).isEmpty()) { + return null; + } + + return intent; + } + + /** + * Check if there is a carrier app that is currently defining the billing + * relationship plan through {@link #setSubscriptionPlans(int, List)} that + * supports refreshing of subscription plans. + * + * @hide + */ + public boolean isSubscriptionPlansRefreshSupported(int subId) { + return createRefreshSubscriptionIntent(subId) != null; + } + + /** + * Request that the carrier app that is currently defining the billing + * relationship plan through {@link #setSubscriptionPlans(int, List)} + * refresh its subscription plans. + * <p> + * If the app is able to successfully update the plans, you'll expect to + * receive the {@link #ACTION_SUBSCRIPTION_PLANS_CHANGED} broadcast. + * + * @hide + */ + public void requestSubscriptionPlansRefresh(int subId) { + final Intent intent = createRefreshSubscriptionIntent(subId); + final BroadcastOptions options = BroadcastOptions.makeBasic(); + options.setTemporaryAppWhitelistDuration(TimeUnit.MINUTES.toMillis(1)); + mContext.sendBroadcast(intent, null, options.toBundle()); + } } |