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