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