Skip to content

Commit

Permalink
fw/b: Squash of app fw restriction commits
Browse files Browse the repository at this point in the history
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

----

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

----

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Tue Apr 14 17:47:58 2020 -0400

    fw/b: Prevent double interface restriction remove on interface name change

    * When temporarily removing a restriction owing to interface name
      change, update the boolean state array to match.  Otherwise, we
      get out of sync, follow-on double removes can occur and the system
      server will crash.

    * In addition, it was observed that it is possible to receive a network
      callback for a (VPN) network that has both WIFI and VPN transports set (it
      looked transient rather than persisent but difficult to tell).  So make
      the list of use cases in priority of match order, putting VPN first.

    Change-Id: If484b5a715e0a972769c847ea4549fd84afb3ccf

----

Author: Sam Mortimer <sam@mortimer.me.uk>
Date:   Sun May 03 17:18:00 2020 -0400

    fw/b data restrictions: Don't call getNetworkCapabilities() in the callback

    * Docs say that calling getNetworkCapabilities() from within a network
      callback is racy and not to do it.

    * Refactor to make use of onCapabilitiesChanged() to glean capabilities
      instead.

    Change-Id: If9c4cd7c1bd0594697b0ac98903600ecd583e55b

Change-Id: If925f7f794d09664eac37da9478e443bce7cc496
  • Loading branch information
Danesh authored and mikeNG committed Dec 12, 2020
1 parent eaba9b4 commit a04932b
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 0 deletions.
6 changes: 6 additions & 0 deletions core/java/android/net/NetworkPolicyManager.java
Expand Up @@ -73,6 +73,12 @@ public class NetworkPolicyManager {
* @hide
*/
public static final int POLICY_ALLOW_METERED_BACKGROUND = 0x4;
/** Reject network usage on cellular network */
public static final int POLICY_REJECT_CELLULAR = 0x10000;
/** Reject network usage on virtual private network */
public static final int POLICY_REJECT_VPN = 0x20000;
/** Reject network usage on wifi network */
public static final int POLICY_REJECT_WIFI = 0x8000;

/*
* Rules defining whether an uid has access to a network given its type (metered / non-metered).
Expand Down
5 changes: 5 additions & 0 deletions core/java/android/os/INetworkManagementService.aidl
Expand Up @@ -389,4 +389,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);
}
202 changes: 202 additions & 0 deletions services/core/java/com/android/server/NetworkManagementService.java
Expand Up @@ -36,6 +36,7 @@
import static android.net.NetworkStats.STATS_PER_UID;
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;

Expand All @@ -52,8 +53,11 @@
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.NetworkStack;
import android.net.NetworkStats;
import android.net.NetworkUtils;
Expand Down Expand Up @@ -248,6 +252,55 @@ private static class IdleTimerParams {
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_CELLULAR = "cellular";
private static final String RESTRICT_USECASE_VPN = "vpn";
private static final String RESTRICT_USECASE_WIFI = "wifi";

// 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 = {
// Ordered by match preference (in the event we get a callback with
// multiple transports).
new RestrictIf(RESTRICT_USECASE_VPN, NetworkCapabilities.TRANSPORT_VPN),
new RestrictIf(RESTRICT_USECASE_CELLULAR, NetworkCapabilities.TRANSPORT_CELLULAR),
new RestrictIf(RESTRICT_USECASE_WIFI, 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");
}

private final HashMap<Network, NetworkCapabilities> mNetworkCapabilitiesMap = new HashMap<>();

/**
* Constructs a new NetworkManagementService instance
*
Expand Down Expand Up @@ -293,6 +346,65 @@ public static NetworkManagementService create(Context context) throws Interrupte
}

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 onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {
mNetworkCapabilitiesMap.put(network, networkCapabilities);
}

@Override
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
// Callback ordering in Oreo+ is documented to be:
// onCapabilitiesChanged, onLinkPropertiesChanged
// At this point, we should always find the network in our
// local map but guard anyway.
NetworkCapabilities nc = mNetworkCapabilitiesMap.get(network);
if (nc == null) {
Slog.e(TAG, "onLinkPropertiesChanged: network was not in map: "
+ "network=" + network + " linkProperties=" + linkProperties);
return;
}
RestrictIf matchedRestrictIf = null;
for (RestrictIf restrictIf : mRestrictIf) {
if (nc.hasTransport(restrictIf.transport)) {
matchedRestrictIf = restrictIf;
break;
}
}
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));
}

@Override
public void onLost(Network network) {
mNetworkCapabilitiesMap.remove(network);
}
};

mConnectivityManager.registerNetworkCallback(request, mNetworkCallback);

if (DBG) {
final long start = System.currentTimeMillis();
prepareNativeDaemon();
Expand Down Expand Up @@ -1405,6 +1517,96 @@ private static UidRangeParcel[] toStableParcels(UidRange[] ranges) {
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);
restrictIf.active.setValueAt(i, 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 {
Expand Down
Expand Up @@ -60,6 +60,9 @@
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_CELLULAR;
import static android.net.NetworkPolicyManager.POLICY_REJECT_VPN;
import static android.net.NetworkPolicyManager.POLICY_REJECT_WIFI;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
import static android.net.NetworkPolicyManager.RULE_NONE;
Expand Down Expand Up @@ -4322,6 +4325,17 @@ private void updateRulesForDataUsageRestrictionsULInner(int uid) {
final int oldRule = oldUidRules & MASK_METERED_NETWORKS;
int newRule = RULE_NONE;

try {
mNetworkManager.restrictAppOnInterface("cellular", uid,
(uidPolicy & POLICY_REJECT_CELLULAR) != 0);
mNetworkManager.restrictAppOnInterface("vpn", uid,
(uidPolicy & POLICY_REJECT_VPN) != 0);
mNetworkManager.restrictAppOnInterface("wifi", uid,
(uidPolicy & POLICY_REJECT_WIFI) != 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;
Expand Down

0 comments on commit a04932b

Please sign in to comment.