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