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

Commit c667aec6 authored by Danesh M's avatar Danesh M Committed by Gerrit Code Review
Browse files

NetworkManagement : Add ability to restrict app data/wifi usage

CYAN-3976
CRACKLING-834
Change-Id: Iaa0483d0ad64511184f0f31d93552a93fbab6dd0
parent 53ece467
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -49,6 +49,10 @@ public class NetworkPolicyManager {
    public static final int POLICY_REJECT_METERED_BACKGROUND = 0x1;
    /** Allow network use (metered or not) in the background in battery save mode. */
    public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
    /** Reject application network traffic on wifi network **/
    public static final int POLICY_REJECT_ON_WLAN = 0x8000;
    /** Reject application network traffic on cellular network **/
    public static final int POLICY_REJECT_ON_DATA = 0x10000;

    /* RULE_* are not masks and they must be exclusive */
    public static final int RULE_UNKNOWN = -1;
+3 −0
Original line number Diff line number Diff line
@@ -440,4 +440,7 @@ interface INetworkManagementService

    void addInterfaceToLocalNetwork(String iface, in List<RouteInfo> routes);
    void removeInterfaceFromLocalNetwork(String iface);

    void restrictAppOnData(int uid, boolean restrict);
    void restrictAppOnWlan(int uid, boolean restrict);
}
+102 −1
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkPolicyManager;
import android.net.NetworkStats;
@@ -77,6 +78,7 @@ import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -110,6 +112,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;

@@ -188,7 +191,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    private final Handler mFgHandler;
    private final Handler mDaemonHandler;
    private final PhoneStateListener mPhoneStateListener;

    private String mWifiInterfaceName, mDataInterfaceName;
    private IBatteryStats mBatteryStats;

    private final Thread mThread;
@@ -231,6 +234,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    /** Set of states for the child firewall chains. True if the chain is active. */
    @GuardedBy("mQuotaLock")
    final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
    @GuardedBy("mQuotaLock")
    final Map<Integer, Boolean> mWlanBlacklist = new HashMap<Integer, Boolean>();
    @GuardedBy("mQuotaLock")
    final Map<Integer, Boolean> mDataBlacklist = new HashMap<Integer, Boolean>();

    private Object mIdleTimerLock = new Object();
    /** Set of interfaces with active idle timers. */
@@ -258,6 +265,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
            new RemoteCallbackList<INetworkActivityListener>();
    private boolean mNetworkActive;
    private HashMap<Integer, Boolean> mPendingRestrictOnData = new HashMap<Integer, Boolean>();

    /**
     * Constructs a new NetworkManagementService instance
@@ -282,6 +290,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                FgThread.get().getLooper());
        mThread = new Thread(mConnector, NETD_TAG);

        mWifiInterfaceName = SystemProperties.get("wifi.interface");
        mDaemonHandler = new Handler(FgThread.get().getLooper());

        mPhoneStateListener = new PhoneStateListener(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
@@ -292,6 +301,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
                if (DBG) Slog.d(TAG, "onDataConnectionRealTimeInfoChanged: " + dcRtInfo);
                notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
                        dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
                processPendingDataRestrictRequests();
            }
        };
        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -1821,6 +1831,85 @@ public class NetworkManagementService extends INetworkManagementService.Stub
        }
    }

    private void processPendingDataRestrictRequests() {
        initDataInterface();
        if (TextUtils.isEmpty(mDataInterfaceName) || mPendingRestrictOnData.isEmpty()) {
            return;
        }
        for (Integer key : mPendingRestrictOnData.keySet()) {
            restrictAppOnData(key, mPendingRestrictOnData.get(key));
        }
        mPendingRestrictOnData.clear();
    }

    @Override
    public void restrictAppOnData(int uid, boolean restrict) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
        // silently discard when control disabled
        // TODO: eventually migrate to be always enabled
        if (!mBandwidthControlEnabled) return;

        initDataInterface();
        if (TextUtils.isEmpty(mDataInterfaceName)) {
            // We don't have an interface name since data is not active
            // yet, so queue up the request for when it comes up alive
            mPendingRestrictOnData.put(uid, restrict);
            return;
        }

        synchronized (mQuotaLock) {
            if (!mDataBlacklist.containsKey(uid) && !restrict) {
                return;
            }
            Boolean wasRestricted = mDataBlacklist.get(uid);
            if (Objects.equals(wasRestricted, restrict)) {
                return;
            }
            mDataBlacklist.put(uid, restrict);
        }

        try {
            if (restrict) {
                mConnector.execute("bandwidth", "addrestrictappsondata", mDataInterfaceName, uid);
            } else {
                mConnector.execute("bandwidth", "removerestrictappsondata", mDataInterfaceName, uid);
            }
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }

    @Override
    public void restrictAppOnWlan(int uid, boolean restrict) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);

        // silently discard when control disabled
        // TODO: eventually migrate to be always enabled
        if (!mBandwidthControlEnabled) return;

        synchronized (mQuotaLock) {
            if (!mWlanBlacklist.containsKey(uid) && !restrict) {
                return;
            }
            Boolean wasRestricted = mWlanBlacklist.get(uid);
            if (Objects.equals(wasRestricted, restrict) || TextUtils.isEmpty(mWifiInterfaceName)) {
                return;
            }
            mWlanBlacklist.put(uid, restrict);
        }


        try {
            if (restrict) {
                mConnector.execute("bandwidth", "addrestrictappsonwlan", mWifiInterfaceName, uid);
            } else {
                mConnector.execute("bandwidth", "removerestrictappsonwlan", mWifiInterfaceName, uid);
            }
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }

    @Override
    public void setUidCleartextNetworkPolicy(int uid, int policy) {
        if (Binder.getCallingUid() != uid) {
@@ -2573,4 +2662,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub
    public void removeInterfaceFromLocalNetwork(String iface) {
        modifyInterfaceInNetwork("remove", "local", iface);
    }

    private void initDataInterface() {
        if (!TextUtils.isEmpty(mDataInterfaceName)) {
            return;
        }
        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
                Context.CONNECTIVITY_SERVICE);
        LinkProperties linkProperties = cm.getLinkProperties(ConnectivityManager.TYPE_MOBILE);
        if (linkProperties != null) {
            mDataInterfaceName = linkProperties.getInterfaceName();
        }
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -45,6 +45,8 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
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_WLAN;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -2397,6 +2399,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
            uidRules = RULE_REJECT_ALL;
        }

        try {
            mNetworkManager.restrictAppOnWlan(uid, (uidPolicy & POLICY_REJECT_ON_WLAN) != 0);
            mNetworkManager.restrictAppOnData(uid, (uidPolicy & POLICY_REJECT_ON_DATA) != 0);
        } catch (RemoteException e) {
            // ignored; service lives in system_server
        }

        final int oldRules = mUidRules.get(uid);
        if (uidRules == RULE_ALLOW_ALL) {
            mUidRules.delete(uid);