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