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.NetworkStats.SET_DEFAULT;
23import static android.net.NetworkStats.TAG_ALL;
24import static android.net.NetworkStats.TAG_NONE;
25import static android.net.NetworkStats.UID_ALL;
26import static android.net.TrafficStats.UID_TETHERING;
27import static android.net.RouteInfo.RTN_THROW;
28import static android.net.RouteInfo.RTN_UNICAST;
29import static android.net.RouteInfo.RTN_UNREACHABLE;
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        cmd.appendArg(route.getInterface());
959        cmd.appendArg(route.getDestination().toString());
960
961        switch (route.getType()) {
962            case RouteInfo.RTN_UNICAST:
963                if (route.hasGateway()) {
964                    cmd.appendArg(route.getGateway().getHostAddress());
965                }
966                break;
967            case RouteInfo.RTN_UNREACHABLE:
968                cmd.appendArg("unreachable");
969                break;
970            case RouteInfo.RTN_THROW:
971                cmd.appendArg("throw");
972                break;
973        }
974
975        try {
976            mConnector.execute(cmd);
977        } catch (NativeDaemonConnectorException e) {
978            throw e.rethrowAsParcelableException();
979        }
980    }
981
982    private ArrayList<String> readRouteList(String filename) {
983        FileInputStream fstream = null;
984        ArrayList<String> list = new ArrayList<String>();
985
986        try {
987            fstream = new FileInputStream(filename);
988            DataInputStream in = new DataInputStream(fstream);
989            BufferedReader br = new BufferedReader(new InputStreamReader(in));
990            String s;
991
992            // throw away the title line
993
994            while (((s = br.readLine()) != null) && (s.length() != 0)) {
995                list.add(s);
996            }
997        } catch (IOException ex) {
998            // return current list, possibly empty
999        } finally {
1000            if (fstream != null) {
1001                try {
1002                    fstream.close();
1003                } catch (IOException ex) {}
1004            }
1005        }
1006
1007        return list;
1008    }
1009
1010    @Override
1011    public RouteInfo[] getRoutes(String interfaceName) {
1012        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1013        ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
1014
1015        // v4 routes listed as:
1016        // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
1017        for (String s : readRouteList("/proc/net/route")) {
1018            String[] fields = s.split("\t");
1019
1020            if (fields.length > 7) {
1021                String iface = fields[0];
1022
1023                if (interfaceName.equals(iface)) {
1024                    String dest = fields[1];
1025                    String gate = fields[2];
1026                    String flags = fields[3]; // future use?
1027                    String mask = fields[7];
1028                    try {
1029                        // address stored as a hex string, ex: 0014A8C0
1030                        InetAddress destAddr =
1031                                NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
1032                        int prefixLength =
1033                                NetworkUtils.netmaskIntToPrefixLength(
1034                                (int)Long.parseLong(mask, 16));
1035                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
1036
1037                        // address stored as a hex string, ex 0014A8C0
1038                        InetAddress gatewayAddr =
1039                                NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
1040
1041                        RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
1042                        routes.add(route);
1043                    } catch (Exception e) {
1044                        Log.e(TAG, "Error parsing route " + s + " : " + e);
1045                        continue;
1046                    }
1047                }
1048            }
1049        }
1050
1051        // v6 routes listed as:
1052        // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
1053        for (String s : readRouteList("/proc/net/ipv6_route")) {
1054            String[]fields = s.split("\\s+");
1055            if (fields.length > 9) {
1056                String iface = fields[9].trim();
1057                if (interfaceName.equals(iface)) {
1058                    String dest = fields[0];
1059                    String prefix = fields[1];
1060                    String gate = fields[4];
1061
1062                    try {
1063                        // prefix length stored as a hex string, ex 40
1064                        int prefixLength = Integer.parseInt(prefix, 16);
1065
1066                        // address stored as a 32 char hex string
1067                        // ex fe800000000000000000000000000000
1068                        InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
1069                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
1070
1071                        InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
1072
1073                        RouteInfo route = new RouteInfo(linkAddress, gateAddr);
1074                        routes.add(route);
1075                    } catch (Exception e) {
1076                        Log.e(TAG, "Error parsing route " + s + " : " + e);
1077                        continue;
1078                    }
1079                }
1080            }
1081        }
1082        return routes.toArray(new RouteInfo[routes.size()]);
1083    }
1084
1085    @Override
1086    public void setMtu(String iface, int mtu) {
1087        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1088
1089        final NativeDaemonEvent event;
1090        try {
1091            event = mConnector.execute("interface", "setmtu", iface, mtu);
1092        } catch (NativeDaemonConnectorException e) {
1093            throw e.rethrowAsParcelableException();
1094        }
1095    }
1096
1097    @Override
1098    public void shutdown() {
1099        // TODO: remove from aidl if nobody calls externally
1100        mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
1101
1102        Slog.d(TAG, "Shutting down");
1103    }
1104
1105    @Override
1106    public boolean getIpForwardingEnabled() throws IllegalStateException{
1107        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1108
1109        final NativeDaemonEvent event;
1110        try {
1111            event = mConnector.execute("ipfwd", "status");
1112        } catch (NativeDaemonConnectorException e) {
1113            throw e.rethrowAsParcelableException();
1114        }
1115
1116        // 211 Forwarding enabled
1117        event.checkCode(IpFwdStatusResult);
1118        return event.getMessage().endsWith("enabled");
1119    }
1120
1121    @Override
1122    public void setIpForwardingEnabled(boolean enable) {
1123        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1124        try {
1125            mConnector.execute("ipfwd", enable ? "enable" : "disable");
1126        } catch (NativeDaemonConnectorException e) {
1127            throw e.rethrowAsParcelableException();
1128        }
1129    }
1130
1131    @Override
1132    public void startTethering(String[] dhcpRange) {
1133        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1134        // cmd is "tether start first_start first_stop second_start second_stop ..."
1135        // an odd number of addrs will fail
1136
1137        final Command cmd = new Command("tether", "start");
1138        for (String d : dhcpRange) {
1139            cmd.appendArg(d);
1140        }
1141
1142        try {
1143            mConnector.execute(cmd);
1144        } catch (NativeDaemonConnectorException e) {
1145            throw e.rethrowAsParcelableException();
1146        }
1147    }
1148
1149    @Override
1150    public void stopTethering() {
1151        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1152        try {
1153            mConnector.execute("tether", "stop");
1154        } catch (NativeDaemonConnectorException e) {
1155            throw e.rethrowAsParcelableException();
1156        }
1157    }
1158
1159    @Override
1160    public boolean isTetheringStarted() {
1161        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1162
1163        final NativeDaemonEvent event;
1164        try {
1165            event = mConnector.execute("tether", "status");
1166        } catch (NativeDaemonConnectorException e) {
1167            throw e.rethrowAsParcelableException();
1168        }
1169
1170        // 210 Tethering services started
1171        event.checkCode(TetherStatusResult);
1172        return event.getMessage().endsWith("started");
1173    }
1174
1175    @Override
1176    public void tetherInterface(String iface) {
1177        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1178        try {
1179            mConnector.execute("tether", "interface", "add", iface);
1180        } catch (NativeDaemonConnectorException e) {
1181            throw e.rethrowAsParcelableException();
1182        }
1183        List<RouteInfo> routes = new ArrayList<RouteInfo>();
1184        // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
1185        // suitable to use as a route destination.
1186        routes.add(new RouteInfo(getInterfaceConfig(iface).getLinkAddress(), null, iface));
1187        addInterfaceToLocalNetwork(iface, routes);
1188    }
1189
1190    @Override
1191    public void untetherInterface(String iface) {
1192        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1193        try {
1194            mConnector.execute("tether", "interface", "remove", iface);
1195        } catch (NativeDaemonConnectorException e) {
1196            throw e.rethrowAsParcelableException();
1197        }
1198        removeInterfaceFromLocalNetwork(iface);
1199    }
1200
1201    @Override
1202    public String[] listTetheredInterfaces() {
1203        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1204        try {
1205            return NativeDaemonEvent.filterMessageList(
1206                    mConnector.executeForList("tether", "interface", "list"),
1207                    TetherInterfaceListResult);
1208        } catch (NativeDaemonConnectorException e) {
1209            throw e.rethrowAsParcelableException();
1210        }
1211    }
1212
1213    @Override
1214    public void setDnsForwarders(Network network, String[] dns) {
1215        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1216
1217        int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET;
1218        final Command cmd = new Command("tether", "dns", "set", netId);
1219
1220        for (String s : dns) {
1221            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
1222        }
1223
1224        try {
1225            mConnector.execute(cmd);
1226        } catch (NativeDaemonConnectorException e) {
1227            throw e.rethrowAsParcelableException();
1228        }
1229    }
1230
1231    @Override
1232    public String[] getDnsForwarders() {
1233        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1234        try {
1235            return NativeDaemonEvent.filterMessageList(
1236                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
1237        } catch (NativeDaemonConnectorException e) {
1238            throw e.rethrowAsParcelableException();
1239        }
1240    }
1241
1242    private List<InterfaceAddress> excludeLinkLocal(List<InterfaceAddress> addresses) {
1243        ArrayList<InterfaceAddress> filtered = new ArrayList<InterfaceAddress>(addresses.size());
1244        for (InterfaceAddress ia : addresses) {
1245            if (!ia.getAddress().isLinkLocalAddress())
1246                filtered.add(ia);
1247        }
1248        return filtered;
1249    }
1250
1251    private void modifyNat(String action, String internalInterface, String externalInterface)
1252            throws SocketException {
1253        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
1254
1255        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
1256                internalInterface);
1257        if (internalNetworkInterface == null) {
1258            cmd.appendArg("0");
1259        } else {
1260            // Don't touch link-local routes, as link-local addresses aren't routable,
1261            // kernel creates link-local routes on all interfaces automatically
1262            List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
1263                    internalNetworkInterface.getInterfaceAddresses());
1264            cmd.appendArg(interfaceAddresses.size());
1265            for (InterfaceAddress ia : interfaceAddresses) {
1266                InetAddress addr = NetworkUtils.getNetworkPart(
1267                        ia.getAddress(), ia.getNetworkPrefixLength());
1268                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
1269            }
1270        }
1271
1272        try {
1273            mConnector.execute(cmd);
1274        } catch (NativeDaemonConnectorException e) {
1275            throw e.rethrowAsParcelableException();
1276        }
1277    }
1278
1279    @Override
1280    public void enableNat(String internalInterface, String externalInterface) {
1281        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1282        try {
1283            modifyNat("enable", internalInterface, externalInterface);
1284        } catch (SocketException e) {
1285            throw new IllegalStateException(e);
1286        }
1287    }
1288
1289    @Override
1290    public void disableNat(String internalInterface, String externalInterface) {
1291        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1292        try {
1293            modifyNat("disable", internalInterface, externalInterface);
1294        } catch (SocketException e) {
1295            throw new IllegalStateException(e);
1296        }
1297    }
1298
1299    @Override
1300    public String[] listTtys() {
1301        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1302        try {
1303            return NativeDaemonEvent.filterMessageList(
1304                    mConnector.executeForList("list_ttys"), TtyListResult);
1305        } catch (NativeDaemonConnectorException e) {
1306            throw e.rethrowAsParcelableException();
1307        }
1308    }
1309
1310    @Override
1311    public void attachPppd(
1312            String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
1313        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1314        try {
1315            mConnector.execute("pppd", "attach", tty,
1316                    NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
1317                    NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
1318                    NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
1319                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
1320        } catch (NativeDaemonConnectorException e) {
1321            throw e.rethrowAsParcelableException();
1322        }
1323    }
1324
1325    @Override
1326    public void detachPppd(String tty) {
1327        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1328        try {
1329            mConnector.execute("pppd", "detach", tty);
1330        } catch (NativeDaemonConnectorException e) {
1331            throw e.rethrowAsParcelableException();
1332        }
1333    }
1334
1335    @Override
1336    public void startAccessPoint(
1337            WifiConfiguration wifiConfig, String wlanIface) {
1338        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1339        try {
1340            wifiFirmwareReload(wlanIface, "AP");
1341            if (wifiConfig == null) {
1342                mConnector.execute("softap", "set", wlanIface);
1343            } else {
1344                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1345                                   "broadcast", "6", getSecurityType(wifiConfig),
1346                                   new SensitiveArg(wifiConfig.preSharedKey));
1347            }
1348            mConnector.execute("softap", "startap");
1349        } catch (NativeDaemonConnectorException e) {
1350            throw e.rethrowAsParcelableException();
1351        }
1352    }
1353
1354    private static String getSecurityType(WifiConfiguration wifiConfig) {
1355        switch (wifiConfig.getAuthType()) {
1356            case KeyMgmt.WPA_PSK:
1357                return "wpa-psk";
1358            case KeyMgmt.WPA2_PSK:
1359                return "wpa2-psk";
1360            default:
1361                return "open";
1362        }
1363    }
1364
1365    /* @param mode can be "AP", "STA" or "P2P" */
1366    @Override
1367    public void wifiFirmwareReload(String wlanIface, String mode) {
1368        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1369        try {
1370            mConnector.execute("softap", "fwreload", wlanIface, mode);
1371        } catch (NativeDaemonConnectorException e) {
1372            throw e.rethrowAsParcelableException();
1373        }
1374    }
1375
1376    @Override
1377    public void stopAccessPoint(String wlanIface) {
1378        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1379        try {
1380            mConnector.execute("softap", "stopap");
1381            wifiFirmwareReload(wlanIface, "STA");
1382        } catch (NativeDaemonConnectorException e) {
1383            throw e.rethrowAsParcelableException();
1384        }
1385    }
1386
1387    @Override
1388    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
1389        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1390        try {
1391            if (wifiConfig == null) {
1392                mConnector.execute("softap", "set", wlanIface);
1393            } else {
1394                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1395                                   "broadcast", "6", getSecurityType(wifiConfig),
1396                                   new SensitiveArg(wifiConfig.preSharedKey));
1397            }
1398        } catch (NativeDaemonConnectorException e) {
1399            throw e.rethrowAsParcelableException();
1400        }
1401    }
1402
1403    @Override
1404    public void addIdleTimer(String iface, int timeout, final int type) {
1405        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1406
1407        if (DBG) Slog.d(TAG, "Adding idletimer");
1408
1409        synchronized (mIdleTimerLock) {
1410            IdleTimerParams params = mActiveIdleTimers.get(iface);
1411            if (params != null) {
1412                // the interface already has idletimer, update network count
1413                params.networkCount++;
1414                return;
1415            }
1416
1417            try {
1418                mConnector.execute("idletimer", "add", iface, Integer.toString(timeout),
1419                        Integer.toString(type));
1420            } catch (NativeDaemonConnectorException e) {
1421                throw e.rethrowAsParcelableException();
1422            }
1423            mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
1424
1425            // Networks start up.
1426            if (ConnectivityManager.isNetworkTypeMobile(type)) {
1427                mNetworkActive = false;
1428            }
1429            mDaemonHandler.post(new Runnable() {
1430                @Override public void run() {
1431                    notifyInterfaceClassActivity(type,
1432                            DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
1433                            SystemClock.elapsedRealtimeNanos(), false);
1434                }
1435            });
1436        }
1437    }
1438
1439    @Override
1440    public void removeIdleTimer(String iface) {
1441        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1442
1443        if (DBG) Slog.d(TAG, "Removing idletimer");
1444
1445        synchronized (mIdleTimerLock) {
1446            final IdleTimerParams params = mActiveIdleTimers.get(iface);
1447            if (params == null || --(params.networkCount) > 0) {
1448                return;
1449            }
1450
1451            try {
1452                mConnector.execute("idletimer", "remove", iface,
1453                        Integer.toString(params.timeout), Integer.toString(params.type));
1454            } catch (NativeDaemonConnectorException e) {
1455                throw e.rethrowAsParcelableException();
1456            }
1457            mActiveIdleTimers.remove(iface);
1458            mDaemonHandler.post(new Runnable() {
1459                @Override public void run() {
1460                    notifyInterfaceClassActivity(params.type,
1461                            DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
1462                            SystemClock.elapsedRealtimeNanos(), false);
1463                }
1464            });
1465        }
1466    }
1467
1468    @Override
1469    public NetworkStats getNetworkStatsSummaryDev() {
1470        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1471        try {
1472            return mStatsFactory.readNetworkStatsSummaryDev();
1473        } catch (IOException e) {
1474            throw new IllegalStateException(e);
1475        }
1476    }
1477
1478    @Override
1479    public NetworkStats getNetworkStatsSummaryXt() {
1480        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1481        try {
1482            return mStatsFactory.readNetworkStatsSummaryXt();
1483        } catch (IOException e) {
1484            throw new IllegalStateException(e);
1485        }
1486    }
1487
1488    @Override
1489    public NetworkStats getNetworkStatsDetail() {
1490        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1491        try {
1492            return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
1493        } catch (IOException e) {
1494            throw new IllegalStateException(e);
1495        }
1496    }
1497
1498    @Override
1499    public void setInterfaceQuota(String iface, long quotaBytes) {
1500        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1501
1502        // silently discard when control disabled
1503        // TODO: eventually migrate to be always enabled
1504        if (!mBandwidthControlEnabled) return;
1505
1506        synchronized (mQuotaLock) {
1507            if (mActiveQuotas.containsKey(iface)) {
1508                throw new IllegalStateException("iface " + iface + " already has quota");
1509            }
1510
1511            try {
1512                // TODO: support quota shared across interfaces
1513                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
1514                mActiveQuotas.put(iface, quotaBytes);
1515            } catch (NativeDaemonConnectorException e) {
1516                throw e.rethrowAsParcelableException();
1517            }
1518        }
1519    }
1520
1521    @Override
1522    public void removeInterfaceQuota(String iface) {
1523        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1524
1525        // silently discard when control disabled
1526        // TODO: eventually migrate to be always enabled
1527        if (!mBandwidthControlEnabled) return;
1528
1529        synchronized (mQuotaLock) {
1530            if (!mActiveQuotas.containsKey(iface)) {
1531                // TODO: eventually consider throwing
1532                return;
1533            }
1534
1535            mActiveQuotas.remove(iface);
1536            mActiveAlerts.remove(iface);
1537
1538            try {
1539                // TODO: support quota shared across interfaces
1540                mConnector.execute("bandwidth", "removeiquota", iface);
1541            } catch (NativeDaemonConnectorException e) {
1542                throw e.rethrowAsParcelableException();
1543            }
1544        }
1545    }
1546
1547    @Override
1548    public void setInterfaceAlert(String iface, long alertBytes) {
1549        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1550
1551        // silently discard when control disabled
1552        // TODO: eventually migrate to be always enabled
1553        if (!mBandwidthControlEnabled) return;
1554
1555        // quick sanity check
1556        if (!mActiveQuotas.containsKey(iface)) {
1557            throw new IllegalStateException("setting alert requires existing quota on iface");
1558        }
1559
1560        synchronized (mQuotaLock) {
1561            if (mActiveAlerts.containsKey(iface)) {
1562                throw new IllegalStateException("iface " + iface + " already has alert");
1563            }
1564
1565            try {
1566                // TODO: support alert shared across interfaces
1567                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
1568                mActiveAlerts.put(iface, alertBytes);
1569            } catch (NativeDaemonConnectorException e) {
1570                throw e.rethrowAsParcelableException();
1571            }
1572        }
1573    }
1574
1575    @Override
1576    public void removeInterfaceAlert(String iface) {
1577        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1578
1579        // silently discard when control disabled
1580        // TODO: eventually migrate to be always enabled
1581        if (!mBandwidthControlEnabled) return;
1582
1583        synchronized (mQuotaLock) {
1584            if (!mActiveAlerts.containsKey(iface)) {
1585                // TODO: eventually consider throwing
1586                return;
1587            }
1588
1589            try {
1590                // TODO: support alert shared across interfaces
1591                mConnector.execute("bandwidth", "removeinterfacealert", iface);
1592                mActiveAlerts.remove(iface);
1593            } catch (NativeDaemonConnectorException e) {
1594                throw e.rethrowAsParcelableException();
1595            }
1596        }
1597    }
1598
1599    @Override
1600    public void setGlobalAlert(long alertBytes) {
1601        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1602
1603        // silently discard when control disabled
1604        // TODO: eventually migrate to be always enabled
1605        if (!mBandwidthControlEnabled) return;
1606
1607        try {
1608            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
1609        } catch (NativeDaemonConnectorException e) {
1610            throw e.rethrowAsParcelableException();
1611        }
1612    }
1613
1614    @Override
1615    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1616        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1617
1618        // silently discard when control disabled
1619        // TODO: eventually migrate to be always enabled
1620        if (!mBandwidthControlEnabled) return;
1621
1622        synchronized (mQuotaLock) {
1623            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1624            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1625                // TODO: eventually consider throwing
1626                return;
1627            }
1628
1629            try {
1630                mConnector.execute("bandwidth",
1631                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
1632                if (rejectOnQuotaInterfaces) {
1633                    mUidRejectOnQuota.put(uid, true);
1634                } else {
1635                    mUidRejectOnQuota.delete(uid);
1636                }
1637            } catch (NativeDaemonConnectorException e) {
1638                throw e.rethrowAsParcelableException();
1639            }
1640        }
1641    }
1642
1643    @Override
1644    public boolean isBandwidthControlEnabled() {
1645        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1646        return mBandwidthControlEnabled;
1647    }
1648
1649    @Override
1650    public NetworkStats getNetworkStatsUidDetail(int uid) {
1651        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1652        try {
1653            return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null);
1654        } catch (IOException e) {
1655            throw new IllegalStateException(e);
1656        }
1657    }
1658
1659    @Override
1660    public NetworkStats getNetworkStatsTethering() {
1661        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1662
1663        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
1664        try {
1665            final NativeDaemonEvent[] events = mConnector.executeForList(
1666                    "bandwidth", "gettetherstats");
1667            for (NativeDaemonEvent event : events) {
1668                if (event.getCode() != TetheringStatsListResult) continue;
1669
1670                // 114 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
1671                final StringTokenizer tok = new StringTokenizer(event.getMessage());
1672                try {
1673                    final String ifaceIn = tok.nextToken();
1674                    final String ifaceOut = tok.nextToken();
1675
1676                    final NetworkStats.Entry entry = new NetworkStats.Entry();
1677                    entry.iface = ifaceOut;
1678                    entry.uid = UID_TETHERING;
1679                    entry.set = SET_DEFAULT;
1680                    entry.tag = TAG_NONE;
1681                    entry.rxBytes = Long.parseLong(tok.nextToken());
1682                    entry.rxPackets = Long.parseLong(tok.nextToken());
1683                    entry.txBytes = Long.parseLong(tok.nextToken());
1684                    entry.txPackets = Long.parseLong(tok.nextToken());
1685                    stats.combineValues(entry);
1686                } catch (NoSuchElementException e) {
1687                    throw new IllegalStateException("problem parsing tethering stats: " + event);
1688                } catch (NumberFormatException e) {
1689                    throw new IllegalStateException("problem parsing tethering stats: " + event);
1690                }
1691            }
1692        } catch (NativeDaemonConnectorException e) {
1693            throw e.rethrowAsParcelableException();
1694        }
1695        return stats;
1696    }
1697
1698    @Override
1699    public void setDnsServersForNetwork(int netId, String[] servers, String domains) {
1700        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1701
1702        final Command cmd = new Command("resolver", "setnetdns", netId,
1703                (domains == null ? "" : domains));
1704
1705        for (String s : servers) {
1706            InetAddress a = NetworkUtils.numericToInetAddress(s);
1707            if (a.isAnyLocalAddress() == false) {
1708                cmd.appendArg(a.getHostAddress());
1709            }
1710        }
1711
1712        try {
1713            mConnector.execute(cmd);
1714        } catch (NativeDaemonConnectorException e) {
1715            throw e.rethrowAsParcelableException();
1716        }
1717    }
1718
1719    @Override
1720    public void addVpnUidRanges(int netId, UidRange[] ranges) {
1721        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1722        Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
1723        argv[0] = "users";
1724        argv[1] = "add";
1725        argv[2] = netId;
1726        int argc = 3;
1727        // Avoid overly long commands by limiting number of UID ranges per command.
1728        for (int i = 0; i < ranges.length; i++) {
1729            argv[argc++] = ranges[i].toString();
1730            if (i == (ranges.length - 1) || argc == argv.length) {
1731                try {
1732                    mConnector.execute("network", Arrays.copyOf(argv, argc));
1733                } catch (NativeDaemonConnectorException e) {
1734                    throw e.rethrowAsParcelableException();
1735                }
1736                argc = 3;
1737            }
1738        }
1739    }
1740
1741    @Override
1742    public void removeVpnUidRanges(int netId, UidRange[] ranges) {
1743        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1744        Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
1745        argv[0] = "users";
1746        argv[1] = "remove";
1747        argv[2] = netId;
1748        int argc = 3;
1749        // Avoid overly long commands by limiting number of UID ranges per command.
1750        for (int i = 0; i < ranges.length; i++) {
1751            argv[argc++] = ranges[i].toString();
1752            if (i == (ranges.length - 1) || argc == argv.length) {
1753                try {
1754                    mConnector.execute("network", Arrays.copyOf(argv, argc));
1755                } catch (NativeDaemonConnectorException e) {
1756                    throw e.rethrowAsParcelableException();
1757                }
1758                argc = 3;
1759            }
1760        }
1761    }
1762
1763    @Override
1764    public void flushNetworkDnsCache(int netId) {
1765        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1766        try {
1767            mConnector.execute("resolver", "flushnet", netId);
1768        } catch (NativeDaemonConnectorException e) {
1769            throw e.rethrowAsParcelableException();
1770        }
1771    }
1772
1773    @Override
1774    public void setFirewallEnabled(boolean enabled) {
1775        enforceSystemUid();
1776        try {
1777            mConnector.execute("firewall", enabled ? "enable" : "disable");
1778            mFirewallEnabled = enabled;
1779        } catch (NativeDaemonConnectorException e) {
1780            throw e.rethrowAsParcelableException();
1781        }
1782    }
1783
1784    @Override
1785    public boolean isFirewallEnabled() {
1786        enforceSystemUid();
1787        return mFirewallEnabled;
1788    }
1789
1790    @Override
1791    public void setFirewallInterfaceRule(String iface, boolean allow) {
1792        enforceSystemUid();
1793        Preconditions.checkState(mFirewallEnabled);
1794        final String rule = allow ? "allow" : "deny";
1795        try {
1796            mConnector.execute("firewall", "set_interface_rule", iface, rule);
1797        } catch (NativeDaemonConnectorException e) {
1798            throw e.rethrowAsParcelableException();
1799        }
1800    }
1801
1802    @Override
1803    public void setFirewallEgressSourceRule(String addr, boolean allow) {
1804        enforceSystemUid();
1805        Preconditions.checkState(mFirewallEnabled);
1806        final String rule = allow ? "allow" : "deny";
1807        try {
1808            mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
1809        } catch (NativeDaemonConnectorException e) {
1810            throw e.rethrowAsParcelableException();
1811        }
1812    }
1813
1814    @Override
1815    public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
1816        enforceSystemUid();
1817        Preconditions.checkState(mFirewallEnabled);
1818        final String rule = allow ? "allow" : "deny";
1819        try {
1820            mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
1821        } catch (NativeDaemonConnectorException e) {
1822            throw e.rethrowAsParcelableException();
1823        }
1824    }
1825
1826    @Override
1827    public void setFirewallUidRule(int uid, boolean allow) {
1828        enforceSystemUid();
1829        Preconditions.checkState(mFirewallEnabled);
1830        final String rule = allow ? "allow" : "deny";
1831        try {
1832            mConnector.execute("firewall", "set_uid_rule", uid, rule);
1833        } catch (NativeDaemonConnectorException e) {
1834            throw e.rethrowAsParcelableException();
1835        }
1836    }
1837
1838    private static void enforceSystemUid() {
1839        final int uid = Binder.getCallingUid();
1840        if (uid != Process.SYSTEM_UID) {
1841            throw new SecurityException("Only available to AID_SYSTEM");
1842        }
1843    }
1844
1845    @Override
1846    public void startClatd(String interfaceName) throws IllegalStateException {
1847        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1848
1849        try {
1850            mConnector.execute("clatd", "start", interfaceName);
1851        } catch (NativeDaemonConnectorException e) {
1852            throw e.rethrowAsParcelableException();
1853        }
1854    }
1855
1856    @Override
1857    public void stopClatd() throws IllegalStateException {
1858        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1859
1860        try {
1861            mConnector.execute("clatd", "stop");
1862        } catch (NativeDaemonConnectorException e) {
1863            throw e.rethrowAsParcelableException();
1864        }
1865    }
1866
1867    @Override
1868    public boolean isClatdStarted() {
1869        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1870
1871        final NativeDaemonEvent event;
1872        try {
1873            event = mConnector.execute("clatd", "status");
1874        } catch (NativeDaemonConnectorException e) {
1875            throw e.rethrowAsParcelableException();
1876        }
1877
1878        event.checkCode(ClatdStatusResult);
1879        return event.getMessage().endsWith("started");
1880    }
1881
1882    @Override
1883    public void registerNetworkActivityListener(INetworkActivityListener listener) {
1884        mNetworkActivityListeners.register(listener);
1885    }
1886
1887    @Override
1888    public void unregisterNetworkActivityListener(INetworkActivityListener listener) {
1889        mNetworkActivityListeners.unregister(listener);
1890    }
1891
1892    @Override
1893    public boolean isNetworkActive() {
1894        synchronized (mNetworkActivityListeners) {
1895            return mNetworkActive || mActiveIdleTimers.isEmpty();
1896        }
1897    }
1898
1899    private void reportNetworkActive() {
1900        final int length = mNetworkActivityListeners.beginBroadcast();
1901        try {
1902            for (int i = 0; i < length; i++) {
1903                try {
1904                    mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
1905                } catch (RemoteException e) {
1906                } catch (RuntimeException e) {
1907                }
1908            }
1909        } finally {
1910            mNetworkActivityListeners.finishBroadcast();
1911        }
1912    }
1913
1914    /** {@inheritDoc} */
1915    @Override
1916    public void monitor() {
1917        if (mConnector != null) {
1918            mConnector.monitor();
1919        }
1920    }
1921
1922    @Override
1923    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1924        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1925
1926        pw.println("NetworkManagementService NativeDaemonConnector Log:");
1927        mConnector.dump(fd, pw, args);
1928        pw.println();
1929
1930        pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
1931        pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio);
1932                pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio);
1933        pw.print("mNetworkActive="); pw.println(mNetworkActive);
1934
1935        synchronized (mQuotaLock) {
1936            pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
1937            pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
1938        }
1939
1940        synchronized (mUidRejectOnQuota) {
1941            pw.print("UID reject on quota ifaces: [");
1942            final int size = mUidRejectOnQuota.size();
1943            for (int i = 0; i < size; i++) {
1944                pw.print(mUidRejectOnQuota.keyAt(i));
1945                if (i < size - 1) pw.print(",");
1946            }
1947            pw.println("]");
1948        }
1949
1950        synchronized (mIdleTimerLock) {
1951            pw.println("Idle timers:");
1952            for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {
1953                pw.print("  "); pw.print(ent.getKey()); pw.println(":");
1954                IdleTimerParams params = ent.getValue();
1955                pw.print("    timeout="); pw.print(params.timeout);
1956                pw.print(" type="); pw.print(params.type);
1957                pw.print(" networkCount="); pw.println(params.networkCount);
1958            }
1959        }
1960
1961        pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
1962    }
1963
1964    @Override
1965    public void createPhysicalNetwork(int netId) {
1966        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1967
1968        try {
1969            mConnector.execute("network", "create", netId);
1970        } catch (NativeDaemonConnectorException e) {
1971            throw e.rethrowAsParcelableException();
1972        }
1973    }
1974
1975    @Override
1976    public void createVirtualNetwork(int netId, boolean hasDNS, boolean secure) {
1977        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1978
1979        try {
1980            mConnector.execute("network", "create", netId, "vpn", hasDNS ? "1" : "0",
1981                    secure ? "1" : "0");
1982        } catch (NativeDaemonConnectorException e) {
1983            throw e.rethrowAsParcelableException();
1984        }
1985    }
1986
1987    @Override
1988    public void removeNetwork(int netId) {
1989        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1990
1991        try {
1992            mConnector.execute("network", "destroy", netId);
1993        } catch (NativeDaemonConnectorException e) {
1994            throw e.rethrowAsParcelableException();
1995        }
1996    }
1997
1998    @Override
1999    public void addInterfaceToNetwork(String iface, int netId) {
2000        modifyInterfaceInNetwork("add", "" + netId, iface);
2001    }
2002
2003    @Override
2004    public void removeInterfaceFromNetwork(String iface, int netId) {
2005        modifyInterfaceInNetwork("remove", "" + netId, iface);
2006    }
2007
2008    private void modifyInterfaceInNetwork(String action, String netId, String iface) {
2009        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2010        try {
2011            mConnector.execute("network", "interface", action, netId, iface);
2012        } catch (NativeDaemonConnectorException e) {
2013            throw e.rethrowAsParcelableException();
2014        }
2015    }
2016
2017    @Override
2018    public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
2019        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2020
2021        final Command cmd = new Command("network", "route", "legacy", uid, "add", netId);
2022
2023        // create triplet: interface dest-ip-addr/prefixlength gateway-ip-addr
2024        final LinkAddress la = routeInfo.getDestinationLinkAddress();
2025        cmd.appendArg(routeInfo.getInterface());
2026        cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getPrefixLength());
2027        if (routeInfo.hasGateway()) {
2028            cmd.appendArg(routeInfo.getGateway().getHostAddress());
2029        }
2030
2031        try {
2032            mConnector.execute(cmd);
2033        } catch (NativeDaemonConnectorException e) {
2034            throw e.rethrowAsParcelableException();
2035        }
2036    }
2037
2038    @Override
2039    public void setDefaultNetId(int netId) {
2040        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2041
2042        try {
2043            mConnector.execute("network", "default", "set", netId);
2044        } catch (NativeDaemonConnectorException e) {
2045            throw e.rethrowAsParcelableException();
2046        }
2047    }
2048
2049    @Override
2050    public void clearDefaultNetId() {
2051        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2052
2053        try {
2054            mConnector.execute("network", "default", "clear");
2055        } catch (NativeDaemonConnectorException e) {
2056            throw e.rethrowAsParcelableException();
2057        }
2058    }
2059
2060    @Override
2061    public void setPermission(String permission, int[] uids) {
2062        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2063
2064        Object[] argv = new Object[4 + MAX_UID_RANGES_PER_COMMAND];
2065        argv[0] = "permission";
2066        argv[1] = "user";
2067        argv[2] = "set";
2068        argv[3] = permission;
2069        int argc = 4;
2070        // Avoid overly long commands by limiting number of UIDs per command.
2071        for (int i = 0; i < uids.length; ++i) {
2072            argv[argc++] = uids[i];
2073            if (i == uids.length - 1 || argc == argv.length) {
2074                try {
2075                    mConnector.execute("network", Arrays.copyOf(argv, argc));
2076                } catch (NativeDaemonConnectorException e) {
2077                    throw e.rethrowAsParcelableException();
2078                }
2079                argc = 4;
2080            }
2081        }
2082    }
2083
2084    @Override
2085    public void clearPermission(int[] uids) {
2086        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2087
2088        Object[] argv = new Object[3 + MAX_UID_RANGES_PER_COMMAND];
2089        argv[0] = "permission";
2090        argv[1] = "user";
2091        argv[2] = "clear";
2092        int argc = 3;
2093        // Avoid overly long commands by limiting number of UIDs per command.
2094        for (int i = 0; i < uids.length; ++i) {
2095            argv[argc++] = uids[i];
2096            if (i == uids.length - 1 || argc == argv.length) {
2097                try {
2098                    mConnector.execute("network", Arrays.copyOf(argv, argc));
2099                } catch (NativeDaemonConnectorException e) {
2100                    throw e.rethrowAsParcelableException();
2101                }
2102                argc = 3;
2103            }
2104        }
2105    }
2106
2107    @Override
2108    public void allowProtect(int uid) {
2109        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2110
2111        try {
2112            mConnector.execute("network", "protect", "allow", uid);
2113        } catch (NativeDaemonConnectorException e) {
2114            throw e.rethrowAsParcelableException();
2115        }
2116    }
2117
2118    @Override
2119    public void denyProtect(int uid) {
2120        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2121
2122        try {
2123            mConnector.execute("network", "protect", "deny", uid);
2124        } catch (NativeDaemonConnectorException e) {
2125            throw e.rethrowAsParcelableException();
2126        }
2127    }
2128
2129    @Override
2130    public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
2131        modifyInterfaceInNetwork("add", "local", iface);
2132
2133        for (RouteInfo route : routes) {
2134            if (!route.isDefaultRoute()) {
2135                modifyRoute("add", "local", route);
2136            }
2137        }
2138    }
2139
2140    @Override
2141    public void removeInterfaceFromLocalNetwork(String iface) {
2142        modifyInterfaceInNetwork("remove", "local", iface);
2143    }
2144}
2145