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