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