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