1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
20import static android.Manifest.permission.DUMP;
21import static android.Manifest.permission.SHUTDOWN;
22import static android.net.NetworkStats.SET_DEFAULT;
23import static android.net.NetworkStats.TAG_NONE;
24import static android.net.NetworkStats.UID_ALL;
25import static android.net.TrafficStats.UID_TETHERING;
26import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
27import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
28import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceRxThrottleResult;
29import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceTxThrottleResult;
30import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
31import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
32import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
33import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
34import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsResult;
35import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
36import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
37
38import android.content.Context;
39import android.net.INetworkManagementEventObserver;
40import android.net.InterfaceConfiguration;
41import android.net.LinkAddress;
42import android.net.NetworkStats;
43import android.net.NetworkUtils;
44import android.net.RouteInfo;
45import android.net.wifi.WifiConfiguration;
46import android.net.wifi.WifiConfiguration.KeyMgmt;
47import android.os.Handler;
48import android.os.INetworkManagementService;
49import android.os.RemoteCallbackList;
50import android.os.RemoteException;
51import android.os.SystemClock;
52import android.os.SystemProperties;
53import android.util.Log;
54import android.util.Slog;
55import android.util.SparseBooleanArray;
56
57import com.android.internal.net.NetworkStatsFactory;
58import com.android.server.NativeDaemonConnector.Command;
59import com.google.android.collect.Maps;
60
61import java.io.BufferedReader;
62import java.io.DataInputStream;
63import java.io.File;
64import java.io.FileDescriptor;
65import java.io.FileInputStream;
66import java.io.IOException;
67import java.io.InputStreamReader;
68import java.io.PrintWriter;
69import java.net.Inet4Address;
70import java.net.InetAddress;
71import java.net.InterfaceAddress;
72import java.net.NetworkInterface;
73import java.net.SocketException;
74import java.util.ArrayList;
75import java.util.Collection;
76import java.util.HashMap;
77import java.util.Map;
78import java.util.NoSuchElementException;
79import java.util.StringTokenizer;
80import java.util.concurrent.CountDownLatch;
81
82/**
83 * @hide
84 */
85public class NetworkManagementService extends INetworkManagementService.Stub
86        implements Watchdog.Monitor {
87    private static final String TAG = "NetworkManagementService";
88    private static final boolean DBG = false;
89    private static final String NETD_TAG = "NetdConnector";
90
91    private static final String ADD = "add";
92    private static final String REMOVE = "remove";
93
94    private static final String DEFAULT = "default";
95    private static final String SECONDARY = "secondary";
96
97    /**
98     * Name representing {@link #setGlobalAlert(long)} limit when delivered to
99     * {@link INetworkManagementEventObserver#limitReached(String, String)}.
100     */
101    public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
102
103    class NetdResponseCode {
104        /* Keep in sync with system/netd/ResponseCode.h */
105        public static final int InterfaceListResult       = 110;
106        public static final int TetherInterfaceListResult = 111;
107        public static final int TetherDnsFwdTgtListResult = 112;
108        public static final int TtyListResult             = 113;
109
110        public static final int TetherStatusResult        = 210;
111        public static final int IpFwdStatusResult         = 211;
112        public static final int InterfaceGetCfgResult     = 213;
113        public static final int SoftapStatusResult        = 214;
114        public static final int InterfaceRxCounterResult  = 216;
115        public static final int InterfaceTxCounterResult  = 217;
116        public static final int InterfaceRxThrottleResult = 218;
117        public static final int InterfaceTxThrottleResult = 219;
118        public static final int QuotaCounterResult        = 220;
119        public static final int TetheringStatsResult      = 221;
120        public static final int DnsProxyQueryResult       = 222;
121
122        public static final int InterfaceChange           = 600;
123        public static final int BandwidthControl          = 601;
124    }
125
126    /**
127     * Binder context for this service
128     */
129    private Context mContext;
130
131    /**
132     * connector object for communicating with netd
133     */
134    private NativeDaemonConnector mConnector;
135
136    private final Handler mMainHandler = new Handler();
137
138    private Thread mThread;
139    private CountDownLatch mConnectedSignal = new CountDownLatch(1);
140
141    private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
142            new RemoteCallbackList<INetworkManagementEventObserver>();
143
144    private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
145
146    private Object mQuotaLock = new Object();
147    /** Set of interfaces with active quotas. */
148    private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
149    /** Set of interfaces with active alerts. */
150    private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
151    /** Set of UIDs with active reject rules. */
152    private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
153
154    private volatile boolean mBandwidthControlEnabled;
155
156    /**
157     * Constructs a new NetworkManagementService instance
158     *
159     * @param context  Binder context for this service
160     */
161    private NetworkManagementService(Context context) {
162        mContext = context;
163
164        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
165            return;
166        }
167
168        mConnector = new NativeDaemonConnector(
169                new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
170        mThread = new Thread(mConnector, NETD_TAG);
171
172        // Add ourself to the Watchdog monitors.
173        Watchdog.getInstance().addMonitor(this);
174    }
175
176    public static NetworkManagementService create(Context context) throws InterruptedException {
177        final NetworkManagementService service = new NetworkManagementService(context);
178        final CountDownLatch connectedSignal = service.mConnectedSignal;
179        if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
180        service.mThread.start();
181        if (DBG) Slog.d(TAG, "Awaiting socket connection");
182        connectedSignal.await();
183        if (DBG) Slog.d(TAG, "Connected");
184        return service;
185    }
186
187    public void systemReady() {
188        prepareNativeDaemon();
189        if (DBG) Slog.d(TAG, "Prepared");
190    }
191
192    @Override
193    public void registerObserver(INetworkManagementEventObserver observer) {
194        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
195        mObservers.register(observer);
196    }
197
198    @Override
199    public void unregisterObserver(INetworkManagementEventObserver observer) {
200        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
201        mObservers.unregister(observer);
202    }
203
204    /**
205     * Notify our observers of an interface status change
206     */
207    private void notifyInterfaceStatusChanged(String iface, boolean up) {
208        final int length = mObservers.beginBroadcast();
209        for (int i = 0; i < length; i++) {
210            try {
211                mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
212            } catch (RemoteException e) {
213            }
214        }
215        mObservers.finishBroadcast();
216    }
217
218    /**
219     * Notify our observers of an interface link state change
220     * (typically, an Ethernet cable has been plugged-in or unplugged).
221     */
222    private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
223        final int length = mObservers.beginBroadcast();
224        for (int i = 0; i < length; i++) {
225            try {
226                mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
227            } catch (RemoteException e) {
228            }
229        }
230        mObservers.finishBroadcast();
231    }
232
233    /**
234     * Notify our observers of an interface addition.
235     */
236    private void notifyInterfaceAdded(String iface) {
237        final int length = mObservers.beginBroadcast();
238        for (int i = 0; i < length; i++) {
239            try {
240                mObservers.getBroadcastItem(i).interfaceAdded(iface);
241            } catch (RemoteException e) {
242            }
243        }
244        mObservers.finishBroadcast();
245    }
246
247    /**
248     * Notify our observers of an interface removal.
249     */
250    private void notifyInterfaceRemoved(String iface) {
251        // netd already clears out quota and alerts for removed ifaces; update
252        // our sanity-checking state.
253        mActiveAlerts.remove(iface);
254        mActiveQuotas.remove(iface);
255
256        final int length = mObservers.beginBroadcast();
257        for (int i = 0; i < length; i++) {
258            try {
259                mObservers.getBroadcastItem(i).interfaceRemoved(iface);
260            } catch (RemoteException e) {
261            }
262        }
263        mObservers.finishBroadcast();
264    }
265
266    /**
267     * Notify our observers of a limit reached.
268     */
269    private void notifyLimitReached(String limitName, String iface) {
270        final int length = mObservers.beginBroadcast();
271        for (int i = 0; i < length; i++) {
272            try {
273                mObservers.getBroadcastItem(i).limitReached(limitName, iface);
274            } catch (RemoteException e) {
275            }
276        }
277        mObservers.finishBroadcast();
278    }
279
280    /**
281     * Prepare native daemon once connected, enabling modules and pushing any
282     * existing in-memory rules.
283     */
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");
302
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    }
336
337    //
338    // Netd Callback handling
339    //
340
341    private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
342        @Override
343        public void 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            }
357        }
358
359        @Override
360        public boolean onEvent(int code, String raw, String[] cooked) {
361            switch (code) {
362            case NetdResponseCode.InterfaceChange:
363                    /*
364                     * a network interface change occured
365                     * Format: "NNN Iface added <name>"
366                     *         "NNN Iface removed <name>"
367                     *         "NNN Iface changed <name> <up/down>"
368                     *         "NNN Iface linkstatus <name> <up/down>"
369                     */
370                    if (cooked.length < 4 || !cooked[1].equals("Iface")) {
371                        throw new IllegalStateException(
372                                String.format("Invalid event from daemon (%s)", raw));
373                    }
374                    if (cooked[2].equals("added")) {
375                        notifyInterfaceAdded(cooked[3]);
376                        return true;
377                    } else if (cooked[2].equals("removed")) {
378                        notifyInterfaceRemoved(cooked[3]);
379                        return true;
380                    } else if (cooked[2].equals("changed") && cooked.length == 5) {
381                        notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
382                        return true;
383                    } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
384                        notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
385                        return true;
386                    }
387                    throw new IllegalStateException(
388                            String.format("Invalid event from daemon (%s)", raw));
389                    // break;
390            case NetdResponseCode.BandwidthControl:
391                    /*
392                     * Bandwidth control needs some attention
393                     * Format: "NNN limit alert <alertName> <ifaceName>"
394                     */
395                    if (cooked.length < 5 || !cooked[1].equals("limit")) {
396                        throw new IllegalStateException(
397                                String.format("Invalid event from daemon (%s)", raw));
398                    }
399                    if (cooked[2].equals("alert")) {
400                        notifyLimitReached(cooked[3], cooked[4]);
401                        return true;
402                    }
403                    throw new IllegalStateException(
404                            String.format("Invalid event from daemon (%s)", raw));
405                    // break;
406            default: break;
407            }
408            return false;
409        }
410    }
411
412
413    //
414    // INetworkManagementService members
415    //
416
417    @Override
418    public String[] listInterfaces() {
419        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
420        try {
421            return NativeDaemonEvent.filterMessageList(
422                    mConnector.executeForList("interface", "list"), InterfaceListResult);
423        } catch (NativeDaemonConnectorException e) {
424            throw e.rethrowAsParcelableException();
425        }
426    }
427
428    @Override
429    public InterfaceConfiguration getInterfaceConfig(String iface) {
430        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
431
432        final NativeDaemonEvent event;
433        try {
434            event = mConnector.execute("interface", "getcfg", iface);
435        } catch (NativeDaemonConnectorException e) {
436            throw e.rethrowAsParcelableException();
437        }
438
439        event.checkCode(InterfaceGetCfgResult);
440
441        // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3
442        final StringTokenizer st = new StringTokenizer(event.getMessage());
443
444        InterfaceConfiguration cfg;
445        try {
446            cfg = new InterfaceConfiguration();
447            cfg.setHardwareAddress(st.nextToken(" "));
448            InetAddress addr = null;
449            int prefixLength = 0;
450            try {
451                addr = NetworkUtils.numericToInetAddress(st.nextToken());
452            } catch (IllegalArgumentException iae) {
453                Slog.e(TAG, "Failed to parse ipaddr", iae);
454            }
455
456            try {
457                prefixLength = Integer.parseInt(st.nextToken());
458            } catch (NumberFormatException nfe) {
459                Slog.e(TAG, "Failed to parse prefixLength", nfe);
460            }
461
462            cfg.setLinkAddress(new LinkAddress(addr, prefixLength));
463            while (st.hasMoreTokens()) {
464                cfg.setFlag(st.nextToken());
465            }
466        } catch (NoSuchElementException nsee) {
467            throw new IllegalStateException("Invalid response from daemon: " + event);
468        }
469        return cfg;
470    }
471
472    @Override
473    public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
474        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
475        LinkAddress linkAddr = cfg.getLinkAddress();
476        if (linkAddr == null || linkAddr.getAddress() == null) {
477            throw new IllegalStateException("Null LinkAddress given");
478        }
479
480        final Command cmd = new Command("interface", "setcfg", iface,
481                linkAddr.getAddress().getHostAddress(),
482                linkAddr.getNetworkPrefixLength());
483        for (String flag : cfg.getFlags()) {
484            cmd.appendArg(flag);
485        }
486
487        try {
488            mConnector.execute(cmd);
489        } catch (NativeDaemonConnectorException e) {
490            throw e.rethrowAsParcelableException();
491        }
492    }
493
494    @Override
495    public void setInterfaceDown(String iface) {
496        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
497        final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
498        ifcg.setInterfaceDown();
499        setInterfaceConfig(iface, ifcg);
500    }
501
502    @Override
503    public void setInterfaceUp(String iface) {
504        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
505        final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
506        ifcg.setInterfaceUp();
507        setInterfaceConfig(iface, ifcg);
508    }
509
510    @Override
511    public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
512        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
513        try {
514            mConnector.execute(
515                    "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable");
516        } catch (NativeDaemonConnectorException e) {
517            throw e.rethrowAsParcelableException();
518        }
519    }
520
521    /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
522       IPv6 addresses on interface down, but we need to do full clean up here */
523    @Override
524    public void clearInterfaceAddresses(String iface) {
525        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
526        try {
527            mConnector.execute("interface", "clearaddrs", iface);
528        } catch (NativeDaemonConnectorException e) {
529            throw e.rethrowAsParcelableException();
530        }
531    }
532
533    @Override
534    public void enableIpv6(String iface) {
535        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
536        try {
537            mConnector.execute("interface", "ipv6", iface, "enable");
538        } catch (NativeDaemonConnectorException e) {
539            throw e.rethrowAsParcelableException();
540        }
541    }
542
543    @Override
544    public void disableIpv6(String iface) {
545        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
546        try {
547            mConnector.execute("interface", "ipv6", iface, "disable");
548        } catch (NativeDaemonConnectorException e) {
549            throw e.rethrowAsParcelableException();
550        }
551    }
552
553    @Override
554    public void addRoute(String interfaceName, RouteInfo route) {
555        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
556        modifyRoute(interfaceName, ADD, route, DEFAULT);
557    }
558
559    @Override
560    public void removeRoute(String interfaceName, RouteInfo route) {
561        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
562        modifyRoute(interfaceName, REMOVE, route, DEFAULT);
563    }
564
565    @Override
566    public void addSecondaryRoute(String interfaceName, RouteInfo route) {
567        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
568        modifyRoute(interfaceName, ADD, route, SECONDARY);
569    }
570
571    @Override
572    public void removeSecondaryRoute(String interfaceName, RouteInfo route) {
573        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
574        modifyRoute(interfaceName, REMOVE, route, SECONDARY);
575    }
576
577    private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) {
578        final Command cmd = new Command("interface", "route", action, interfaceName, type);
579
580        // create triplet: dest-ip-addr prefixlength gateway-ip-addr
581        final LinkAddress la = route.getDestination();
582        cmd.appendArg(la.getAddress().getHostAddress());
583        cmd.appendArg(la.getNetworkPrefixLength());
584
585        if (route.getGateway() == null) {
586            if (la.getAddress() instanceof Inet4Address) {
587                cmd.appendArg("0.0.0.0");
588            } else {
589                cmd.appendArg("::0");
590            }
591        } else {
592            cmd.appendArg(route.getGateway().getHostAddress());
593        }
594
595        try {
596            mConnector.execute(cmd);
597        } catch (NativeDaemonConnectorException e) {
598            throw e.rethrowAsParcelableException();
599        }
600    }
601
602    private ArrayList<String> readRouteList(String filename) {
603        FileInputStream fstream = null;
604        ArrayList<String> list = new ArrayList<String>();
605
606        try {
607            fstream = new FileInputStream(filename);
608            DataInputStream in = new DataInputStream(fstream);
609            BufferedReader br = new BufferedReader(new InputStreamReader(in));
610            String s;
611
612            // throw away the title line
613
614            while (((s = br.readLine()) != null) && (s.length() != 0)) {
615                list.add(s);
616            }
617        } catch (IOException ex) {
618            // return current list, possibly empty
619        } finally {
620            if (fstream != null) {
621                try {
622                    fstream.close();
623                } catch (IOException ex) {}
624            }
625        }
626
627        return list;
628    }
629
630    @Override
631    public RouteInfo[] getRoutes(String interfaceName) {
632        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
633        ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
634
635        // v4 routes listed as:
636        // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
637        for (String s : readRouteList("/proc/net/route")) {
638            String[] fields = s.split("\t");
639
640            if (fields.length > 7) {
641                String iface = fields[0];
642
643                if (interfaceName.equals(iface)) {
644                    String dest = fields[1];
645                    String gate = fields[2];
646                    String flags = fields[3]; // future use?
647                    String mask = fields[7];
648                    try {
649                        // address stored as a hex string, ex: 0014A8C0
650                        InetAddress destAddr =
651                                NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
652                        int prefixLength =
653                                NetworkUtils.netmaskIntToPrefixLength(
654                                (int)Long.parseLong(mask, 16));
655                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
656
657                        // address stored as a hex string, ex 0014A8C0
658                        InetAddress gatewayAddr =
659                                NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
660
661                        RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
662                        routes.add(route);
663                    } catch (Exception e) {
664                        Log.e(TAG, "Error parsing route " + s + " : " + e);
665                        continue;
666                    }
667                }
668            }
669        }
670
671        // v6 routes listed as:
672        // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
673        for (String s : readRouteList("/proc/net/ipv6_route")) {
674            String[]fields = s.split("\\s+");
675            if (fields.length > 9) {
676                String iface = fields[9].trim();
677                if (interfaceName.equals(iface)) {
678                    String dest = fields[0];
679                    String prefix = fields[1];
680                    String gate = fields[4];
681
682                    try {
683                        // prefix length stored as a hex string, ex 40
684                        int prefixLength = Integer.parseInt(prefix, 16);
685
686                        // address stored as a 32 char hex string
687                        // ex fe800000000000000000000000000000
688                        InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
689                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
690
691                        InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
692
693                        RouteInfo route = new RouteInfo(linkAddress, gateAddr);
694                        routes.add(route);
695                    } catch (Exception e) {
696                        Log.e(TAG, "Error parsing route " + s + " : " + e);
697                        continue;
698                    }
699                }
700            }
701        }
702        return routes.toArray(new RouteInfo[routes.size()]);
703    }
704
705    @Override
706    public void shutdown() {
707        // TODO: remove from aidl if nobody calls externally
708        mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
709
710        Slog.d(TAG, "Shutting down");
711    }
712
713    @Override
714    public boolean getIpForwardingEnabled() throws IllegalStateException{
715        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
716
717        final NativeDaemonEvent event;
718        try {
719            event = mConnector.execute("ipfwd", "status");
720        } catch (NativeDaemonConnectorException e) {
721            throw e.rethrowAsParcelableException();
722        }
723
724        // 211 Forwarding enabled
725        event.checkCode(IpFwdStatusResult);
726        return event.getMessage().endsWith("enabled");
727    }
728
729    @Override
730    public void setIpForwardingEnabled(boolean enable) {
731        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
732        try {
733            mConnector.execute("ipfwd", enable ? "enable" : "disable");
734        } catch (NativeDaemonConnectorException e) {
735            throw e.rethrowAsParcelableException();
736        }
737    }
738
739    @Override
740    public void startTethering(String[] dhcpRange) {
741        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
742        // cmd is "tether start first_start first_stop second_start second_stop ..."
743        // an odd number of addrs will fail
744
745        final Command cmd = new Command("tether", "start");
746        for (String d : dhcpRange) {
747            cmd.appendArg(d);
748        }
749
750        try {
751            mConnector.execute(cmd);
752        } catch (NativeDaemonConnectorException e) {
753            throw e.rethrowAsParcelableException();
754        }
755    }
756
757    @Override
758    public void stopTethering() {
759        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
760        try {
761            mConnector.execute("tether", "stop");
762        } catch (NativeDaemonConnectorException e) {
763            throw e.rethrowAsParcelableException();
764        }
765    }
766
767    @Override
768    public boolean isTetheringStarted() {
769        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
770
771        final NativeDaemonEvent event;
772        try {
773            event = mConnector.execute("tether", "status");
774        } catch (NativeDaemonConnectorException e) {
775            throw e.rethrowAsParcelableException();
776        }
777
778        // 210 Tethering services started
779        event.checkCode(TetherStatusResult);
780        return event.getMessage().endsWith("started");
781    }
782
783    @Override
784    public void tetherInterface(String iface) {
785        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
786        try {
787            mConnector.execute("tether", "interface", "add", iface);
788        } catch (NativeDaemonConnectorException e) {
789            throw e.rethrowAsParcelableException();
790        }
791    }
792
793    @Override
794    public void untetherInterface(String iface) {
795        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
796        try {
797            mConnector.execute("tether", "interface", "remove", iface);
798        } catch (NativeDaemonConnectorException e) {
799            throw e.rethrowAsParcelableException();
800        }
801    }
802
803    @Override
804    public String[] listTetheredInterfaces() {
805        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
806        try {
807            return NativeDaemonEvent.filterMessageList(
808                    mConnector.executeForList("tether", "interface", "list"),
809                    TetherInterfaceListResult);
810        } catch (NativeDaemonConnectorException e) {
811            throw e.rethrowAsParcelableException();
812        }
813    }
814
815    @Override
816    public void setDnsForwarders(String[] dns) {
817        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
818
819        final Command cmd = new Command("tether", "dns", "set");
820        for (String s : dns) {
821            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
822        }
823
824        try {
825            mConnector.execute(cmd);
826        } catch (NativeDaemonConnectorException e) {
827            throw e.rethrowAsParcelableException();
828        }
829    }
830
831    @Override
832    public String[] getDnsForwarders() {
833        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
834        try {
835            return NativeDaemonEvent.filterMessageList(
836                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
837        } catch (NativeDaemonConnectorException e) {
838            throw e.rethrowAsParcelableException();
839        }
840    }
841
842    private void modifyNat(String action, String internalInterface, String externalInterface)
843            throws SocketException {
844        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
845
846        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
847                internalInterface);
848        if (internalNetworkInterface == null) {
849            cmd.appendArg("0");
850        } else {
851            Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
852                    .getInterfaceAddresses();
853            cmd.appendArg(interfaceAddresses.size());
854            for (InterfaceAddress ia : interfaceAddresses) {
855                InetAddress addr = NetworkUtils.getNetworkPart(
856                        ia.getAddress(), ia.getNetworkPrefixLength());
857                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
858            }
859        }
860
861        try {
862            mConnector.execute(cmd);
863        } catch (NativeDaemonConnectorException e) {
864            throw e.rethrowAsParcelableException();
865        }
866    }
867
868    @Override
869    public void enableNat(String internalInterface, String externalInterface) {
870        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
871        try {
872            modifyNat("enable", internalInterface, externalInterface);
873        } catch (SocketException e) {
874            throw new IllegalStateException(e);
875        }
876    }
877
878    @Override
879    public void disableNat(String internalInterface, String externalInterface) {
880        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
881        try {
882            modifyNat("disable", internalInterface, externalInterface);
883        } catch (SocketException e) {
884            throw new IllegalStateException(e);
885        }
886    }
887
888    @Override
889    public String[] listTtys() {
890        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
891        try {
892            return NativeDaemonEvent.filterMessageList(
893                    mConnector.executeForList("list_ttys"), TtyListResult);
894        } catch (NativeDaemonConnectorException e) {
895            throw e.rethrowAsParcelableException();
896        }
897    }
898
899    @Override
900    public void attachPppd(
901            String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
902        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
903        try {
904            mConnector.execute("pppd", "attach", tty,
905                    NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
906                    NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
907                    NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
908                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
909        } catch (NativeDaemonConnectorException e) {
910            throw e.rethrowAsParcelableException();
911        }
912    }
913
914    @Override
915    public void detachPppd(String tty) {
916        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
917        try {
918            mConnector.execute("pppd", "detach", tty);
919        } catch (NativeDaemonConnectorException e) {
920            throw e.rethrowAsParcelableException();
921        }
922    }
923
924    @Override
925    public void startAccessPoint(
926            WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
927        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
928        try {
929            wifiFirmwareReload(wlanIface, "AP");
930            if (wifiConfig == null) {
931                mConnector.execute("softap", "set", wlanIface, softapIface);
932            } else {
933                mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
934                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);
935            }
936            mConnector.execute("softap", "startap");
937        } catch (NativeDaemonConnectorException e) {
938            throw e.rethrowAsParcelableException();
939        }
940    }
941
942    private static String getSecurityType(WifiConfiguration wifiConfig) {
943        switch (wifiConfig.getAuthType()) {
944            case KeyMgmt.WPA_PSK:
945                return "wpa-psk";
946            case KeyMgmt.WPA2_PSK:
947                return "wpa2-psk";
948            default:
949                return "open";
950        }
951    }
952
953    /* @param mode can be "AP", "STA" or "P2P" */
954    @Override
955    public void wifiFirmwareReload(String wlanIface, String mode) {
956        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
957        try {
958            mConnector.execute("softap", "fwreload", wlanIface, mode);
959        } catch (NativeDaemonConnectorException e) {
960            throw e.rethrowAsParcelableException();
961        }
962    }
963
964    @Override
965    public void stopAccessPoint(String wlanIface) {
966        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
967        try {
968            mConnector.execute("softap", "stopap");
969            mConnector.execute("softap", "stop", wlanIface);
970            wifiFirmwareReload(wlanIface, "STA");
971        } catch (NativeDaemonConnectorException e) {
972            throw e.rethrowAsParcelableException();
973        }
974    }
975
976    @Override
977    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
978        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
979        try {
980            if (wifiConfig == null) {
981                mConnector.execute("softap", "set", wlanIface, softapIface);
982            } else {
983                mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
984                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);
985            }
986        } catch (NativeDaemonConnectorException e) {
987            throw e.rethrowAsParcelableException();
988        }
989    }
990
991    @Override
992    public NetworkStats getNetworkStatsSummaryDev() {
993        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
994        return mStatsFactory.readNetworkStatsSummaryDev();
995    }
996
997    @Override
998    public NetworkStats getNetworkStatsSummaryXt() {
999        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1000        return mStatsFactory.readNetworkStatsSummaryXt();
1001    }
1002
1003    @Override
1004    public NetworkStats getNetworkStatsDetail() {
1005        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1006        return mStatsFactory.readNetworkStatsDetail(UID_ALL);
1007    }
1008
1009    @Override
1010    public void setInterfaceQuota(String iface, long quotaBytes) {
1011        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1012
1013        // silently discard when control disabled
1014        // TODO: eventually migrate to be always enabled
1015        if (!mBandwidthControlEnabled) return;
1016
1017        synchronized (mQuotaLock) {
1018            if (mActiveQuotas.containsKey(iface)) {
1019                throw new IllegalStateException("iface " + iface + " already has quota");
1020            }
1021
1022            try {
1023                // TODO: support quota shared across interfaces
1024                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
1025                mActiveQuotas.put(iface, quotaBytes);
1026            } catch (NativeDaemonConnectorException e) {
1027                throw e.rethrowAsParcelableException();
1028            }
1029        }
1030    }
1031
1032    @Override
1033    public void removeInterfaceQuota(String iface) {
1034        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1035
1036        // silently discard when control disabled
1037        // TODO: eventually migrate to be always enabled
1038        if (!mBandwidthControlEnabled) return;
1039
1040        synchronized (mQuotaLock) {
1041            if (!mActiveQuotas.containsKey(iface)) {
1042                // TODO: eventually consider throwing
1043                return;
1044            }
1045
1046            mActiveQuotas.remove(iface);
1047            mActiveAlerts.remove(iface);
1048
1049            try {
1050                // TODO: support quota shared across interfaces
1051                mConnector.execute("bandwidth", "removeiquota", iface);
1052            } catch (NativeDaemonConnectorException e) {
1053                throw e.rethrowAsParcelableException();
1054            }
1055        }
1056    }
1057
1058    @Override
1059    public void setInterfaceAlert(String iface, long alertBytes) {
1060        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1061
1062        // silently discard when control disabled
1063        // TODO: eventually migrate to be always enabled
1064        if (!mBandwidthControlEnabled) return;
1065
1066        // quick sanity check
1067        if (!mActiveQuotas.containsKey(iface)) {
1068            throw new IllegalStateException("setting alert requires existing quota on iface");
1069        }
1070
1071        synchronized (mQuotaLock) {
1072            if (mActiveAlerts.containsKey(iface)) {
1073                throw new IllegalStateException("iface " + iface + " already has alert");
1074            }
1075
1076            try {
1077                // TODO: support alert shared across interfaces
1078                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
1079                mActiveAlerts.put(iface, alertBytes);
1080            } catch (NativeDaemonConnectorException e) {
1081                throw e.rethrowAsParcelableException();
1082            }
1083        }
1084    }
1085
1086    @Override
1087    public void removeInterfaceAlert(String iface) {
1088        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1089
1090        // silently discard when control disabled
1091        // TODO: eventually migrate to be always enabled
1092        if (!mBandwidthControlEnabled) return;
1093
1094        synchronized (mQuotaLock) {
1095            if (!mActiveAlerts.containsKey(iface)) {
1096                // TODO: eventually consider throwing
1097                return;
1098            }
1099
1100            try {
1101                // TODO: support alert shared across interfaces
1102                mConnector.execute("bandwidth", "removeinterfacealert", iface);
1103                mActiveAlerts.remove(iface);
1104            } catch (NativeDaemonConnectorException e) {
1105                throw e.rethrowAsParcelableException();
1106            }
1107        }
1108    }
1109
1110    @Override
1111    public void setGlobalAlert(long alertBytes) {
1112        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1113
1114        // silently discard when control disabled
1115        // TODO: eventually migrate to be always enabled
1116        if (!mBandwidthControlEnabled) return;
1117
1118        try {
1119            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
1120        } catch (NativeDaemonConnectorException e) {
1121            throw e.rethrowAsParcelableException();
1122        }
1123    }
1124
1125    @Override
1126    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1127        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1128
1129        // silently discard when control disabled
1130        // TODO: eventually migrate to be always enabled
1131        if (!mBandwidthControlEnabled) return;
1132
1133        synchronized (mQuotaLock) {
1134            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1135            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1136                // TODO: eventually consider throwing
1137                return;
1138            }
1139
1140            try {
1141                mConnector.execute("bandwidth",
1142                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
1143                if (rejectOnQuotaInterfaces) {
1144                    mUidRejectOnQuota.put(uid, true);
1145                } else {
1146                    mUidRejectOnQuota.delete(uid);
1147                }
1148            } catch (NativeDaemonConnectorException e) {
1149                throw e.rethrowAsParcelableException();
1150            }
1151        }
1152    }
1153
1154    @Override
1155    public boolean isBandwidthControlEnabled() {
1156        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1157        return mBandwidthControlEnabled;
1158    }
1159
1160    @Override
1161    public NetworkStats getNetworkStatsUidDetail(int uid) {
1162        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1163        return mStatsFactory.readNetworkStatsDetail(uid);
1164    }
1165
1166    @Override
1167    public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
1168        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1169
1170        if (ifacePairs.length % 2 != 0) {
1171            throw new IllegalArgumentException(
1172                    "unexpected ifacePairs; length=" + ifacePairs.length);
1173        }
1174
1175        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
1176        for (int i = 0; i < ifacePairs.length; i += 2) {
1177            final String ifaceIn = ifacePairs[i];
1178            final String ifaceOut = ifacePairs[i + 1];
1179            if (ifaceIn != null && ifaceOut != null) {
1180                stats.combineValues(getNetworkStatsTethering(ifaceIn, ifaceOut));
1181            }
1182        }
1183        return stats;
1184    }
1185
1186    private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
1187        final NativeDaemonEvent event;
1188        try {
1189            event = mConnector.execute("bandwidth", "gettetherstats", ifaceIn, ifaceOut);
1190        } catch (NativeDaemonConnectorException e) {
1191            throw e.rethrowAsParcelableException();
1192        }
1193
1194        event.checkCode(TetheringStatsResult);
1195
1196        // 221 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
1197        final StringTokenizer tok = new StringTokenizer(event.getMessage());
1198        tok.nextToken();
1199        tok.nextToken();
1200
1201        try {
1202            final NetworkStats.Entry entry = new NetworkStats.Entry();
1203            entry.iface = ifaceIn;
1204            entry.uid = UID_TETHERING;
1205            entry.set = SET_DEFAULT;
1206            entry.tag = TAG_NONE;
1207            entry.rxBytes = Long.parseLong(tok.nextToken());
1208            entry.rxPackets = Long.parseLong(tok.nextToken());
1209            entry.txBytes = Long.parseLong(tok.nextToken());
1210            entry.txPackets = Long.parseLong(tok.nextToken());
1211            return entry;
1212        } catch (NumberFormatException e) {
1213            throw new IllegalStateException(
1214                    "problem parsing tethering stats for " + ifaceIn + " " + ifaceOut + ": " + e);
1215        }
1216    }
1217
1218    @Override
1219    public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
1220        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1221        try {
1222            mConnector.execute("interface", "setthrottle", iface, rxKbps, txKbps);
1223        } catch (NativeDaemonConnectorException e) {
1224            throw e.rethrowAsParcelableException();
1225        }
1226    }
1227
1228    private int getInterfaceThrottle(String iface, boolean rx) {
1229        final NativeDaemonEvent event;
1230        try {
1231            event = mConnector.execute("interface", "getthrottle", iface, rx ? "rx" : "tx");
1232        } catch (NativeDaemonConnectorException e) {
1233            throw e.rethrowAsParcelableException();
1234        }
1235
1236        if (rx) {
1237            event.checkCode(InterfaceRxThrottleResult);
1238        } else {
1239            event.checkCode(InterfaceTxThrottleResult);
1240        }
1241
1242        try {
1243            return Integer.parseInt(event.getMessage());
1244        } catch (NumberFormatException e) {
1245            throw new IllegalStateException("unexpected response:" + event);
1246        }
1247    }
1248
1249    @Override
1250    public int getInterfaceRxThrottle(String iface) {
1251        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1252        return getInterfaceThrottle(iface, true);
1253    }
1254
1255    @Override
1256    public int getInterfaceTxThrottle(String iface) {
1257        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1258        return getInterfaceThrottle(iface, false);
1259    }
1260
1261    @Override
1262    public void setDefaultInterfaceForDns(String iface) {
1263        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1264        try {
1265            mConnector.execute("resolver", "setdefaultif", iface);
1266        } catch (NativeDaemonConnectorException e) {
1267            throw e.rethrowAsParcelableException();
1268        }
1269    }
1270
1271    @Override
1272    public void setDnsServersForInterface(String iface, String[] servers) {
1273        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1274
1275        final Command cmd = new Command("resolver", "setifdns", iface);
1276        for (String s : servers) {
1277            InetAddress a = NetworkUtils.numericToInetAddress(s);
1278            if (a.isAnyLocalAddress() == false) {
1279                cmd.appendArg(a.getHostAddress());
1280            }
1281        }
1282
1283        try {
1284            mConnector.execute(cmd);
1285        } catch (NativeDaemonConnectorException e) {
1286            throw e.rethrowAsParcelableException();
1287        }
1288    }
1289
1290    @Override
1291    public void flushDefaultDnsCache() {
1292        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1293        try {
1294            mConnector.execute("resolver", "flushdefaultif");
1295        } catch (NativeDaemonConnectorException e) {
1296            throw e.rethrowAsParcelableException();
1297        }
1298    }
1299
1300    @Override
1301    public void flushInterfaceDnsCache(String iface) {
1302        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1303        try {
1304            mConnector.execute("resolver", "flushif", iface);
1305        } catch (NativeDaemonConnectorException e) {
1306            throw e.rethrowAsParcelableException();
1307        }
1308    }
1309
1310    /** {@inheritDoc} */
1311    public void monitor() {
1312        if (mConnector != null) {
1313            mConnector.monitor();
1314        }
1315    }
1316
1317    @Override
1318    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1319        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1320
1321        pw.println("NetworkManagementService NativeDaemonConnector Log:");
1322        mConnector.dump(fd, pw, args);
1323        pw.println();
1324
1325        pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
1326
1327        synchronized (mQuotaLock) {
1328            pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
1329            pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
1330        }
1331
1332        synchronized (mUidRejectOnQuota) {
1333            pw.print("UID reject on quota ifaces: [");
1334            final int size = mUidRejectOnQuota.size();
1335            for (int i = 0; i < size; i++) {
1336                pw.print(mUidRejectOnQuota.keyAt(i));
1337                if (i < size - 1) pw.print(",");
1338            }
1339            pw.println("]");
1340        }
1341    }
1342}
1343