NetworkManagementService.java revision 913c895216c0cb248ed0ce910e69dd84b285c064
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.CONNECTIVITY_INTERNAL;
20import static android.Manifest.permission.DUMP;
21import static android.Manifest.permission.SHUTDOWN;
22import static android.net.NetworkStats.SET_DEFAULT;
23import static android.net.NetworkStats.TAG_ALL;
24import static android.net.NetworkStats.TAG_NONE;
25import static android.net.NetworkStats.UID_ALL;
26import static android.net.TrafficStats.UID_TETHERING;
27import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
28import static com.android.server.NetworkManagementService.NetdResponseCode.GetMarkResult;
29import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
30import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
31import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
32import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
33import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
34import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
35import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult;
36import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
37import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
38
39import android.content.Context;
40import android.net.ConnectivityManager;
41import android.net.INetworkManagementEventObserver;
42import android.net.InterfaceConfiguration;
43import android.net.LinkAddress;
44import android.net.NetworkStats;
45import android.net.NetworkUtils;
46import android.net.RouteInfo;
47import android.net.wifi.WifiConfiguration;
48import android.net.wifi.WifiConfiguration.KeyMgmt;
49import android.os.BatteryStats;
50import android.os.Binder;
51import android.os.Handler;
52import android.os.INetworkActivityListener;
53import android.os.INetworkManagementService;
54import android.os.PowerManager;
55import android.os.Process;
56import android.os.RemoteCallbackList;
57import android.os.RemoteException;
58import android.os.ServiceManager;
59import android.os.SystemClock;
60import android.os.SystemProperties;
61import android.telephony.DataConnectionRealTimeInfo;
62import android.telephony.PhoneStateListener;
63import android.util.Log;
64import android.util.Slog;
65import android.util.SparseBooleanArray;
66
67import com.android.internal.app.IBatteryStats;
68import com.android.internal.net.NetworkStatsFactory;
69import com.android.internal.util.Preconditions;
70import com.android.server.NativeDaemonConnector.Command;
71import com.android.server.NativeDaemonConnector.SensitiveArg;
72import com.android.server.net.LockdownVpnTracker;
73import com.google.android.collect.Maps;
74
75import java.io.BufferedReader;
76import java.io.DataInputStream;
77import java.io.File;
78import java.io.FileDescriptor;
79import java.io.FileInputStream;
80import java.io.IOException;
81import java.io.InputStreamReader;
82import java.io.PrintWriter;
83import java.net.Inet4Address;
84import java.net.InetAddress;
85import java.net.InterfaceAddress;
86import java.net.NetworkInterface;
87import java.net.SocketException;
88import java.util.ArrayList;
89import java.util.HashMap;
90import java.util.List;
91import java.util.Map;
92import java.util.NoSuchElementException;
93import java.util.StringTokenizer;
94import java.util.concurrent.CountDownLatch;
95
96/**
97 * @hide
98 */
99public class NetworkManagementService extends INetworkManagementService.Stub
100        implements Watchdog.Monitor {
101    private static final String TAG = "NetworkManagementService";
102    private static final boolean DBG = false;
103    private static final String NETD_TAG = "NetdConnector";
104    private static final String NETD_SOCKET_NAME = "netd";
105
106    private static final String ADD = "add";
107    private static final String REMOVE = "remove";
108
109    private static final String ALLOW = "allow";
110    private static final String DENY = "deny";
111
112    private static final String DEFAULT = "default";
113    private static final String SECONDARY = "secondary";
114
115    /**
116     * Name representing {@link #setGlobalAlert(long)} limit when delivered to
117     * {@link INetworkManagementEventObserver#limitReached(String, String)}.
118     */
119    public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
120
121    class NetdResponseCode {
122        /* Keep in sync with system/netd/ResponseCode.h */
123        public static final int InterfaceListResult       = 110;
124        public static final int TetherInterfaceListResult = 111;
125        public static final int TetherDnsFwdTgtListResult = 112;
126        public static final int TtyListResult             = 113;
127        public static final int TetheringStatsListResult  = 114;
128
129        public static final int TetherStatusResult        = 210;
130        public static final int IpFwdStatusResult         = 211;
131        public static final int InterfaceGetCfgResult     = 213;
132        public static final int SoftapStatusResult        = 214;
133        public static final int InterfaceRxCounterResult  = 216;
134        public static final int InterfaceTxCounterResult  = 217;
135        public static final int QuotaCounterResult        = 220;
136        public static final int TetheringStatsResult      = 221;
137        public static final int DnsProxyQueryResult       = 222;
138        public static final int ClatdStatusResult         = 223;
139        public static final int GetMarkResult             = 225;
140
141        public static final int InterfaceChange           = 600;
142        public static final int BandwidthControl          = 601;
143        public static final int InterfaceClassActivity    = 613;
144        public static final int InterfaceAddressChange    = 614;
145        public static final int InterfaceDnsServerInfo    = 615;
146    }
147
148    static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
149
150    /**
151     * Binder context for this service
152     */
153    private final Context mContext;
154
155    /**
156     * connector object for communicating with netd
157     */
158    private final NativeDaemonConnector mConnector;
159
160    private final Handler mFgHandler;
161    private final Handler mDaemonHandler;
162    private final PhoneStateListener mPhoneStateListener;
163
164    private IBatteryStats mBatteryStats;
165
166    private final Thread mThread;
167    private CountDownLatch mConnectedSignal = new CountDownLatch(1);
168
169    private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
170            new RemoteCallbackList<INetworkManagementEventObserver>();
171
172    private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
173
174    private Object mQuotaLock = new Object();
175    /** Set of interfaces with active quotas. */
176    private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
177    /** Set of interfaces with active alerts. */
178    private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
179    /** Set of UIDs with active reject rules. */
180    private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
181
182    private Object mIdleTimerLock = new Object();
183    /** Set of interfaces with active idle timers. */
184    private static class IdleTimerParams {
185        public final int timeout;
186        public final int type;
187        public int networkCount;
188
189        IdleTimerParams(int timeout, int type) {
190            this.timeout = timeout;
191            this.type = type;
192            this.networkCount = 1;
193        }
194    }
195    private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
196
197    private volatile boolean mBandwidthControlEnabled;
198    private volatile boolean mFirewallEnabled;
199
200    private boolean mMobileActivityFromRadio = false;
201    private int mLastPowerStateFromRadio = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
202
203    private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
204            new RemoteCallbackList<INetworkActivityListener>();
205    private boolean mNetworkActive;
206
207    /**
208     * Constructs a new NetworkManagementService instance
209     *
210     * @param context  Binder context for this service
211     */
212    private NetworkManagementService(Context context, String socket) {
213        mContext = context;
214
215        // make sure this is on the same looper as our NativeDaemonConnector for sync purposes
216        mFgHandler = new Handler(FgThread.get().getLooper());
217
218        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
219            mConnector = null;
220            mThread = null;
221            mDaemonHandler = null;
222            mPhoneStateListener = null;
223            return;
224        }
225
226        // Don't need this wake lock, since we now have a time stamp for when
227        // the network actually went inactive.  (It might be nice to still do this,
228        // but I don't want to do it through the power manager because that pollutes the
229        // battery stats history with pointless noise.)
230        //PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
231        PowerManager.WakeLock wl = null; //pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG);
232
233        mConnector = new NativeDaemonConnector(
234                new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl,
235                FgThread.get().getLooper());
236        mThread = new Thread(mConnector, NETD_TAG);
237
238        mDaemonHandler = new Handler(FgThread.get().getLooper());
239        mPhoneStateListener = new PhoneStateListener(mDaemonHandler.getLooper()) {
240            public void onDataConnectionRealTimeInfoChanged(
241                    DataConnectionRealTimeInfo dcRtInfo) {
242                // Disabled for now, until we are getting good data.
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(String interfaceName, RouteInfo route) {
871        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
872        modifyRoute(interfaceName, ADD, route, DEFAULT);
873    }
874
875    @Override
876    public void removeRoute(String interfaceName, RouteInfo route) {
877        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
878        modifyRoute(interfaceName, REMOVE, route, DEFAULT);
879    }
880
881    @Override
882    public void addSecondaryRoute(String interfaceName, RouteInfo route) {
883        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
884        modifyRoute(interfaceName, ADD, route, SECONDARY);
885    }
886
887    @Override
888    public void removeSecondaryRoute(String interfaceName, RouteInfo route) {
889        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
890        modifyRoute(interfaceName, REMOVE, route, SECONDARY);
891    }
892
893    private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) {
894        final Command cmd = new Command("interface", "route", action, interfaceName, type);
895
896        // create triplet: dest-ip-addr prefixlength gateway-ip-addr
897        final LinkAddress la = route.getDestination();
898        cmd.appendArg(la.getAddress().getHostAddress());
899        cmd.appendArg(la.getNetworkPrefixLength());
900        cmd.appendArg(route.getGateway().getHostAddress());
901
902        try {
903            mConnector.execute(cmd);
904        } catch (NativeDaemonConnectorException e) {
905            throw e.rethrowAsParcelableException();
906        }
907    }
908
909    private ArrayList<String> readRouteList(String filename) {
910        FileInputStream fstream = null;
911        ArrayList<String> list = new ArrayList<String>();
912
913        try {
914            fstream = new FileInputStream(filename);
915            DataInputStream in = new DataInputStream(fstream);
916            BufferedReader br = new BufferedReader(new InputStreamReader(in));
917            String s;
918
919            // throw away the title line
920
921            while (((s = br.readLine()) != null) && (s.length() != 0)) {
922                list.add(s);
923            }
924        } catch (IOException ex) {
925            // return current list, possibly empty
926        } finally {
927            if (fstream != null) {
928                try {
929                    fstream.close();
930                } catch (IOException ex) {}
931            }
932        }
933
934        return list;
935    }
936
937    @Override
938    public RouteInfo[] getRoutes(String interfaceName) {
939        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
940        ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
941
942        // v4 routes listed as:
943        // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
944        for (String s : readRouteList("/proc/net/route")) {
945            String[] fields = s.split("\t");
946
947            if (fields.length > 7) {
948                String iface = fields[0];
949
950                if (interfaceName.equals(iface)) {
951                    String dest = fields[1];
952                    String gate = fields[2];
953                    String flags = fields[3]; // future use?
954                    String mask = fields[7];
955                    try {
956                        // address stored as a hex string, ex: 0014A8C0
957                        InetAddress destAddr =
958                                NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
959                        int prefixLength =
960                                NetworkUtils.netmaskIntToPrefixLength(
961                                (int)Long.parseLong(mask, 16));
962                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
963
964                        // address stored as a hex string, ex 0014A8C0
965                        InetAddress gatewayAddr =
966                                NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
967
968                        RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
969                        routes.add(route);
970                    } catch (Exception e) {
971                        Log.e(TAG, "Error parsing route " + s + " : " + e);
972                        continue;
973                    }
974                }
975            }
976        }
977
978        // v6 routes listed as:
979        // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
980        for (String s : readRouteList("/proc/net/ipv6_route")) {
981            String[]fields = s.split("\\s+");
982            if (fields.length > 9) {
983                String iface = fields[9].trim();
984                if (interfaceName.equals(iface)) {
985                    String dest = fields[0];
986                    String prefix = fields[1];
987                    String gate = fields[4];
988
989                    try {
990                        // prefix length stored as a hex string, ex 40
991                        int prefixLength = Integer.parseInt(prefix, 16);
992
993                        // address stored as a 32 char hex string
994                        // ex fe800000000000000000000000000000
995                        InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
996                        LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
997
998                        InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
999
1000                        RouteInfo route = new RouteInfo(linkAddress, gateAddr);
1001                        routes.add(route);
1002                    } catch (Exception e) {
1003                        Log.e(TAG, "Error parsing route " + s + " : " + e);
1004                        continue;
1005                    }
1006                }
1007            }
1008        }
1009        return routes.toArray(new RouteInfo[routes.size()]);
1010    }
1011
1012    @Override
1013    public void setMtu(String iface, int mtu) {
1014        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1015
1016        final NativeDaemonEvent event;
1017        try {
1018            event = mConnector.execute("interface", "setmtu", iface, mtu);
1019        } catch (NativeDaemonConnectorException e) {
1020            throw e.rethrowAsParcelableException();
1021        }
1022    }
1023
1024    @Override
1025    public void shutdown() {
1026        // TODO: remove from aidl if nobody calls externally
1027        mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
1028
1029        Slog.d(TAG, "Shutting down");
1030    }
1031
1032    @Override
1033    public boolean getIpForwardingEnabled() throws IllegalStateException{
1034        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1035
1036        final NativeDaemonEvent event;
1037        try {
1038            event = mConnector.execute("ipfwd", "status");
1039        } catch (NativeDaemonConnectorException e) {
1040            throw e.rethrowAsParcelableException();
1041        }
1042
1043        // 211 Forwarding enabled
1044        event.checkCode(IpFwdStatusResult);
1045        return event.getMessage().endsWith("enabled");
1046    }
1047
1048    @Override
1049    public void setIpForwardingEnabled(boolean enable) {
1050        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1051        try {
1052            mConnector.execute("ipfwd", enable ? "enable" : "disable");
1053        } catch (NativeDaemonConnectorException e) {
1054            throw e.rethrowAsParcelableException();
1055        }
1056    }
1057
1058    @Override
1059    public void startTethering(String[] dhcpRange) {
1060        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1061        // cmd is "tether start first_start first_stop second_start second_stop ..."
1062        // an odd number of addrs will fail
1063
1064        final Command cmd = new Command("tether", "start");
1065        for (String d : dhcpRange) {
1066            cmd.appendArg(d);
1067        }
1068
1069        try {
1070            mConnector.execute(cmd);
1071        } catch (NativeDaemonConnectorException e) {
1072            throw e.rethrowAsParcelableException();
1073        }
1074    }
1075
1076    @Override
1077    public void stopTethering() {
1078        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1079        try {
1080            mConnector.execute("tether", "stop");
1081        } catch (NativeDaemonConnectorException e) {
1082            throw e.rethrowAsParcelableException();
1083        }
1084    }
1085
1086    @Override
1087    public boolean isTetheringStarted() {
1088        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1089
1090        final NativeDaemonEvent event;
1091        try {
1092            event = mConnector.execute("tether", "status");
1093        } catch (NativeDaemonConnectorException e) {
1094            throw e.rethrowAsParcelableException();
1095        }
1096
1097        // 210 Tethering services started
1098        event.checkCode(TetherStatusResult);
1099        return event.getMessage().endsWith("started");
1100    }
1101
1102    @Override
1103    public void tetherInterface(String iface) {
1104        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1105        try {
1106            mConnector.execute("tether", "interface", "add", iface);
1107        } catch (NativeDaemonConnectorException e) {
1108            throw e.rethrowAsParcelableException();
1109        }
1110    }
1111
1112    @Override
1113    public void untetherInterface(String iface) {
1114        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1115        try {
1116            mConnector.execute("tether", "interface", "remove", iface);
1117        } catch (NativeDaemonConnectorException e) {
1118            throw e.rethrowAsParcelableException();
1119        }
1120    }
1121
1122    @Override
1123    public String[] listTetheredInterfaces() {
1124        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1125        try {
1126            return NativeDaemonEvent.filterMessageList(
1127                    mConnector.executeForList("tether", "interface", "list"),
1128                    TetherInterfaceListResult);
1129        } catch (NativeDaemonConnectorException e) {
1130            throw e.rethrowAsParcelableException();
1131        }
1132    }
1133
1134    @Override
1135    public void setDnsForwarders(String[] dns) {
1136        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1137
1138        final Command cmd = new Command("tether", "dns", "set");
1139        for (String s : dns) {
1140            cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
1141        }
1142
1143        try {
1144            mConnector.execute(cmd);
1145        } catch (NativeDaemonConnectorException e) {
1146            throw e.rethrowAsParcelableException();
1147        }
1148    }
1149
1150    @Override
1151    public String[] getDnsForwarders() {
1152        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1153        try {
1154            return NativeDaemonEvent.filterMessageList(
1155                    mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
1156        } catch (NativeDaemonConnectorException e) {
1157            throw e.rethrowAsParcelableException();
1158        }
1159    }
1160
1161    private List<InterfaceAddress> excludeLinkLocal(List<InterfaceAddress> addresses) {
1162        ArrayList<InterfaceAddress> filtered = new ArrayList<InterfaceAddress>(addresses.size());
1163        for (InterfaceAddress ia : addresses) {
1164            if (!ia.getAddress().isLinkLocalAddress())
1165                filtered.add(ia);
1166        }
1167        return filtered;
1168    }
1169
1170    private void modifyNat(String action, String internalInterface, String externalInterface)
1171            throws SocketException {
1172        final Command cmd = new Command("nat", action, internalInterface, externalInterface);
1173
1174        final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
1175                internalInterface);
1176        if (internalNetworkInterface == null) {
1177            cmd.appendArg("0");
1178        } else {
1179            // Don't touch link-local routes, as link-local addresses aren't routable,
1180            // kernel creates link-local routes on all interfaces automatically
1181            List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
1182                    internalNetworkInterface.getInterfaceAddresses());
1183            cmd.appendArg(interfaceAddresses.size());
1184            for (InterfaceAddress ia : interfaceAddresses) {
1185                InetAddress addr = NetworkUtils.getNetworkPart(
1186                        ia.getAddress(), ia.getNetworkPrefixLength());
1187                cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
1188            }
1189        }
1190
1191        try {
1192            mConnector.execute(cmd);
1193        } catch (NativeDaemonConnectorException e) {
1194            throw e.rethrowAsParcelableException();
1195        }
1196    }
1197
1198    @Override
1199    public void enableNat(String internalInterface, String externalInterface) {
1200        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1201        try {
1202            modifyNat("enable", internalInterface, externalInterface);
1203        } catch (SocketException e) {
1204            throw new IllegalStateException(e);
1205        }
1206    }
1207
1208    @Override
1209    public void disableNat(String internalInterface, String externalInterface) {
1210        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1211        try {
1212            modifyNat("disable", internalInterface, externalInterface);
1213        } catch (SocketException e) {
1214            throw new IllegalStateException(e);
1215        }
1216    }
1217
1218    @Override
1219    public String[] listTtys() {
1220        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1221        try {
1222            return NativeDaemonEvent.filterMessageList(
1223                    mConnector.executeForList("list_ttys"), TtyListResult);
1224        } catch (NativeDaemonConnectorException e) {
1225            throw e.rethrowAsParcelableException();
1226        }
1227    }
1228
1229    @Override
1230    public void attachPppd(
1231            String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
1232        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1233        try {
1234            mConnector.execute("pppd", "attach", tty,
1235                    NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
1236                    NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
1237                    NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
1238                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
1239        } catch (NativeDaemonConnectorException e) {
1240            throw e.rethrowAsParcelableException();
1241        }
1242    }
1243
1244    @Override
1245    public void detachPppd(String tty) {
1246        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1247        try {
1248            mConnector.execute("pppd", "detach", tty);
1249        } catch (NativeDaemonConnectorException e) {
1250            throw e.rethrowAsParcelableException();
1251        }
1252    }
1253
1254    @Override
1255    public void startAccessPoint(
1256            WifiConfiguration wifiConfig, String wlanIface) {
1257        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1258        try {
1259            wifiFirmwareReload(wlanIface, "AP");
1260            if (wifiConfig == null) {
1261                mConnector.execute("softap", "set", wlanIface);
1262            } else {
1263                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1264                                   "broadcast", "6", getSecurityType(wifiConfig),
1265                                   new SensitiveArg(wifiConfig.preSharedKey));
1266            }
1267            mConnector.execute("softap", "startap");
1268        } catch (NativeDaemonConnectorException e) {
1269            throw e.rethrowAsParcelableException();
1270        }
1271    }
1272
1273    private static String getSecurityType(WifiConfiguration wifiConfig) {
1274        switch (wifiConfig.getAuthType()) {
1275            case KeyMgmt.WPA_PSK:
1276                return "wpa-psk";
1277            case KeyMgmt.WPA2_PSK:
1278                return "wpa2-psk";
1279            default:
1280                return "open";
1281        }
1282    }
1283
1284    /* @param mode can be "AP", "STA" or "P2P" */
1285    @Override
1286    public void wifiFirmwareReload(String wlanIface, String mode) {
1287        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1288        try {
1289            mConnector.execute("softap", "fwreload", wlanIface, mode);
1290        } catch (NativeDaemonConnectorException e) {
1291            throw e.rethrowAsParcelableException();
1292        }
1293    }
1294
1295    @Override
1296    public void stopAccessPoint(String wlanIface) {
1297        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1298        try {
1299            mConnector.execute("softap", "stopap");
1300            wifiFirmwareReload(wlanIface, "STA");
1301        } catch (NativeDaemonConnectorException e) {
1302            throw e.rethrowAsParcelableException();
1303        }
1304    }
1305
1306    @Override
1307    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
1308        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1309        try {
1310            if (wifiConfig == null) {
1311                mConnector.execute("softap", "set", wlanIface);
1312            } else {
1313                mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
1314                                   "broadcast", "6", getSecurityType(wifiConfig),
1315                                   new SensitiveArg(wifiConfig.preSharedKey));
1316            }
1317        } catch (NativeDaemonConnectorException e) {
1318            throw e.rethrowAsParcelableException();
1319        }
1320    }
1321
1322    @Override
1323    public void addIdleTimer(String iface, int timeout, final int type) {
1324        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1325
1326        if (DBG) Slog.d(TAG, "Adding idletimer");
1327
1328        synchronized (mIdleTimerLock) {
1329            IdleTimerParams params = mActiveIdleTimers.get(iface);
1330            if (params != null) {
1331                // the interface already has idletimer, update network count
1332                params.networkCount++;
1333                return;
1334            }
1335
1336            try {
1337                mConnector.execute("idletimer", "add", iface, Integer.toString(timeout),
1338                        Integer.toString(type));
1339            } catch (NativeDaemonConnectorException e) {
1340                throw e.rethrowAsParcelableException();
1341            }
1342            mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type));
1343
1344            // Networks start up.
1345            if (ConnectivityManager.isNetworkTypeMobile(type)) {
1346                mNetworkActive = false;
1347            }
1348            mDaemonHandler.post(new Runnable() {
1349                @Override public void run() {
1350                    notifyInterfaceClassActivity(type,
1351                            DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
1352                            SystemClock.elapsedRealtimeNanos(), false);
1353                }
1354            });
1355        }
1356    }
1357
1358    @Override
1359    public void removeIdleTimer(String iface) {
1360        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1361
1362        if (DBG) Slog.d(TAG, "Removing idletimer");
1363
1364        synchronized (mIdleTimerLock) {
1365            final IdleTimerParams params = mActiveIdleTimers.get(iface);
1366            if (params == null || --(params.networkCount) > 0) {
1367                return;
1368            }
1369
1370            try {
1371                mConnector.execute("idletimer", "remove", iface,
1372                        Integer.toString(params.timeout), Integer.toString(params.type));
1373            } catch (NativeDaemonConnectorException e) {
1374                throw e.rethrowAsParcelableException();
1375            }
1376            mActiveIdleTimers.remove(iface);
1377            mDaemonHandler.post(new Runnable() {
1378                @Override public void run() {
1379                    notifyInterfaceClassActivity(params.type,
1380                            DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
1381                            SystemClock.elapsedRealtimeNanos(), false);
1382                }
1383            });
1384        }
1385    }
1386
1387    @Override
1388    public NetworkStats getNetworkStatsSummaryDev() {
1389        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1390        try {
1391            return mStatsFactory.readNetworkStatsSummaryDev();
1392        } catch (IOException e) {
1393            throw new IllegalStateException(e);
1394        }
1395    }
1396
1397    @Override
1398    public NetworkStats getNetworkStatsSummaryXt() {
1399        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1400        try {
1401            return mStatsFactory.readNetworkStatsSummaryXt();
1402        } catch (IOException e) {
1403            throw new IllegalStateException(e);
1404        }
1405    }
1406
1407    @Override
1408    public NetworkStats getNetworkStatsDetail() {
1409        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1410        try {
1411            return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null);
1412        } catch (IOException e) {
1413            throw new IllegalStateException(e);
1414        }
1415    }
1416
1417    @Override
1418    public void setInterfaceQuota(String iface, long quotaBytes) {
1419        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1420
1421        // silently discard when control disabled
1422        // TODO: eventually migrate to be always enabled
1423        if (!mBandwidthControlEnabled) return;
1424
1425        synchronized (mQuotaLock) {
1426            if (mActiveQuotas.containsKey(iface)) {
1427                throw new IllegalStateException("iface " + iface + " already has quota");
1428            }
1429
1430            try {
1431                // TODO: support quota shared across interfaces
1432                mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
1433                mActiveQuotas.put(iface, quotaBytes);
1434            } catch (NativeDaemonConnectorException e) {
1435                throw e.rethrowAsParcelableException();
1436            }
1437        }
1438    }
1439
1440    @Override
1441    public void removeInterfaceQuota(String iface) {
1442        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1443
1444        // silently discard when control disabled
1445        // TODO: eventually migrate to be always enabled
1446        if (!mBandwidthControlEnabled) return;
1447
1448        synchronized (mQuotaLock) {
1449            if (!mActiveQuotas.containsKey(iface)) {
1450                // TODO: eventually consider throwing
1451                return;
1452            }
1453
1454            mActiveQuotas.remove(iface);
1455            mActiveAlerts.remove(iface);
1456
1457            try {
1458                // TODO: support quota shared across interfaces
1459                mConnector.execute("bandwidth", "removeiquota", iface);
1460            } catch (NativeDaemonConnectorException e) {
1461                throw e.rethrowAsParcelableException();
1462            }
1463        }
1464    }
1465
1466    @Override
1467    public void setInterfaceAlert(String iface, long alertBytes) {
1468        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1469
1470        // silently discard when control disabled
1471        // TODO: eventually migrate to be always enabled
1472        if (!mBandwidthControlEnabled) return;
1473
1474        // quick sanity check
1475        if (!mActiveQuotas.containsKey(iface)) {
1476            throw new IllegalStateException("setting alert requires existing quota on iface");
1477        }
1478
1479        synchronized (mQuotaLock) {
1480            if (mActiveAlerts.containsKey(iface)) {
1481                throw new IllegalStateException("iface " + iface + " already has alert");
1482            }
1483
1484            try {
1485                // TODO: support alert shared across interfaces
1486                mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
1487                mActiveAlerts.put(iface, alertBytes);
1488            } catch (NativeDaemonConnectorException e) {
1489                throw e.rethrowAsParcelableException();
1490            }
1491        }
1492    }
1493
1494    @Override
1495    public void removeInterfaceAlert(String iface) {
1496        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1497
1498        // silently discard when control disabled
1499        // TODO: eventually migrate to be always enabled
1500        if (!mBandwidthControlEnabled) return;
1501
1502        synchronized (mQuotaLock) {
1503            if (!mActiveAlerts.containsKey(iface)) {
1504                // TODO: eventually consider throwing
1505                return;
1506            }
1507
1508            try {
1509                // TODO: support alert shared across interfaces
1510                mConnector.execute("bandwidth", "removeinterfacealert", iface);
1511                mActiveAlerts.remove(iface);
1512            } catch (NativeDaemonConnectorException e) {
1513                throw e.rethrowAsParcelableException();
1514            }
1515        }
1516    }
1517
1518    @Override
1519    public void setGlobalAlert(long alertBytes) {
1520        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1521
1522        // silently discard when control disabled
1523        // TODO: eventually migrate to be always enabled
1524        if (!mBandwidthControlEnabled) return;
1525
1526        try {
1527            mConnector.execute("bandwidth", "setglobalalert", alertBytes);
1528        } catch (NativeDaemonConnectorException e) {
1529            throw e.rethrowAsParcelableException();
1530        }
1531    }
1532
1533    @Override
1534    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
1535        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1536
1537        // silently discard when control disabled
1538        // TODO: eventually migrate to be always enabled
1539        if (!mBandwidthControlEnabled) return;
1540
1541        synchronized (mQuotaLock) {
1542            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
1543            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
1544                // TODO: eventually consider throwing
1545                return;
1546            }
1547
1548            try {
1549                mConnector.execute("bandwidth",
1550                        rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
1551                if (rejectOnQuotaInterfaces) {
1552                    mUidRejectOnQuota.put(uid, true);
1553                } else {
1554                    mUidRejectOnQuota.delete(uid);
1555                }
1556            } catch (NativeDaemonConnectorException e) {
1557                throw e.rethrowAsParcelableException();
1558            }
1559        }
1560    }
1561
1562    @Override
1563    public boolean isBandwidthControlEnabled() {
1564        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1565        return mBandwidthControlEnabled;
1566    }
1567
1568    @Override
1569    public NetworkStats getNetworkStatsUidDetail(int uid) {
1570        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1571        try {
1572            return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null);
1573        } catch (IOException e) {
1574            throw new IllegalStateException(e);
1575        }
1576    }
1577
1578    @Override
1579    public NetworkStats getNetworkStatsTethering() {
1580        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1581
1582        final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
1583        try {
1584            final NativeDaemonEvent[] events = mConnector.executeForList(
1585                    "bandwidth", "gettetherstats");
1586            for (NativeDaemonEvent event : events) {
1587                if (event.getCode() != TetheringStatsListResult) continue;
1588
1589                // 114 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
1590                final StringTokenizer tok = new StringTokenizer(event.getMessage());
1591                try {
1592                    final String ifaceIn = tok.nextToken();
1593                    final String ifaceOut = tok.nextToken();
1594
1595                    final NetworkStats.Entry entry = new NetworkStats.Entry();
1596                    entry.iface = ifaceOut;
1597                    entry.uid = UID_TETHERING;
1598                    entry.set = SET_DEFAULT;
1599                    entry.tag = TAG_NONE;
1600                    entry.rxBytes = Long.parseLong(tok.nextToken());
1601                    entry.rxPackets = Long.parseLong(tok.nextToken());
1602                    entry.txBytes = Long.parseLong(tok.nextToken());
1603                    entry.txPackets = Long.parseLong(tok.nextToken());
1604                    stats.combineValues(entry);
1605                } catch (NoSuchElementException e) {
1606                    throw new IllegalStateException("problem parsing tethering stats: " + event);
1607                } catch (NumberFormatException e) {
1608                    throw new IllegalStateException("problem parsing tethering stats: " + event);
1609                }
1610            }
1611        } catch (NativeDaemonConnectorException e) {
1612            throw e.rethrowAsParcelableException();
1613        }
1614        return stats;
1615    }
1616
1617    @Override
1618    public void setDefaultInterfaceForDns(String iface) {
1619        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1620        try {
1621            mConnector.execute("resolver", "setdefaultif", iface);
1622        } catch (NativeDaemonConnectorException e) {
1623            throw e.rethrowAsParcelableException();
1624        }
1625    }
1626
1627    @Override
1628    public void setDnsServersForInterface(String iface, String[] servers, String domains) {
1629        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1630
1631        final Command cmd = new Command("resolver", "setifdns", iface,
1632                (domains == null ? "" : domains));
1633
1634        for (String s : servers) {
1635            InetAddress a = NetworkUtils.numericToInetAddress(s);
1636            if (a.isAnyLocalAddress() == false) {
1637                cmd.appendArg(a.getHostAddress());
1638            }
1639        }
1640
1641        try {
1642            mConnector.execute(cmd);
1643        } catch (NativeDaemonConnectorException e) {
1644            throw e.rethrowAsParcelableException();
1645        }
1646    }
1647
1648    @Override
1649    public void setUidRangeRoute(String iface, int uid_start, int uid_end) {
1650        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1651        try {
1652            mConnector.execute("interface", "fwmark",
1653                    "uid", "add", iface, uid_start, uid_end);
1654        } catch (NativeDaemonConnectorException e) {
1655            throw e.rethrowAsParcelableException();
1656        }
1657    }
1658
1659    @Override
1660    public void clearUidRangeRoute(String iface, int uid_start, int uid_end) {
1661        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1662        try {
1663            mConnector.execute("interface", "fwmark",
1664                    "uid", "remove", iface, uid_start, uid_end);
1665        } catch (NativeDaemonConnectorException e) {
1666            throw e.rethrowAsParcelableException();
1667        }
1668    }
1669
1670    @Override
1671    public void setMarkedForwarding(String iface) {
1672        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1673        try {
1674            mConnector.execute("interface", "fwmark", "rule", "add", iface);
1675        } catch (NativeDaemonConnectorException e) {
1676            throw e.rethrowAsParcelableException();
1677        }
1678    }
1679
1680    @Override
1681    public void clearMarkedForwarding(String iface) {
1682        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1683        try {
1684            mConnector.execute("interface", "fwmark", "rule", "remove", iface);
1685        } catch (NativeDaemonConnectorException e) {
1686            throw e.rethrowAsParcelableException();
1687        }
1688    }
1689
1690    @Override
1691    public int getMarkForUid(int uid) {
1692        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1693        final NativeDaemonEvent event;
1694        try {
1695            event = mConnector.execute("interface", "fwmark", "get", "mark", uid);
1696        } catch (NativeDaemonConnectorException e) {
1697            throw e.rethrowAsParcelableException();
1698        }
1699        event.checkCode(GetMarkResult);
1700        return Integer.parseInt(event.getMessage());
1701    }
1702
1703    @Override
1704    public int getMarkForProtect() {
1705        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1706        final NativeDaemonEvent event;
1707        try {
1708            event = mConnector.execute("interface", "fwmark", "get", "protect");
1709        } catch (NativeDaemonConnectorException e) {
1710            throw e.rethrowAsParcelableException();
1711        }
1712        event.checkCode(GetMarkResult);
1713        return Integer.parseInt(event.getMessage());
1714    }
1715
1716    @Override
1717    public void setMarkedForwardingRoute(String iface, RouteInfo route) {
1718        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1719        try {
1720            LinkAddress dest = route.getDestination();
1721            mConnector.execute("interface", "fwmark", "route", "add", iface,
1722                    dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
1723        } catch (NativeDaemonConnectorException e) {
1724            throw e.rethrowAsParcelableException();
1725        }
1726    }
1727
1728    @Override
1729    public void clearMarkedForwardingRoute(String iface, RouteInfo route) {
1730        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1731        try {
1732            LinkAddress dest = route.getDestination();
1733            mConnector.execute("interface", "fwmark", "route", "remove", iface,
1734                    dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
1735        } catch (NativeDaemonConnectorException e) {
1736            throw e.rethrowAsParcelableException();
1737        }
1738    }
1739
1740    @Override
1741    public void setHostExemption(LinkAddress host) {
1742        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1743        try {
1744            mConnector.execute("interface", "fwmark", "exempt", "add", host);
1745        } catch (NativeDaemonConnectorException e) {
1746            throw e.rethrowAsParcelableException();
1747        }
1748    }
1749
1750    @Override
1751    public void clearHostExemption(LinkAddress host) {
1752        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1753        try {
1754            mConnector.execute("interface", "fwmark", "exempt", "remove", host);
1755        } catch (NativeDaemonConnectorException e) {
1756            throw e.rethrowAsParcelableException();
1757        }
1758    }
1759
1760    @Override
1761    public void setDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) {
1762        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1763        try {
1764            mConnector.execute("resolver", "setifaceforuidrange", iface, uid_start, uid_end);
1765        } catch (NativeDaemonConnectorException e) {
1766            throw e.rethrowAsParcelableException();
1767        }
1768    }
1769
1770    @Override
1771    public void clearDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) {
1772        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1773        try {
1774            mConnector.execute("resolver", "clearifaceforuidrange", iface, uid_start, uid_end);
1775        } catch (NativeDaemonConnectorException e) {
1776            throw e.rethrowAsParcelableException();
1777        }
1778    }
1779
1780    @Override
1781    public void clearDnsInterfaceMaps() {
1782        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1783        try {
1784            mConnector.execute("resolver", "clearifacemapping");
1785        } catch (NativeDaemonConnectorException e) {
1786            throw e.rethrowAsParcelableException();
1787        }
1788    }
1789
1790
1791    @Override
1792    public void flushDefaultDnsCache() {
1793        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1794        try {
1795            mConnector.execute("resolver", "flushdefaultif");
1796        } catch (NativeDaemonConnectorException e) {
1797            throw e.rethrowAsParcelableException();
1798        }
1799    }
1800
1801    @Override
1802    public void flushInterfaceDnsCache(String iface) {
1803        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1804        try {
1805            mConnector.execute("resolver", "flushif", iface);
1806        } catch (NativeDaemonConnectorException e) {
1807            throw e.rethrowAsParcelableException();
1808        }
1809    }
1810
1811    @Override
1812    public void setFirewallEnabled(boolean enabled) {
1813        enforceSystemUid();
1814        try {
1815            mConnector.execute("firewall", enabled ? "enable" : "disable");
1816            mFirewallEnabled = enabled;
1817        } catch (NativeDaemonConnectorException e) {
1818            throw e.rethrowAsParcelableException();
1819        }
1820    }
1821
1822    @Override
1823    public boolean isFirewallEnabled() {
1824        enforceSystemUid();
1825        return mFirewallEnabled;
1826    }
1827
1828    @Override
1829    public void setFirewallInterfaceRule(String iface, boolean allow) {
1830        enforceSystemUid();
1831        Preconditions.checkState(mFirewallEnabled);
1832        final String rule = allow ? ALLOW : DENY;
1833        try {
1834            mConnector.execute("firewall", "set_interface_rule", iface, rule);
1835        } catch (NativeDaemonConnectorException e) {
1836            throw e.rethrowAsParcelableException();
1837        }
1838    }
1839
1840    @Override
1841    public void setFirewallEgressSourceRule(String addr, boolean allow) {
1842        enforceSystemUid();
1843        Preconditions.checkState(mFirewallEnabled);
1844        final String rule = allow ? ALLOW : DENY;
1845        try {
1846            mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
1847        } catch (NativeDaemonConnectorException e) {
1848            throw e.rethrowAsParcelableException();
1849        }
1850    }
1851
1852    @Override
1853    public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
1854        enforceSystemUid();
1855        Preconditions.checkState(mFirewallEnabled);
1856        final String rule = allow ? ALLOW : DENY;
1857        try {
1858            mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
1859        } catch (NativeDaemonConnectorException e) {
1860            throw e.rethrowAsParcelableException();
1861        }
1862    }
1863
1864    @Override
1865    public void setFirewallUidRule(int uid, boolean allow) {
1866        enforceSystemUid();
1867        Preconditions.checkState(mFirewallEnabled);
1868        final String rule = allow ? ALLOW : DENY;
1869        try {
1870            mConnector.execute("firewall", "set_uid_rule", uid, rule);
1871        } catch (NativeDaemonConnectorException e) {
1872            throw e.rethrowAsParcelableException();
1873        }
1874    }
1875
1876    private static void enforceSystemUid() {
1877        final int uid = Binder.getCallingUid();
1878        if (uid != Process.SYSTEM_UID) {
1879            throw new SecurityException("Only available to AID_SYSTEM");
1880        }
1881    }
1882
1883    @Override
1884    public void setDnsInterfaceForPid(String iface, int pid) throws IllegalStateException {
1885        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1886        try {
1887            mConnector.execute("resolver", "setifaceforpid", iface, pid);
1888        } catch (NativeDaemonConnectorException e) {
1889            throw new IllegalStateException(
1890                    "Error communicating with native deamon to set interface for pid" + iface, e);
1891        }
1892    }
1893
1894    @Override
1895    public void clearDnsInterfaceForPid(int pid) throws IllegalStateException {
1896        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1897        try {
1898            mConnector.execute("resolver", "clearifaceforpid", pid);
1899        } catch (NativeDaemonConnectorException e) {
1900            throw new IllegalStateException(
1901                    "Error communicating with native deamon to clear interface for pid " + pid, e);
1902        }
1903    }
1904
1905    @Override
1906    public void startClatd(String interfaceName) throws IllegalStateException {
1907        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1908
1909        try {
1910            mConnector.execute("clatd", "start", interfaceName);
1911        } catch (NativeDaemonConnectorException e) {
1912            throw e.rethrowAsParcelableException();
1913        }
1914    }
1915
1916    @Override
1917    public void stopClatd() throws IllegalStateException {
1918        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1919
1920        try {
1921            mConnector.execute("clatd", "stop");
1922        } catch (NativeDaemonConnectorException e) {
1923            throw e.rethrowAsParcelableException();
1924        }
1925    }
1926
1927    @Override
1928    public boolean isClatdStarted() {
1929        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
1930
1931        final NativeDaemonEvent event;
1932        try {
1933            event = mConnector.execute("clatd", "status");
1934        } catch (NativeDaemonConnectorException e) {
1935            throw e.rethrowAsParcelableException();
1936        }
1937
1938        event.checkCode(ClatdStatusResult);
1939        return event.getMessage().endsWith("started");
1940    }
1941
1942    @Override
1943    public void registerNetworkActivityListener(INetworkActivityListener listener) {
1944        mNetworkActivityListeners.register(listener);
1945    }
1946
1947    @Override
1948    public void unregisterNetworkActivityListener(INetworkActivityListener listener) {
1949        mNetworkActivityListeners.unregister(listener);
1950    }
1951
1952    @Override
1953    public boolean isNetworkActive() {
1954        synchronized (mNetworkActivityListeners) {
1955            return mNetworkActive || mActiveIdleTimers.isEmpty();
1956        }
1957    }
1958
1959    private void reportNetworkActive() {
1960        final int length = mNetworkActivityListeners.beginBroadcast();
1961        try {
1962            for (int i = 0; i < length; i++) {
1963                try {
1964                    mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive();
1965                } catch (RemoteException e) {
1966                } catch (RuntimeException e) {
1967                }
1968            }
1969        } finally {
1970            mNetworkActivityListeners.finishBroadcast();
1971        }
1972    }
1973
1974    /** {@inheritDoc} */
1975    @Override
1976    public void monitor() {
1977        if (mConnector != null) {
1978            mConnector.monitor();
1979        }
1980    }
1981
1982    @Override
1983    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1984        mContext.enforceCallingOrSelfPermission(DUMP, TAG);
1985
1986        pw.println("NetworkManagementService NativeDaemonConnector Log:");
1987        mConnector.dump(fd, pw, args);
1988        pw.println();
1989
1990        pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
1991        pw.print("mMobileActivityFromRadio="); pw.print(mMobileActivityFromRadio);
1992                pw.print(" mLastPowerStateFromRadio="); pw.println(mLastPowerStateFromRadio);
1993        pw.print("mNetworkActive="); pw.println(mNetworkActive);
1994
1995        synchronized (mQuotaLock) {
1996            pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
1997            pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
1998        }
1999
2000        synchronized (mUidRejectOnQuota) {
2001            pw.print("UID reject on quota ifaces: [");
2002            final int size = mUidRejectOnQuota.size();
2003            for (int i = 0; i < size; i++) {
2004                pw.print(mUidRejectOnQuota.keyAt(i));
2005                if (i < size - 1) pw.print(",");
2006            }
2007            pw.println("]");
2008        }
2009
2010        synchronized (mIdleTimerLock) {
2011            pw.println("Idle timers:");
2012            for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) {
2013                pw.print("  "); pw.print(ent.getKey()); pw.println(":");
2014                IdleTimerParams params = ent.getValue();
2015                pw.print("    timeout="); pw.print(params.timeout);
2016                pw.print(" type="); pw.print(params.type);
2017                pw.print(" networkCount="); pw.println(params.networkCount);
2018            }
2019        }
2020
2021        pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
2022    }
2023
2024    @Override
2025    public void createNetwork(int netId, String iface) {
2026        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2027
2028        try {
2029            mConnector.execute("network", "create", netId, iface);
2030        } catch (NativeDaemonConnectorException e) {
2031            throw e.rethrowAsParcelableException();
2032        }
2033    }
2034
2035    @Override
2036    public void removeNetwork(int netId) {
2037        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2038
2039        try {
2040            mConnector.execute("network", "destroy", netId);
2041        } catch (NativeDaemonConnectorException e) {
2042            throw e.rethrowAsParcelableException();
2043        }
2044    }
2045
2046    @Override
2047    public void addDnsServersForNetId(int netId, String[] servers, String domains) {
2048        modifyDnsServersForNetId(netId, servers, domains, ADD);
2049    }
2050
2051    @Override
2052    public void removeDnsServersForNetId(int netId, String[] servers,
2053            String domains) {
2054        modifyDnsServersForNetId(netId, servers, domains, REMOVE);
2055    }
2056
2057    private void modifyDnsServersForNetId(int netId, String[] servers,
2058            String domains, String action) {
2059        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2060
2061        final Command cmd = new Command("network", "dns", action, netId, servers.length);
2062        for(int i=0; i<servers.length; i++) {
2063            cmd.appendArg(servers[i]);
2064        }
2065        cmd.appendArg((domains == null ? "" : domains));
2066
2067        try {
2068            mConnector.execute(cmd);
2069        } catch (NativeDaemonConnectorException e) {
2070            throw e.rethrowAsParcelableException();
2071        }
2072    }
2073
2074    @Override
2075    public void addRouteForNetId(int netId, RouteInfo routeInfo) {
2076        modifyRouteForNetId(netId, routeInfo, ADD);
2077    }
2078
2079    @Override
2080    public void removeRouteForNetId(int netId, RouteInfo routeInfo) {
2081        modifyRouteForNetId(netId, routeInfo, REMOVE);
2082    }
2083
2084    private void modifyRouteForNetId(int netId, RouteInfo routeInfo, String action) {
2085        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2086
2087        final Command cmd = new Command("network", "route", action, netId);
2088
2089        // create quadlet: dest-ip-addr prefixlength gateway-ip-addr iface
2090        final LinkAddress la = routeInfo.getDestination();
2091        cmd.appendArg(la.getAddress().getHostAddress());
2092        cmd.appendArg(la.getNetworkPrefixLength());
2093        cmd.appendArg(routeInfo.getGateway().getHostAddress());
2094        cmd.appendArg(routeInfo.getInterface());
2095
2096        try {
2097            mConnector.execute(cmd);
2098        } catch (NativeDaemonConnectorException e) {
2099            throw e.rethrowAsParcelableException();
2100        }
2101    }
2102
2103    @Override
2104    public void addLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
2105        modifyLegacyRouteForNetId(netId, routeInfo, uid, ADD);
2106    }
2107
2108    @Override
2109    public void removeLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid) {
2110        modifyLegacyRouteForNetId(netId, routeInfo, uid, REMOVE);
2111    }
2112
2113    private void modifyLegacyRouteForNetId(int netId, RouteInfo routeInfo, int uid, String action) {
2114        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2115
2116        final Command cmd = new Command("network", "legacy", uid, "route", action, netId);
2117
2118        // create quadlet: dest-ip-addr prefixlength gateway-ip-addr iface
2119        final LinkAddress la = routeInfo.getDestination();
2120        cmd.appendArg(la.getAddress().getHostAddress());
2121        cmd.appendArg(la.getNetworkPrefixLength());
2122        cmd.appendArg(routeInfo.getGateway().getHostAddress());
2123        cmd.appendArg(routeInfo.getInterface());
2124
2125        try {
2126            mConnector.execute(cmd);
2127        } catch (NativeDaemonConnectorException e) {
2128            throw e.rethrowAsParcelableException();
2129        }
2130    }
2131
2132    @Override
2133    public void setDefaultNetId(int netId, boolean resetOldSockets) {
2134        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2135
2136        try {
2137            mConnector.execute("network", "default", "set", netId, resetOldSockets);
2138        } catch (NativeDaemonConnectorException e) {
2139            throw e.rethrowAsParcelableException();
2140        }
2141    }
2142
2143    @Override
2144    public void clearDefaultNetId() {
2145        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2146
2147        try {
2148            mConnector.execute("network", "default", "clear");
2149        } catch (NativeDaemonConnectorException e) {
2150            throw e.rethrowAsParcelableException();
2151        }
2152    }
2153
2154    @Override
2155    public void setPermission(boolean internal, boolean changeNetState, int[] uids) {
2156        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2157
2158        final Command cmd = new Command("network", "permission", "set");
2159        if (internal) cmd.appendArg("CI");
2160        if (changeNetState) cmd.appendArg("CNS");
2161        for (int i=0; i<uids.length; i++) {
2162            cmd.appendArg(uids[i]);
2163        }
2164
2165        try {
2166            mConnector.execute(cmd);
2167        } catch (NativeDaemonConnectorException e) {
2168            throw e.rethrowAsParcelableException();
2169        }
2170    }
2171
2172    @Override
2173    public void clearPermission(int[] uids) {
2174        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
2175
2176        final Command cmd = new Command("network", "permission", "clear");
2177        for (int i=0; i<uids.length; i++) {
2178            cmd.appendArg(uids[i]);
2179        }
2180
2181        try {
2182            mConnector.execute(cmd);
2183        } catch (NativeDaemonConnectorException e) {
2184            throw e.rethrowAsParcelableException();
2185        }
2186    }
2187}
2188