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