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