Home | History | Annotate | Download | only in server
      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 
     17 package com.android.server;
     18 
     19 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
     20 import static android.Manifest.permission.DUMP;
     21 import static android.Manifest.permission.SHUTDOWN;
     22 import static android.net.NetworkStats.SET_DEFAULT;
     23 import static android.net.NetworkStats.TAG_NONE;
     24 import static android.net.NetworkStats.UID_ALL;
     25 import static android.net.TrafficStats.UID_TETHERING;
     26 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
     27 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
     28 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceRxThrottleResult;
     29 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceTxThrottleResult;
     30 import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
     31 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
     32 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
     33 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
     34 import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsResult;
     35 import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
     36 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
     37 
     38 import android.bluetooth.BluetoothTetheringDataTracker;
     39 import android.content.Context;
     40 import android.net.INetworkManagementEventObserver;
     41 import android.net.InterfaceConfiguration;
     42 import android.net.LinkAddress;
     43 import android.net.NetworkStats;
     44 import android.net.NetworkUtils;
     45 import android.net.RouteInfo;
     46 import android.net.wifi.WifiConfiguration;
     47 import android.net.wifi.WifiConfiguration.KeyMgmt;
     48 import android.os.Binder;
     49 import android.os.Handler;
     50 import android.os.INetworkManagementService;
     51 import android.os.Process;
     52 import android.os.RemoteCallbackList;
     53 import android.os.RemoteException;
     54 import android.os.SystemClock;
     55 import android.os.SystemProperties;
     56 import android.util.Log;
     57 import android.util.Slog;
     58 import android.util.SparseBooleanArray;
     59 
     60 import com.android.internal.net.NetworkStatsFactory;
     61 import com.android.internal.util.Preconditions;
     62 import com.android.server.NativeDaemonConnector.Command;
     63 import com.android.server.net.LockdownVpnTracker;
     64 import com.google.android.collect.Maps;
     65 
     66 import java.io.BufferedReader;
     67 import java.io.DataInputStream;
     68 import java.io.File;
     69 import java.io.FileDescriptor;
     70 import java.io.FileInputStream;
     71 import java.io.IOException;
     72 import java.io.InputStreamReader;
     73 import java.io.PrintWriter;
     74 import java.net.Inet4Address;
     75 import java.net.InetAddress;
     76 import java.net.InterfaceAddress;
     77 import java.net.NetworkInterface;
     78 import java.net.SocketException;
     79 import java.util.ArrayList;
     80 import java.util.Collection;
     81 import java.util.HashMap;
     82 import java.util.Map;
     83 import java.util.NoSuchElementException;
     84 import java.util.StringTokenizer;
     85 import java.util.concurrent.CountDownLatch;
     86 
     87 /**
     88  * @hide
     89  */
     90 public class NetworkManagementService extends INetworkManagementService.Stub
     91         implements Watchdog.Monitor {
     92     private static final String TAG = "NetworkManagementService";
     93     private static final boolean DBG = false;
     94     private static final String NETD_TAG = "NetdConnector";
     95 
     96     private static final String ADD = "add";
     97     private static final String REMOVE = "remove";
     98 
     99     private static final String ALLOW = "allow";
    100     private static final String DENY = "deny";
    101 
    102     private static final String DEFAULT = "default";
    103     private static final String SECONDARY = "secondary";
    104 
    105     /**
    106      * Name representing {@link #setGlobalAlert(long)} limit when delivered to
    107      * {@link INetworkManagementEventObserver#limitReached(String, String)}.
    108      */
    109     public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
    110 
    111     class NetdResponseCode {
    112         /* Keep in sync with system/netd/ResponseCode.h */
    113         public static final int InterfaceListResult       = 110;
    114         public static final int TetherInterfaceListResult = 111;
    115         public static final int TetherDnsFwdTgtListResult = 112;
    116         public static final int TtyListResult             = 113;
    117 
    118         public static final int TetherStatusResult        = 210;
    119         public static final int IpFwdStatusResult         = 211;
    120         public static final int InterfaceGetCfgResult     = 213;
    121         public static final int SoftapStatusResult        = 214;
    122         public static final int InterfaceRxCounterResult  = 216;
    123         public static final int InterfaceTxCounterResult  = 217;
    124         public static final int InterfaceRxThrottleResult = 218;
    125         public static final int InterfaceTxThrottleResult = 219;
    126         public static final int QuotaCounterResult        = 220;
    127         public static final int TetheringStatsResult      = 221;
    128         public static final int DnsProxyQueryResult       = 222;
    129 
    130         public static final int InterfaceChange           = 600;
    131         public static final int BandwidthControl          = 601;
    132         public static final int InterfaceClassActivity    = 613;
    133     }
    134 
    135     /**
    136      * Binder context for this service
    137      */
    138     private Context mContext;
    139 
    140     /**
    141      * connector object for communicating with netd
    142      */
    143     private NativeDaemonConnector mConnector;
    144 
    145     private final Handler mMainHandler = new Handler();
    146 
    147     private Thread mThread;
    148     private CountDownLatch mConnectedSignal = new CountDownLatch(1);
    149 
    150     private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
    151             new RemoteCallbackList<INetworkManagementEventObserver>();
    152 
    153     private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
    154 
    155     private Object mQuotaLock = new Object();
    156     /** Set of interfaces with active quotas. */
    157     private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
    158     /** Set of interfaces with active alerts. */
    159     private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
    160     /** Set of UIDs with active reject rules. */
    161     private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
    162 
    163     private Object mIdleTimerLock = new Object();
    164     /** Set of interfaces with active idle timers. */
    165     private static class IdleTimerParams {
    166         public final int timeout;
    167         public final String label;
    168         public int networkCount;
    169 
    170         IdleTimerParams(int timeout, String label) {
    171             this.timeout = timeout;
    172             this.label = label;
    173             this.networkCount = 1;
    174         }
    175     }
    176     private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
    177 
    178     private volatile boolean mBandwidthControlEnabled;
    179     private volatile boolean mFirewallEnabled;
    180 
    181     /**
    182      * Constructs a new NetworkManagementService instance
    183      *
    184      * @param context  Binder context for this service
    185      */
    186     private NetworkManagementService(Context context) {
    187         mContext = context;
    188 
    189         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
    190             return;
    191         }
    192 
    193         mConnector = new NativeDaemonConnector(
    194                 new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
    195         mThread = new Thread(mConnector, NETD_TAG);
    196 
    197         // Add ourself to the Watchdog monitors.
    198         Watchdog.getInstance().addMonitor(this);
    199     }
    200 
    201     public static NetworkManagementService create(Context context) throws InterruptedException {
    202         final NetworkManagementService service = new NetworkManagementService(context);
    203         final CountDownLatch connectedSignal = service.mConnectedSignal;
    204         if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
    205         service.mThread.start();
    206         if (DBG) Slog.d(TAG, "Awaiting socket connection");
    207         connectedSignal.await();
    208         if (DBG) Slog.d(TAG, "Connected");
    209         return service;
    210     }
    211 
    212     public void systemReady() {
    213         prepareNativeDaemon();
    214         if (DBG) Slog.d(TAG, "Prepared");
    215     }
    216 
    217     @Override
    218     public void registerObserver(INetworkManagementEventObserver observer) {
    219         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    220         mObservers.register(observer);
    221     }
    222 
    223     @Override
    224     public void unregisterObserver(INetworkManagementEventObserver observer) {
    225         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    226         mObservers.unregister(observer);
    227     }
    228 
    229     /**
    230      * Notify our observers of an interface status change
    231      */
    232     private void notifyInterfaceStatusChanged(String iface, boolean up) {
    233         final int length = mObservers.beginBroadcast();
    234         for (int i = 0; i < length; i++) {
    235             try {
    236                 mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
    237             } catch (RemoteException e) {
    238             }
    239         }
    240         mObservers.finishBroadcast();
    241     }
    242 
    243     /**
    244      * Notify our observers of an interface link state change
    245      * (typically, an Ethernet cable has been plugged-in or unplugged).
    246      */
    247     private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
    248         final int length = mObservers.beginBroadcast();
    249         for (int i = 0; i < length; i++) {
    250             try {
    251                 mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
    252             } catch (RemoteException e) {
    253             }
    254         }
    255         mObservers.finishBroadcast();
    256     }
    257 
    258     /**
    259      * Notify our observers of an interface addition.
    260      */
    261     private void notifyInterfaceAdded(String iface) {
    262         final int length = mObservers.beginBroadcast();
    263         for (int i = 0; i < length; i++) {
    264             try {
    265                 mObservers.getBroadcastItem(i).interfaceAdded(iface);
    266             } catch (RemoteException e) {
    267             }
    268         }
    269         mObservers.finishBroadcast();
    270     }
    271 
    272     /**
    273      * Notify our observers of an interface removal.
    274      */
    275     private void notifyInterfaceRemoved(String iface) {
    276         // netd already clears out quota and alerts for removed ifaces; update
    277         // our sanity-checking state.
    278         mActiveAlerts.remove(iface);
    279         mActiveQuotas.remove(iface);
    280 
    281         final int length = mObservers.beginBroadcast();
    282         for (int i = 0; i < length; i++) {
    283             try {
    284                 mObservers.getBroadcastItem(i).interfaceRemoved(iface);
    285             } catch (RemoteException e) {
    286             }
    287         }
    288         mObservers.finishBroadcast();
    289     }
    290 
    291     /**
    292      * Notify our observers of a limit reached.
    293      */
    294     private void notifyLimitReached(String limitName, String iface) {
    295         final int length = mObservers.beginBroadcast();
    296         for (int i = 0; i < length; i++) {
    297             try {
    298                 mObservers.getBroadcastItem(i).limitReached(limitName, iface);
    299             } catch (RemoteException e) {
    300             }
    301         }
    302         mObservers.finishBroadcast();
    303     }
    304 
    305     /**
    306      * Notify our observers of a change in the data activity state of the interface
    307      */
    308     private void notifyInterfaceClassActivity(String label, boolean active) {
    309         final int length = mObservers.beginBroadcast();
    310         for (int i = 0; i < length; i++) {
    311             try {
    312                 mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
    313             } catch (RemoteException e) {
    314             }
    315         }
    316         mObservers.finishBroadcast();
    317     }
    318 
    319     /**
    320      * Prepare native daemon once connected, enabling modules and pushing any
    321      * existing in-memory rules.
    322      */
    323     private void prepareNativeDaemon() {
    324         mBandwidthControlEnabled = false;
    325 
    326         // only enable bandwidth control when support exists
    327         final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
    328         if (hasKernelSupport) {
    329             Slog.d(TAG, "enabling bandwidth control");
    330             try {
    331                 mConnector.execute("bandwidth", "enable");
    332                 mBandwidthControlEnabled = true;
    333             } catch (NativeDaemonConnectorException e) {
    334                 Log.wtf(TAG, "problem enabling bandwidth controls", e);
    335             }
    336         } else {
    337             Slog.d(TAG, "not enabling bandwidth control");
    338         }
    339 
    340         SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
    341 
    342         // push any existing quota or UID rules
    343         synchronized (mQuotaLock) {
    344             int size = mActiveQuotas.size();
    345             if (size > 0) {
    346                 Slog.d(TAG, "pushing " + size + " active quota rules");
    347                 final HashMap<String, Long> activeQuotas = mActiveQuotas;
    348                 mActiveQuotas = Maps.newHashMap();
    349                 for (Map.Entry<String, Long> entry : activeQuotas.entrySet()) {
    350                     setInterfaceQuota(entry.getKey(), entry.getValue());
    351                 }
    352             }
    353 
    354             size = mActiveAlerts.size();
    355             if (size > 0) {
    356                 Slog.d(TAG, "pushing " + size + " active alert rules");
    357                 final HashMap<String, Long> activeAlerts = mActiveAlerts;
    358                 mActiveAlerts = Maps.newHashMap();
    359                 for (Map.Entry<String, Long> entry : activeAlerts.entrySet()) {
    360                     setInterfaceAlert(entry.getKey(), entry.getValue());
    361                 }
    362             }
    363 
    364             size = mUidRejectOnQuota.size();
    365             if (size > 0) {
    366                 Slog.d(TAG, "pushing " + size + " active uid rules");
    367                 final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
    368                 mUidRejectOnQuota = new SparseBooleanArray();
    369                 for (int i = 0; i < uidRejectOnQuota.size(); i++) {
    370                     setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i));
    371                 }
    372             }
    373         }
    374 
    375         // TODO: Push any existing firewall state
    376         setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
    377     }
    378 
    379     //
    380     // Netd Callback handling
    381     //
    382 
    383     private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
    384         @Override
    385         public void onDaemonConnected() {
    386             // event is dispatched from internal NDC thread, so we prepare the
    387             // daemon back on main thread.
    388             if (mConnectedSignal != null) {
    389                 mConnectedSignal.countDown();
    390                 mConnectedSignal = null;
    391             } else {
    392                 mMainHandler.post(new Runnable() {
    393                     @Override
    394                     public void run() {
    395                         prepareNativeDaemon();
    396                     }
    397                 });
    398             }
    399         }
    400 
    401         @Override
    402         public boolean onEvent(int code, String raw, String[] cooked) {
    403             switch (code) {
    404             case NetdResponseCode.InterfaceChange:
    405                     /*
    406                      * a network interface change occured
    407                      * Format: "NNN Iface added <name>"
    408                      *         "NNN Iface removed <name>"
    409                      *         "NNN Iface changed <name> <up/down>"
    410                      *         "NNN Iface linkstatus <name> <up/down>"
    411                      */
    412                     if (cooked.length < 4 || !cooked[1].equals("Iface")) {
    413                         throw new IllegalStateException(
    414                                 String.format("Invalid event from daemon (%s)", raw));
    415                     }
    416                     if (cooked[2].equals("added")) {
    417                         notifyInterfaceAdded(cooked[3]);
    418                         return true;
    419                     } else if (cooked[2].equals("removed")) {
    420                         notifyInterfaceRemoved(cooked[3]);
    421                         return true;
    422                     } else if (cooked[2].equals("changed") && cooked.length == 5) {
    423                         notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
    424                         return true;
    425                     } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
    426                         notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
    427                         return true;
    428                     }
    429                     throw new IllegalStateException(
    430                             String.format("Invalid event from daemon (%s)", raw));
    431                     // break;
    432             case NetdResponseCode.BandwidthControl:
    433                     /*
    434                      * Bandwidth control needs some attention
    435                      * Format: "NNN limit alert <alertName> <ifaceName>"
    436                      */
    437                     if (cooked.length < 5 || !cooked[1].equals("limit")) {
    438                         throw new IllegalStateException(
    439                                 String.format("Invalid event from daemon (%s)", raw));
    440                     }
    441                     if (cooked[2].equals("alert")) {
    442                         notifyLimitReached(cooked[3], cooked[4]);
    443                         return true;
    444                     }
    445                     throw new IllegalStateException(
    446                             String.format("Invalid event from daemon (%s)", raw));
    447                     // break;
    448             case NetdResponseCode.InterfaceClassActivity:
    449                     /*
    450                      * An network interface class state changed (active/idle)
    451                      * Format: "NNN IfaceClass <active/idle> <label>"
    452                      */
    453                     if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
    454                         throw new IllegalStateException(
    455                                 String.format("Invalid event from daemon (%s)", raw));
    456                     }
    457                     boolean isActive = cooked[2].equals("active");
    458                     notifyInterfaceClassActivity(cooked[3], isActive);
    459                     return true;
    460                     // break;
    461             default: break;
    462             }
    463             return false;
    464         }
    465     }
    466 
    467 
    468     //
    469     // INetworkManagementService members
    470     //
    471 
    472     @Override
    473     public String[] listInterfaces() {
    474         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    475         try {
    476             return NativeDaemonEvent.filterMessageList(
    477                     mConnector.executeForList("interface", "list"), InterfaceListResult);
    478         } catch (NativeDaemonConnectorException e) {
    479             throw e.rethrowAsParcelableException();
    480         }
    481     }
    482 
    483     @Override
    484     public InterfaceConfiguration getInterfaceConfig(String iface) {
    485         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    486 
    487         final NativeDaemonEvent event;
    488         try {
    489             event = mConnector.execute("interface", "getcfg", iface);
    490         } catch (NativeDaemonConnectorException e) {
    491             throw e.rethrowAsParcelableException();
    492         }
    493 
    494         event.checkCode(InterfaceGetCfgResult);
    495 
    496         // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3
    497         final StringTokenizer st = new StringTokenizer(event.getMessage());
    498 
    499         InterfaceConfiguration cfg;
    500         try {
    501             cfg = new InterfaceConfiguration();
    502             cfg.setHardwareAddress(st.nextToken(" "));
    503             InetAddress addr = null;
    504             int prefixLength = 0;
    505             try {
    506                 addr = NetworkUtils.numericToInetAddress(st.nextToken());
    507             } catch (IllegalArgumentException iae) {
    508                 Slog.e(TAG, "Failed to parse ipaddr", iae);
    509             }
    510 
    511             try {
    512                 prefixLength = Integer.parseInt(st.nextToken());
    513             } catch (NumberFormatException nfe) {
    514                 Slog.e(TAG, "Failed to parse prefixLength", nfe);
    515             }
    516 
    517             cfg.setLinkAddress(new LinkAddress(addr, prefixLength));
    518             while (st.hasMoreTokens()) {
    519                 cfg.setFlag(st.nextToken());
    520             }
    521         } catch (NoSuchElementException nsee) {
    522             throw new IllegalStateException("Invalid response from daemon: " + event);
    523         }
    524         return cfg;
    525     }
    526 
    527     @Override
    528     public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
    529         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    530         LinkAddress linkAddr = cfg.getLinkAddress();
    531         if (linkAddr == null || linkAddr.getAddress() == null) {
    532             throw new IllegalStateException("Null LinkAddress given");
    533         }
    534 
    535         final Command cmd = new Command("interface", "setcfg", iface,
    536                 linkAddr.getAddress().getHostAddress(),
    537                 linkAddr.getNetworkPrefixLength());
    538         for (String flag : cfg.getFlags()) {
    539             cmd.appendArg(flag);
    540         }
    541 
    542         try {
    543             mConnector.execute(cmd);
    544         } catch (NativeDaemonConnectorException e) {
    545             throw e.rethrowAsParcelableException();
    546         }
    547     }
    548 
    549     @Override
    550     public void setInterfaceDown(String iface) {
    551         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    552         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
    553         ifcg.setInterfaceDown();
    554         setInterfaceConfig(iface, ifcg);
    555     }
    556 
    557     @Override
    558     public void setInterfaceUp(String iface) {
    559         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    560         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
    561         ifcg.setInterfaceUp();
    562         setInterfaceConfig(iface, ifcg);
    563     }
    564 
    565     @Override
    566     public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
    567         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    568         try {
    569             mConnector.execute(
    570                     "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable");
    571         } catch (NativeDaemonConnectorException e) {
    572             throw e.rethrowAsParcelableException();
    573         }
    574     }
    575 
    576     /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
    577        IPv6 addresses on interface down, but we need to do full clean up here */
    578     @Override
    579     public void clearInterfaceAddresses(String iface) {
    580         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    581         try {
    582             mConnector.execute("interface", "clearaddrs", iface);
    583         } catch (NativeDaemonConnectorException e) {
    584             throw e.rethrowAsParcelableException();
    585         }
    586     }
    587 
    588     @Override
    589     public void enableIpv6(String iface) {
    590         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    591         try {
    592             mConnector.execute("interface", "ipv6", iface, "enable");
    593         } catch (NativeDaemonConnectorException e) {
    594             throw e.rethrowAsParcelableException();
    595         }
    596     }
    597 
    598     @Override
    599     public void disableIpv6(String iface) {
    600         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    601         try {
    602             mConnector.execute("interface", "ipv6", iface, "disable");
    603         } catch (NativeDaemonConnectorException e) {
    604             throw e.rethrowAsParcelableException();
    605         }
    606     }
    607 
    608     @Override
    609     public void addRoute(String interfaceName, RouteInfo route) {
    610         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    611         modifyRoute(interfaceName, ADD, route, DEFAULT);
    612     }
    613 
    614     @Override
    615     public void removeRoute(String interfaceName, RouteInfo route) {
    616         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    617         modifyRoute(interfaceName, REMOVE, route, DEFAULT);
    618     }
    619 
    620     @Override
    621     public void addSecondaryRoute(String interfaceName, RouteInfo route) {
    622         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    623         modifyRoute(interfaceName, ADD, route, SECONDARY);
    624     }
    625 
    626     @Override
    627     public void removeSecondaryRoute(String interfaceName, RouteInfo route) {
    628         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    629         modifyRoute(interfaceName, REMOVE, route, SECONDARY);
    630     }
    631 
    632     private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) {
    633         final Command cmd = new Command("interface", "route", action, interfaceName, type);
    634 
    635         // create triplet: dest-ip-addr prefixlength gateway-ip-addr
    636         final LinkAddress la = route.getDestination();
    637         cmd.appendArg(la.getAddress().getHostAddress());
    638         cmd.appendArg(la.getNetworkPrefixLength());
    639 
    640         if (route.getGateway() == null) {
    641             if (la.getAddress() instanceof Inet4Address) {
    642                 cmd.appendArg("0.0.0.0");
    643             } else {
    644                 cmd.appendArg("::0");
    645             }
    646         } else {
    647             cmd.appendArg(route.getGateway().getHostAddress());
    648         }
    649 
    650         try {
    651             mConnector.execute(cmd);
    652         } catch (NativeDaemonConnectorException e) {
    653             throw e.rethrowAsParcelableException();
    654         }
    655     }
    656 
    657     private ArrayList<String> readRouteList(String filename) {
    658         FileInputStream fstream = null;
    659         ArrayList<String> list = new ArrayList<String>();
    660 
    661         try {
    662             fstream = new FileInputStream(filename);
    663             DataInputStream in = new DataInputStream(fstream);
    664             BufferedReader br = new BufferedReader(new InputStreamReader(in));
    665             String s;
    666 
    667             // throw away the title line
    668 
    669             while (((s = br.readLine()) != null) && (s.length() != 0)) {
    670                 list.add(s);
    671             }
    672         } catch (IOException ex) {
    673             // return current list, possibly empty
    674         } finally {
    675             if (fstream != null) {
    676                 try {
    677                     fstream.close();
    678                 } catch (IOException ex) {}
    679             }
    680         }
    681 
    682         return list;
    683     }
    684 
    685     @Override
    686     public RouteInfo[] getRoutes(String interfaceName) {
    687         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    688         ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
    689 
    690         // v4 routes listed as:
    691         // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
    692         for (String s : readRouteList("/proc/net/route")) {
    693             String[] fields = s.split("\t");
    694 
    695             if (fields.length > 7) {
    696                 String iface = fields[0];
    697 
    698                 if (interfaceName.equals(iface)) {
    699                     String dest = fields[1];
    700                     String gate = fields[2];
    701                     String flags = fields[3]; // future use?
    702                     String mask = fields[7];
    703                     try {
    704                         // address stored as a hex string, ex: 0014A8C0
    705                         InetAddress destAddr =
    706                                 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
    707                         int prefixLength =
    708                                 NetworkUtils.netmaskIntToPrefixLength(
    709                                 (int)Long.parseLong(mask, 16));
    710                         LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
    711 
    712                         // address stored as a hex string, ex 0014A8C0
    713                         InetAddress gatewayAddr =
    714                                 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
    715 
    716                         RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
    717                         routes.add(route);
    718                     } catch (Exception e) {
    719                         Log.e(TAG, "Error parsing route " + s + " : " + e);
    720                         continue;
    721                     }
    722                 }
    723             }
    724         }
    725 
    726         // v6 routes listed as:
    727         // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
    728         for (String s : readRouteList("/proc/net/ipv6_route")) {
    729             String[]fields = s.split("\\s+");
    730             if (fields.length > 9) {
    731                 String iface = fields[9].trim();
    732                 if (interfaceName.equals(iface)) {
    733                     String dest = fields[0];
    734                     String prefix = fields[1];
    735                     String gate = fields[4];
    736 
    737                     try {
    738                         // prefix length stored as a hex string, ex 40
    739                         int prefixLength = Integer.parseInt(prefix, 16);
    740 
    741                         // address stored as a 32 char hex string
    742                         // ex fe800000000000000000000000000000
    743                         InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
    744                         LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
    745 
    746                         InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
    747 
    748                         RouteInfo route = new RouteInfo(linkAddress, gateAddr);
    749                         routes.add(route);
    750                     } catch (Exception e) {
    751                         Log.e(TAG, "Error parsing route " + s + " : " + e);
    752                         continue;
    753                     }
    754                 }
    755             }
    756         }
    757         return routes.toArray(new RouteInfo[routes.size()]);
    758     }
    759 
    760     @Override
    761     public void shutdown() {
    762         // TODO: remove from aidl if nobody calls externally
    763         mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
    764 
    765         Slog.d(TAG, "Shutting down");
    766     }
    767 
    768     @Override
    769     public boolean getIpForwardingEnabled() throws IllegalStateException{
    770         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    771 
    772         final NativeDaemonEvent event;
    773         try {
    774             event = mConnector.execute("ipfwd", "status");
    775         } catch (NativeDaemonConnectorException e) {
    776             throw e.rethrowAsParcelableException();
    777         }
    778 
    779         // 211 Forwarding enabled
    780         event.checkCode(IpFwdStatusResult);
    781         return event.getMessage().endsWith("enabled");
    782     }
    783 
    784     @Override
    785     public void setIpForwardingEnabled(boolean enable) {
    786         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    787         try {
    788             mConnector.execute("ipfwd", enable ? "enable" : "disable");
    789         } catch (NativeDaemonConnectorException e) {
    790             throw e.rethrowAsParcelableException();
    791         }
    792     }
    793 
    794     @Override
    795     public void startTethering(String[] dhcpRange) {
    796         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    797         // cmd is "tether start first_start first_stop second_start second_stop ..."
    798         // an odd number of addrs will fail
    799 
    800         final Command cmd = new Command("tether", "start");
    801         for (String d : dhcpRange) {
    802             cmd.appendArg(d);
    803         }
    804 
    805         try {
    806             mConnector.execute(cmd);
    807         } catch (NativeDaemonConnectorException e) {
    808             throw e.rethrowAsParcelableException();
    809         }
    810     }
    811 
    812     @Override
    813     public void stopTethering() {
    814         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    815         try {
    816             mConnector.execute("tether", "stop");
    817         } catch (NativeDaemonConnectorException e) {
    818             throw e.rethrowAsParcelableException();
    819         }
    820     }
    821 
    822     @Override
    823     public boolean isTetheringStarted() {
    824         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    825 
    826         final NativeDaemonEvent event;
    827         try {
    828             event = mConnector.execute("tether", "status");
    829         } catch (NativeDaemonConnectorException e) {
    830             throw e.rethrowAsParcelableException();
    831         }
    832 
    833         // 210 Tethering services started
    834         event.checkCode(TetherStatusResult);
    835         return event.getMessage().endsWith("started");
    836     }
    837 
    838     // TODO(BT) Remove
    839     public void startReverseTethering(String iface)
    840              throws IllegalStateException {
    841         if (DBG) Slog.d(TAG, "startReverseTethering in");
    842         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    843         // cmd is "tether start first_start first_stop second_start second_stop ..."
    844         // an odd number of addrs will fail
    845         String cmd = "tether start-reverse";
    846         cmd += " " + iface;
    847         if (DBG) Slog.d(TAG, "startReverseTethering cmd: " + cmd);
    848         try {
    849             mConnector.doCommand(cmd);
    850         } catch (NativeDaemonConnectorException e) {
    851             throw new IllegalStateException("Unable to communicate to native daemon");
    852         }
    853         BluetoothTetheringDataTracker.getInstance().startReverseTether(iface);
    854 
    855     }
    856 
    857     // TODO(BT) Remove
    858     public void stopReverseTethering() throws IllegalStateException {
    859         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    860         try {
    861             mConnector.doCommand("tether stop-reverse");
    862         } catch (NativeDaemonConnectorException e) {
    863             throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
    864         }
    865         BluetoothTetheringDataTracker.getInstance().stopReverseTether();
    866     }
    867 
    868     @Override
    869     public void tetherInterface(String iface) {
    870         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    871         try {
    872             mConnector.execute("tether", "interface", "add", iface);
    873         } catch (NativeDaemonConnectorException e) {
    874             throw e.rethrowAsParcelableException();
    875         }
    876     }
    877 
    878     @Override
    879     public void untetherInterface(String iface) {
    880         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    881         try {
    882             mConnector.execute("tether", "interface", "remove", iface);
    883         } catch (NativeDaemonConnectorException e) {
    884             throw e.rethrowAsParcelableException();
    885         }
    886     }
    887 
    888     @Override
    889     public String[] listTetheredInterfaces() {
    890         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    891         try {
    892             return NativeDaemonEvent.filterMessageList(
    893                     mConnector.executeForList("tether", "interface", "list"),
    894                     TetherInterfaceListResult);
    895         } catch (NativeDaemonConnectorException e) {
    896             throw e.rethrowAsParcelableException();
    897         }
    898     }
    899 
    900     @Override
    901     public void setDnsForwarders(String[] dns) {
    902         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    903 
    904         final Command cmd = new Command("tether", "dns", "set");
    905         for (String s : dns) {
    906             cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
    907         }
    908 
    909         try {
    910             mConnector.execute(cmd);
    911         } catch (NativeDaemonConnectorException e) {
    912             throw e.rethrowAsParcelableException();
    913         }
    914     }
    915 
    916     @Override
    917     public String[] getDnsForwarders() {
    918         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    919         try {
    920             return NativeDaemonEvent.filterMessageList(
    921                     mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
    922         } catch (NativeDaemonConnectorException e) {
    923             throw e.rethrowAsParcelableException();
    924         }
    925     }
    926 
    927     private void modifyNat(String action, String internalInterface, String externalInterface)
    928             throws SocketException {
    929         final Command cmd = new Command("nat", action, internalInterface, externalInterface);
    930 
    931         final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
    932                 internalInterface);
    933         if (internalNetworkInterface == null) {
    934             cmd.appendArg("0");
    935         } else {
    936             Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
    937                     .getInterfaceAddresses();
    938             cmd.appendArg(interfaceAddresses.size());
    939             for (InterfaceAddress ia : interfaceAddresses) {
    940                 InetAddress addr = NetworkUtils.getNetworkPart(
    941                         ia.getAddress(), ia.getNetworkPrefixLength());
    942                 cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
    943             }
    944         }
    945 
    946         try {
    947             mConnector.execute(cmd);
    948         } catch (NativeDaemonConnectorException e) {
    949             throw e.rethrowAsParcelableException();
    950         }
    951     }
    952 
    953     @Override
    954     public void enableNat(String internalInterface, String externalInterface) {
    955         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    956         try {
    957             modifyNat("enable", internalInterface, externalInterface);
    958         } catch (SocketException e) {
    959             throw new IllegalStateException(e);
    960         }
    961     }
    962 
    963     @Override
    964     public void disableNat(String internalInterface, String externalInterface) {
    965         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    966         try {
    967             modifyNat("disable", internalInterface, externalInterface);
    968         } catch (SocketException e) {
    969             throw new IllegalStateException(e);
    970         }
    971     }
    972 
    973     @Override
    974     public String[] listTtys() {
    975         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    976         try {
    977             return NativeDaemonEvent.filterMessageList(
    978                     mConnector.executeForList("list_ttys"), TtyListResult);
    979         } catch (NativeDaemonConnectorException e) {
    980             throw e.rethrowAsParcelableException();
    981         }
    982     }
    983 
    984     @Override
    985     public void attachPppd(
    986             String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
    987         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    988         try {
    989             mConnector.execute("pppd", "attach", tty,
    990                     NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
    991                     NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
    992                     NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
    993                     NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
    994         } catch (NativeDaemonConnectorException e) {
    995             throw e.rethrowAsParcelableException();
    996         }
    997     }
    998 
    999     @Override
   1000     public void detachPppd(String tty) {
   1001         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1002         try {
   1003             mConnector.execute("pppd", "detach", tty);
   1004         } catch (NativeDaemonConnectorException e) {
   1005             throw e.rethrowAsParcelableException();
   1006         }
   1007     }
   1008 
   1009     @Override
   1010     public void startAccessPoint(
   1011             WifiConfiguration wifiConfig, String wlanIface) {
   1012         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1013         try {
   1014             wifiFirmwareReload(wlanIface, "AP");
   1015             if (wifiConfig == null) {
   1016                 mConnector.execute("softap", "set", wlanIface);
   1017             } else {
   1018                 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
   1019                         getSecurityType(wifiConfig), wifiConfig.preSharedKey);
   1020             }
   1021             mConnector.execute("softap", "startap");
   1022         } catch (NativeDaemonConnectorException e) {
   1023             throw e.rethrowAsParcelableException();
   1024         }
   1025     }
   1026 
   1027     private static String getSecurityType(WifiConfiguration wifiConfig) {
   1028         switch (wifiConfig.getAuthType()) {
   1029             case KeyMgmt.WPA_PSK:
   1030                 return "wpa-psk";
   1031             case KeyMgmt.WPA2_PSK:
   1032                 return "wpa2-psk";
   1033             default:
   1034                 return "open";
   1035         }
   1036     }
   1037 
   1038     /* @param mode can be "AP", "STA" or "P2P" */
   1039     @Override
   1040     public void wifiFirmwareReload(String wlanIface, String mode) {
   1041         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1042         try {
   1043             mConnector.execute("softap", "fwreload", wlanIface, mode);
   1044         } catch (NativeDaemonConnectorException e) {
   1045             throw e.rethrowAsParcelableException();
   1046         }
   1047     }
   1048 
   1049     @Override
   1050     public void stopAccessPoint(String wlanIface) {
   1051         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1052         try {
   1053             mConnector.execute("softap", "stopap");
   1054             wifiFirmwareReload(wlanIface, "STA");
   1055         } catch (NativeDaemonConnectorException e) {
   1056             throw e.rethrowAsParcelableException();
   1057         }
   1058     }
   1059 
   1060     @Override
   1061     public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
   1062         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1063         try {
   1064             if (wifiConfig == null) {
   1065                 mConnector.execute("softap", "set", wlanIface);
   1066             } else {
   1067                 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
   1068                         getSecurityType(wifiConfig), wifiConfig.preSharedKey);
   1069             }
   1070         } catch (NativeDaemonConnectorException e) {
   1071             throw e.rethrowAsParcelableException();
   1072         }
   1073     }
   1074 
   1075     @Override
   1076     public void addIdleTimer(String iface, int timeout, String label) {
   1077         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1078 
   1079         if (DBG) Slog.d(TAG, "Adding idletimer");
   1080 
   1081         synchronized (mIdleTimerLock) {
   1082             IdleTimerParams params = mActiveIdleTimers.get(iface);
   1083             if (params != null) {
   1084                 // the interface already has idletimer, update network count
   1085                 params.networkCount++;
   1086                 return;
   1087             }
   1088 
   1089             try {
   1090                 mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label);
   1091             } catch (NativeDaemonConnectorException e) {
   1092                 throw e.rethrowAsParcelableException();
   1093             }
   1094             mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label));
   1095         }
   1096     }
   1097 
   1098     @Override
   1099     public void removeIdleTimer(String iface) {
   1100         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1101 
   1102         if (DBG) Slog.d(TAG, "Removing idletimer");
   1103 
   1104         synchronized (mIdleTimerLock) {
   1105             IdleTimerParams params = mActiveIdleTimers.get(iface);
   1106             if (params == null || --(params.networkCount) > 0) {
   1107                 return;
   1108             }
   1109 
   1110             try {
   1111                 mConnector.execute("idletimer", "remove", iface,
   1112                         Integer.toString(params.timeout), params.label);
   1113             } catch (NativeDaemonConnectorException e) {
   1114                 throw e.rethrowAsParcelableException();
   1115             }
   1116             mActiveIdleTimers.remove(iface);
   1117         }
   1118     }
   1119 
   1120     @Override
   1121     public NetworkStats getNetworkStatsSummaryDev() {
   1122         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1123         return mStatsFactory.readNetworkStatsSummaryDev();
   1124     }
   1125 
   1126     @Override
   1127     public NetworkStats getNetworkStatsSummaryXt() {
   1128         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1129         return mStatsFactory.readNetworkStatsSummaryXt();
   1130     }
   1131 
   1132     @Override
   1133     public NetworkStats getNetworkStatsDetail() {
   1134         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1135         return mStatsFactory.readNetworkStatsDetail(UID_ALL);
   1136     }
   1137 
   1138     @Override
   1139     public void setInterfaceQuota(String iface, long quotaBytes) {
   1140         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1141 
   1142         // silently discard when control disabled
   1143         // TODO: eventually migrate to be always enabled
   1144         if (!mBandwidthControlEnabled) return;
   1145 
   1146         synchronized (mQuotaLock) {
   1147             if (mActiveQuotas.containsKey(iface)) {
   1148                 throw new IllegalStateException("iface " + iface + " already has quota");
   1149             }
   1150 
   1151             try {
   1152                 // TODO: support quota shared across interfaces
   1153                 mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
   1154                 mActiveQuotas.put(iface, quotaBytes);
   1155             } catch (NativeDaemonConnectorException e) {
   1156                 throw e.rethrowAsParcelableException();
   1157             }
   1158         }
   1159     }
   1160 
   1161     @Override
   1162     public void removeInterfaceQuota(String iface) {
   1163         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1164 
   1165         // silently discard when control disabled
   1166         // TODO: eventually migrate to be always enabled
   1167         if (!mBandwidthControlEnabled) return;
   1168 
   1169         synchronized (mQuotaLock) {
   1170             if (!mActiveQuotas.containsKey(iface)) {
   1171                 // TODO: eventually consider throwing
   1172                 return;
   1173             }
   1174 
   1175             mActiveQuotas.remove(iface);
   1176             mActiveAlerts.remove(iface);
   1177 
   1178             try {
   1179                 // TODO: support quota shared across interfaces
   1180                 mConnector.execute("bandwidth", "removeiquota", iface);
   1181             } catch (NativeDaemonConnectorException e) {
   1182                 throw e.rethrowAsParcelableException();
   1183             }
   1184         }
   1185     }
   1186 
   1187     @Override
   1188     public void setInterfaceAlert(String iface, long alertBytes) {
   1189         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1190 
   1191         // silently discard when control disabled
   1192         // TODO: eventually migrate to be always enabled
   1193         if (!mBandwidthControlEnabled) return;
   1194 
   1195         // quick sanity check
   1196         if (!mActiveQuotas.containsKey(iface)) {
   1197             throw new IllegalStateException("setting alert requires existing quota on iface");
   1198         }
   1199 
   1200         synchronized (mQuotaLock) {
   1201             if (mActiveAlerts.containsKey(iface)) {
   1202                 throw new IllegalStateException("iface " + iface + " already has alert");
   1203             }
   1204 
   1205             try {
   1206                 // TODO: support alert shared across interfaces
   1207                 mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
   1208                 mActiveAlerts.put(iface, alertBytes);
   1209             } catch (NativeDaemonConnectorException e) {
   1210                 throw e.rethrowAsParcelableException();
   1211             }
   1212         }
   1213     }
   1214 
   1215     @Override
   1216     public void removeInterfaceAlert(String iface) {
   1217         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1218 
   1219         // silently discard when control disabled
   1220         // TODO: eventually migrate to be always enabled
   1221         if (!mBandwidthControlEnabled) return;
   1222 
   1223         synchronized (mQuotaLock) {
   1224             if (!mActiveAlerts.containsKey(iface)) {
   1225                 // TODO: eventually consider throwing
   1226                 return;
   1227             }
   1228 
   1229             try {
   1230                 // TODO: support alert shared across interfaces
   1231                 mConnector.execute("bandwidth", "removeinterfacealert", iface);
   1232                 mActiveAlerts.remove(iface);
   1233             } catch (NativeDaemonConnectorException e) {
   1234                 throw e.rethrowAsParcelableException();
   1235             }
   1236         }
   1237     }
   1238 
   1239     @Override
   1240     public void setGlobalAlert(long alertBytes) {
   1241         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1242 
   1243         // silently discard when control disabled
   1244         // TODO: eventually migrate to be always enabled
   1245         if (!mBandwidthControlEnabled) return;
   1246 
   1247         try {
   1248             mConnector.execute("bandwidth", "setglobalalert", alertBytes);
   1249         } catch (NativeDaemonConnectorException e) {
   1250             throw e.rethrowAsParcelableException();
   1251         }
   1252     }
   1253 
   1254     @Override
   1255     public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
   1256         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1257 
   1258         // silently discard when control disabled
   1259         // TODO: eventually migrate to be always enabled
   1260         if (!mBandwidthControlEnabled) return;
   1261 
   1262         synchronized (mQuotaLock) {
   1263             final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
   1264             if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
   1265                 // TODO: eventually consider throwing
   1266                 return;
   1267             }
   1268 
   1269             try {
   1270                 mConnector.execute("bandwidth",
   1271                         rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
   1272                 if (rejectOnQuotaInterfaces) {
   1273                     mUidRejectOnQuota.put(uid, true);
   1274                 } else {
   1275                     mUidRejectOnQuota.delete(uid);
   1276                 }
   1277             } catch (NativeDaemonConnectorException e) {
   1278                 throw e.rethrowAsParcelableException();
   1279             }
   1280         }
   1281     }
   1282 
   1283     @Override
   1284     public boolean isBandwidthControlEnabled() {
   1285         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1286         return mBandwidthControlEnabled;
   1287     }
   1288 
   1289     @Override
   1290     public NetworkStats getNetworkStatsUidDetail(int uid) {
   1291         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1292         return mStatsFactory.readNetworkStatsDetail(uid);
   1293     }
   1294 
   1295     @Override
   1296     public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
   1297         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1298 
   1299         if (ifacePairs.length % 2 != 0) {
   1300             throw new IllegalArgumentException(
   1301                     "unexpected ifacePairs; length=" + ifacePairs.length);
   1302         }
   1303 
   1304         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
   1305         for (int i = 0; i < ifacePairs.length; i += 2) {
   1306             final String ifaceIn = ifacePairs[i];
   1307             final String ifaceOut = ifacePairs[i + 1];
   1308             if (ifaceIn != null && ifaceOut != null) {
   1309                 stats.combineValues(getNetworkStatsTethering(ifaceIn, ifaceOut));
   1310             }
   1311         }
   1312         return stats;
   1313     }
   1314 
   1315     private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
   1316         final NativeDaemonEvent event;
   1317         try {
   1318             event = mConnector.execute("bandwidth", "gettetherstats", ifaceIn, ifaceOut);
   1319         } catch (NativeDaemonConnectorException e) {
   1320             throw e.rethrowAsParcelableException();
   1321         }
   1322 
   1323         event.checkCode(TetheringStatsResult);
   1324 
   1325         // 221 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
   1326         final StringTokenizer tok = new StringTokenizer(event.getMessage());
   1327         tok.nextToken();
   1328         tok.nextToken();
   1329 
   1330         try {
   1331             final NetworkStats.Entry entry = new NetworkStats.Entry();
   1332             entry.iface = ifaceIn;
   1333             entry.uid = UID_TETHERING;
   1334             entry.set = SET_DEFAULT;
   1335             entry.tag = TAG_NONE;
   1336             entry.rxBytes = Long.parseLong(tok.nextToken());
   1337             entry.rxPackets = Long.parseLong(tok.nextToken());
   1338             entry.txBytes = Long.parseLong(tok.nextToken());
   1339             entry.txPackets = Long.parseLong(tok.nextToken());
   1340             return entry;
   1341         } catch (NumberFormatException e) {
   1342             throw new IllegalStateException(
   1343                     "problem parsing tethering stats for " + ifaceIn + " " + ifaceOut + ": " + e);
   1344         }
   1345     }
   1346 
   1347     @Override
   1348     public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
   1349         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1350         try {
   1351             mConnector.execute("interface", "setthrottle", iface, rxKbps, txKbps);
   1352         } catch (NativeDaemonConnectorException e) {
   1353             throw e.rethrowAsParcelableException();
   1354         }
   1355     }
   1356 
   1357     private int getInterfaceThrottle(String iface, boolean rx) {
   1358         final NativeDaemonEvent event;
   1359         try {
   1360             event = mConnector.execute("interface", "getthrottle", iface, rx ? "rx" : "tx");
   1361         } catch (NativeDaemonConnectorException e) {
   1362             throw e.rethrowAsParcelableException();
   1363         }
   1364 
   1365         if (rx) {
   1366             event.checkCode(InterfaceRxThrottleResult);
   1367         } else {
   1368             event.checkCode(InterfaceTxThrottleResult);
   1369         }
   1370 
   1371         try {
   1372             return Integer.parseInt(event.getMessage());
   1373         } catch (NumberFormatException e) {
   1374             throw new IllegalStateException("unexpected response:" + event);
   1375         }
   1376     }
   1377 
   1378     @Override
   1379     public int getInterfaceRxThrottle(String iface) {
   1380         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1381         return getInterfaceThrottle(iface, true);
   1382     }
   1383 
   1384     @Override
   1385     public int getInterfaceTxThrottle(String iface) {
   1386         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1387         return getInterfaceThrottle(iface, false);
   1388     }
   1389 
   1390     @Override
   1391     public void setDefaultInterfaceForDns(String iface) {
   1392         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1393         try {
   1394             mConnector.execute("resolver", "setdefaultif", iface);
   1395         } catch (NativeDaemonConnectorException e) {
   1396             throw e.rethrowAsParcelableException();
   1397         }
   1398     }
   1399 
   1400     @Override
   1401     public void setDnsServersForInterface(String iface, String[] servers) {
   1402         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1403 
   1404         final Command cmd = new Command("resolver", "setifdns", iface);
   1405         for (String s : servers) {
   1406             InetAddress a = NetworkUtils.numericToInetAddress(s);
   1407             if (a.isAnyLocalAddress() == false) {
   1408                 cmd.appendArg(a.getHostAddress());
   1409             }
   1410         }
   1411 
   1412         try {
   1413             mConnector.execute(cmd);
   1414         } catch (NativeDaemonConnectorException e) {
   1415             throw e.rethrowAsParcelableException();
   1416         }
   1417     }
   1418 
   1419     @Override
   1420     public void flushDefaultDnsCache() {
   1421         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1422         try {
   1423             mConnector.execute("resolver", "flushdefaultif");
   1424         } catch (NativeDaemonConnectorException e) {
   1425             throw e.rethrowAsParcelableException();
   1426         }
   1427     }
   1428 
   1429     @Override
   1430     public void flushInterfaceDnsCache(String iface) {
   1431         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1432         try {
   1433             mConnector.execute("resolver", "flushif", iface);
   1434         } catch (NativeDaemonConnectorException e) {
   1435             throw e.rethrowAsParcelableException();
   1436         }
   1437     }
   1438 
   1439     @Override
   1440     public void setFirewallEnabled(boolean enabled) {
   1441         enforceSystemUid();
   1442         try {
   1443             mConnector.execute("firewall", enabled ? "enable" : "disable");
   1444             mFirewallEnabled = enabled;
   1445         } catch (NativeDaemonConnectorException e) {
   1446             throw e.rethrowAsParcelableException();
   1447         }
   1448     }
   1449 
   1450     @Override
   1451     public boolean isFirewallEnabled() {
   1452         enforceSystemUid();
   1453         return mFirewallEnabled;
   1454     }
   1455 
   1456     @Override
   1457     public void setFirewallInterfaceRule(String iface, boolean allow) {
   1458         enforceSystemUid();
   1459         Preconditions.checkState(mFirewallEnabled);
   1460         final String rule = allow ? ALLOW : DENY;
   1461         try {
   1462             mConnector.execute("firewall", "set_interface_rule", iface, rule);
   1463         } catch (NativeDaemonConnectorException e) {
   1464             throw e.rethrowAsParcelableException();
   1465         }
   1466     }
   1467 
   1468     @Override
   1469     public void setFirewallEgressSourceRule(String addr, boolean allow) {
   1470         enforceSystemUid();
   1471         Preconditions.checkState(mFirewallEnabled);
   1472         final String rule = allow ? ALLOW : DENY;
   1473         try {
   1474             mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
   1475         } catch (NativeDaemonConnectorException e) {
   1476             throw e.rethrowAsParcelableException();
   1477         }
   1478     }
   1479 
   1480     @Override
   1481     public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
   1482         enforceSystemUid();
   1483         Preconditions.checkState(mFirewallEnabled);
   1484         final String rule = allow ? ALLOW : DENY;
   1485         try {
   1486             mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
   1487         } catch (NativeDaemonConnectorException e) {
   1488             throw e.rethrowAsParcelableException();
   1489         }
   1490     }
   1491 
   1492     @Override
   1493     public void setFirewallUidRule(int uid, boolean allow) {
   1494         enforceSystemUid();
   1495         Preconditions.checkState(mFirewallEnabled);
   1496         final String rule = allow ? ALLOW : DENY;
   1497         try {
   1498             mConnector.execute("firewall", "set_uid_rule", uid, rule);
   1499         } catch (NativeDaemonConnectorException e) {
   1500             throw e.rethrowAsParcelableException();
   1501         }
   1502     }
   1503 
   1504     private static void enforceSystemUid() {
   1505         final int uid = Binder.getCallingUid();
   1506         if (uid != Process.SYSTEM_UID) {
   1507             throw new SecurityException("Only available to AID_SYSTEM");
   1508         }
   1509     }
   1510 
   1511     @Override
   1512     public void monitor() {
   1513         if (mConnector != null) {
   1514             mConnector.monitor();
   1515         }
   1516     }
   1517 
   1518     @Override
   1519     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1520         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
   1521 
   1522         pw.println("NetworkManagementService NativeDaemonConnector Log:");
   1523         mConnector.dump(fd, pw, args);
   1524         pw.println();
   1525 
   1526         pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
   1527 
   1528         synchronized (mQuotaLock) {
   1529             pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
   1530             pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
   1531         }
   1532 
   1533         synchronized (mUidRejectOnQuota) {
   1534             pw.print("UID reject on quota ifaces: [");
   1535             final int size = mUidRejectOnQuota.size();
   1536             for (int i = 0; i < size; i++) {
   1537                 pw.print(mUidRejectOnQuota.keyAt(i));
   1538                 if (i < size - 1) pw.print(",");
   1539             }
   1540             pw.println("]");
   1541         }
   1542 
   1543         pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
   1544     }
   1545 }
   1546