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