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