NetworkManagementService.java revision 69ddab4575ff684c533c995e07ca15fe18543fc0
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.bluetooth.BluetoothTetheringDataTracker;
39import android.content.Context;
40import android.net.INetworkManagementEventObserver;
41import android.net.InterfaceConfiguration;
42import android.net.LinkAddress;
43import android.net.NetworkStats;
44import android.net.NetworkUtils;
45import android.net.RouteInfo;
46import android.net.wifi.WifiConfiguration;
47import android.net.wifi.WifiConfiguration.KeyMgmt;
48import android.os.Handler;
49import android.os.INetworkManagementService;
50import android.os.RemoteCallbackList;
51import android.os.RemoteException;
52import android.os.SystemClock;
53import android.os.SystemProperties;
54import android.util.Log;
55import android.util.Slog;
56import android.util.SparseBooleanArray;
57
58import com.android.internal.net.NetworkStatsFactory;
59import com.android.internal.util.Preconditions;
60import com.android.server.NativeDaemonConnector.Command;
61import com.android.server.net.LockdownVpnTracker;
62import com.google.android.collect.Maps;
63
64import java.io.BufferedReader;
65import java.io.DataInputStream;
66import java.io.File;
67import java.io.FileDescriptor;
68import java.io.FileInputStream;
69import java.io.IOException;
70import java.io.InputStreamReader;
71import java.io.PrintWriter;
72import java.net.Inet4Address;
73import java.net.InetAddress;
74import java.net.InterfaceAddress;
75import java.net.NetworkInterface;
76import java.net.SocketException;
77import java.util.ArrayList;
78import java.util.Collection;
79import java.util.HashMap;
80import java.util.Map;
81import java.util.NoSuchElementException;
82import java.util.StringTokenizer;
83import java.util.concurrent.CountDownLatch;
84
85/**
86 * @hide
87 */
88public class NetworkManagementService extends INetworkManagementService.Stub
89        implements Watchdog.Monitor {
90    private static final String TAG = "NetworkManagementService";
91    private static final boolean DBG = false;
92    private static final String NETD_TAG = "NetdConnector";
93
94    private static final String ADD = "add";
95    private static final String REMOVE = "remove";
96
97    private static final String ALLOW = "allow";
98    private static final String DENY = "deny";
99
100    private static final String DEFAULT = "default";
101    private static final String SECONDARY = "secondary";
102
103    /**
104     * Name representing {@link #setGlobalAlert(long)} limit when delivered to
105     * {@link INetworkManagementEventObserver#limitReached(String, String)}.
106     */
107    public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
108
109    class NetdResponseCode {
110        /* Keep in sync with system/netd/ResponseCode.h */
111        public static final int InterfaceListResult       = 110;
112        public static final int TetherInterfaceListResult = 111;
113        public static final int TetherDnsFwdTgtListResult = 112;
114        public static final int TtyListResult             = 113;
115
116        public static final int TetherStatusResult        = 210;
117        public static final int IpFwdStatusResult         = 211;
118        public static final int InterfaceGetCfgResult     = 213;
119        public static final int SoftapStatusResult        = 214;
120        public static final int InterfaceRxCounterResult  = 216;
121        public static final int InterfaceTxCounterResult  = 217;
122        public static final int InterfaceRxThrottleResult = 218;
123        public static final int InterfaceTxThrottleResult = 219;
124        public static final int QuotaCounterResult        = 220;
125        public static final int TetheringStatsResult      = 221;
126        public static final int DnsProxyQueryResult       = 222;
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    public void startReverseTethering(String iface)
838             throws IllegalStateException {
839        if (DBG) Slog.d(TAG, "startReverseTethering in");
840        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
841        // cmd is "tether start first_start first_stop second_start second_stop ..."
842        // an odd number of addrs will fail
843        String cmd = "tether start-reverse";
844        cmd += " " + iface;
845        if (DBG) Slog.d(TAG, "startReverseTethering cmd: " + cmd);
846        try {
847            mConnector.doCommand(cmd);
848        } catch (NativeDaemonConnectorException e) {
849            throw new IllegalStateException("Unable to communicate to native daemon");
850        }
851        BluetoothTetheringDataTracker.getInstance().startReverseTether(iface);
852
853    }
854
855    // TODO(BT) Remove
856    public void stopReverseTethering() throws IllegalStateException {
857        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
858        try {
859            mConnector.doCommand("tether stop-reverse");
860        } catch (NativeDaemonConnectorException e) {
861            throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
862        }
863        BluetoothTetheringDataTracker.getInstance().stopReverseTether();
864    }
865
866    @Override
867    public void tetherInterface(String iface) {
868        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
869        try {
870            mConnector.execute("tether", "interface", "add", iface);
871        } catch (NativeDaemonConnectorException e) {
872            throw e.rethrowAsParcelableException();
873        }
874    }
875
876    @Override
877    public void untetherInterface(String iface) {
878        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
879        try {
880            mConnector.execute("tether", "interface", "remove", iface);
881        } catch (NativeDaemonConnectorException e) {
882            throw e.rethrowAsParcelableException();
883        }
884    }
885
886    @Override
887    public String[] listTetheredInterfaces() {
888        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
889        try {
890            return NativeDaemonEvent.filterMessageList(
891                    mConnector.executeForList("tether", "interface", "list"),
892                    TetherInterfaceListResult);
893        } catch (NativeDaemonConnectorException e) {
894            throw e.rethrowAsParcelableException();
895        }
896    }
897
898    @Override
899    public void setDnsForwarders(String[] dns) {
900        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
901
902        final Command cmd = new Command("tether", "dns", "set");
903        for (String s : dns) {
904            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
905        }
906
907        try {
908            mConnector.execute(cmd);
909        } catch (NativeDaemonConnectorException e) {
910            throw e.rethrowAsParcelableException();
911        }
912    }
913
914    @Override
915    public String[] getDnsForwarders() {
916        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
917        try {
918            return NativeDaemonEvent.filterMessageList(
919                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
920        } catch (NativeDaemonConnectorException e) {
921            throw e.rethrowAsParcelableException();
922        }
923    }
924
925    private void modifyNat(String action, String internalInterface, String externalInterface)
926            throws SocketException {
927        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
928
929        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
930                internalInterface);
931        if (internalNetworkInterface == null) {
932            cmd.appendArg("0");
933        } else {
934            Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
935                    .getInterfaceAddresses();
936            cmd.appendArg(interfaceAddresses.size());
937            for (InterfaceAddress ia : interfaceAddresses) {
938                InetAddress addr = NetworkUtils.getNetworkPart(
939                        ia.getAddress(), ia.getNetworkPrefixLength());
940                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
941            }
942        }
943
944        try {
945            mConnector.execute(cmd);
946        } catch (NativeDaemonConnectorException e) {
947            throw e.rethrowAsParcelableException();
948        }
949    }
950
951    @Override
952    public void enableNat(String internalInterface, String externalInterface) {
953        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
954        try {
955            modifyNat("enable", internalInterface, externalInterface);
956        } catch (SocketException e) {
957            throw new IllegalStateException(e);
958        }
959    }
960
961    @Override
962    public void disableNat(String internalInterface, String externalInterface) {
963        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
964        try {
965            modifyNat("disable", internalInterface, externalInterface);
966        } catch (SocketException e) {
967            throw new IllegalStateException(e);
968        }
969    }
970
971    @Override
972    public String[] listTtys() {
973        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
974        try {
975            return NativeDaemonEvent.filterMessageList(
976                    mConnector.executeForList("list_ttys"), TtyListResult);
977        } catch (NativeDaemonConnectorException e) {
978            throw e.rethrowAsParcelableException();
979        }
980    }
981
982    @Override
983    public void attachPppd(
984            String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
985        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
986        try {
987            mConnector.execute("pppd", "attach", tty,
988                    NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
989                    NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
990                    NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
991                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
992        } catch (NativeDaemonConnectorException e) {
993            throw e.rethrowAsParcelableException();
994        }
995    }
996
997    @Override
998    public void detachPppd(String tty) {
999        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1000        try {
1001            mConnector.execute("pppd", "detach", tty);
1002        } catch (NativeDaemonConnectorException e) {
1003            throw e.rethrowAsParcelableException();
1004        }
1005    }
1006
1007    @Override
1008    public void startAccessPoint(
1009            WifiConfiguration wifiConfig, String wlanIface) {
1010        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1011        try {
1012            wifiFirmwareReload(wlanIface, "AP");
1013            if (wifiConfig == null) {
1014                mConnector.execute("softap", "set", wlanIface);
1015            } else {
1016                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1017                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);
1018            }
1019            mConnector.execute("softap", "startap");
1020        } catch (NativeDaemonConnectorException e) {
1021            throw e.rethrowAsParcelableException();
1022        }
1023    }
1024
1025    private static String getSecurityType(WifiConfiguration wifiConfig) {
1026        switch (wifiConfig.getAuthType()) {
1027            case KeyMgmt.WPA_PSK:
1028                return "wpa-psk";
1029            case KeyMgmt.WPA2_PSK:
1030                return "wpa2-psk";
1031            default:
1032                return "open";
1033        }
1034    }
1035
1036    /* @param mode can be "AP", "STA" or "P2P" */
1037    @Override
1038    public void wifiFirmwareReload(String wlanIface, String mode) {
1039        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1040        try {
1041            mConnector.execute("softap", "fwreload", wlanIface, mode);
1042        } catch (NativeDaemonConnectorException e) {
1043            throw e.rethrowAsParcelableException();
1044        }
1045    }
1046
1047    @Override
1048    public void stopAccessPoint(String wlanIface) {
1049        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1050        try {
1051            mConnector.execute("softap", "stopap");
1052            wifiFirmwareReload(wlanIface, "STA");
1053        } catch (NativeDaemonConnectorException e) {
1054            throw e.rethrowAsParcelableException();
1055        }
1056    }
1057
1058    @Override
1059    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
1060        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1061        try {
1062            if (wifiConfig == null) {
1063                mConnector.execute("softap", "set", wlanIface);
1064            } else {
1065                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1066                        getSecurityType(wifiConfig), wifiConfig.preSharedKey);
1067            }
1068        } catch (NativeDaemonConnectorException e) {
1069            throw e.rethrowAsParcelableException();
1070        }
1071    }
1072
1073    @Override
1074    public void addIdleTimer(String iface, int timeout, String label) {
1075        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1076
1077        if (DBG) Slog.d(TAG, "Adding idletimer");
1078
1079        synchronized (mIdleTimerLock) {
1080            IdleTimerParams params = mActiveIdleTimers.get(iface);
1081            if (params != null) {
1082                // the interface already has idletimer, update network count
1083                params.networkCount++;
1084                return;
1085            }
1086
1087            try {
1088                mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label);
1089            } catch (NativeDaemonConnectorException e) {
1090                throw e.rethrowAsParcelableException();
1091            }
1092            mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label));
1093        }
1094    }
1095
1096    @Override
1097    public void removeIdleTimer(String iface) {
1098        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1099
1100        if (DBG) Slog.d(TAG, "Removing idletimer");
1101
1102        synchronized (mIdleTimerLock) {
1103            IdleTimerParams params = mActiveIdleTimers.get(iface);
1104            if (params == null || --(params.networkCount) > 0) {
1105                return;
1106            }
1107
1108            try {
1109                mConnector.execute("idletimer", "remove", iface,
1110                        Integer.toString(params.timeout), params.label);
1111            } catch (NativeDaemonConnectorException e) {
1112                throw e.rethrowAsParcelableException();
1113            }
1114            mActiveIdleTimers.remove(iface);
1115        }
1116    }
1117
1118    @Override
1119    public NetworkStats getNetworkStatsSummaryDev() {
1120        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1121        return mStatsFactory.readNetworkStatsSummaryDev();
1122    }
1123
1124    @Override
1125    public NetworkStats getNetworkStatsSummaryXt() {
1126        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1127        return mStatsFactory.readNetworkStatsSummaryXt();
1128    }
1129
1130    @Override
1131    public NetworkStats getNetworkStatsDetail() {
1132        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1133        return mStatsFactory.readNetworkStatsDetail(UID_ALL);
1134    }
1135
1136    @Override
1137    public void setInterfaceQuota(String iface, long quotaBytes) {
1138        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1139
1140        // silently discard when control disabled
1141        // TODO: eventually migrate to be always enabled
1142        if (!mBandwidthControlEnabled) return;
1143
1144        synchronized (mQuotaLock) {
1145            if (mActiveQuotas.containsKey(iface)) {
1146                throw new IllegalStateException("iface " + iface + " already has quota");
1147            }
1148
1149            try {
1150                // TODO: support quota shared across interfaces
1151                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
1152                mActiveQuotas.put(iface, quotaBytes);
1153            } catch (NativeDaemonConnectorException e) {
1154                throw e.rethrowAsParcelableException();
1155            }
1156        }
1157    }
1158
1159    @Override
1160    public void removeInterfaceQuota(String iface) {
1161        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1162
1163        // silently discard when control disabled
1164        // TODO: eventually migrate to be always enabled
1165        if (!mBandwidthControlEnabled) return;
1166
1167        synchronized (mQuotaLock) {
1168            if (!mActiveQuotas.containsKey(iface)) {
1169                // TODO: eventually consider throwing
1170                return;
1171            }
1172
1173            mActiveQuotas.remove(iface);
1174            mActiveAlerts.remove(iface);
1175
1176            try {
1177                // TODO: support quota shared across interfaces
1178                mConnector.execute("bandwidth", "removeiquota", iface);
1179            } catch (NativeDaemonConnectorException e) {
1180                throw e.rethrowAsParcelableException();
1181            }
1182        }
1183    }
1184
1185    @Override
1186    public void setInterfaceAlert(String iface, long alertBytes) {
1187        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1188
1189        // silently discard when control disabled
1190        // TODO: eventually migrate to be always enabled
1191        if (!mBandwidthControlEnabled) return;
1192
1193        // quick sanity check
1194        if (!mActiveQuotas.containsKey(iface)) {
1195            throw new IllegalStateException("setting alert requires existing quota on iface");
1196        }
1197
1198        synchronized (mQuotaLock) {
1199            if (mActiveAlerts.containsKey(iface)) {
1200                throw new IllegalStateException("iface " + iface + " already has alert");
1201            }
1202
1203            try {
1204                // TODO: support alert shared across interfaces
1205                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
1206                mActiveAlerts.put(iface, alertBytes);
1207            } catch (NativeDaemonConnectorException e) {
1208                throw e.rethrowAsParcelableException();
1209            }
1210        }
1211    }
1212
1213    @Override
1214    public void removeInterfaceAlert(String iface) {
1215        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1216
1217        // silently discard when control disabled
1218        // TODO: eventually migrate to be always enabled
1219        if (!mBandwidthControlEnabled) return;
1220
1221        synchronized (mQuotaLock) {
1222            if (!mActiveAlerts.containsKey(iface)) {
1223                // TODO: eventually consider throwing
1224                return;
1225            }
1226
1227            try {
1228                // TODO: support alert shared across interfaces
1229                mConnector.execute("bandwidth", "removeinterfacealert", iface);
1230                mActiveAlerts.remove(iface);
1231            } catch (NativeDaemonConnectorException e) {
1232                throw e.rethrowAsParcelableException();
1233            }
1234        }
1235    }
1236
1237    @Override
1238    public void setGlobalAlert(long alertBytes) {
1239        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1240
1241        // silently discard when control disabled
1242        // TODO: eventually migrate to be always enabled
1243        if (!mBandwidthControlEnabled) return;
1244
1245        try {
1246            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
1247        } catch (NativeDaemonConnectorException e) {
1248            throw e.rethrowAsParcelableException();
1249        }
1250    }
1251
1252    @Override
1253    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1254        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1255
1256        // silently discard when control disabled
1257        // TODO: eventually migrate to be always enabled
1258        if (!mBandwidthControlEnabled) return;
1259
1260        synchronized (mQuotaLock) {
1261            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1262            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1263                // TODO: eventually consider throwing
1264                return;
1265            }
1266
1267            try {
1268                mConnector.execute("bandwidth",
1269                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
1270                if (rejectOnQuotaInterfaces) {
1271                    mUidRejectOnQuota.put(uid, true);
1272                } else {
1273                    mUidRejectOnQuota.delete(uid);
1274                }
1275            } catch (NativeDaemonConnectorException e) {
1276                throw e.rethrowAsParcelableException();
1277            }
1278        }
1279    }
1280
1281    @Override
1282    public boolean isBandwidthControlEnabled() {
1283        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1284        return mBandwidthControlEnabled;
1285    }
1286
1287    @Override
1288    public NetworkStats getNetworkStatsUidDetail(int uid) {
1289        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1290        return mStatsFactory.readNetworkStatsDetail(uid);
1291    }
1292
1293    @Override
1294    public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
1295        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1296
1297        if (ifacePairs.length % 2 != 0) {
1298            throw new IllegalArgumentException(
1299                    "unexpected ifacePairs; length=" + ifacePairs.length);
1300        }
1301
1302        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
1303        for (int i = 0; i < ifacePairs.length; i += 2) {
1304            final String ifaceIn = ifacePairs[i];
1305            final String ifaceOut = ifacePairs[i + 1];
1306            if (ifaceIn != null && ifaceOut != null) {
1307                stats.combineValues(getNetworkStatsTethering(ifaceIn, ifaceOut));
1308            }
1309        }
1310        return stats;
1311    }
1312
1313    private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
1314        final NativeDaemonEvent event;
1315        try {
1316            event = mConnector.execute("bandwidth", "gettetherstats", ifaceIn, ifaceOut);
1317        } catch (NativeDaemonConnectorException e) {
1318            throw e.rethrowAsParcelableException();
1319        }
1320
1321        event.checkCode(TetheringStatsResult);
1322
1323        // 221 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
1324        final StringTokenizer tok = new StringTokenizer(event.getMessage());
1325        tok.nextToken();
1326        tok.nextToken();
1327
1328        try {
1329            final NetworkStats.Entry entry = new NetworkStats.Entry();
1330            entry.iface = ifaceIn;
1331            entry.uid = UID_TETHERING;
1332            entry.set = SET_DEFAULT;
1333            entry.tag = TAG_NONE;
1334            entry.rxBytes = Long.parseLong(tok.nextToken());
1335            entry.rxPackets = Long.parseLong(tok.nextToken());
1336            entry.txBytes = Long.parseLong(tok.nextToken());
1337            entry.txPackets = Long.parseLong(tok.nextToken());
1338            return entry;
1339        } catch (NumberFormatException e) {
1340            throw new IllegalStateException(
1341                    "problem parsing tethering stats for " + ifaceIn + " " + ifaceOut + ": " + e);
1342        }
1343    }
1344
1345    @Override
1346    public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
1347        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1348        try {
1349            mConnector.execute("interface", "setthrottle", iface, rxKbps, txKbps);
1350        } catch (NativeDaemonConnectorException e) {
1351            throw e.rethrowAsParcelableException();
1352        }
1353    }
1354
1355    private int getInterfaceThrottle(String iface, boolean rx) {
1356        final NativeDaemonEvent event;
1357        try {
1358            event = mConnector.execute("interface", "getthrottle", iface, rx ? "rx" : "tx");
1359        } catch (NativeDaemonConnectorException e) {
1360            throw e.rethrowAsParcelableException();
1361        }
1362
1363        if (rx) {
1364            event.checkCode(InterfaceRxThrottleResult);
1365        } else {
1366            event.checkCode(InterfaceTxThrottleResult);
1367        }
1368
1369        try {
1370            return Integer.parseInt(event.getMessage());
1371        } catch (NumberFormatException e) {
1372            throw new IllegalStateException("unexpected response:" + event);
1373        }
1374    }
1375
1376    @Override
1377    public int getInterfaceRxThrottle(String iface) {
1378        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1379        return getInterfaceThrottle(iface, true);
1380    }
1381
1382    @Override
1383    public int getInterfaceTxThrottle(String iface) {
1384        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1385        return getInterfaceThrottle(iface, false);
1386    }
1387
1388    @Override
1389    public void setDefaultInterfaceForDns(String iface) {
1390        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1391        try {
1392            mConnector.execute("resolver", "setdefaultif", iface);
1393        } catch (NativeDaemonConnectorException e) {
1394            throw e.rethrowAsParcelableException();
1395        }
1396    }
1397
1398    @Override
1399    public void setDnsServersForInterface(String iface, String[] servers) {
1400        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1401
1402        final Command cmd = new Command("resolver", "setifdns", iface);
1403        for (String s : servers) {
1404            InetAddress a = NetworkUtils.numericToInetAddress(s);
1405            if (a.isAnyLocalAddress() == false) {
1406                cmd.appendArg(a.getHostAddress());
1407            }
1408        }
1409
1410        try {
1411            mConnector.execute(cmd);
1412        } catch (NativeDaemonConnectorException e) {
1413            throw e.rethrowAsParcelableException();
1414        }
1415    }
1416
1417    @Override
1418    public void flushDefaultDnsCache() {
1419        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1420        try {
1421            mConnector.execute("resolver", "flushdefaultif");
1422        } catch (NativeDaemonConnectorException e) {
1423            throw e.rethrowAsParcelableException();
1424        }
1425    }
1426
1427    @Override
1428    public void flushInterfaceDnsCache(String iface) {
1429        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1430        try {
1431            mConnector.execute("resolver", "flushif", iface);
1432        } catch (NativeDaemonConnectorException e) {
1433            throw e.rethrowAsParcelableException();
1434        }
1435    }
1436
1437    @Override
1438    public void setFirewallEnabled(boolean enabled) {
1439        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1440        try {
1441            mConnector.execute("firewall", enabled ? "enable" : "disable");
1442            mFirewallEnabled = enabled;
1443        } catch (NativeDaemonConnectorException e) {
1444            throw e.rethrowAsParcelableException();
1445        }
1446    }
1447
1448    @Override
1449    public boolean isFirewallEnabled() {
1450        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1451        return mFirewallEnabled;
1452    }
1453
1454    @Override
1455    public void setFirewallInterfaceRule(String iface, boolean allow) {
1456        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1457        Preconditions.checkState(mFirewallEnabled);
1458        final String rule = allow ? ALLOW : DENY;
1459        try {
1460            mConnector.execute("firewall", "set_interface_rule", iface, rule);
1461        } catch (NativeDaemonConnectorException e) {
1462            throw e.rethrowAsParcelableException();
1463        }
1464    }
1465
1466    @Override
1467    public void setFirewallEgressSourceRule(String addr, boolean allow) {
1468        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1469        Preconditions.checkState(mFirewallEnabled);
1470        final String rule = allow ? ALLOW : DENY;
1471        try {
1472            mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
1473        } catch (NativeDaemonConnectorException e) {
1474            throw e.rethrowAsParcelableException();
1475        }
1476    }
1477
1478    @Override
1479    public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
1480        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1481        Preconditions.checkState(mFirewallEnabled);
1482        final String rule = allow ? ALLOW : DENY;
1483        try {
1484            mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
1485        } catch (NativeDaemonConnectorException e) {
1486            throw e.rethrowAsParcelableException();
1487        }
1488    }
1489
1490    @Override
1491    public void setFirewallUidRule(int uid, boolean allow) {
1492        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1493        Preconditions.checkState(mFirewallEnabled);
1494        final String rule = allow ? ALLOW : DENY;
1495        try {
1496            mConnector.execute("firewall", "set_uid_rule", uid, rule);
1497        } catch (NativeDaemonConnectorException e) {
1498            throw e.rethrowAsParcelableException();
1499        }
1500    }
1501
1502    @Override
1503    public void monitor() {
1504        if (mConnector != null) {
1505            mConnector.monitor();
1506        }
1507    }
1508
1509    @Override
1510    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1511        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1512
1513        pw.println("NetworkManagementService NativeDaemonConnector Log:");
1514        mConnector.dump(fd, pw, args);
1515        pw.println();
1516
1517        pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
1518
1519        synchronized (mQuotaLock) {
1520            pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
1521            pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
1522        }
1523
1524        synchronized (mUidRejectOnQuota) {
1525            pw.print("UID reject on quota ifaces: [");
1526            final int size = mUidRejectOnQuota.size();
1527            for (int i = 0; i < size; i++) {
1528                pw.print(mUidRejectOnQuota.keyAt(i));
1529                if (i < size - 1) pw.print(",");
1530            }
1531            pw.println("]");
1532        }
1533
1534        pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
1535    }
1536}
1537