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