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 android.app.PendingIntent;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.res.Resources;
     25 import android.content.pm.PackageManager;
     26 import android.net.Uri;
     27 import android.net.InterfaceConfiguration;
     28 import android.net.INetworkManagementEventObserver;
     29 import android.net.wifi.WifiConfiguration;
     30 import android.net.wifi.WifiConfiguration.KeyMgmt;
     31 import android.os.INetworkManagementService;
     32 import android.os.Handler;
     33 import android.os.SystemProperties;
     34 import android.text.TextUtils;
     35 import android.util.Log;
     36 import android.util.Slog;
     37 import java.util.ArrayList;
     38 import java.util.StringTokenizer;
     39 import android.provider.Settings;
     40 import android.content.ContentResolver;
     41 import android.database.ContentObserver;
     42 
     43 import java.io.File;
     44 import java.io.FileReader;
     45 import java.lang.IllegalStateException;
     46 
     47 import java.net.InetAddress;
     48 import java.net.UnknownHostException;
     49 
     50 /**
     51  * @hide
     52  */
     53 class NetworkManagementService extends INetworkManagementService.Stub {
     54 
     55     private static final String TAG = "NetworkManagmentService";
     56 
     57     class NetdResponseCode {
     58         public static final int InterfaceListResult       = 110;
     59         public static final int TetherInterfaceListResult = 111;
     60         public static final int TetherDnsFwdTgtListResult = 112;
     61         public static final int TtyListResult             = 113;
     62 
     63         public static final int TetherStatusResult        = 210;
     64         public static final int IpFwdStatusResult         = 211;
     65         public static final int InterfaceGetCfgResult     = 213;
     66         public static final int SoftapStatusResult        = 214;
     67         public static final int UsbRNDISStatusResult      = 215;
     68         public static final int InterfaceRxCounterResult  = 216;
     69         public static final int InterfaceTxCounterResult  = 217;
     70         public static final int InterfaceRxThrottleResult = 218;
     71         public static final int InterfaceTxThrottleResult = 219;
     72 
     73         public static final int InterfaceChange           = 600;
     74     }
     75 
     76     /**
     77      * Binder context for this service
     78      */
     79     private Context mContext;
     80 
     81     /**
     82      * connector object for communicating with netd
     83      */
     84     private NativeDaemonConnector mConnector;
     85 
     86     private ArrayList<INetworkManagementEventObserver> mObservers;
     87 
     88     /**
     89      * Constructs a new NetworkManagementService instance
     90      *
     91      * @param context  Binder context for this service
     92      */
     93     public NetworkManagementService(Context context) {
     94         mContext = context;
     95 
     96         mObservers = new ArrayList<INetworkManagementEventObserver>();
     97 
     98         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
     99             return;
    100         }
    101 
    102         mConnector = new NativeDaemonConnector(
    103                 new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
    104         Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
    105         thread.start();
    106     }
    107 
    108     public void registerObserver(INetworkManagementEventObserver obs) {
    109         Slog.d(TAG, "Registering observer");
    110         mObservers.add(obs);
    111     }
    112 
    113     public void unregisterObserver(INetworkManagementEventObserver obs) {
    114         Slog.d(TAG, "Unregistering observer");
    115         mObservers.remove(mObservers.indexOf(obs));
    116     }
    117 
    118     /**
    119      * Notify our observers of an interface link status change
    120      */
    121     private void notifyInterfaceLinkStatusChanged(String iface, boolean link) {
    122         for (INetworkManagementEventObserver obs : mObservers) {
    123             try {
    124                 obs.interfaceLinkStatusChanged(iface, link);
    125             } catch (Exception ex) {
    126                 Slog.w(TAG, "Observer notifier failed", ex);
    127             }
    128         }
    129     }
    130 
    131     /**
    132      * Notify our observers of an interface addition.
    133      */
    134     private void notifyInterfaceAdded(String iface) {
    135         for (INetworkManagementEventObserver obs : mObservers) {
    136             try {
    137                 obs.interfaceAdded(iface);
    138             } catch (Exception ex) {
    139                 Slog.w(TAG, "Observer notifier failed", ex);
    140             }
    141         }
    142     }
    143 
    144     /**
    145      * Notify our observers of an interface removal.
    146      */
    147     private void notifyInterfaceRemoved(String iface) {
    148         for (INetworkManagementEventObserver obs : mObservers) {
    149             try {
    150                 obs.interfaceRemoved(iface);
    151             } catch (Exception ex) {
    152                 Slog.w(TAG, "Observer notifier failed", ex);
    153             }
    154         }
    155     }
    156 
    157 
    158     //
    159     // Netd Callback handling
    160     //
    161 
    162     class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
    163         public void onDaemonConnected() {
    164             new Thread() {
    165                 public void run() {
    166                 }
    167             }.start();
    168         }
    169         public boolean onEvent(int code, String raw, String[] cooked) {
    170             if (code == NetdResponseCode.InterfaceChange) {
    171                 /*
    172                  * a network interface change occured
    173                  * Format: "NNN Iface added <name>"
    174                  *         "NNN Iface removed <name>"
    175                  *         "NNN Iface changed <name> <up/down>"
    176                  */
    177                 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
    178                     throw new IllegalStateException(
    179                             String.format("Invalid event from daemon (%s)", raw));
    180                 }
    181                 if (cooked[2].equals("added")) {
    182                     notifyInterfaceAdded(cooked[3]);
    183                     return true;
    184                 } else if (cooked[2].equals("removed")) {
    185                     notifyInterfaceRemoved(cooked[3]);
    186                     return true;
    187                 } else if (cooked[2].equals("changed") && cooked.length == 5) {
    188                     notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up"));
    189                     return true;
    190                 }
    191                 throw new IllegalStateException(
    192                         String.format("Invalid event from daemon (%s)", raw));
    193             }
    194             return false;
    195         }
    196     }
    197 
    198     private static int stringToIpAddr(String addrString) throws UnknownHostException {
    199         try {
    200             String[] parts = addrString.split("\\.");
    201             if (parts.length != 4) {
    202                 throw new UnknownHostException(addrString);
    203             }
    204 
    205             int a = Integer.parseInt(parts[0])      ;
    206             int b = Integer.parseInt(parts[1]) <<  8;
    207             int c = Integer.parseInt(parts[2]) << 16;
    208             int d = Integer.parseInt(parts[3]) << 24;
    209 
    210             return a | b | c | d;
    211         } catch (NumberFormatException ex) {
    212             throw new UnknownHostException(addrString);
    213         }
    214     }
    215 
    216     public static String intToIpString(int i) {
    217         return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >>  8 ) & 0xFF) + "." +
    218                (i & 0xFF);
    219     }
    220 
    221     //
    222     // INetworkManagementService members
    223     //
    224 
    225     public String[] listInterfaces() throws IllegalStateException {
    226         mContext.enforceCallingOrSelfPermission(
    227                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    228 
    229         return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
    230     }
    231 
    232     public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
    233         String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
    234         Slog.d(TAG, String.format("rsp <%s>", rsp));
    235 
    236         // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
    237         StringTokenizer st = new StringTokenizer(rsp);
    238 
    239         try {
    240             int code = Integer.parseInt(st.nextToken(" "));
    241             if (code != NetdResponseCode.InterfaceGetCfgResult) {
    242                 throw new IllegalStateException(
    243                     String.format("Expected code %d, but got %d",
    244                             NetdResponseCode.InterfaceGetCfgResult, code));
    245             }
    246         } catch (NumberFormatException nfe) {
    247             throw new IllegalStateException(
    248                     String.format("Invalid response from daemon (%s)", rsp));
    249         }
    250 
    251         InterfaceConfiguration cfg = new InterfaceConfiguration();
    252         cfg.hwAddr = st.nextToken(" ");
    253         try {
    254             cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
    255         } catch (UnknownHostException uhe) {
    256             Slog.e(TAG, "Failed to parse ipaddr", uhe);
    257             cfg.ipAddr = 0;
    258         }
    259 
    260         try {
    261             cfg.netmask = stringToIpAddr(st.nextToken(" "));
    262         } catch (UnknownHostException uhe) {
    263             Slog.e(TAG, "Failed to parse netmask", uhe);
    264             cfg.netmask = 0;
    265         }
    266         cfg.interfaceFlags = st.nextToken("]").trim() +"]";
    267         Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
    268         return cfg;
    269     }
    270 
    271     public void setInterfaceConfig(
    272             String iface, InterfaceConfiguration cfg) throws IllegalStateException {
    273         String cmd = String.format("interface setcfg %s %s %s %s", iface,
    274                 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
    275         mConnector.doCommand(cmd);
    276     }
    277 
    278     public void shutdown() {
    279         if (mContext.checkCallingOrSelfPermission(
    280                 android.Manifest.permission.SHUTDOWN)
    281                 != PackageManager.PERMISSION_GRANTED) {
    282             throw new SecurityException("Requires SHUTDOWN permission");
    283         }
    284 
    285         Slog.d(TAG, "Shutting down");
    286     }
    287 
    288     public boolean getIpForwardingEnabled() throws IllegalStateException{
    289         mContext.enforceCallingOrSelfPermission(
    290                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    291 
    292         ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
    293 
    294         for (String line : rsp) {
    295             String []tok = line.split(" ");
    296             int code = Integer.parseInt(tok[0]);
    297             if (code == NetdResponseCode.IpFwdStatusResult) {
    298                 // 211 Forwarding <enabled/disabled>
    299                 if (tok.length !=2) {
    300                     throw new IllegalStateException(
    301                             String.format("Malformatted list entry '%s'", line));
    302                 }
    303                 if (tok[2].equals("enabled"))
    304                     return true;
    305                 return false;
    306             } else {
    307                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
    308             }
    309         }
    310         throw new IllegalStateException("Got an empty response");
    311     }
    312 
    313     public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
    314         mContext.enforceCallingOrSelfPermission(
    315                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    316         mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
    317     }
    318 
    319     public void startTethering(String[] dhcpRange)
    320              throws IllegalStateException {
    321         mContext.enforceCallingOrSelfPermission(
    322                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    323         // cmd is "tether start first_start first_stop second_start second_stop ..."
    324         // an odd number of addrs will fail
    325         String cmd = "tether start";
    326         for (String d : dhcpRange) {
    327             cmd += " " + d;
    328         }
    329         mConnector.doCommand(cmd);
    330     }
    331 
    332     public void stopTethering() throws IllegalStateException {
    333         mContext.enforceCallingOrSelfPermission(
    334                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    335         mConnector.doCommand("tether stop");
    336     }
    337 
    338     public boolean isTetheringStarted() throws IllegalStateException {
    339         mContext.enforceCallingOrSelfPermission(
    340                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    341 
    342         ArrayList<String> rsp = mConnector.doCommand("tether status");
    343 
    344         for (String line : rsp) {
    345             String []tok = line.split(" ");
    346             int code = Integer.parseInt(tok[0]);
    347             if (code == NetdResponseCode.TetherStatusResult) {
    348                 // XXX: Tethering services <started/stopped> <TBD>...
    349                 if (tok[2].equals("started"))
    350                     return true;
    351                 return false;
    352             } else {
    353                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
    354             }
    355         }
    356         throw new IllegalStateException("Got an empty response");
    357     }
    358 
    359     public void tetherInterface(String iface) throws IllegalStateException {
    360         mContext.enforceCallingOrSelfPermission(
    361                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    362         mConnector.doCommand("tether interface add " + iface);
    363     }
    364 
    365     public void untetherInterface(String iface) {
    366         mContext.enforceCallingOrSelfPermission(
    367                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    368         mConnector.doCommand("tether interface remove " + iface);
    369     }
    370 
    371     public String[] listTetheredInterfaces() throws IllegalStateException {
    372         mContext.enforceCallingOrSelfPermission(
    373                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    374         return mConnector.doListCommand(
    375                 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
    376     }
    377 
    378     public void setDnsForwarders(String[] dns) throws IllegalStateException {
    379         mContext.enforceCallingOrSelfPermission(
    380                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    381         try {
    382             String cmd = "tether dns set";
    383             for (String s : dns) {
    384                 cmd += " " + InetAddress.getByName(s).getHostAddress();
    385             }
    386             mConnector.doCommand(cmd);
    387         } catch (UnknownHostException e) {
    388             throw new IllegalStateException("Error resolving dns name", e);
    389         }
    390     }
    391 
    392     public String[] getDnsForwarders() throws IllegalStateException {
    393         mContext.enforceCallingOrSelfPermission(
    394                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    395         return mConnector.doListCommand(
    396                 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
    397     }
    398 
    399     public void enableNat(String internalInterface, String externalInterface)
    400             throws IllegalStateException {
    401         mContext.enforceCallingOrSelfPermission(
    402                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    403         mConnector.doCommand(
    404                 String.format("nat enable %s %s", internalInterface, externalInterface));
    405     }
    406 
    407     public void disableNat(String internalInterface, String externalInterface)
    408             throws IllegalStateException {
    409         mContext.enforceCallingOrSelfPermission(
    410                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    411         mConnector.doCommand(
    412                 String.format("nat disable %s %s", internalInterface, externalInterface));
    413     }
    414 
    415     public String[] listTtys() throws IllegalStateException {
    416         mContext.enforceCallingOrSelfPermission(
    417                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    418         return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
    419     }
    420 
    421     public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
    422             String dns2Addr) throws IllegalStateException {
    423         try {
    424             mContext.enforceCallingOrSelfPermission(
    425                     android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    426             mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
    427                     InetAddress.getByName(localAddr).getHostAddress(),
    428                     InetAddress.getByName(remoteAddr).getHostAddress(),
    429                     InetAddress.getByName(dns1Addr).getHostAddress(),
    430                     InetAddress.getByName(dns2Addr).getHostAddress()));
    431         } catch (UnknownHostException e) {
    432             throw new IllegalStateException("Error resolving addr", e);
    433         }
    434     }
    435 
    436     public void detachPppd(String tty) throws IllegalStateException {
    437         mContext.enforceCallingOrSelfPermission(
    438                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    439         mConnector.doCommand(String.format("pppd detach %s", tty));
    440     }
    441 
    442     public void startUsbRNDIS() throws IllegalStateException {
    443         mContext.enforceCallingOrSelfPermission(
    444                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    445         mConnector.doCommand("usb startrndis");
    446     }
    447 
    448     public void stopUsbRNDIS() throws IllegalStateException {
    449         mContext.enforceCallingOrSelfPermission(
    450                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    451         mConnector.doCommand("usb stoprndis");
    452     }
    453 
    454     public boolean isUsbRNDISStarted() throws IllegalStateException {
    455         mContext.enforceCallingOrSelfPermission(
    456                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    457         ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus");
    458 
    459         for (String line : rsp) {
    460             String []tok = line.split(" ");
    461             int code = Integer.parseInt(tok[0]);
    462             if (code == NetdResponseCode.UsbRNDISStatusResult) {
    463                 if (tok[3].equals("started"))
    464                     return true;
    465                 return false;
    466             } else {
    467                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
    468             }
    469         }
    470         throw new IllegalStateException("Got an empty response");
    471     }
    472 
    473     public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
    474              throws IllegalStateException {
    475         mContext.enforceCallingOrSelfPermission(
    476                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    477         mContext.enforceCallingOrSelfPermission(
    478                 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
    479         mConnector.doCommand(String.format("softap stop " + wlanIface));
    480         mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
    481         mConnector.doCommand(String.format("softap start " + wlanIface));
    482         if (wifiConfig == null) {
    483             mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
    484         } else {
    485             /**
    486              * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
    487              * argv1 - wlan interface
    488              * argv2 - softap interface
    489              * argv3 - SSID
    490              * argv4 - Security
    491              * argv5 - Key
    492              * argv6 - Channel
    493              * argv7 - Preamble
    494              * argv8 - Max SCB
    495              */
    496             String str = String.format("softap set " + wlanIface + " " + softapIface +
    497                                        " %s %s %s", convertQuotedString(wifiConfig.SSID),
    498                                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
    499                                        "wpa2-psk" : "open",
    500                                        convertQuotedString(wifiConfig.preSharedKey));
    501             mConnector.doCommand(str);
    502         }
    503         mConnector.doCommand(String.format("softap startap"));
    504     }
    505 
    506     private String convertQuotedString(String s) {
    507         if (s == null) {
    508             return s;
    509         }
    510         /* Replace \ with \\, then " with \" and add quotes at end */
    511         return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
    512     }
    513 
    514     public void stopAccessPoint() throws IllegalStateException {
    515         mContext.enforceCallingOrSelfPermission(
    516                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    517         mContext.enforceCallingOrSelfPermission(
    518                 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
    519         mConnector.doCommand("softap stopap");
    520     }
    521 
    522     public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
    523             throws IllegalStateException {
    524         mContext.enforceCallingOrSelfPermission(
    525                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    526         mContext.enforceCallingOrSelfPermission(
    527             android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
    528         if (wifiConfig == null) {
    529             mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
    530         } else {
    531             String str = String.format("softap set " + wlanIface + " " + softapIface +
    532                                        " %s %s %s", convertQuotedString(wifiConfig.SSID),
    533                                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
    534                                        "wpa2-psk" : "open",
    535                                        convertQuotedString(wifiConfig.preSharedKey));
    536             mConnector.doCommand(str);
    537         }
    538     }
    539 
    540     private long getInterfaceCounter(String iface, boolean rx) {
    541         mContext.enforceCallingOrSelfPermission(
    542                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    543         try {
    544             String rsp = mConnector.doCommand(
    545                     String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
    546             String []tok = rsp.split(" ");
    547             int code;
    548             try {
    549                 code = Integer.parseInt(tok[0]);
    550             } catch (NumberFormatException nfe) {
    551                 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
    552                 return -1;
    553             }
    554             if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
    555                     !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
    556                 Slog.e(TAG, String.format("Unexpected response code %d", code));
    557                 return -1;
    558             }
    559             return Long.parseLong(tok[1]);
    560         } catch (Exception e) {
    561             Slog.e(TAG, String.format(
    562                     "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
    563         }
    564         return -1;
    565     }
    566 
    567     public long getInterfaceRxCounter(String iface) {
    568         return getInterfaceCounter(iface, true);
    569     }
    570 
    571     public long getInterfaceTxCounter(String iface) {
    572         return getInterfaceCounter(iface, false);
    573     }
    574 
    575     public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
    576         mContext.enforceCallingOrSelfPermission(
    577                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    578         mConnector.doCommand(String.format(
    579                 "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
    580     }
    581 
    582     private int getInterfaceThrottle(String iface, boolean rx) {
    583         mContext.enforceCallingOrSelfPermission(
    584                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    585         try {
    586             String rsp = mConnector.doCommand(
    587                     String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0);
    588             String []tok = rsp.split(" ");
    589             int code;
    590             try {
    591                 code = Integer.parseInt(tok[0]);
    592             } catch (NumberFormatException nfe) {
    593                 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
    594                 return -1;
    595             }
    596             if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
    597                     !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
    598                 Slog.e(TAG, String.format("Unexpected response code %d", code));
    599                 return -1;
    600             }
    601             return Integer.parseInt(tok[1]);
    602         } catch (Exception e) {
    603             Slog.e(TAG, String.format(
    604                     "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
    605         }
    606         return -1;
    607     }
    608 
    609     public int getInterfaceRxThrottle(String iface) {
    610         return getInterfaceThrottle(iface, true);
    611     }
    612 
    613     public int getInterfaceTxThrottle(String iface) {
    614         return getInterfaceThrottle(iface, false);
    615     }
    616 }
    617