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