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