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