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