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