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