Tests for SubscriptionManager APIs.

Ensure that new SubscriptionManager APIs behave as documented.  Test
based on device claiming FEATURE_TELEPHONY, and require that we have
a valid getDefaultDataSubscriptionId() to run our tests against.

Test: atest android.telephony.cts.SubscriptionManagerTest
Bug: 77327990, 71816837
Change-Id: Ib90ceeacf546c9b36ecf69694442cb661a62d353
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ddb2a85..946d22e6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -149,7 +149,6 @@
 import android.net.wifi.WifiManager;
 import android.os.BestClock;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -360,6 +359,8 @@
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
 
+    private static final String PROP_SUB_PLAN_OWNER = "persist.sys.sub_plan_owner";
+
     private final Context mContext;
     private final IActivityManager mActivityManager;
     private NetworkStatsManagerInternal mNetworkStats;
@@ -2786,10 +2787,17 @@
             return;
         }
 
-        // Fourth check: is caller a testing app on a debug build?
-        final boolean enableDebug = Build.IS_USERDEBUG || Build.IS_ENG;
-        if (enableDebug && callingPackage
-                .equals(SystemProperties.get("fw.sub_plan_owner." + subId, null))) {
+        // Fourth check: is caller a testing app?
+        final String testPackage = SystemProperties.get(PROP_SUB_PLAN_OWNER + "." + subId, null);
+        if (!TextUtils.isEmpty(testPackage)
+                && Objects.equals(testPackage, callingPackage)) {
+            return;
+        }
+
+        // Fifth check: is caller a legacy testing app?
+        final String legacyTestPackage = SystemProperties.get("fw.sub_plan_owner." + subId, null);
+        if (!TextUtils.isEmpty(legacyTestPackage)
+                && Objects.equals(legacyTestPackage, callingPackage)) {
             return;
         }
 
@@ -2990,6 +2998,14 @@
         }
     }
 
+    /**
+     * Only visible for testing purposes. This doesn't give any access to
+     * existing plans; it simply lets the debug package define new plans.
+     */
+    void setSubscriptionPlansOwner(int subId, String packageName) {
+        SystemProperties.set(PROP_SUB_PLAN_OWNER + "." + subId, packageName);
+    }
+
     @Override
     public String getSubscriptionPlansOwner(int subId) {
         if (UserHandle.getCallingAppId() != android.os.Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
index b65b9d7..56d41c5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerShellCommand.java
@@ -21,7 +21,6 @@
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 
 import android.content.Context;
-import android.net.INetworkPolicyManager;
 import android.net.NetworkPolicyManager;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
@@ -33,10 +32,10 @@
 
 class NetworkPolicyManagerShellCommand extends ShellCommand {
 
-    private final INetworkPolicyManager mInterface;
+    private final NetworkPolicyManagerService mInterface;
     private final WifiManager mWifiManager;
 
-    NetworkPolicyManagerShellCommand(Context context, INetworkPolicyManager service) {
+    NetworkPolicyManagerShellCommand(Context context, NetworkPolicyManagerService service) {
         mInterface = service;
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     }
@@ -97,6 +96,8 @@
         pw.println("    Toggles whether the given wi-fi network is metered.");
         pw.println("  set restrict-background BOOLEAN");
         pw.println("    Sets the global restrict background usage status.");
+        pw.println("  set sub-plan-owner subId [packageName]");
+        pw.println("    Sets the data plan owner package for subId.");
     }
 
     private int runGet() throws RemoteException {
@@ -126,6 +127,8 @@
                 return setMeteredWifiNetwork();
             case "restrict-background":
                 return setRestrictBackground();
+            case "sub-plan-owner":
+                return setSubPlanOwner();
         }
         pw.println("Error: unknown set type '" + type + "'");
         return -1;
@@ -227,6 +230,13 @@
         return 0;
     }
 
+    private int setSubPlanOwner() throws RemoteException {
+        final int subId = Integer.parseInt(getNextArgRequired());
+        final String packageName = getNextArg();
+        mInterface.setSubscriptionPlansOwner(subId, packageName);
+        return 0;
+    }
+
     private int setUidPolicy(int policy) throws RemoteException {
         final int uid = getUidFromNextArg();
         if (uid < 0) {
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index 9411652..4ffb70b 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -34,6 +34,7 @@
 import java.time.Period;
 import java.time.ZonedDateTime;
 import java.util.Iterator;
+import java.util.Objects;
 
 /**
  * Description of a billing relationship plan between a carrier and a specific
@@ -124,6 +125,27 @@
                 .append("}").toString();
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
+                dataUsageBytes, dataUsageTime);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof SubscriptionPlan) {
+            final SubscriptionPlan other = (SubscriptionPlan) obj;
+            return Objects.equals(cycleRule, other.cycleRule)
+                    && Objects.equals(title, other.title)
+                    && Objects.equals(summary, other.summary)
+                    && dataLimitBytes == other.dataLimitBytes
+                    && dataLimitBehavior == other.dataLimitBehavior
+                    && dataUsageBytes == other.dataUsageBytes
+                    && dataUsageTime == other.dataUsageTime;
+        }
+        return false;
+    }
+
     public static final Parcelable.Creator<SubscriptionPlan> CREATOR = new Parcelable.Creator<SubscriptionPlan>() {
         @Override
         public SubscriptionPlan createFromParcel(Parcel source) {