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