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