Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d05921c6 authored by Junyu Lai's avatar Junyu Lai Committed by Android (Google) Code Review
Browse files

Merge changes I6c486303,I073934f3,I6ee66149,Ibbb4325c into mainline-prod

* changes:
  [SP29] Send interface warning bytes to NetworkStatsProvider
  [SP29.1] Simplify logic of calculating and applying data limit
  [SP28] Add API for set data warning
  NPMS lock improvement.
parents beb3bd42 a3e06358
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -23,6 +23,6 @@ package android.net.netstats.provider;
 */
oneway interface INetworkStatsProvider {
    void onRequestStatsUpdate(int token);
    void onSetLimit(String iface, long quotaBytes);
    void onSetAlert(long quotaBytes);
    void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes);
}
+1 −1
Original line number Diff line number Diff line
@@ -26,6 +26,6 @@ import android.net.NetworkStats;
oneway interface INetworkStatsProviderCallback {
    void notifyStatsUpdated(int token, in NetworkStats ifaceStats, in NetworkStats uidStats);
    void notifyAlertReached();
    void notifyLimitReached();
    void notifyWarningOrLimitReached();
    void unregister();
}
+51 −7
Original line number Diff line number Diff line
@@ -29,7 +29,8 @@ import android.os.RemoteException;
@SystemApi
public abstract class NetworkStatsProvider {
    /**
     * A value used by {@link #onSetLimit} and {@link #onSetAlert} indicates there is no limit.
     * A value used by {@link #onSetLimit}, {@link #onSetAlert} and {@link #onSetWarningAndLimit}
     * indicates there is no limit.
     */
    public static final int QUOTA_UNLIMITED = -1;

@@ -42,13 +43,13 @@ public abstract class NetworkStatsProvider {
        }

        @Override
        public void onSetLimit(String iface, long quotaBytes) {
            NetworkStatsProvider.this.onSetLimit(iface, quotaBytes);
        public void onSetAlert(long quotaBytes) {
            NetworkStatsProvider.this.onSetAlert(quotaBytes);
        }

        @Override
        public void onSetAlert(long quotaBytes) {
            NetworkStatsProvider.this.onSetAlert(quotaBytes);
        public void onSetWarningAndLimit(String iface, long warningBytes, long limitBytes) {
            NetworkStatsProvider.this.onSetWarningAndLimit(iface, warningBytes, limitBytes);
        }
    };

@@ -145,11 +146,28 @@ public abstract class NetworkStatsProvider {
    }

    /**
     * Notify system that the quota set by {@code onSetLimit} has been reached.
     * Notify system that the warning set by {@link #onSetWarningAndLimit} has been reached.
     *
     * @hide
     */
    // TODO: Expose as system API.
    public void notifyWarningReached() {
        try {
            // Reuse the code path to notify warning reached with limit reached
            // since framework handles them in the same way.
            getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached();
        } catch (RemoteException e) {
            e.rethrowAsRuntimeException();
        }
    }

    /**
     * Notify system that the quota set by {@link #onSetLimit} or limit set by
     * {@link #onSetWarningAndLimit} has been reached.
     */
    public void notifyLimitReached() {
        try {
            getProviderCallbackBinderOrThrow().notifyLimitReached();
            getProviderCallbackBinderOrThrow().notifyWarningOrLimitReached();
        } catch (RemoteException e) {
            e.rethrowAsRuntimeException();
        }
@@ -180,8 +198,34 @@ public abstract class NetworkStatsProvider {
     * @param quotaBytes the quota defined as the number of bytes, starting from zero and counting
     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
     */
    // TODO: deprecate this once onSetWarningAndLimit is ready.
    public abstract void onSetLimit(@NonNull String iface, long quotaBytes);

    /**
     * Called by {@code NetworkStatsService} when setting the interface quotas for the specified
     * upstream interface. If a provider implements {@link #onSetWarningAndLimit}, the system
     * will not call {@link #onSetLimit}. When this method is called, the implementation
     * should behave as follows:
     *   1. If {@code warningBytes} is reached on {@code iface}, block all further traffic on
     *      {@code iface} and call {@link NetworkStatsProvider@notifyWarningReached()}.
     *   2. If {@code limitBytes} is reached on {@code iface}, block all further traffic on
     *   {@code iface} and call {@link NetworkStatsProvider#notifyLimitReached()}.
     *
     * @param iface the interface requiring the operation.
     * @param warningBytes the warning defined as the number of bytes, starting from zero and
     *                     counting from now. A value of {@link #QUOTA_UNLIMITED} indicates
     *                     there is no warning.
     * @param limitBytes the limit defined as the number of bytes, starting from zero and counting
     *                   from now. A value of {@link #QUOTA_UNLIMITED} indicates there is no limit.
     *
     * @hide
     */
    // TODO: Expose as system API.
    public void onSetWarningAndLimit(@NonNull String iface, long warningBytes, long limitBytes) {
        // Backward compatibility for those who didn't override this function.
        onSetLimit(iface, limitBytes);
    }

    /**
     * Called by {@code NetworkStatsService} when setting the alert bytes. Custom implementations
     * MUST call {@link NetworkStatsProvider#notifyAlertReached()} when {@code quotaBytes} bytes
+3 −2
Original line number Diff line number Diff line
@@ -131,9 +131,10 @@ public abstract class NetworkPolicyManagerInternal {

    /**
     *  Notifies that the specified {@link NetworkStatsProvider} has reached its quota
     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)}.
     *  which was set through {@link NetworkStatsProvider#onSetLimit(String, long)} or
     *  {@link NetworkStatsProvider#onSetWarningAndLimit(String, long, long)}.
     *
     * @param tag the human readable identifier of the custom network stats provider.
     */
    public abstract void onStatsProviderLimitReached(@NonNull String tag);
    public abstract void onStatsProviderWarningOrLimitReached(@NonNull String tag);
}
+94 −61
Original line number Diff line number Diff line
@@ -383,15 +383,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    private static final int MSG_LIMIT_REACHED = 5;
    private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
    private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
    private static final int MSG_UPDATE_INTERFACE_QUOTA = 10;
    private static final int MSG_REMOVE_INTERFACE_QUOTA = 11;
    private static final int MSG_UPDATE_INTERFACE_QUOTAS = 10;
    private static final int MSG_REMOVE_INTERFACE_QUOTAS = 11;
    private static final int MSG_POLICIES_CHANGED = 13;
    private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
    private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
    private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
    private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
    private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
    private static final int MSG_STATS_PROVIDER_LIMIT_REACHED = 20;
    private static final int MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED = 20;

    private static final int UID_MSG_STATE_CHANGED = 100;
    private static final int UID_MSG_GONE = 101;
@@ -518,8 +518,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
    private final SparseBooleanArray mRestrictBackgroundWhitelistRevokedUids =
            new SparseBooleanArray();

    final Object mMeteredIfacesLock = new Object();
    /** Set of ifaces that are metered. */
    @GuardedBy("mNetworkPoliciesSecondLock")
    @GuardedBy("mMeteredIfacesLock")
    private ArraySet<String> mMeteredIfaces = new ArraySet<>();
    /** Set of over-limit templates that have been notified. */
    @GuardedBy("mNetworkPoliciesSecondLock")
@@ -1908,39 +1909,45 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {

            final boolean hasWarning = policy.warningBytes != LIMIT_DISABLED;
            final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
            if (hasLimit || policy.metered) {
                final long quotaBytes;
                if (hasLimit && policy.hasCycle()) {
            long limitBytes = Long.MAX_VALUE;
            long warningBytes = Long.MAX_VALUE;
            if ((hasLimit || hasWarning) && policy.hasCycle()) {
                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);

                    if (policy.lastLimitSnooze >= start) {
                        // snoozing past quota, but we still need to restrict apps,
                        // so push really high quota.
                        quotaBytes = Long.MAX_VALUE;
                    } else {
                // If the limit notification is not snoozed, the limit quota needs to be calculated.
                if (hasLimit && policy.lastLimitSnooze < start) {
                    // remaining "quota" bytes are based on total usage in
                    // current cycle. kernel doesn't like 0-byte rules, so we
                    // set 1-byte quota and disable the radio later.
                        quotaBytes = Math.max(1, policy.limitBytes - totalBytes);
                    limitBytes = Math.max(1, policy.limitBytes - totalBytes);
                }

                // If the warning notification was snoozed by user, or the service already knows
                // it is over warning bytes, doesn't need to calculate warning bytes.
                if (hasWarning && policy.lastWarningSnooze < start
                        && !policy.isOverWarning(totalBytes)) {
                    warningBytes = Math.max(1, policy.warningBytes - totalBytes);
                }
                } else {
                    // metered network, but no policy limit; we still need to
                    // restrict apps, so push really high quota.
                    quotaBytes = Long.MAX_VALUE;
            }

            if (hasWarning || hasLimit || policy.metered) {
                if (matchingIfaces.size() > 1) {
                    // TODO: switch to shared quota once NMS supports
                    Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
                }

                // Set the interface warning and limit. For interfaces which has no cycle,
                // or metered with no policy quotas, or snoozed notification; we still need to put
                // iptables rule hooks to restrict apps for data saver, so push really high quota.
                // TODO: Push NetworkStatsProvider.QUOTA_UNLIMITED instead of Long.MAX_VALUE to
                //  providers.
                for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
                    final String iface = matchingIfaces.valueAt(j);
                    setInterfaceQuotaAsync(iface, quotaBytes);
                    setInterfaceQuotasAsync(iface, warningBytes, limitBytes);
                    newMeteredIfaces.add(iface);
                }
            }
@@ -1964,7 +1971,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                for (int j = matchingIfaces.size() - 1; j >= 0; j--) {
                    final String iface = matchingIfaces.valueAt(j);
                    if (!newMeteredIfaces.contains(iface)) {
                        setInterfaceQuotaAsync(iface, Long.MAX_VALUE);
                        setInterfaceQuotasAsync(iface, Long.MAX_VALUE, Long.MAX_VALUE);
                        newMeteredIfaces.add(iface);
                    }
                }
@@ -1972,13 +1979,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }

        // Remove quota from any interfaces that are no longer metered.
        synchronized (mMeteredIfacesLock) {
            for (int i = mMeteredIfaces.size() - 1; i >= 0; i--) {
                final String iface = mMeteredIfaces.valueAt(i);
                if (!newMeteredIfaces.contains(iface)) {
                removeInterfaceQuotaAsync(iface);
                    removeInterfaceQuotasAsync(iface);
                }
            }
            mMeteredIfaces = newMeteredIfaces;
        }

        final ContentResolver cr = mContext.getContentResolver();
        final boolean quotaEnabled = Settings.Global.getInt(cr,
@@ -2030,7 +2039,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            mSubscriptionOpportunisticQuota.put(subId, quotaBytes);
        }

        final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
        final String[] meteredIfaces;
        synchronized (mMeteredIfacesLock) {
            meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
        }
        mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();

        mHandler.obtainMessage(MSG_ADVISE_PERSIST_THRESHOLD, lowestRule).sendToTarget();
@@ -3436,7 +3448,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                fout.print("Restrict background: "); fout.println(mRestrictBackground);
                fout.print("Restrict power: "); fout.println(mRestrictPower);
                fout.print("Device idle: "); fout.println(mDeviceIdleMode);
                fout.print("Metered ifaces: "); fout.println(mMeteredIfaces);
                synchronized (mMeteredIfacesLock) {
                    fout.print("Metered ifaces: ");
                    fout.println(mMeteredIfaces);
                }

                fout.println();
                fout.print("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4618,7 +4633,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                    mListeners.finishBroadcast();
                    return true;
                }
                case MSG_STATS_PROVIDER_LIMIT_REACHED: {
                case MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED: {
                    mNetworkStats.forceUpdate();

                    synchronized (mNetworkPoliciesSecondLock) {
@@ -4632,7 +4647,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                }
                case MSG_LIMIT_REACHED: {
                    final String iface = (String) msg.obj;
                    synchronized (mNetworkPoliciesSecondLock) {
                    synchronized (mMeteredIfacesLock) {
                        // fast return if not needed.
                        if (!mMeteredIfaces.contains(iface)) {
                            return true;
@@ -4689,19 +4704,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                    mNetworkStats.advisePersistThreshold(persistThreshold);
                    return true;
                }
                case MSG_UPDATE_INTERFACE_QUOTA: {
                    final String iface = (String) msg.obj;
                    // int params need to be stitched back into a long
                    final long quota = ((long) msg.arg1 << 32) | (msg.arg2 & 0xFFFFFFFFL);
                    removeInterfaceQuota(iface);
                    setInterfaceQuota(iface, quota);
                    mNetworkStats.setStatsProviderLimitAsync(iface, quota);
                case MSG_UPDATE_INTERFACE_QUOTAS: {
                    final IfaceQuotas val = (IfaceQuotas) msg.obj;
                    // TODO: Consider set a new limit before removing the original one.
                    removeInterfaceLimit(val.iface);
                    setInterfaceLimit(val.iface, val.limit);
                    mNetworkStats.setStatsProviderWarningAndLimitAsync(val.iface, val.warning,
                            val.limit);
                    return true;
                }
                case MSG_REMOVE_INTERFACE_QUOTA: {
                case MSG_REMOVE_INTERFACE_QUOTAS: {
                    final String iface = (String) msg.obj;
                    removeInterfaceQuota(iface);
                    mNetworkStats.setStatsProviderLimitAsync(iface, QUOTA_UNLIMITED);
                    removeInterfaceLimit(iface);
                    mNetworkStats.setStatsProviderWarningAndLimitAsync(iface, QUOTA_UNLIMITED,
                            QUOTA_UNLIMITED);
                    return true;
                }
                case MSG_RESET_FIREWALL_RULES_BY_UID: {
@@ -4829,15 +4845,32 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }
    }

    private void setInterfaceQuotaAsync(String iface, long quotaBytes) {
        // long quotaBytes split up into two ints to fit in message
        mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTA, (int) (quotaBytes >> 32),
                (int) (quotaBytes & 0xFFFFFFFF), iface).sendToTarget();
    private static final class IfaceQuotas {
        @NonNull public final String iface;
        // Warning and limit bytes of interface qutoas, could be QUOTA_UNLIMITED or Long.MAX_VALUE
        // if not set. 0 is not acceptable since kernel doesn't like 0-byte rules.
        public final long warning;
        public final long limit;

        private IfaceQuotas(@NonNull String iface, long warning, long limit) {
            this.iface = iface;
            this.warning = warning;
            this.limit = limit;
        }
    }

    private void setInterfaceQuotasAsync(@NonNull String iface,
            long warningBytes, long limitBytes) {
        mHandler.obtainMessage(MSG_UPDATE_INTERFACE_QUOTAS,
                new IfaceQuotas(iface, warningBytes, limitBytes)).sendToTarget();
    }

    private void setInterfaceQuota(String iface, long quotaBytes) {
    private void setInterfaceLimit(String iface, long limitBytes) {
        try {
            mNetworkManager.setInterfaceQuota(iface, quotaBytes);
            // For legacy design the data warning is covered by global alert, where the
            // kernel will notify upper layer for a small amount of change of traffic
            // statistics. Thus, passing warning is not needed.
            mNetworkManager.setInterfaceQuota(iface, limitBytes);
        } catch (IllegalStateException e) {
            Log.wtf(TAG, "problem setting interface quota", e);
        } catch (RemoteException e) {
@@ -4845,11 +4878,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }
    }

    private void removeInterfaceQuotaAsync(String iface) {
        mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTA, iface).sendToTarget();
    private void removeInterfaceQuotasAsync(String iface) {
        mHandler.obtainMessage(MSG_REMOVE_INTERFACE_QUOTAS, iface).sendToTarget();
    }

    private void removeInterfaceQuota(String iface) {
    private void removeInterfaceLimit(String iface) {
        try {
            mNetworkManager.removeInterfaceQuota(iface);
        } catch (IllegalStateException e) {
@@ -5274,7 +5307,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
                isBackgroundRestricted = mRestrictBackground;
            }
            final boolean isNetworkMetered;
            synchronized (mNetworkPoliciesSecondLock) {
            synchronized (mMeteredIfacesLock) {
                isNetworkMetered = mMeteredIfaces.contains(ifname);
            }
            final boolean ret = isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
@@ -5361,9 +5394,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        }

        @Override
        public void onStatsProviderLimitReached(@NonNull String tag) {
            Log.v(TAG, "onStatsProviderLimitReached: " + tag);
            mHandler.obtainMessage(MSG_STATS_PROVIDER_LIMIT_REACHED).sendToTarget();
        public void onStatsProviderWarningOrLimitReached(@NonNull String tag) {
            Log.v(TAG, "onStatsProviderWarningOrLimitReached: " + tag);
            mHandler.obtainMessage(MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED).sendToTarget();
        }
    }

Loading