Polished rapid data usage alerting.

Switch to reading limit information from NetworkPolicy, which is
typically populated from SubscriptionPlan.  This lets users have
direct control over the limits we're using to trigger rapid usage
alerts, and makes the feature work without requiring that the carrier
wire up SubscriptionPlan information.

Let the user "snooze" the rapid usage alerting for a day at a time,
so we're less annoying to them.  Send the snooze broadcasts as
foreground, so that we don't re-post notifications while working
through a long background broadcast queue.

Fix notifications to use the "ALERTS" channel, since these alerts
really are higher priority than simple "STATUS" updates; this also
gives us HUN behavior when in full-screen apps.

Update both service and unit tests to work directly with
NotificationManager, instead of the raw AIDL.

Test: bit FrameworksServicesTests:com.android.server.NetworkPolicyManagerServiceTest
Bug: 72444638, 72436702
Change-Id: I8d9138522a7779cc68eb9fa4777b50facb6567b7
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 5df742c..9f47f62 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -42,6 +42,7 @@
 public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
     private static final int VERSION_INIT = 1;
     private static final int VERSION_RULE = 2;
+    private static final int VERSION_RAPID = 3;
 
     public static final int CYCLE_NONE = -1;
     public static final long WARNING_DISABLED = -1;
@@ -54,6 +55,7 @@
     public long limitBytes = LIMIT_DISABLED;
     public long lastWarningSnooze = SNOOZE_NEVER;
     public long lastLimitSnooze = SNOOZE_NEVER;
+    public long lastRapidSnooze = SNOOZE_NEVER;
     @Deprecated public boolean metered = true;
     public boolean inferred = false;
 
@@ -82,15 +84,24 @@
                 limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
     }
 
+    @Deprecated
     public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
             long limitBytes, long lastWarningSnooze, long lastLimitSnooze, boolean metered,
             boolean inferred) {
+        this(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze, lastLimitSnooze,
+                SNOOZE_NEVER, metered, inferred);
+    }
+
+    public NetworkPolicy(NetworkTemplate template, RecurrenceRule cycleRule, long warningBytes,
+            long limitBytes, long lastWarningSnooze, long lastLimitSnooze, long lastRapidSnooze,
+            boolean metered, boolean inferred) {
         this.template = Preconditions.checkNotNull(template, "missing NetworkTemplate");
         this.cycleRule = Preconditions.checkNotNull(cycleRule, "missing RecurrenceRule");
         this.warningBytes = warningBytes;
         this.limitBytes = limitBytes;
         this.lastWarningSnooze = lastWarningSnooze;
         this.lastLimitSnooze = lastLimitSnooze;
+        this.lastRapidSnooze = lastRapidSnooze;
         this.metered = metered;
         this.inferred = inferred;
     }
@@ -102,6 +113,7 @@
         limitBytes = source.readLong();
         lastWarningSnooze = source.readLong();
         lastLimitSnooze = source.readLong();
+        lastRapidSnooze = source.readLong();
         metered = source.readInt() != 0;
         inferred = source.readInt() != 0;
     }
@@ -114,6 +126,7 @@
         dest.writeLong(limitBytes);
         dest.writeLong(lastWarningSnooze);
         dest.writeLong(lastLimitSnooze);
+        dest.writeLong(lastRapidSnooze);
         dest.writeInt(metered ? 1 : 0);
         dest.writeInt(inferred ? 1 : 0);
     }
@@ -151,6 +164,7 @@
     public void clearSnooze() {
         lastWarningSnooze = SNOOZE_NEVER;
         lastLimitSnooze = SNOOZE_NEVER;
+        lastRapidSnooze = SNOOZE_NEVER;
     }
 
     /**
@@ -176,7 +190,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(template, cycleRule, warningBytes, limitBytes,
-                lastWarningSnooze, lastLimitSnooze, metered, inferred);
+                lastWarningSnooze, lastLimitSnooze, lastRapidSnooze, metered, inferred);
     }
 
     @Override
@@ -186,7 +200,9 @@
             return warningBytes == other.warningBytes
                     && limitBytes == other.limitBytes
                     && lastWarningSnooze == other.lastWarningSnooze
-                    && lastLimitSnooze == other.lastLimitSnooze && metered == other.metered
+                    && lastLimitSnooze == other.lastLimitSnooze
+                    && lastRapidSnooze == other.lastRapidSnooze
+                    && metered == other.metered
                     && inferred == other.inferred
                     && Objects.equals(template, other.template)
                     && Objects.equals(cycleRule, other.cycleRule);
@@ -203,6 +219,7 @@
                 .append(" limitBytes=").append(limitBytes)
                 .append(" lastWarningSnooze=").append(lastWarningSnooze)
                 .append(" lastLimitSnooze=").append(lastLimitSnooze)
+                .append(" lastRapidSnooze=").append(lastRapidSnooze)
                 .append(" metered=").append(metered)
                 .append(" inferred=").append(inferred)
                 .append("}").toString();
@@ -224,13 +241,14 @@
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream out = new DataOutputStream(baos);
 
-        out.writeInt(VERSION_RULE);
+        out.writeInt(VERSION_RAPID);
         out.write(template.getBytesForBackup());
         cycleRule.writeToStream(out);
         out.writeLong(warningBytes);
         out.writeLong(limitBytes);
         out.writeLong(lastWarningSnooze);
         out.writeLong(lastLimitSnooze);
+        out.writeLong(lastRapidSnooze);
         out.writeInt(metered ? 1 : 0);
         out.writeInt(inferred ? 1 : 0);
         return baos.toByteArray();
@@ -239,35 +257,32 @@
     public static NetworkPolicy getNetworkPolicyFromBackup(DataInputStream in) throws IOException,
             BackupUtils.BadVersionException {
         final int version = in.readInt();
-        switch (version) {
-            case VERSION_INIT: {
-                NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
-                int cycleDay = in.readInt();
-                String cycleTimeZone = BackupUtils.readString(in);
-                long warningBytes = in.readLong();
-                long limitBytes = in.readLong();
-                long lastWarningSnooze = in.readLong();
-                long lastLimitSnooze = in.readLong();
-                boolean metered = in.readInt() == 1;
-                boolean inferred = in.readInt() == 1;
-                return new NetworkPolicy(template, cycleDay, cycleTimeZone, warningBytes,
-                        limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
-            }
-            case VERSION_RULE: {
-                NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
-                RecurrenceRule cycleRule = new RecurrenceRule(in);
-                long warningBytes = in.readLong();
-                long limitBytes = in.readLong();
-                long lastWarningSnooze = in.readLong();
-                long lastLimitSnooze = in.readLong();
-                boolean metered = in.readInt() == 1;
-                boolean inferred = in.readInt() == 1;
-                return new NetworkPolicy(template, cycleRule, warningBytes,
-                        limitBytes, lastWarningSnooze, lastLimitSnooze, metered, inferred);
-            }
-            default: {
-                throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
-            }
+        if (version > VERSION_RAPID) {
+            throw new BackupUtils.BadVersionException("Unknown backup version: " + version);
         }
+
+        final NetworkTemplate template = NetworkTemplate.getNetworkTemplateFromBackup(in);
+        final RecurrenceRule cycleRule;
+        if (version >= VERSION_RULE) {
+            cycleRule = new RecurrenceRule(in);
+        } else {
+            final int cycleDay = in.readInt();
+            final String cycleTimezone = BackupUtils.readString(in);
+            cycleRule = buildRule(cycleDay, ZoneId.of(cycleTimezone));
+        }
+        final long warningBytes = in.readLong();
+        final long limitBytes = in.readLong();
+        final long lastWarningSnooze = in.readLong();
+        final long lastLimitSnooze = in.readLong();
+        final long lastRapidSnooze;
+        if (version >= VERSION_RAPID) {
+            lastRapidSnooze = in.readLong();
+        } else {
+            lastRapidSnooze = SNOOZE_NEVER;
+        }
+        final boolean metered = in.readInt() == 1;
+        final boolean inferred = in.readInt() == 1;
+        return new NetworkPolicy(template, cycleRule, warningBytes, limitBytes, lastWarningSnooze,
+                lastLimitSnooze, lastRapidSnooze, metered, inferred);
     }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d58b95a..770385e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -323,6 +323,7 @@
     <protected-broadcast android:name="com.android.server.usb.ACTION_OPEN_IN_APPS" />
     <protected-broadcast android:name="com.android.server.am.DELETE_DUMPHEAP" />
     <protected-broadcast android:name="com.android.server.net.action.SNOOZE_WARNING" />
+    <protected-broadcast android:name="com.android.server.net.action.SNOOZE_RAPID" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index f09de52..a6f049e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -106,9 +106,9 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
-import android.app.INotificationManager;
 import android.app.IUidObserver;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
@@ -175,6 +175,7 @@
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.text.format.Formatter;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -208,10 +209,8 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
-import com.android.server.SystemService;
 
 import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlSerializer;
@@ -332,6 +331,8 @@
             "com.android.server.net.action.ALLOW_BACKGROUND";
     private static final String ACTION_SNOOZE_WARNING =
             "com.android.server.net.action.SNOOZE_WARNING";
+    private static final String ACTION_SNOOZE_RAPID =
+            "com.android.server.net.action.SNOOZE_RAPID";
 
     private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
@@ -365,7 +366,6 @@
     private final CarrierConfigManager mCarrierConfigManager;
 
     private IConnectivityManager mConnManager;
-    private INotificationManager mNotifManager;
     private PowerManagerInternal mPowerManagerInternal;
     private IDeviceIdleController mDeviceIdleController;
     @GuardedBy("mUidRulesFirstLock")
@@ -564,10 +564,6 @@
         mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
     }
 
-    public void bindNotificationManager(INotificationManager notifManager) {
-        mNotifManager = checkNotNull(notifManager, "missing INotificationManager");
-    }
-
     void updatePowerSaveWhitelistUL() {
         try {
             int[] whitelist = mDeviceIdleController.getAppIdWhitelistExceptIdle();
@@ -772,10 +768,11 @@
             final IntentFilter allowFilter = new IntentFilter(ACTION_ALLOW_BACKGROUND);
             mContext.registerReceiver(mAllowReceiver, allowFilter, MANAGE_NETWORK_POLICY, mHandler);
 
-            // listen for snooze warning from notifications
-            final IntentFilter snoozeWarningFilter = new IntentFilter(ACTION_SNOOZE_WARNING);
-            mContext.registerReceiver(mSnoozeWarningReceiver, snoozeWarningFilter,
-                    MANAGE_NETWORK_POLICY, mHandler);
+            // Listen for snooze from notifications
+            mContext.registerReceiver(mSnoozeReceiver,
+                    new IntentFilter(ACTION_SNOOZE_WARNING), MANAGE_NETWORK_POLICY, mHandler);
+            mContext.registerReceiver(mSnoozeReceiver,
+                    new IntentFilter(ACTION_SNOOZE_RAPID), MANAGE_NETWORK_POLICY, mHandler);
 
             // listen for configured wifi networks to be loaded
             final IntentFilter wifiFilter =
@@ -960,14 +957,18 @@
      * Receiver that watches for {@link Notification} control of
      * {@link NetworkPolicy#lastWarningSnooze}.
      */
-    final private BroadcastReceiver mSnoozeWarningReceiver = new BroadcastReceiver() {
+    final private BroadcastReceiver mSnoozeReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             // on background handler thread, and verified MANAGE_NETWORK_POLICY
             // permission above.
 
             final NetworkTemplate template = intent.getParcelableExtra(EXTRA_NETWORK_TEMPLATE);
-            performSnooze(template, TYPE_WARNING);
+            if (ACTION_SNOOZE_WARNING.equals(intent.getAction())) {
+                performSnooze(template, TYPE_WARNING);
+            } else if (ACTION_SNOOZE_RAPID.equals(intent.getAction())) {
+                performSnooze(template, TYPE_RAPID);
+            }
         }
     };
 
@@ -1025,13 +1026,6 @@
         }
     };
 
-    @VisibleForTesting
-    public void updateNotifications() {
-        synchronized (mNetworkPoliciesSecondLock) {
-            updateNotificationsNL();
-        }
-    }
-
     /**
      * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
      * to show visible notifications as needed.
@@ -1047,6 +1041,7 @@
         // cycle boundary to recompute notifications.
 
         // examine stats for each active policy
+        final long now = currentTimeMillis();
         for (int i = mNetworkPolicy.size()-1; i >= 0; i--) {
             final NetworkPolicy policy = mNetworkPolicy.valueAt(i);
             // ignore policies that aren't relevant to user
@@ -1055,12 +1050,14 @@
 
             final Pair<ZonedDateTime, ZonedDateTime> cycle = NetworkPolicyManager
                     .cycleIterator(policy).next();
-            final long start = cycle.first.toInstant().toEpochMilli();
-            final long end = cycle.second.toInstant().toEpochMilli();
-            final long totalBytes = getTotalBytes(policy.template, start, end);
+            final long cycleStart = cycle.first.toInstant().toEpochMilli();
+            final long cycleEnd = cycle.second.toInstant().toEpochMilli();
+            final long totalBytes = getTotalBytes(policy.template, cycleStart, cycleEnd);
 
+            // Notify when data usage is over warning/limit
             if (policy.isOverLimit(totalBytes)) {
-                if (policy.lastLimitSnooze >= start) {
+                final boolean snoozedThisCycle = policy.lastLimitSnooze >= cycleStart;
+                if (snoozedThisCycle) {
                     enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
                 } else {
                     enqueueNotification(policy, TYPE_LIMIT, totalBytes);
@@ -1070,45 +1067,30 @@
             } else {
                 notifyUnderLimitNL(policy.template);
 
-                if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
+                final boolean snoozedThisCycle = policy.lastWarningSnooze >= cycleStart;
+                if (policy.isOverWarning(totalBytes) && !snoozedThisCycle) {
                     enqueueNotification(policy, TYPE_WARNING, totalBytes);
                 }
             }
-        }
 
-        // Alert the user about heavy recent data usage that might result in
-        // going over their carrier limit.
-        for (int i = 0; i < mNetIdToSubId.size(); i++) {
-            final int subId = mNetIdToSubId.valueAt(i);
-            final SubscriptionPlan plan = getPrimarySubscriptionPlanLocked(subId);
-            if (plan == null) continue;
-
-            final long limitBytes = plan.getDataLimitBytes();
-            if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) {
-                // Ignore missing limits
-            } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) {
-                // Unlimited data; no rapid usage alerting
-            } else {
-                // Warn if average usage over last 4 days is on track to blow
-                // pretty far past the plan limits.
+            // Warn if average usage over last 4 days is on track to blow pretty
+            // far past the plan limits.
+            if (policy.limitBytes != LIMIT_DISABLED) {
                 final long recentDuration = TimeUnit.DAYS.toMillis(4);
-                final long end = RecurrenceRule.sClock.millis();
-                final long start = end - recentDuration;
+                final long recentBytes = getTotalBytes(policy.template, now - recentDuration, now);
 
-                final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(
-                        mContext.getSystemService(TelephonyManager.class).getSubscriberId(subId));
-                final long recentBytes = getTotalBytes(template, start, end);
-
-                final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next();
-                final long cycleDuration = cycle.second.toInstant().toEpochMilli()
-                        - cycle.first.toInstant().toEpochMilli();
-
+                final long cycleDuration = cycleEnd - cycleStart;
                 final long projectedBytes = (recentBytes * cycleDuration) / recentDuration;
-                final long alertBytes = (limitBytes * 3) / 2;
-                if (projectedBytes > alertBytes) {
-                    final NetworkPolicy policy = new NetworkPolicy(template, plan.getCycleRule(),
-                            NetworkPolicy.WARNING_DISABLED, NetworkPolicy.LIMIT_DISABLED,
-                            NetworkPolicy.SNOOZE_NEVER, NetworkPolicy.SNOOZE_NEVER, true, true);
+                final long alertBytes = (policy.limitBytes * 3) / 2;
+
+                if (LOGD) {
+                    Slog.d(TAG, "Rapid usage considering recent " + recentBytes + " projected "
+                            + projectedBytes + " alert " + alertBytes);
+                }
+
+                final boolean snoozedRecently = policy.lastRapidSnooze >= now
+                        - DateUtils.DAY_IN_MILLIS;
+                if (projectedBytes > alertBytes && !snoozedRecently) {
                     enqueueNotification(policy, TYPE_RAPID, 0);
                 }
             }
@@ -1131,8 +1113,8 @@
      */
     private boolean isTemplateRelevant(NetworkTemplate template) {
         if (template.isMatchRuleMobile()) {
-            final TelephonyManager tele = TelephonyManager.from(mContext);
-            final SubscriptionManager sub = SubscriptionManager.from(mContext);
+            final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+            final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
 
             // Mobile template is relevant when any active subscriber matches
             final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
@@ -1173,7 +1155,7 @@
     private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) {
         final NotificationId notificationId = new NotificationId(policy, type);
         final Notification.Builder builder =
-                new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_STATUS);
+                new Notification.Builder(mContext, SystemNotificationChannels.NETWORK_ALERTS);
         builder.setOnlyAlertOnce(true);
         builder.setWhen(0L);
         builder.setColor(mContext.getColor(
@@ -1190,8 +1172,6 @@
                 builder.setTicker(title);
                 builder.setContentTitle(title);
                 builder.setContentText(body);
-                builder.setDefaults(Notification.DEFAULT_ALL);
-                builder.setChannelId(SystemNotificationChannels.NETWORK_ALERTS);
 
                 final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
                 builder.setDeleteIntent(PendingIntent.getBroadcast(
@@ -1267,6 +1247,7 @@
                 builder.setTicker(title);
                 builder.setContentTitle(title);
                 builder.setContentText(body);
+                builder.setChannelId(SystemNotificationChannels.NETWORK_STATUS);
 
                 final Intent intent = buildViewDataUsageIntent(res, policy.template);
                 builder.setContentIntent(PendingIntent.getActivity(
@@ -1277,45 +1258,34 @@
                 final CharSequence title = res.getText(R.string.data_usage_rapid_title);
                 body = res.getText(R.string.data_usage_rapid_body);
 
-                builder.setOngoing(true);
                 builder.setSmallIcon(R.drawable.stat_notify_error);
                 builder.setTicker(title);
                 builder.setContentTitle(title);
                 builder.setContentText(body);
 
-                final Intent intent = buildViewDataUsageIntent(res, policy.template);
+                final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template);
+                builder.setDeleteIntent(PendingIntent.getBroadcast(
+                        mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+
+                final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
                 builder.setContentIntent(PendingIntent.getActivity(
-                        mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+                        mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
                 break;
             }
         }
 
-        // TODO: move to NotificationManager once we can mock it
-        try {
-            final String packageName = mContext.getPackageName();
-            if (!TextUtils.isEmpty(body)) {
-                builder.setStyle(new Notification.BigTextStyle()
-                        .bigText(body));
-            }
-            mNotifManager.enqueueNotificationWithTag(
-                    packageName, packageName, notificationId.getTag(), notificationId.getId(),
-                    builder.build(), UserHandle.USER_ALL);
-            mActiveNotifs.add(notificationId);
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
+        if (!TextUtils.isEmpty(body)) {
+            builder.setStyle(new Notification.BigTextStyle().bigText(body));
         }
+
+        mContext.getSystemService(NotificationManager.class).notifyAsUser(notificationId.getTag(),
+                notificationId.getId(), builder.build(), UserHandle.ALL);
+        mActiveNotifs.add(notificationId);
     }
 
     private void cancelNotification(NotificationId notificationId) {
-        // TODO: move to NotificationManager once we can mock it
-        try {
-            final String packageName = mContext.getPackageName();
-            mNotifManager.cancelNotificationWithTag(
-                    packageName, notificationId.getTag(), notificationId.getId(),
-                    UserHandle.USER_ALL);
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
-        }
+        mContext.getSystemService(NotificationManager.class).cancel(notificationId.getTag(),
+                notificationId.getId());
     }
 
     /**
@@ -1342,8 +1312,13 @@
     };
 
     @VisibleForTesting
-    public void updateNetworks() {
+    public void updateNetworks() throws InterruptedException {
         mConnReceiver.onReceive(null, null);
+        final CountDownLatch latch = new CountDownLatch(1);
+        mHandler.post(() -> {
+            latch.countDown();
+        });
+        latch.await(5, TimeUnit.SECONDS);
     }
 
     /**
@@ -1357,7 +1332,8 @@
         if (LOGV) Slog.v(TAG, "maybeUpdateMobilePolicyCycleAL()");
 
         boolean policyUpdated = false;
-        final String subscriberId = TelephonyManager.from(mContext).getSubscriberId(subId);
+        final String subscriberId = mContext.getSystemService(TelephonyManager.class)
+                .getSubscriberId(subId);
 
         // find and update the mobile NetworkPolicy for this subscriber id
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
@@ -1482,7 +1458,7 @@
                 return;
             }
             final int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, -1);
-            final TelephonyManager tele = TelephonyManager.from(mContext);
+            final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
             final String subscriberId = tele.getSubscriberId(subId);
 
             maybeRefreshTrustedTime();
@@ -1561,8 +1537,8 @@
         if (template.getMatchRule() == MATCH_MOBILE_ALL) {
             // If mobile data usage hits the limit or if the user resumes the data, we need to
             // notify telephony.
-            final SubscriptionManager sm = SubscriptionManager.from(mContext);
-            final TelephonyManager tm = TelephonyManager.from(mContext);
+            final SubscriptionManager sm = mContext.getSystemService(SubscriptionManager.class);
+            final TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
 
             final int[] subIds = ArrayUtils.defeatNullable(sm.getActiveSubscriptionIdList());
             for (int subId : subIds) {
@@ -1746,7 +1722,7 @@
                 final long totalBytes = getTotalBytes(
                         NetworkTemplate.buildTemplateMobileAll(state.subscriberId), start, end);
                 final long remainingBytes = limitBytes - totalBytes;
-                final long remainingDays = Math.min(1, (end - RecurrenceRule.sClock.millis())
+                final long remainingDays = Math.min(1, (end - currentTimeMillis())
                         / TimeUnit.DAYS.toMillis(1));
                 if (remainingBytes > 0) {
                     quotaBytes = (remainingBytes / remainingDays) / 10;
@@ -1770,8 +1746,8 @@
         if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyAL()");
         if (mSuppressDefaultPolicy) return;
 
-        final TelephonyManager tele = TelephonyManager.from(mContext);
-        final SubscriptionManager sub = SubscriptionManager.from(mContext);
+        final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
+        final SubscriptionManager sub = mContext.getSystemService(SubscriptionManager.class);
 
         final int[] subIds = ArrayUtils.defeatNullable(sub.getActiveSubscriptionIdList());
         for (int subId : subIds) {
@@ -2516,7 +2492,7 @@
     }
 
     private void normalizePoliciesNL(NetworkPolicy[] policies) {
-        final TelephonyManager tele = TelephonyManager.from(mContext);
+        final TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
         final String[] merged = tele.getMergedSubscriberIds();
 
         mNetworkPolicy.clear();
@@ -2564,6 +2540,9 @@
                     case TYPE_LIMIT:
                         policy.lastLimitSnooze = currentTime;
                         break;
+                    case TYPE_RAPID:
+                        policy.lastRapidSnooze = currentTime;
+                        break;
                     default:
                         throw new IllegalArgumentException("unexpected type");
                 }
@@ -4425,6 +4404,14 @@
 
     private static Intent buildSnoozeWarningIntent(NetworkTemplate template) {
         final Intent intent = new Intent(ACTION_SNOOZE_WARNING);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
+        return intent;
+    }
+
+    private static Intent buildSnoozeRapidIntent(NetworkTemplate template) {
+        final Intent intent = new Intent(ACTION_SNOOZE_RAPID);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(EXTRA_NETWORK_TEMPLATE, template);
         return intent;
     }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d5ed98e..3210f1a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1193,7 +1193,6 @@
             SystemNotificationChannels.createAll(context);
             notification = INotificationManager.Stub.asInterface(
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-            networkPolicy.bindNotificationManager(notification);
             traceEnd();
 
             traceBeginAndSlog("StartDeviceMonitor");
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 045b73c..184e8de 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -27,7 +27,6 @@
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
-import static android.net.TrafficStats.KB_IN_BYTES;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
@@ -35,12 +34,12 @@
 import static android.telephony.CarrierConfigManager.KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG;
 import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_BYTES_LONG;
 import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT;
-import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED;
-import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED;
 import static android.text.format.Time.TIMEZONE_UTC;
 
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
+import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -72,9 +71,9 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.IActivityManager;
-import android.app.INotificationManager;
 import android.app.IUidObserver;
 import android.app.Notification;
+import android.app.NotificationManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -107,12 +106,12 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionPlan;
 import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.DataUnit;
@@ -193,6 +192,8 @@
     private static final String TEST_IFACE = "test0";
     private static final String TEST_SSID = "AndroidAP";
     private static final String TEST_IMSI = "310210";
+    private static final int TEST_SUB_ID = 42;
+    private static final int TEST_NET_ID = 24;
 
     private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
 
@@ -216,7 +217,7 @@
     private @Mock INetworkManagementService mNetworkManager;
     private @Mock TrustedTime mTime;
     private @Mock IConnectivityManager mConnManager;
-    private @Mock INotificationManager mNotifManager;
+    private @Mock NotificationManager mNotifManager;
     private @Mock PackageManager mPackageManager;
     private @Mock IPackageManager mIpm;
     private @Mock SubscriptionManager mSubscriptionManager;
@@ -312,6 +313,8 @@
                         return mCarrierConfigManager;
                     case Context.TELEPHONY_SERVICE:
                         return mTelephonyManager;
+                    case Context.NOTIFICATION_SERVICE:
+                        return mNotifManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -340,7 +343,6 @@
         mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mStatsService,
                 mNetworkManager, mIpm, mTime, mPolicyDir, true);
         mService.bindConnectivityManager(mConnManager);
-        mService.bindNotificationManager(mNotifManager);
         mPolicyListener = new NetworkPolicyListenerAnswer(mService);
 
         // Sets some common expectations.
@@ -960,124 +962,12 @@
                 sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
         mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
 
-        // TODO: consider making strongly ordered mock
-        verifyPolicyDataEnable(TYPE_WIFI, true);
-        verifyRemoveInterfaceQuota(TEST_IFACE);
-        verifySetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
+        verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+                (2 * MB_IN_BYTES) - 512);
     }
 
     @Test
-    public void testOverWarningLimitNotification() throws Exception {
-        NetworkState[] state = null;
-        NetworkStats stats = null;
-        Future<String> tagFuture = null;
-
-        final int CYCLE_DAY = 15;
-        final long NOW = parseTime("2007-03-10T00:00Z");
-        final long CYCLE_START = parseTime("2007-02-15T00:00Z");
-        final long CYCLE_END = parseTime("2007-03-15T00:00Z");
-
-        setCurrentTimeMillis(NOW);
-
-        // assign wifi policy
-        state = new NetworkState[] {};
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
-
-        {
-            expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
-                    CYCLE_END)).thenReturn(stats.getTotalBytes());
-
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
-            setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, TIMEZONE_UTC, 1
-                    * MB_IN_BYTES, 2 * MB_IN_BYTES, false));
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(any());
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-        }
-
-        // bring up wifi network
-        incrementCurrentTime(MINUTE_IN_MILLIS);
-        state = new NetworkState[] { buildWifi() };
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
-
-        {
-            expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
-                    CYCLE_END)).thenReturn(stats.getTotalBytes());
-
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
-            mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-            verifyRemoveInterfaceQuota(TEST_IFACE);
-            verifySetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
-        }
-
-        // go over warning, which should kick notification
-        incrementCurrentTime(MINUTE_IN_MILLIS);
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 1536 * KB_IN_BYTES, 15L, 0L, 0L);
-
-        {
-            expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
-                    CYCLE_END)).thenReturn(stats.getTotalBytes());
-            tagFuture = expectEnqueueNotification();
-
-            mNetworkObserver.limitReached(null, TEST_IFACE);
-
-            assertNotificationType(TYPE_WARNING, tagFuture.get());
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-
-        }
-
-        // go over limit, which should kick notification and dialog
-        incrementCurrentTime(MINUTE_IN_MILLIS);
-        stats = new NetworkStats(getElapsedRealtime(), 1)
-                .addIfaceValues(TEST_IFACE, 5 * MB_IN_BYTES, 512L, 0L, 0L);
-
-        {
-            expectCurrentTime();
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
-                    CYCLE_END)).thenReturn(stats.getTotalBytes());
-            tagFuture = expectEnqueueNotification();
-
-            mNetworkObserver.limitReached(null, TEST_IFACE);
-
-            assertNotificationType(TYPE_LIMIT, tagFuture.get());
-            verifyPolicyDataEnable(TYPE_WIFI, false);
-        }
-
-        // now snooze policy, which should remove quota
-        incrementCurrentTime(MINUTE_IN_MILLIS);
-
-        {
-            expectCurrentTime();
-            when(mConnManager.getAllNetworkState()).thenReturn(state);
-            when(mStatsService.getNetworkTotalBytes(sTemplateWifi, CYCLE_START,
-                    CYCLE_END)).thenReturn(stats.getTotalBytes());
-            tagFuture = expectEnqueueNotification();
-
-            mPolicyListener.expect().onMeteredIfacesChanged(any());
-            mService.snoozeLimit(sTemplateWifi);
-            mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
-
-            assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get());
-            // snoozed interface still has high quota so background data is
-            // still restricted.
-            verifyRemoveInterfaceQuota(TEST_IFACE);
-            verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-        }
-    }
-
-    @Test
-    public void testRapidNotification() throws Exception {
+    public void testNotificationWarningLimitSnooze() throws Exception {
         // Create a place to store fake usage
         final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
@@ -1090,38 +980,133 @@
                     }
                 });
 
-        // Define simple data plan which gives us effectively 60MB/day
+        // Get active mobile network in place
+        expectMobileDefaults();
+        mService.updateNetworks();
+
+        // Define simple data plan
         final SubscriptionPlan plan = SubscriptionPlan.Builder
                 .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
-                .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_THROTTLED)
+                .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
                 .build();
-        mService.setSubscriptionPlans(42, new SubscriptionPlan[] { plan },
+        mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
-        // And get that active network in place
-        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
-                new NetworkState(null, new LinkProperties(),
-                        new NetworkCapabilities().addTransportType(TRANSPORT_CELLULAR)
-                                .setNetworkSpecifier(new StringNetworkSpecifier("42")),
-                        new Network(42), TEST_IMSI, null)
-        });
-        mService.updateNetworks();
-
         // We're 20% through the month (6 days)
         final long start = parseTime("2015-11-01T00:00Z");
         final long end = parseTime("2015-11-07T00:00Z");
         setCurrentTimeMillis(end);
+        expectCurrentTime();
 
-        // Using 20% of data in 20% is normal
+        // Normal usage means no notification
+        {
+            history.removeBucketsBefore(Long.MAX_VALUE);
+            history.recordData(start, end,
+                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+                    DataUnit.MEGABYTES.toBytes(1800 - 360));
+            verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+        }
+
+        // Push over warning
+        {
+            history.removeBucketsBefore(Long.MAX_VALUE);
+            history.recordData(start, end,
+                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1799), 0L, 0L, 0L, 0));
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+                    DataUnit.MEGABYTES.toBytes(1800 - 1799));
+            verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_WARNING),
+                    isA(Notification.class), eq(UserHandle.ALL));
+        }
+
+        // Push over limit
+        {
+            history.removeBucketsBefore(Long.MAX_VALUE);
+            history.recordData(start, end,
+                    new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1810), 0L, 0L, 0L, 0));
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(false, TEST_SUB_ID);
+            verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE, 1);
+            verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT),
+                    isA(Notification.class), eq(UserHandle.ALL));
+        }
+
+        // Snooze limit
+        {
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.snoozeLimit(NetworkTemplate.buildTemplateMobileAll(TEST_IMSI));
+            mService.updateNetworks();
+
+            verify(mTelephonyManager, atLeastOnce()).setPolicyDataEnabled(true, TEST_SUB_ID);
+            verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+                    Long.MAX_VALUE);
+            verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_LIMIT_SNOOZED),
+                    isA(Notification.class), eq(UserHandle.ALL));
+        }
+    }
+
+    @Test
+    public void testNotificationRapid() throws Exception {
+        // Create a place to store fake usage
+        final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
+        when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
+                .thenAnswer(new Answer<Long>() {
+                    @Override
+                    public Long answer(InvocationOnMock invocation) throws Throwable {
+                        final NetworkStatsHistory.Entry entry = history.getValues(
+                                invocation.getArgument(1), invocation.getArgument(2), null);
+                        return entry.rxBytes + entry.txBytes;
+                    }
+                });
+
+        // Get active mobile network in place
+        expectMobileDefaults();
+        mService.updateNetworks();
+
+        // Define simple data plan which gives us effectively 60MB/day
+        final SubscriptionPlan plan = SubscriptionPlan.Builder
+                .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
+                .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
+                .build();
+        mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+                mServiceContext.getOpPackageName());
+
+        // We're 20% through the month (6 days)
+        final long start = parseTime("2015-11-01T00:00Z");
+        final long end = parseTime("2015-11-07T00:00Z");
+        setCurrentTimeMillis(end);
+        expectCurrentTime();
+
+        // Using 20% data in 20% time is normal
         {
             history.removeBucketsBefore(Long.MAX_VALUE);
             history.recordData(start, end,
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(360), 0L, 0L, 0L, 0));
 
             reset(mNotifManager);
-            mService.updateNotifications();
-            verify(mNotifManager, never()).enqueueNotificationWithTag(any(), any(), any(),
-                    anyInt(), any(), anyInt());
+            mService.updateNetworks();
+            verify(mNotifManager, never()).notifyAsUser(any(), anyInt(), any(), any());
         }
 
         // Using 80% data in 20% time is alarming
@@ -1131,9 +1116,9 @@
                     new NetworkStats.Entry(DataUnit.MEGABYTES.toBytes(1440), 0L, 0L, 0L, 0));
 
             reset(mNotifManager);
-            mService.updateNotifications();
-            verify(mNotifManager, atLeastOnce()).enqueueNotificationWithTag(any(), any(), any(),
-                    anyInt(), any(), anyInt());
+            mService.updateNetworks();
+            verify(mNotifManager, atLeastOnce()).notifyAsUser(any(), eq(TYPE_RAPID),
+                    isA(Notification.class), eq(UserHandle.ALL));
         }
     }
 
@@ -1165,9 +1150,8 @@
                     true));
             mPolicyListener.waitAndVerify().onMeteredIfacesChanged(eq(new String[]{TEST_IFACE}));
 
-            verifyPolicyDataEnable(TYPE_WIFI, true);
-            verifyRemoveInterfaceQuota(TEST_IFACE);
-            verifySetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
+            verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(TEST_IFACE,
+                    Long.MAX_VALUE);
         }
     }
 
@@ -1426,6 +1410,26 @@
                 true);
     }
 
+    private NetworkInfo buildNetworkInfo() {
+        final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_MOBILE,
+                TelephonyManager.NETWORK_TYPE_LTE, null, null);
+        ni.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        return ni;
+    }
+
+    private LinkProperties buildLinkProperties(String iface) {
+        final LinkProperties lp = new LinkProperties();
+        lp.setInterfaceName(iface);
+        return lp;
+    }
+
+    private NetworkCapabilities buildNetworkCapabilities(int subId) {
+        final NetworkCapabilities nc = new NetworkCapabilities();
+        nc.addTransportType(TRANSPORT_CELLULAR);
+        nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId)));
+        return nc;
+    }
+
     private NetworkPolicy buildDefaultFakeMobilePolicy() {
         NetworkPolicy p = mService.buildDefaultMobilePolicy(FAKE_SUB_ID, FAKE_SUBSCRIBER_ID);
         // set a deterministic cycle date
@@ -1478,30 +1482,21 @@
         when(mTime.getCacheCertainty()).thenReturn(0L);
     }
 
-    private Future<String> expectEnqueueNotification() throws Exception {
-        final FutureAnswer<String> futureAnswer = new FutureAnswer<String>(2);
-        doAnswer(futureAnswer).when(mNotifManager).enqueueNotificationWithTag(
-                anyString(), anyString(), anyString() /* capture here (index 2)*/,
-                anyInt(), isA(Notification.class), anyInt());
-        return futureAnswer;
-    }
-
     private void expectHasInternetPermission(int uid, boolean hasIt) throws Exception {
         when(mIpm.checkUidPermission(Manifest.permission.INTERNET, uid)).thenReturn(
                 hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
     }
 
-    private void verifySetInterfaceQuota(String iface, long quotaBytes) throws Exception {
-        verify(mNetworkManager, atLeastOnce()).setInterfaceQuota(iface, quotaBytes);
-    }
-
-    private void verifyRemoveInterfaceQuota(String iface) throws Exception {
-        verify(mNetworkManager, atLeastOnce()).removeInterfaceQuota(iface);
-    }
-
-    private Future<Void> verifyPolicyDataEnable(int type, boolean enabled) throws Exception {
-        // TODO: bring back this test
-        return null;
+    private void expectMobileDefaults() throws Exception {
+        when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
+                new int[] { TEST_SUB_ID });
+        when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
+                new NetworkState(buildNetworkInfo(),
+                        buildLinkProperties(TEST_IFACE),
+                        buildNetworkCapabilities(TEST_SUB_ID),
+                        new Network(TEST_NET_ID), TEST_IMSI, null)
+        });
     }
 
     private void verifyAdvisePersistThreshold() throws Exception {
@@ -1519,21 +1514,6 @@
         }
     }
 
-    private static class FutureAnswer<T> extends TestAbstractFuture<T> implements Answer<Void> {
-        private final int index;
-
-        FutureAnswer(int index) {
-            this.index = index;
-        }
-        @Override
-        public Void answer(InvocationOnMock invocation) throws Throwable {
-            @SuppressWarnings("unchecked")
-            T captured = (T) invocation.getArguments()[index];
-            set(captured);
-            return null;
-        }
-    }
-
     private static void assertTimeEquals(long expected, long actual) {
         if (expected != actual) {
             fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));