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