Skip to content

Commit b24a785

Browse files
committed
Push existing rules when netd reconnects.
When netd drops its socket connection to framework, assume that it has restarted, and push any existing rules to keep netd and iptables consistent. Bug: 6376246 Change-Id: Id93138938321bcf885eb0e4fecaff8b150cfdfcf
1 parent f2dc6fc commit b24a785

File tree

1 file changed

+98
-51
lines changed

1 file changed

+98
-51
lines changed

services/java/com/android/server/NetworkManagementService.java

Lines changed: 98 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import static android.net.NetworkStats.TAG_NONE;
2424
import static android.net.NetworkStats.UID_ALL;
2525
import static android.net.TrafficStats.UID_TETHERING;
26-
import static android.provider.Settings.Secure.NETSTATS_ENABLED;
2726
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
2827
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
2928
import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceRxThrottleResult;
@@ -45,19 +44,19 @@
4544
import android.net.RouteInfo;
4645
import android.net.wifi.WifiConfiguration;
4746
import android.net.wifi.WifiConfiguration.KeyMgmt;
47+
import android.os.Handler;
4848
import android.os.INetworkManagementService;
4949
import android.os.RemoteCallbackList;
5050
import android.os.RemoteException;
5151
import android.os.SystemClock;
5252
import android.os.SystemProperties;
53-
import android.provider.Settings;
5453
import android.util.Log;
5554
import android.util.Slog;
5655
import android.util.SparseBooleanArray;
5756

5857
import com.android.internal.net.NetworkStatsFactory;
5958
import com.android.server.NativeDaemonConnector.Command;
60-
import com.google.android.collect.Sets;
59+
import com.google.android.collect.Maps;
6160

6261
import java.io.BufferedReader;
6362
import java.io.DataInputStream;
@@ -74,7 +73,8 @@
7473
import java.net.SocketException;
7574
import java.util.ArrayList;
7675
import java.util.Collection;
77-
import java.util.HashSet;
76+
import java.util.HashMap;
77+
import java.util.Map;
7878
import java.util.NoSuchElementException;
7979
import java.util.StringTokenizer;
8080
import java.util.concurrent.CountDownLatch;
@@ -133,8 +133,10 @@ class NetdResponseCode {
133133
*/
134134
private NativeDaemonConnector mConnector;
135135

136+
private final Handler mMainHandler = new Handler();
137+
136138
private Thread mThread;
137-
private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
139+
private CountDownLatch mConnectedSignal = new CountDownLatch(1);
138140

139141
private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
140142
new RemoteCallbackList<INetworkManagementEventObserver>();
@@ -143,9 +145,9 @@ class NetdResponseCode {
143145

144146
private Object mQuotaLock = new Object();
145147
/** Set of interfaces with active quotas. */
146-
private HashSet<String> mActiveQuotaIfaces = Sets.newHashSet();
148+
private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
147149
/** Set of interfaces with active alerts. */
148-
private HashSet<String> mActiveAlertIfaces = Sets.newHashSet();
150+
private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
149151
/** Set of UIDs with active reject rules. */
150152
private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
151153

@@ -172,35 +174,19 @@ private NetworkManagementService(Context context) {
172174
}
173175

174176
public static NetworkManagementService create(Context context) throws InterruptedException {
175-
NetworkManagementService service = new NetworkManagementService(context);
177+
final NetworkManagementService service = new NetworkManagementService(context);
178+
final CountDownLatch connectedSignal = service.mConnectedSignal;
176179
if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
177180
service.mThread.start();
178181
if (DBG) Slog.d(TAG, "Awaiting socket connection");
179-
service.mConnectedSignal.await();
182+
connectedSignal.await();
180183
if (DBG) Slog.d(TAG, "Connected");
181184
return service;
182185
}
183186

184187
public void systemReady() {
185-
// only enable bandwidth control when support exists, and requested by
186-
// system setting.
187-
final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
188-
final boolean shouldEnable =
189-
Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 1) != 0;
190-
191-
if (hasKernelSupport && shouldEnable) {
192-
Slog.d(TAG, "enabling bandwidth control");
193-
try {
194-
mConnector.execute("bandwidth", "enable");
195-
mBandwidthControlEnabled = true;
196-
} catch (NativeDaemonConnectorException e) {
197-
Log.wtf(TAG, "problem enabling bandwidth controls", e);
198-
}
199-
} else {
200-
Slog.d(TAG, "not enabling bandwidth control");
201-
}
202-
203-
SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
188+
prepareNativeDaemon();
189+
if (DBG) Slog.d(TAG, "Prepared");
204190
}
205191

206192
@Override
@@ -264,8 +250,8 @@ private void notifyInterfaceAdded(String iface) {
264250
private void notifyInterfaceRemoved(String iface) {
265251
// netd already clears out quota and alerts for removed ifaces; update
266252
// our sanity-checking state.
267-
mActiveAlertIfaces.remove(iface);
268-
mActiveQuotaIfaces.remove(iface);
253+
mActiveAlerts.remove(iface);
254+
mActiveQuotas.remove(iface);
269255

270256
final int length = mObservers.beginBroadcast();
271257
for (int i = 0; i < length; i++) {
@@ -292,24 +278,85 @@ private void notifyLimitReached(String limitName, String iface) {
292278
}
293279

294280
/**
295-
* Let us know the daemon is connected
281+
* Prepare native daemon once connected, enabling modules and pushing any
282+
* existing in-memory rules.
296283
*/
297-
protected void onDaemonConnected() {
298-
mConnectedSignal.countDown();
299-
}
284+
private void prepareNativeDaemon() {
285+
mBandwidthControlEnabled = false;
286+
287+
// only enable bandwidth control when support exists
288+
final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
289+
if (hasKernelSupport) {
290+
Slog.d(TAG, "enabling bandwidth control");
291+
try {
292+
mConnector.execute("bandwidth", "enable");
293+
mBandwidthControlEnabled = true;
294+
} catch (NativeDaemonConnectorException e) {
295+
Log.wtf(TAG, "problem enabling bandwidth controls", e);
296+
}
297+
} else {
298+
Slog.d(TAG, "not enabling bandwidth control");
299+
}
300+
301+
SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
300302

303+
// push any existing quota or UID rules
304+
synchronized (mQuotaLock) {
305+
int size = mActiveQuotas.size();
306+
if (size > 0) {
307+
Slog.d(TAG, "pushing " + size + " active quota rules");
308+
final HashMap<String, Long> activeQuotas = mActiveQuotas;
309+
mActiveQuotas = Maps.newHashMap();
310+
for (Map.Entry<String, Long> entry : activeQuotas.entrySet()) {
311+
setInterfaceQuota(entry.getKey(), entry.getValue());
312+
}
313+
}
314+
315+
size = mActiveAlerts.size();
316+
if (size > 0) {
317+
Slog.d(TAG, "pushing " + size + " active alert rules");
318+
final HashMap<String, Long> activeAlerts = mActiveAlerts;
319+
mActiveAlerts = Maps.newHashMap();
320+
for (Map.Entry<String, Long> entry : activeAlerts.entrySet()) {
321+
setInterfaceAlert(entry.getKey(), entry.getValue());
322+
}
323+
}
324+
325+
size = mUidRejectOnQuota.size();
326+
if (size > 0) {
327+
Slog.d(TAG, "pushing " + size + " active uid rules");
328+
final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
329+
mUidRejectOnQuota = new SparseBooleanArray();
330+
for (int i = 0; i < uidRejectOnQuota.size(); i++) {
331+
setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i));
332+
}
333+
}
334+
}
335+
}
301336

302337
//
303338
// Netd Callback handling
304339
//
305340

306-
class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
307-
/** {@inheritDoc} */
341+
private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
342+
@Override
308343
public void onDaemonConnected() {
309-
NetworkManagementService.this.onDaemonConnected();
344+
// event is dispatched from internal NDC thread, so we prepare the
345+
// daemon back on main thread.
346+
if (mConnectedSignal != null) {
347+
mConnectedSignal.countDown();
348+
mConnectedSignal = null;
349+
} else {
350+
mMainHandler.post(new Runnable() {
351+
@Override
352+
public void run() {
353+
prepareNativeDaemon();
354+
}
355+
});
356+
}
310357
}
311358

312-
/** {@inheritDoc} */
359+
@Override
313360
public boolean onEvent(int code, String raw, String[] cooked) {
314361
switch (code) {
315362
case NetdResponseCode.InterfaceChange:
@@ -962,14 +1009,14 @@ public void setInterfaceQuota(String iface, long quotaBytes) {
9621009
if (!mBandwidthControlEnabled) return;
9631010

9641011
synchronized (mQuotaLock) {
965-
if (mActiveQuotaIfaces.contains(iface)) {
1012+
if (mActiveQuotas.containsKey(iface)) {
9661013
throw new IllegalStateException("iface " + iface + " already has quota");
9671014
}
9681015

9691016
try {
9701017
// TODO: support quota shared across interfaces
9711018
mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
972-
mActiveQuotaIfaces.add(iface);
1019+
mActiveQuotas.put(iface, quotaBytes);
9731020
} catch (NativeDaemonConnectorException e) {
9741021
throw e.rethrowAsParcelableException();
9751022
}
@@ -985,13 +1032,13 @@ public void removeInterfaceQuota(String iface) {
9851032
if (!mBandwidthControlEnabled) return;
9861033

9871034
synchronized (mQuotaLock) {
988-
if (!mActiveQuotaIfaces.contains(iface)) {
1035+
if (!mActiveQuotas.containsKey(iface)) {
9891036
// TODO: eventually consider throwing
9901037
return;
9911038
}
9921039

993-
mActiveQuotaIfaces.remove(iface);
994-
mActiveAlertIfaces.remove(iface);
1040+
mActiveQuotas.remove(iface);
1041+
mActiveAlerts.remove(iface);
9951042

9961043
try {
9971044
// TODO: support quota shared across interfaces
@@ -1011,19 +1058,19 @@ public void setInterfaceAlert(String iface, long alertBytes) {
10111058
if (!mBandwidthControlEnabled) return;
10121059

10131060
// quick sanity check
1014-
if (!mActiveQuotaIfaces.contains(iface)) {
1061+
if (!mActiveQuotas.containsKey(iface)) {
10151062
throw new IllegalStateException("setting alert requires existing quota on iface");
10161063
}
10171064

10181065
synchronized (mQuotaLock) {
1019-
if (mActiveAlertIfaces.contains(iface)) {
1066+
if (mActiveAlerts.containsKey(iface)) {
10201067
throw new IllegalStateException("iface " + iface + " already has alert");
10211068
}
10221069

10231070
try {
10241071
// TODO: support alert shared across interfaces
10251072
mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
1026-
mActiveAlertIfaces.add(iface);
1073+
mActiveAlerts.put(iface, alertBytes);
10271074
} catch (NativeDaemonConnectorException e) {
10281075
throw e.rethrowAsParcelableException();
10291076
}
@@ -1039,15 +1086,15 @@ public void removeInterfaceAlert(String iface) {
10391086
if (!mBandwidthControlEnabled) return;
10401087

10411088
synchronized (mQuotaLock) {
1042-
if (!mActiveAlertIfaces.contains(iface)) {
1089+
if (!mActiveAlerts.containsKey(iface)) {
10431090
// TODO: eventually consider throwing
10441091
return;
10451092
}
10461093

10471094
try {
10481095
// TODO: support alert shared across interfaces
10491096
mConnector.execute("bandwidth", "removeinterfacealert", iface);
1050-
mActiveAlertIfaces.remove(iface);
1097+
mActiveAlerts.remove(iface);
10511098
} catch (NativeDaemonConnectorException e) {
10521099
throw e.rethrowAsParcelableException();
10531100
}
@@ -1077,7 +1124,7 @@ public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
10771124
// TODO: eventually migrate to be always enabled
10781125
if (!mBandwidthControlEnabled) return;
10791126

1080-
synchronized (mUidRejectOnQuota) {
1127+
synchronized (mQuotaLock) {
10811128
final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
10821129
if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
10831130
// TODO: eventually consider throwing
@@ -1272,8 +1319,8 @@ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
12721319
pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
12731320

12741321
synchronized (mQuotaLock) {
1275-
pw.print("Active quota ifaces: "); pw.println(mActiveQuotaIfaces.toString());
1276-
pw.print("Active alert ifaces: "); pw.println(mActiveAlertIfaces.toString());
1322+
pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
1323+
pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
12771324
}
12781325

12791326
synchronized (mUidRejectOnQuota) {

0 commit comments

Comments
 (0)