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

Commit 54c58a56 authored by Danesh M's avatar Danesh M Committed by Sam Mortimer
Browse files

fw/b: Squash of app fw restriction commits

* Refactored to simplify for Q

Author: Danesh M <daneshm90@gmail.com>
Date:   Mon Feb 29 10:02:34 2016 -0800

    [2/3] NetworkManagement : Add ability to restrict app data/wifi usage

    CYAN-3976
    CRACKLING-834

    Change-Id: Iaa0483d0ad64511184f0f31d93552a93fbab6dd0

Change-Id: I24391a394b9d4e8eef6d822ca5af4f0ed6674cdd

----

Author: Uldiniad <olivercscott@gmail.com>
Date:   Wed Oct 31 02:32:03 2018 +0000

    NetworkManagement : Add ability to restrict app vpn usage

    Change-Id: Ia6bd0894f3298fe6fb5cca343cbfe025e3b88ee9

----

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Thu Aug 29 17:12:58 2019 -0700

    fw/b: Use common network restrict apps method

    * These are lineage additions (that originated
      from caf).

    * addrestrictappsondata, addrestrictappsonvpn and
      addrestrictappsonwlan all do a similar thing
      (fw/b passes different interface arguments).

    * Consolidate into addrestrictappsoninterface
      (and removerestrictappsoninterface)

    * Requires corresponding system/netd change.

    Change-Id: I1f7cb568dd0415aaec880cf98ae97032ab555bd1

----

Change-Id: If925f7f794d09664eac37da9478e443bce7cc496
parent ffefb88c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -56,6 +56,12 @@ public class NetworkPolicyManager {
    public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
    /** Allow metered network use in the background even when in data usage save mode. */
    public static final int POLICY_ALLOW_METERED_BACKGROUND = 0x4;
    /** Reject network usage on cellular network */
    public static final int POLICY_REJECT_ON_DATA = 0x10000;
    /** Reject network usage on virtual private network */
    public static final int POLICY_REJECT_ON_VPN = 0x20000;
    /** Reject network usage on wifi network */
    public static final int POLICY_REJECT_ON_WLAN = 0x8000;

    /*
     * Rules defining whether an uid has access to a network given its type (metered / non-metered).
+5 −0
Original line number Diff line number Diff line
@@ -401,4 +401,9 @@ interface INetworkManagementService
    void setAllowOnlyVpnForUids(boolean enable, in UidRange[] uidRanges);

    boolean isNetworkRestricted(int uid);

    /**
     * Restrict UID from accessing a network interface
     */
    void restrictAppOnInterface(String key, int uid, boolean restrict);
}
+176 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.TrafficStats.UID_TETHERING;
import static android.system.OsConstants.ENETDOWN;

import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;

@@ -53,8 +54,11 @@ import android.net.InterfaceConfiguration;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkPolicyManager;
import android.net.NetworkRequest;
import android.net.NetworkStats;
import android.net.NetworkUtils;
import android.net.RouteInfo;
@@ -247,6 +251,51 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
            new RemoteCallbackList<>();
    private boolean mNetworkActive;

    /* map keys used by netd to keep per app interface restrictions
     * separate for each use case.
     */
    private static final String RESTRICT_USECASE_DATA = "data";
    private static final String RESTRICT_USECASE_VPN  = "vpn";
    private static final String RESTRICT_USECASE_WLAN = "wlan";

    // Helper class for managing per uid interface blacklists.
    private static class RestrictIf {
        // Use case string
        public String useCase;
        // Interface name
        public String ifName;
        // NetworkCapabilities transport type used for this blacklist
        public int transport;
        // Active uid blacklist
        public SparseBooleanArray active;
        // Desired uid blacklist changes
        public SparseBooleanArray pending;

        RestrictIf(String useCase, int transport) {
            this.useCase = useCase;
            this.ifName = null;
            this.transport = transport;
            this.active = new SparseBooleanArray();
            this.pending = new SparseBooleanArray();
        }
    }

    @GuardedBy("mQuotaLock")
    private RestrictIf[] mRestrictIf = {
            new RestrictIf(RESTRICT_USECASE_DATA, NetworkCapabilities.TRANSPORT_CELLULAR),
            new RestrictIf(RESTRICT_USECASE_VPN, NetworkCapabilities.TRANSPORT_VPN),
            new RestrictIf(RESTRICT_USECASE_WLAN, NetworkCapabilities.TRANSPORT_WIFI),
    };

    private RestrictIf getUseCaseRestrictIf(String useCase) {
        for (RestrictIf restrictIf : mRestrictIf) {
            if (restrictIf.useCase.equals(useCase)) {
                return restrictIf;
            }
        }
        throw new IllegalStateException("Unknown interface restriction");
    }

    /**
     * Constructs a new NetworkManagementService instance
     *
@@ -292,6 +341,44 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
    }

    public void systemReady() {
        final ConnectivityManager mConnectivityManager =
                mContext.getSystemService(ConnectivityManager.class);

        final NetworkRequest.Builder builder = new NetworkRequest.Builder()
                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
        for (RestrictIf restrictIf : mRestrictIf) {
            builder.addTransportType(restrictIf.transport);
        }
        final NetworkRequest request = builder.build();

        final ConnectivityManager.NetworkCallback mNetworkCallback =
                new ConnectivityManager.NetworkCallback() {
            @Override
            public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
                NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(network);
                RestrictIf matchedRestrictIf = null;
                for (RestrictIf restrictIf : mRestrictIf) {
                    if (nc.hasTransport(restrictIf.transport)) {
                        matchedRestrictIf = restrictIf;
                    }
                }
                if (matchedRestrictIf == null) {
                    return;
                }
                final String iface = linkProperties.getInterfaceName();
                if (TextUtils.isEmpty(iface)) {
                    return;
                }
                // The post below requires final arguments so
                final RestrictIf finalRestrictIf = matchedRestrictIf;
                // Exit the callback ASAP and move further work onto daemon thread
                mDaemonHandler.post(() ->
                        updateAppOnInterfaceCallback(finalRestrictIf, iface));
            }
        };

        mConnectivityManager.registerNetworkCallback(request, mNetworkCallback);

        if (DBG) {
            final long start = System.currentTimeMillis();
            prepareNativeDaemon();
@@ -1458,6 +1545,95 @@ public class NetworkManagementService extends INetworkManagementService.Stub {
        return stableRanges;
    }

    private void updateAppOnInterfaceCallback(RestrictIf restrictIf, String newIface) {
        synchronized (mQuotaLock) {
            if (TextUtils.isEmpty(restrictIf.ifName)) {
                restrictIf.ifName = newIface;
            } else if (!restrictIf.ifName.equals(newIface)) { // interface name has changed
                // Prevent new incoming requests colliding with an update in progress
                for (int i = 0; i < restrictIf.active.size(); i++) {
                    final int uid = restrictIf.active.keyAt(i);
                    final boolean restrict = restrictIf.active.valueAt(i);
                    // Only remove/readd if a restriction is currently in place
                    if (!restrict) {
                        continue;
                    }
                    setAppOnInterfaceLocked(restrictIf.useCase, restrictIf.ifName, uid, false);
                    // Use pending list to queue re-add.
                    // (Prefer keeping existing pending status if it exists.)
                    if (restrictIf.pending.indexOfKey(uid) < 0) {
                        restrictIf.pending.put(uid, true);
                    }
                }
                restrictIf.ifName = newIface;
            }
            processPendingAppOnInterfaceLocked(restrictIf);
        }
    }

    @Override
    public void restrictAppOnInterface(String useCase, int uid, boolean restrict) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
        synchronized (mQuotaLock) {
            restrictAppOnInterfaceLocked(getUseCaseRestrictIf(useCase), uid, restrict);
        }
    }

    private void restrictAppOnInterfaceLocked(RestrictIf restrictIf, int uid, boolean restrict) {
        if (TextUtils.isEmpty(restrictIf.ifName)) {
            // We don't have an interface name yet so queue
            // the request for when it comes up
            restrictIf.pending.put(uid, restrict);
            return;
        }

        boolean oldValue = restrictIf.active.get(uid, false);
        if (oldValue == restrict) {
            return;
        }

        if (setAppOnInterfaceLocked(restrictIf.useCase, restrictIf.ifName, uid, restrict)) {
            restrictIf.active.put(uid, restrict);
        } else {
            // Perhaps the interface was down, queue to retry after receipt
            // of the next network callback for this network.
            restrictIf.pending.put(uid, true);
        }
    }

    private boolean setAppOnInterfaceLocked(String useCase, String ifName, int uid,
            boolean restrict) {
        boolean ok = true;
        try {
            if (restrict) {
                mNetdService.bandwidthAddRestrictAppOnInterface(useCase, ifName, uid);
            } else {
                mNetdService.bandwidthRemoveRestrictAppOnInterface(useCase, ifName, uid);
            }
        } catch (RemoteException e) {
            throw new IllegalStateException(e);
        } catch (ServiceSpecificException e) {
            // ENETDOWN is returned when the interface cannot be resolved to an index.
            // (and is only returned by bandwidthAdd... call)
            if (e.errorCode == ENETDOWN) {
                ok = false;
            } else {
                throw new IllegalStateException(e);
            }
        }
        return ok;
    }

    private void processPendingAppOnInterfaceLocked(RestrictIf restrictIf) {
        // Work on a copy of the pending list since failed add requests
        // get put back on.
        SparseBooleanArray pendingList = restrictIf.pending.clone();
        restrictIf.pending = new SparseBooleanArray();
        for (int i = 0; i < pendingList.size(); i++) {
            restrictAppOnInterfaceLocked(restrictIf, pendingList.keyAt(i), pendingList.valueAt(i));
        }
    }

    @Override
    public void setAllowOnlyVpnForUids(boolean add, UidRange[] uidRanges)
            throws ServiceSpecificException {
+14 −0
Original line number Diff line number Diff line
@@ -56,6 +56,9 @@ import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_DATA;
import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_VPN;
import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_WLAN;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
import static android.net.NetworkPolicyManager.RULE_NONE;
@@ -4167,6 +4170,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
        final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
        int newRule = RULE_NONE;

        try {
            mNetworkManager.restrictAppOnInterface("data", uid,
                    (uidPolicy & POLICY_REJECT_ON_DATA) != 0);
            mNetworkManager.restrictAppOnInterface("vpn", uid,
                    (uidPolicy & POLICY_REJECT_ON_VPN) != 0);
            mNetworkManager.restrictAppOnInterface("wlan", uid,
                    (uidPolicy & POLICY_REJECT_ON_WLAN) != 0);
        } catch (RemoteException e) {
            // ignored; service lives in system_server
        }

        // First step: define the new rule based on user restrictions and foreground state.
        if (isRestrictedByAdmin) {
            newRule = RULE_REJECT_METERED;