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