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