NetworkManagementService.java revision 293251318168ad2dbfeb3d18176936fe2cc16325
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.getDestination();
886        cmd.appendArg(route.getInterface());
887        cmd.appendArg(la.getAddress().getHostAddress() + "/" + la.getNetworkPrefixLength());
888        cmd.appendArg(route.getGateway().getHostAddress());
889
890        try {
891            mConnector.execute(cmd);
892        } catch (NativeDaemonConnectorException e) {
893            throw e.rethrowAsParcelableException();
894        }
895    }
896
897    private ArrayList<String> readRouteList(String filename) {
898        FileInputStream fstream = null;
899        ArrayList<String> list = new ArrayList<String>();
900
901        try {
902            fstream = new FileInputStream(filename);
903            DataInputStream in = new DataInputStream(fstream);
904            BufferedReader br = new BufferedReader(new InputStreamReader(in));
905            String s;
906
907            // throw away the title line
908
909            while (((s = br.readLine()) != null) && (s.length() != 0)) {
910                list.add(s);
911            }
912        } catch (IOException ex) {
913            // return current list, possibly empty
914        } finally {
915            if (fstream != null) {
916                try {
917                    fstream.close();
918                } catch (IOException ex) {}
919            }
920        }
921
922        return list;
923    }
924
925    @Override
926    public RouteInfo[] getRoutes(String interfaceName) {
927        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
928        ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
929
930        // v4 routes listed as:
931        // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
932        for (String s : readRouteList("/proc/net/route")) {
933            String[] fields = s.split("\t");
934
935            if (fields.length > 7) {
936                String iface = fields[0];
937
938                if (interfaceName.equals(iface)) {
939                    String dest = fields[1];
940                    String gate = fields[2];
941                    String flags = fields[3]; // future use?
942                    String mask = fields[7];
943                    try {
944                        // address stored as a hex string, ex: 0014A8C0
945                        InetAddress destAddr =
946                                NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
947                        int prefixLength =
948                                NetworkUtils.netmaskIntToPrefixLength(
949                                (int)Long.parseLong(mask, 16));
950                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
951
952                        // address stored as a hex string, ex 0014A8C0
953                        InetAddress gatewayAddr =
954                                NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
955
956                        RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
957                        routes.add(route);
958                    } catch (Exception e) {
959                        Log.e(TAG, "Error parsing route " + s + " : " + e);
960                        continue;
961                    }
962                }
963            }
964        }
965
966        // v6 routes listed as:
967        // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
968        for (String s : readRouteList("/proc/net/ipv6_route")) {
969            String[]fields = s.split("\\s+");
970            if (fields.length > 9) {
971                String iface = fields[9].trim();
972                if (interfaceName.equals(iface)) {
973                    String dest = fields[0];
974                    String prefix = fields[1];
975                    String gate = fields[4];
976
977                    try {
978                        // prefix length stored as a hex string, ex 40
979                        int prefixLength = Integer.parseInt(prefix, 16);
980
981                        // address stored as a 32 char hex string
982                        // ex fe800000000000000000000000000000
983                        InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
984                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
985
986                        InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
987
988                        RouteInfo route = new RouteInfo(linkAddress, gateAddr);
989                        routes.add(route);
990                    } catch (Exception e) {
991                        Log.e(TAG, "Error parsing route " + s + " : " + e);
992                        continue;
993                    }
994                }
995            }
996        }
997        return routes.toArray(new RouteInfo[routes.size()]);
998    }
999
1000    @Override
1001    public void setMtu(String iface, int mtu) {
1002        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1003
1004        final NativeDaemonEvent event;
1005        try {
1006            event = mConnector.execute("interface", "setmtu", iface, mtu);
1007        } catch (NativeDaemonConnectorException e) {
1008            throw e.rethrowAsParcelableException();
1009        }
1010    }
1011
1012    @Override
1013    public void shutdown() {
1014        // TODO: remove from aidl if nobody calls externally
1015        mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
1016
1017        Slog.d(TAG, "Shutting down");
1018    }
1019
1020    @Override
1021    public boolean getIpForwardingEnabled() throws IllegalStateException{
1022        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1023
1024        final NativeDaemonEvent event;
1025        try {
1026            event = mConnector.execute("ipfwd", "status");
1027        } catch (NativeDaemonConnectorException e) {
1028            throw e.rethrowAsParcelableException();
1029        }
1030
1031        // 211 Forwarding enabled
1032        event.checkCode(IpFwdStatusResult);
1033        return event.getMessage().endsWith("enabled");
1034    }
1035
1036    @Override
1037    public void setIpForwardingEnabled(boolean enable) {
1038        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1039        try {
1040            mConnector.execute("ipfwd", enable ? "enable" : "disable");
1041        } catch (NativeDaemonConnectorException e) {
1042            throw e.rethrowAsParcelableException();
1043        }
1044    }
1045
1046    @Override
1047    public void startTethering(String[] dhcpRange) {
1048        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1049        // cmd is "tether start first_start first_stop second_start second_stop ..."
1050        // an odd number of addrs will fail
1051
1052        final Command cmd = new Command("tether", "start");
1053        for (String d : dhcpRange) {
1054            cmd.appendArg(d);
1055        }
1056
1057        try {
1058            mConnector.execute(cmd);
1059        } catch (NativeDaemonConnectorException e) {
1060            throw e.rethrowAsParcelableException();
1061        }
1062    }
1063
1064    @Override
1065    public void stopTethering() {
1066        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1067        try {
1068            mConnector.execute("tether", "stop");
1069        } catch (NativeDaemonConnectorException e) {
1070            throw e.rethrowAsParcelableException();
1071        }
1072    }
1073
1074    @Override
1075    public boolean isTetheringStarted() {
1076        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1077
1078        final NativeDaemonEvent event;
1079        try {
1080            event = mConnector.execute("tether", "status");
1081        } catch (NativeDaemonConnectorException e) {
1082            throw e.rethrowAsParcelableException();
1083        }
1084
1085        // 210 Tethering services started
1086        event.checkCode(TetherStatusResult);
1087        return event.getMessage().endsWith("started");
1088    }
1089
1090    @Override
1091    public void tetherInterface(String iface) {
1092        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1093        try {
1094            mConnector.execute("tether", "interface", "add", iface);
1095        } catch (NativeDaemonConnectorException e) {
1096            throw e.rethrowAsParcelableException();
1097        }
1098    }
1099
1100    @Override
1101    public void untetherInterface(String iface) {
1102        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1103        try {
1104            mConnector.execute("tether", "interface", "remove", iface);
1105        } catch (NativeDaemonConnectorException e) {
1106            throw e.rethrowAsParcelableException();
1107        }
1108    }
1109
1110    @Override
1111    public String[] listTetheredInterfaces() {
1112        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1113        try {
1114            return NativeDaemonEvent.filterMessageList(
1115                    mConnector.executeForList("tether", "interface", "list"),
1116                    TetherInterfaceListResult);
1117        } catch (NativeDaemonConnectorException e) {
1118            throw e.rethrowAsParcelableException();
1119        }
1120    }
1121
1122    @Override
1123    public void setDnsForwarders(String[] dns) {
1124        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1125
1126        final Command cmd = new Command("tether", "dns", "set");
1127        for (String s : dns) {
1128            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
1129        }
1130
1131        try {
1132            mConnector.execute(cmd);
1133        } catch (NativeDaemonConnectorException e) {
1134            throw e.rethrowAsParcelableException();
1135        }
1136    }
1137
1138    @Override
1139    public String[] getDnsForwarders() {
1140        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1141        try {
1142            return NativeDaemonEvent.filterMessageList(
1143                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
1144        } catch (NativeDaemonConnectorException e) {
1145            throw e.rethrowAsParcelableException();
1146        }
1147    }
1148
1149    private List<InterfaceAddress> excludeLinkLocal(List<InterfaceAddress> addresses) {
1150        ArrayList<InterfaceAddress> filtered = new ArrayList<InterfaceAddress>(addresses.size());
1151        for (InterfaceAddress ia : addresses) {
1152            if (!ia.getAddress().isLinkLocalAddress())
1153                filtered.add(ia);
1154        }
1155        return filtered;
1156    }
1157
1158    private void modifyNat(String action, String internalInterface, String externalInterface)
1159            throws SocketException {
1160        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
1161
1162        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
1163                internalInterface);
1164        if (internalNetworkInterface == null) {
1165            cmd.appendArg("0");
1166        } else {
1167            // Don't touch link-local routes, as link-local addresses aren't routable,
1168            // kernel creates link-local routes on all interfaces automatically
1169            List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
1170                    internalNetworkInterface.getInterfaceAddresses());
1171            cmd.appendArg(interfaceAddresses.size());
1172            for (InterfaceAddress ia : interfaceAddresses) {
1173                InetAddress addr = NetworkUtils.getNetworkPart(
1174                        ia.getAddress(), ia.getNetworkPrefixLength());
1175                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
1176            }
1177        }
1178
1179        try {
1180            mConnector.execute(cmd);
1181        } catch (NativeDaemonConnectorException e) {
1182            throw e.rethrowAsParcelableException();
1183        }
1184    }
1185
1186    @Override
1187    public void enableNat(String internalInterface, String externalInterface) {
1188        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1189        try {
1190            modifyNat("enable", internalInterface, externalInterface);
1191        } catch (SocketException e) {
1192            throw new IllegalStateException(e);
1193        }
1194    }
1195
1196    @Override
1197    public void disableNat(String internalInterface, String externalInterface) {
1198        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1199        try {
1200            modifyNat("disable", internalInterface, externalInterface);
1201        } catch (SocketException e) {
1202            throw new IllegalStateException(e);
1203        }
1204    }
1205
1206    @Override
1207    public String[] listTtys() {
1208        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1209        try {
1210            return NativeDaemonEvent.filterMessageList(
1211                    mConnector.executeForList("list_ttys"), TtyListResult);
1212        } catch (NativeDaemonConnectorException e) {
1213            throw e.rethrowAsParcelableException();
1214        }
1215    }
1216
1217    @Override
1218    public void attachPppd(
1219            String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
1220        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1221        try {
1222            mConnector.execute("pppd", "attach", tty,
1223                    NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
1224                    NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
1225                    NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
1226                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
1227        } catch (NativeDaemonConnectorException e) {
1228            throw e.rethrowAsParcelableException();
1229        }
1230    }
1231
1232    @Override
1233    public void detachPppd(String tty) {
1234        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1235        try {
1236            mConnector.execute("pppd", "detach", tty);
1237        } catch (NativeDaemonConnectorException e) {
1238            throw e.rethrowAsParcelableException();
1239        }
1240    }
1241
1242    @Override
1243    public void startAccessPoint(
1244            WifiConfiguration wifiConfig, String wlanIface) {
1245        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1246        try {
1247            wifiFirmwareReload(wlanIface, "AP");
1248            if (wifiConfig == null) {
1249                mConnector.execute("softap", "set", wlanIface);
1250            } else {
1251                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1252                                   "broadcast", "6", getSecurityType(wifiConfig),
1253                                   new SensitiveArg(wifiConfig.preSharedKey));
1254            }
1255            mConnector.execute("softap", "startap");
1256        } catch (NativeDaemonConnectorException e) {
1257            throw e.rethrowAsParcelableException();
1258        }
1259    }
1260
1261    private static String getSecurityType(WifiConfiguration wifiConfig) {
1262        switch (wifiConfig.getAuthType()) {
1263            case KeyMgmt.WPA_PSK:
1264                return "wpa-psk";
1265            case KeyMgmt.WPA2_PSK:
1266                return "wpa2-psk";
1267            default:
1268                return "open";
1269        }
1270    }
1271
1272    /* @param mode can be "AP", "STA" or "P2P" */
1273    @Override
1274    public void wifiFirmwareReload(String wlanIface, String mode) {
1275        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1276        try {
1277            mConnector.execute("softap", "fwreload", wlanIface, mode);
1278        } catch (NativeDaemonConnectorException e) {
1279            throw e.rethrowAsParcelableException();
1280        }
1281    }
1282
1283    @Override
1284    public void stopAccessPoint(String wlanIface) {
1285        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1286        try {
1287            mConnector.execute("softap", "stopap");
1288            wifiFirmwareReload(wlanIface, "STA");
1289        } catch (NativeDaemonConnectorException e) {
1290            throw e.rethrowAsParcelableException();
1291        }
1292    }
1293
1294    @Override
1295    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
1296        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1297        try {
1298            if (wifiConfig == null) {
1299                mConnector.execute("softap", "set", wlanIface);
1300            } else {
1301                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1302                                   "broadcast", "6", getSecurityType(wifiConfig),
1303                                   new SensitiveArg(wifiConfig.preSharedKey));
1304            }
1305        } catch (NativeDaemonConnectorException e) {
1306            throw e.rethrowAsParcelableException();
1307        }
1308    }
1309
1310    @Override
1311    public void addIdleTimer(String iface, int timeout, final int type) {
1312        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1313
1314        if (DBG) Slog.d(TAG, "Adding idletimer");
1315
1316        synchronized (mIdleTimerLock) {
1317            IdleTimerParams params = mActiveIdleTimers.get(iface);
1318            if (params != null) {
1319                // the interface already has idletimer, update network count
1320                params.networkCount++;
1321                return;
1322            }
1323
1324            try {
1325                mConnector.execute("idletimer", "add", iface, Integer.toString(timeout),
1326                        Integer.toString(type));
1327            } catch (NativeDaemonConnectorException e) {
1328                throw e.rethrowAsParcelableException();
1329            }
1330            mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
1331
1332            // Networks start up.
1333            if (ConnectivityManager.isNetworkTypeMobile(type)) {
1334                mNetworkActive = false;
1335            }
1336            mDaemonHandler.post(new Runnable() {
1337                @Override public void run() {
1338                    notifyInterfaceClassActivity(type,
1339                            DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
1340                            SystemClock.elapsedRealtimeNanos(), false);
1341                }
1342            });
1343        }
1344    }
1345
1346    @Override
1347    public void removeIdleTimer(String iface) {
1348        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1349
1350        if (DBG) Slog.d(TAG, "Removing idletimer");
1351
1352        synchronized (mIdleTimerLock) {
1353            final IdleTimerParams params = mActiveIdleTimers.get(iface);
1354            if (params == null || --(params.networkCount) > 0) {
1355                return;
1356            }
1357
1358            try {
1359                mConnector.execute("idletimer", "remove", iface,
1360                        Integer.toString(params.timeout), Integer.toString(params.type));
1361            } catch (NativeDaemonConnectorException e) {
1362                throw e.rethrowAsParcelableException();
1363            }
1364            mActiveIdleTimers.remove(iface);
1365            mDaemonHandler.post(new Runnable() {
1366                @Override public void run() {
1367                    notifyInterfaceClassActivity(params.type,
1368                            DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
1369                            SystemClock.elapsedRealtimeNanos(), false);
1370                }
1371            });
1372        }
1373    }
1374
1375    @Override
1376    public NetworkStats getNetworkStatsSummaryDev() {
1377        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1378        try {
1379            return mStatsFactory.readNetworkStatsSummaryDev();
1380        } catch (IOException e) {
1381            throw new IllegalStateException(e);
1382        }
1383    }
1384
1385    @Override
1386    public NetworkStats getNetworkStatsSummaryXt() {
1387        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1388        try {
1389            return mStatsFactory.readNetworkStatsSummaryXt();
1390        } catch (IOException e) {
1391            throw new IllegalStateException(e);
1392        }
1393    }
1394
1395    @Override
1396    public NetworkStats getNetworkStatsDetail() {
1397        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1398        try {
1399            return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
1400        } catch (IOException e) {
1401            throw new IllegalStateException(e);
1402        }
1403    }
1404
1405    @Override
1406    public void setInterfaceQuota(String iface, long quotaBytes) {
1407        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1408
1409        // silently discard when control disabled
1410        // TODO: eventually migrate to be always enabled
1411        if (!mBandwidthControlEnabled) return;
1412
1413        synchronized (mQuotaLock) {
1414            if (mActiveQuotas.containsKey(iface)) {
1415                throw new IllegalStateException("iface " + iface + " already has quota");
1416            }
1417
1418            try {
1419                // TODO: support quota shared across interfaces
1420                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
1421                mActiveQuotas.put(iface, quotaBytes);
1422            } catch (NativeDaemonConnectorException e) {
1423                throw e.rethrowAsParcelableException();
1424            }
1425        }
1426    }
1427
1428    @Override
1429    public void removeInterfaceQuota(String iface) {
1430        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1431
1432        // silently discard when control disabled
1433        // TODO: eventually migrate to be always enabled
1434        if (!mBandwidthControlEnabled) return;
1435
1436        synchronized (mQuotaLock) {
1437            if (!mActiveQuotas.containsKey(iface)) {
1438                // TODO: eventually consider throwing
1439                return;
1440            }
1441
1442            mActiveQuotas.remove(iface);
1443            mActiveAlerts.remove(iface);
1444
1445            try {
1446                // TODO: support quota shared across interfaces
1447                mConnector.execute("bandwidth", "removeiquota", iface);
1448            } catch (NativeDaemonConnectorException e) {
1449                throw e.rethrowAsParcelableException();
1450            }
1451        }
1452    }
1453
1454    @Override
1455    public void setInterfaceAlert(String iface, long alertBytes) {
1456        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1457
1458        // silently discard when control disabled
1459        // TODO: eventually migrate to be always enabled
1460        if (!mBandwidthControlEnabled) return;
1461
1462        // quick sanity check
1463        if (!mActiveQuotas.containsKey(iface)) {
1464            throw new IllegalStateException("setting alert requires existing quota on iface");
1465        }
1466
1467        synchronized (mQuotaLock) {
1468            if (mActiveAlerts.containsKey(iface)) {
1469                throw new IllegalStateException("iface " + iface + " already has alert");
1470            }
1471
1472            try {
1473                // TODO: support alert shared across interfaces
1474                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
1475                mActiveAlerts.put(iface, alertBytes);
1476            } catch (NativeDaemonConnectorException e) {
1477                throw e.rethrowAsParcelableException();
1478            }
1479        }
1480    }
1481
1482    @Override
1483    public void removeInterfaceAlert(String iface) {
1484        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1485
1486        // silently discard when control disabled
1487        // TODO: eventually migrate to be always enabled
1488        if (!mBandwidthControlEnabled) return;
1489
1490        synchronized (mQuotaLock) {
1491            if (!mActiveAlerts.containsKey(iface)) {
1492                // TODO: eventually consider throwing
1493                return;
1494            }
1495
1496            try {
1497                // TODO: support alert shared across interfaces
1498                mConnector.execute("bandwidth", "removeinterfacealert", iface);
1499                mActiveAlerts.remove(iface);
1500            } catch (NativeDaemonConnectorException e) {
1501                throw e.rethrowAsParcelableException();
1502            }
1503        }
1504    }
1505
1506    @Override
1507    public void setGlobalAlert(long alertBytes) {
1508        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1509
1510        // silently discard when control disabled
1511        // TODO: eventually migrate to be always enabled
1512        if (!mBandwidthControlEnabled) return;
1513
1514        try {
1515            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
1516        } catch (NativeDaemonConnectorException e) {
1517            throw e.rethrowAsParcelableException();
1518        }
1519    }
1520
1521    @Override
1522    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1523        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1524
1525        // silently discard when control disabled
1526        // TODO: eventually migrate to be always enabled
1527        if (!mBandwidthControlEnabled) return;
1528
1529        synchronized (mQuotaLock) {
1530            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1531            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1532                // TODO: eventually consider throwing
1533                return;
1534            }
1535
1536            try {
1537                mConnector.execute("bandwidth",
1538                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
1539                if (rejectOnQuotaInterfaces) {
1540                    mUidRejectOnQuota.put(uid, true);
1541                } else {
1542                    mUidRejectOnQuota.delete(uid);
1543                }
1544            } catch (NativeDaemonConnectorException e) {
1545                throw e.rethrowAsParcelableException();
1546            }
1547        }
1548    }
1549
1550    @Override
1551    public boolean isBandwidthControlEnabled() {
1552        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1553        return mBandwidthControlEnabled;
1554    }
1555
1556    @Override
1557    public NetworkStats getNetworkStatsUidDetail(int uid) {
1558        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1559        try {
1560            return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null);
1561        } catch (IOException e) {
1562            throw new IllegalStateException(e);
1563        }
1564    }
1565
1566    @Override
1567    public NetworkStats getNetworkStatsTethering() {
1568        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1569
1570        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
1571        try {
1572            final NativeDaemonEvent[] events = mConnector.executeForList(
1573                    "bandwidth", "gettetherstats");
1574            for (NativeDaemonEvent event : events) {
1575                if (event.getCode() != TetheringStatsListResult) continue;
1576
1577                // 114 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
1578                final StringTokenizer tok = new StringTokenizer(event.getMessage());
1579                try {
1580                    final String ifaceIn = tok.nextToken();
1581                    final String ifaceOut = tok.nextToken();
1582
1583                    final NetworkStats.Entry entry = new NetworkStats.Entry();
1584                    entry.iface = ifaceOut;
1585                    entry.uid = UID_TETHERING;
1586                    entry.set = SET_DEFAULT;
1587                    entry.tag = TAG_NONE;
1588                    entry.rxBytes = Long.parseLong(tok.nextToken());
1589                    entry.rxPackets = Long.parseLong(tok.nextToken());
1590                    entry.txBytes = Long.parseLong(tok.nextToken());
1591                    entry.txPackets = Long.parseLong(tok.nextToken());
1592                    stats.combineValues(entry);
1593                } catch (NoSuchElementException e) {
1594                    throw new IllegalStateException("problem parsing tethering stats: " + event);
1595                } catch (NumberFormatException e) {
1596                    throw new IllegalStateException("problem parsing tethering stats: " + event);
1597                }
1598            }
1599        } catch (NativeDaemonConnectorException e) {
1600            throw e.rethrowAsParcelableException();
1601        }
1602        return stats;
1603    }
1604
1605    @Override
1606    public void setDnsServersForNetwork(int netId, String[] servers, String domains) {
1607        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1608
1609        final Command cmd = new Command("resolver", "setnetdns", netId,
1610                (domains == null ? "" : domains));
1611
1612        for (String s : servers) {
1613            InetAddress a = NetworkUtils.numericToInetAddress(s);
1614            if (a.isAnyLocalAddress() == false) {
1615                cmd.appendArg(a.getHostAddress());
1616            }
1617        }
1618
1619        try {
1620            mConnector.execute(cmd);
1621        } catch (NativeDaemonConnectorException e) {
1622            throw e.rethrowAsParcelableException();
1623        }
1624    }
1625
1626    @Override
1627    public void setUidRangeRoute(String iface, int uid_start, int uid_end, boolean forward_dns) {
1628        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1629        try {
1630            mConnector.execute("interface", "fwmark",
1631                    "uid", "add", iface, uid_start, uid_end, forward_dns ? 1 : 0);
1632        } catch (NativeDaemonConnectorException e) {
1633            throw e.rethrowAsParcelableException();
1634        }
1635    }
1636
1637    @Override
1638    public void clearUidRangeRoute(String iface, int uid_start, int uid_end) {
1639        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1640        try {
1641            mConnector.execute("interface", "fwmark",
1642                    "uid", "remove", iface, uid_start, uid_end, 0);
1643        } catch (NativeDaemonConnectorException e) {
1644            throw e.rethrowAsParcelableException();
1645        }
1646    }
1647
1648    @Override
1649    public void setMarkedForwarding(String iface) {
1650        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1651        try {
1652            mConnector.execute("interface", "fwmark", "rule", "add", iface);
1653        } catch (NativeDaemonConnectorException e) {
1654            throw e.rethrowAsParcelableException();
1655        }
1656    }
1657
1658    @Override
1659    public void clearMarkedForwarding(String iface) {
1660        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1661        try {
1662            mConnector.execute("interface", "fwmark", "rule", "remove", iface);
1663        } catch (NativeDaemonConnectorException e) {
1664            throw e.rethrowAsParcelableException();
1665        }
1666    }
1667
1668    @Override
1669    public int getMarkForUid(int uid) {
1670        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1671        final NativeDaemonEvent event;
1672        try {
1673            event = mConnector.execute("interface", "fwmark", "get", "mark", uid);
1674        } catch (NativeDaemonConnectorException e) {
1675            throw e.rethrowAsParcelableException();
1676        }
1677        event.checkCode(GetMarkResult);
1678        return Integer.parseInt(event.getMessage());
1679    }
1680
1681    @Override
1682    public int getMarkForProtect() {
1683        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1684        final NativeDaemonEvent event;
1685        try {
1686            event = mConnector.execute("interface", "fwmark", "get", "protect");
1687        } catch (NativeDaemonConnectorException e) {
1688            throw e.rethrowAsParcelableException();
1689        }
1690        event.checkCode(GetMarkResult);
1691        return Integer.parseInt(event.getMessage());
1692    }
1693
1694    @Override
1695    public void setMarkedForwardingRoute(String iface, RouteInfo route) {
1696        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1697        try {
1698            LinkAddress dest = route.getDestination();
1699            mConnector.execute("interface", "fwmark", "route", "add", iface,
1700                    dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
1701        } catch (NativeDaemonConnectorException e) {
1702            throw e.rethrowAsParcelableException();
1703        }
1704    }
1705
1706    @Override
1707    public void clearMarkedForwardingRoute(String iface, RouteInfo route) {
1708        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1709        try {
1710            LinkAddress dest = route.getDestination();
1711            mConnector.execute("interface", "fwmark", "route", "remove", iface,
1712                    dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
1713        } catch (NativeDaemonConnectorException e) {
1714            throw e.rethrowAsParcelableException();
1715        }
1716    }
1717
1718    @Override
1719    public void setHostExemption(LinkAddress host) {
1720        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1721        try {
1722            mConnector.execute("interface", "fwmark", "exempt", "add", host);
1723        } catch (NativeDaemonConnectorException e) {
1724            throw e.rethrowAsParcelableException();
1725        }
1726    }
1727
1728    @Override
1729    public void clearHostExemption(LinkAddress host) {
1730        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1731        try {
1732            mConnector.execute("interface", "fwmark", "exempt", "remove", host);
1733        } catch (NativeDaemonConnectorException e) {
1734            throw e.rethrowAsParcelableException();
1735        }
1736    }
1737
1738    @Override
1739    public void flushNetworkDnsCache(int netId) {
1740        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1741        try {
1742            mConnector.execute("resolver", "flushnet", netId);
1743        } catch (NativeDaemonConnectorException e) {
1744            throw e.rethrowAsParcelableException();
1745        }
1746    }
1747
1748    @Override
1749    public void setFirewallEnabled(boolean enabled) {
1750        enforceSystemUid();
1751        try {
1752            mConnector.execute("firewall", enabled ? "enable" : "disable");
1753            mFirewallEnabled = enabled;
1754        } catch (NativeDaemonConnectorException e) {
1755            throw e.rethrowAsParcelableException();
1756        }
1757    }
1758
1759    @Override
1760    public boolean isFirewallEnabled() {
1761        enforceSystemUid();
1762        return mFirewallEnabled;
1763    }
1764
1765    @Override
1766    public void setFirewallInterfaceRule(String iface, boolean allow) {
1767        enforceSystemUid();
1768        Preconditions.checkState(mFirewallEnabled);
1769        final String rule = allow ? ALLOW : DENY;
1770        try {
1771            mConnector.execute("firewall", "set_interface_rule", iface, rule);
1772        } catch (NativeDaemonConnectorException e) {
1773            throw e.rethrowAsParcelableException();
1774        }
1775    }
1776
1777    @Override
1778    public void setFirewallEgressSourceRule(String addr, boolean allow) {
1779        enforceSystemUid();
1780        Preconditions.checkState(mFirewallEnabled);
1781        final String rule = allow ? ALLOW : DENY;
1782        try {
1783            mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
1784        } catch (NativeDaemonConnectorException e) {
1785            throw e.rethrowAsParcelableException();
1786        }
1787    }
1788
1789    @Override
1790    public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
1791        enforceSystemUid();
1792        Preconditions.checkState(mFirewallEnabled);
1793        final String rule = allow ? ALLOW : DENY;
1794        try {
1795            mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
1796        } catch (NativeDaemonConnectorException e) {
1797            throw e.rethrowAsParcelableException();
1798        }
1799    }
1800
1801    @Override
1802    public void setFirewallUidRule(int uid, boolean allow) {
1803        enforceSystemUid();
1804        Preconditions.checkState(mFirewallEnabled);
1805        final String rule = allow ? ALLOW : DENY;
1806        try {
1807            mConnector.execute("firewall", "set_uid_rule", uid, rule);
1808        } catch (NativeDaemonConnectorException e) {
1809            throw e.rethrowAsParcelableException();
1810        }
1811    }
1812
1813    private static void enforceSystemUid() {
1814        final int uid = Binder.getCallingUid();
1815        if (uid != Process.SYSTEM_UID) {
1816            throw new SecurityException("Only available to AID_SYSTEM");
1817        }
1818    }
1819
1820    @Override
1821    public void startClatd(String interfaceName) throws IllegalStateException {
1822        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1823
1824        try {
1825            mConnector.execute("clatd", "start", interfaceName);
1826        } catch (NativeDaemonConnectorException e) {
1827            throw e.rethrowAsParcelableException();
1828        }
1829    }
1830
1831    @Override
1832    public void stopClatd() throws IllegalStateException {
1833        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1834
1835        try {
1836            mConnector.execute("clatd", "stop");
1837        } catch (NativeDaemonConnectorException e) {
1838            throw e.rethrowAsParcelableException();
1839        }
1840    }
1841
1842    @Override
1843    public boolean isClatdStarted() {
1844        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1845
1846        final NativeDaemonEvent event;
1847        try {
1848            event = mConnector.execute("clatd", "status");
1849        } catch (NativeDaemonConnectorException e) {
1850            throw e.rethrowAsParcelableException();
1851        }
1852
1853        event.checkCode(ClatdStatusResult);
1854        return event.getMessage().endsWith("started");
1855    }
1856
1857    @Override
1858    public void registerNetworkActivityListener(INetworkActivityListener listener) {
1859        mNetworkActivityListeners.register(listener);
1860    }
1861
1862    @Override
1863    public void unregisterNetworkActivityListener(INetworkActivityListener listener) {
1864        mNetworkActivityListeners.unregister(listener);
1865    }
1866
1867    @Override
1868    public boolean isNetworkActive() {
1869        synchronized (mNetworkActivityListeners) {
1870            return mNetworkActive || mActiveIdleTimers.isEmpty();
1871        }
1872    }
1873
1874    private void reportNetworkActive() {
1875        final int length = mNetworkActivityListeners.beginBroadcast();
1876        try {
1877            for (int i = 0; i < length; i++) {
1878                try {
1879                    mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
1880                } catch (RemoteException e) {
1881                } catch (RuntimeException e) {
1882                }
1883            }
1884        } finally {
1885            mNetworkActivityListeners.finishBroadcast();
1886        }
1887    }
1888
1889    /** {@inheritDoc} */
1890    @Override
1891    public void monitor() {
1892        if (mConnector != null) {
1893            mConnector.monitor();
1894        }
1895    }
1896
1897    @Override
1898    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1899        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1900
1901        pw.println("NetworkManagementService NativeDaemonConnector Log:");
1902        mConnector.dump(fd, pw, args);
1903        pw.println();
1904
1905        pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
1906        pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio);
1907                pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio);
1908        pw.print("mNetworkActive="); pw.println(mNetworkActive);
1909
1910        synchronized (mQuotaLock) {
1911            pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
1912            pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
1913        }
1914
1915        synchronized (mUidRejectOnQuota) {
1916            pw.print("UID reject on quota ifaces: [");
1917            final int size = mUidRejectOnQuota.size();
1918            for (int i = 0; i < size; i++) {
1919                pw.print(mUidRejectOnQuota.keyAt(i));
1920                if (i < size - 1) pw.print(",");
1921            }
1922            pw.println("]");
1923        }
1924
1925        synchronized (mIdleTimerLock) {
1926            pw.println("Idle timers:");
1927            for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {
1928                pw.print("  "); pw.print(ent.getKey()); pw.println(":");
1929                IdleTimerParams params = ent.getValue();
1930                pw.print("    timeout="); pw.print(params.timeout);
1931                pw.print(" type="); pw.print(params.type);
1932                pw.print(" networkCount="); pw.println(params.networkCount);
1933            }
1934        }
1935
1936        pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
1937    }
1938
1939    @Override
1940    public void createNetwork(int netId) {
1941        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1942
1943        try {
1944            mConnector.execute("network", "create", netId);
1945        } catch (NativeDaemonConnectorException e) {
1946            throw e.rethrowAsParcelableException();
1947        }
1948    }
1949
1950    @Override
1951    public void removeNetwork(int netId) {
1952        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1953
1954        try {
1955            mConnector.execute("network", "destroy", netId);
1956        } catch (NativeDaemonConnectorException e) {
1957            throw e.rethrowAsParcelableException();
1958        }
1959    }
1960
1961    @Override
1962    public void addInterfaceToNetwork(String iface, int netId) {
1963        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1964
1965        try {
1966            mConnector.execute("network", "addiface", netId, iface);
1967        } catch (NativeDaemonConnectorException e) {
1968            throw e.rethrowAsParcelableException();
1969        }
1970    }
1971
1972    @Override
1973    public void removeInterfaceFromNetwork(String iface, int netId) {
1974        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1975
1976        try {
1977            mConnector.execute("network", "removeiface", netId, iface);
1978        } catch (NativeDaemonConnectorException e) {
1979            throw e.rethrowAsParcelableException();
1980        }
1981    }
1982
1983    @Override
1984    public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
1985        modifyLegacyRouteForNetId(netId, routeInfo, uid, ADD);
1986    }
1987
1988    @Override
1989    public void removeLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
1990        modifyLegacyRouteForNetId(netId, routeInfo, uid, REMOVE);
1991    }
1992
1993    private void modifyLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid, String action) {
1994        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1995
1996        final Command cmd = new Command("network", "legacy", uid, "route", action, netId);
1997
1998        // create quadlet: dest-ip-addr prefixlength gateway-ip-addr iface
1999        final LinkAddress la = routeInfo.getDestination();
2000        cmd.appendArg(la.getAddress().getHostAddress());
2001        cmd.appendArg(la.getNetworkPrefixLength());
2002        cmd.appendArg(routeInfo.getGateway().getHostAddress());
2003        cmd.appendArg(routeInfo.getInterface());
2004
2005        try {
2006            mConnector.execute(cmd);
2007        } catch (NativeDaemonConnectorException e) {
2008            throw e.rethrowAsParcelableException();
2009        }
2010    }
2011
2012    @Override
2013    public void setDefaultNetId(int netId) {
2014        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2015
2016        try {
2017            mConnector.execute("network", "default", "set", netId);
2018        } catch (NativeDaemonConnectorException e) {
2019            throw e.rethrowAsParcelableException();
2020        }
2021    }
2022
2023    @Override
2024    public void clearDefaultNetId() {
2025        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2026
2027        try {
2028            mConnector.execute("network", "default", "clear");
2029        } catch (NativeDaemonConnectorException e) {
2030            throw e.rethrowAsParcelableException();
2031        }
2032    }
2033
2034    @Override
2035    public void setPermission(boolean internal, boolean changeNetState, int[] uids) {
2036        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2037
2038        final Command cmd = new Command("network", "permission", "user", "set");
2039        if (internal) cmd.appendArg(CONNECTIVITY_INTERNAL);
2040        if (changeNetState) cmd.appendArg(CHANGE_NETWORK_STATE);
2041        for (int i=0; i<uids.length; i++) {
2042            cmd.appendArg(uids[i]);
2043        }
2044
2045        try {
2046            mConnector.execute(cmd);
2047        } catch (NativeDaemonConnectorException e) {
2048            throw e.rethrowAsParcelableException();
2049        }
2050    }
2051
2052    @Override
2053    public void clearPermission(int[] uids) {
2054        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2055
2056        final Command cmd = new Command("network", "permission", "user", "clear");
2057        for (int i=0; i<uids.length; i++) {
2058            cmd.appendArg(uids[i]);
2059        }
2060
2061        try {
2062            mConnector.execute(cmd);
2063        } catch (NativeDaemonConnectorException e) {
2064            throw e.rethrowAsParcelableException();
2065        }
2066    }
2067}
2068