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