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