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