Intent to manage subscription plans.
Define an API for carrier apps to provide a deep-link into their app
for the user to see more details about their billing relationship,
such as upgrading plans.
The createManageSubscriptionIntent() method is ready for OS
components to use when deciding if they should show a "MANAGE"
button in their UI, and the returned Intent is fully constructed
and ready to roll.
Test: builds, boots
Bug: 64133169
Change-Id: I9b2775b7cba5313f517996870135eb4682082873
diff --git a/api/current.txt b/api/current.txt
index 32deabb..8611db8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -41194,7 +41194,7 @@
public class SubscriptionManager {
method public void addOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
- method public static android.telephony.SubscriptionManager from(android.content.Context);
+ method public static deprecated android.telephony.SubscriptionManager from(android.content.Context);
method public android.telephony.SubscriptionInfo getActiveSubscriptionInfo(int);
method public int getActiveSubscriptionInfoCount();
method public int getActiveSubscriptionInfoCountMax();
diff --git a/api/system-current.txt b/api/system-current.txt
index 596474c..ec9ac82 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4171,6 +4171,7 @@
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
+ field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
}
public final class SubscriptionPlan implements android.os.Parcelable {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 66cf991..6eb4783 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -494,7 +494,7 @@
registerService(Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class,
new CachedServiceFetcher<SubscriptionManager>() {
@Override
- public SubscriptionManager createService(ContextImpl ctx) {
+ public SubscriptionManager createService(ContextImpl ctx) throws ServiceNotFoundException {
return new SubscriptionManager(ctx.getOuterContext());
}});
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index f75789f..7e37432 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -70,6 +70,7 @@
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
+ String getSubscriptionPlansOwner(int subId);
void factoryReset(String subscriber);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index fdfe241..3c47e85 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2799,6 +2799,17 @@
}
@Override
+ public String getSubscriptionPlansOwner(int subId) {
+ if (UserHandle.getCallingAppId() != android.os.Process.SYSTEM_UID) {
+ throw new SecurityException();
+ }
+
+ synchronized (mNetworkPoliciesSecondLock) {
+ return mSubscriptionPlansOwner.get(subId);
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 9240843..bfba700 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -460,7 +460,8 @@
// DISALLOW_DATA_ROAMING user restriction is set.
// Multi sim device.
- SubscriptionManager subscriptionManager = new SubscriptionManager(context);
+ SubscriptionManager subscriptionManager = context
+ .getSystemService(SubscriptionManager.class);
final List<SubscriptionInfo> subscriptionInfoList =
subscriptionManager.getActiveSubscriptionInfoList();
if (subscriptionInfoList != null) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1e6abf2..a494799 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -17,12 +17,14 @@
package android.telephony;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
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;
@@ -32,11 +34,14 @@
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;
@@ -430,6 +435,26 @@
= "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.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ @SystemApi
+ public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS
+ = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
+
+ /**
* 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 +462,7 @@
public static final String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
private final Context mContext;
+ private final INetworkPolicyManager mNetworkPolicy;
/**
* A listener class for monitoring changes to {@link SubscriptionInfo} records.
@@ -509,22 +535,20 @@
}
/** @hide */
- public SubscriptionManager(Context context) {
+ public SubscriptionManager(Context context) throws ServiceNotFoundException {
if (DBG) logd("SubscriptionManager created");
mContext = context;
+ mNetworkPolicy = INetworkPolicyManager.Stub
+ .asInterface(ServiceManager.getServiceOrThrow(Context.NETWORK_POLICY_SERVICE));
}
/**
- * 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 context.getSystemService(SubscriptionManager.class);
}
/**
@@ -1622,11 +1646,9 @@
*/
@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());
+ mNetworkPolicy.getSubscriptionPlans(subId, mContext.getOpPackageName());
return subscriptionPlans == null
? Collections.emptyList() : Arrays.asList(subscriptionPlans);
} catch (RemoteException e) {
@@ -1654,13 +1676,52 @@
*/
@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());
+ mNetworkPolicy.setSubscriptionPlans(subId,
+ plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+
+ /** @hide */
+ private String getSubscriptionPlansOwner(int subId) {
+ try {
+ return mNetworkPolicy.getSubscriptionPlansOwner(subId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Create an {@link Intent} that will launch 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;
+ }
}