summaryrefslogtreecommitdiff
path: root/android/telephony/SubscriptionManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/telephony/SubscriptionManager.java')
-rw-r--r--android/telephony/SubscriptionManager.java269
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());
+ }
}