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