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