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.NoSuchElementException;
     39 import java.util.StringTokenizer;
     40 import android.provider.Settings;
     41 import android.content.ContentResolver;
     42 import android.database.ContentObserver;
     43 
     44 import java.io.File;
     45 import java.io.FileReader;
     46 import java.lang.IllegalStateException;
     47 
     48 import java.net.InetAddress;
     49 import java.net.UnknownHostException;
     50 import java.util.concurrent.CountDownLatch;
     51 
     52 /**
     53  * @hide
     54  */
     55 class NetworkManagementService extends INetworkManagementService.Stub {
     56 
     57     private static final String TAG = "NetworkManagmentService";
     58     private static final boolean DBG = false;
     59     private static final String NETD_TAG = "NetdConnector";
     60 
     61     class NetdResponseCode {
     62         public static final int InterfaceListResult       = 110;
     63         public static final int TetherInterfaceListResult = 111;
     64         public static final int TetherDnsFwdTgtListResult = 112;
     65         public static final int TtyListResult             = 113;
     66 
     67         public static final int TetherStatusResult        = 210;
     68         public static final int IpFwdStatusResult         = 211;
     69         public static final int InterfaceGetCfgResult     = 213;
     70         public static final int SoftapStatusResult        = 214;
     71         public static final int UsbRNDISStatusResult      = 215;
     72         public static final int InterfaceRxCounterResult  = 216;
     73         public static final int InterfaceTxCounterResult  = 217;
     74         public static final int InterfaceRxThrottleResult = 218;
     75         public static final int InterfaceTxThrottleResult = 219;
     76 
     77         public static final int InterfaceChange           = 600;
     78     }
     79 
     80     /**
     81      * Binder context for this service
     82      */
     83     private Context mContext;
     84 
     85     /**
     86      * connector object for communicating with netd
     87      */
     88     private NativeDaemonConnector mConnector;
     89 
     90     private Thread mThread;
     91     private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
     92 
     93     private ArrayList<INetworkManagementEventObserver> mObservers;
     94 
     95     /**
     96      * Constructs a new NetworkManagementService instance
     97      *
     98      * @param context  Binder context for this service
     99      */
    100     private NetworkManagementService(Context context) {
    101         mContext = context;
    102         mObservers = new ArrayList<INetworkManagementEventObserver>();
    103 
    104         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
    105             return;
    106         }
    107 
    108         mConnector = new NativeDaemonConnector(
    109                 new NetdCallbackReceiver(), "netd", 10, NETD_TAG);
    110         mThread = new Thread(mConnector, NETD_TAG);
    111     }
    112 
    113     public static NetworkManagementService create(Context context) throws InterruptedException {
    114         NetworkManagementService service = new NetworkManagementService(context);
    115         if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
    116         service.mThread.start();
    117         if (DBG) Slog.d(TAG, "Awaiting socket connection");
    118         service.mConnectedSignal.await();
    119         if (DBG) Slog.d(TAG, "Connected");
    120         return service;
    121     }
    122 
    123     public void registerObserver(INetworkManagementEventObserver obs) {
    124         Slog.d(TAG, "Registering observer");
    125         mObservers.add(obs);
    126     }
    127 
    128     public void unregisterObserver(INetworkManagementEventObserver obs) {
    129         Slog.d(TAG, "Unregistering observer");
    130         mObservers.remove(mObservers.indexOf(obs));
    131     }
    132 
    133     /**
    134      * Notify our observers of an interface link status change
    135      */
    136     private void notifyInterfaceLinkStatusChanged(String iface, boolean link) {
    137         for (INetworkManagementEventObserver obs : mObservers) {
    138             try {
    139                 obs.interfaceLinkStatusChanged(iface, link);
    140             } catch (Exception ex) {
    141                 Slog.w(TAG, "Observer notifier failed", ex);
    142             }
    143         }
    144     }
    145 
    146     /**
    147      * Notify our observers of an interface addition.
    148      */
    149     private void notifyInterfaceAdded(String iface) {
    150         for (INetworkManagementEventObserver obs : mObservers) {
    151             try {
    152                 obs.interfaceAdded(iface);
    153             } catch (Exception ex) {
    154                 Slog.w(TAG, "Observer notifier failed", ex);
    155             }
    156         }
    157     }
    158 
    159     /**
    160      * Notify our observers of an interface removal.
    161      */
    162     private void notifyInterfaceRemoved(String iface) {
    163         for (INetworkManagementEventObserver obs : mObservers) {
    164             try {
    165                 obs.interfaceRemoved(iface);
    166             } catch (Exception ex) {
    167                 Slog.w(TAG, "Observer notifier failed", ex);
    168             }
    169         }
    170     }
    171 
    172     /**
    173      * Let us know the daemon is connected
    174      */
    175     protected void onConnected() {
    176         if (DBG) Slog.d(TAG, "onConnected");
    177         mConnectedSignal.countDown();
    178     }
    179 
    180 
    181     //
    182     // Netd Callback handling
    183     //
    184 
    185     class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
    186         public void onDaemonConnected() {
    187             NetworkManagementService.this.onConnected();
    188             new Thread() {
    189                 public void run() {
    190                 }
    191             }.start();
    192         }
    193         public boolean onEvent(int code, String raw, String[] cooked) {
    194             if (code == NetdResponseCode.InterfaceChange) {
    195                 /*
    196                  * a network interface change occured
    197                  * Format: "NNN Iface added <name>"
    198                  *         "NNN Iface removed <name>"
    199                  *         "NNN Iface changed <name> <up/down>"
    200                  */
    201                 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
    202                     throw new IllegalStateException(
    203                             String.format("Invalid event from daemon (%s)", raw));
    204                 }
    205                 if (cooked[2].equals("added")) {
    206                     notifyInterfaceAdded(cooked[3]);
    207                     return true;
    208                 } else if (cooked[2].equals("removed")) {
    209                     notifyInterfaceRemoved(cooked[3]);
    210                     return true;
    211                 } else if (cooked[2].equals("changed") && cooked.length == 5) {
    212                     notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up"));
    213                     return true;
    214                 }
    215                 throw new IllegalStateException(
    216                         String.format("Invalid event from daemon (%s)", raw));
    217             }
    218             return false;
    219         }
    220     }
    221 
    222     private static int stringToIpAddr(String addrString) throws UnknownHostException {
    223         try {
    224             String[] parts = addrString.split("\\.");
    225             if (parts.length != 4) {
    226                 throw new UnknownHostException(addrString);
    227             }
    228 
    229             int a = Integer.parseInt(parts[0])      ;
    230             int b = Integer.parseInt(parts[1]) <<  8;
    231             int c = Integer.parseInt(parts[2]) << 16;
    232             int d = Integer.parseInt(parts[3]) << 24;
    233 
    234             return a | b | c | d;
    235         } catch (NumberFormatException ex) {
    236             throw new UnknownHostException(addrString);
    237         }
    238     }
    239 
    240     public static String intToIpString(int i) {
    241         return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >>  8 ) & 0xFF) + "." +
    242                (i & 0xFF);
    243     }
    244 
    245     //
    246     // INetworkManagementService members
    247     //
    248 
    249     public String[] listInterfaces() throws IllegalStateException {
    250         mContext.enforceCallingOrSelfPermission(
    251                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    252 
    253         try {
    254             return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
    255         } catch (NativeDaemonConnectorException e) {
    256             throw new IllegalStateException(
    257                     "Cannot communicate with native daemon to list interfaces");
    258         }
    259     }
    260 
    261     public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
    262         String rsp;
    263         try {
    264             rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
    265         } catch (NativeDaemonConnectorException e) {
    266             throw new IllegalStateException(
    267                     "Cannot communicate with native daemon to get interface config");
    268         }
    269         Slog.d(TAG, String.format("rsp <%s>", rsp));
    270 
    271         // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
    272         StringTokenizer st = new StringTokenizer(rsp);
    273 
    274         InterfaceConfiguration cfg;
    275         try {
    276             try {
    277                 int code = Integer.parseInt(st.nextToken(" "));
    278                 if (code != NetdResponseCode.InterfaceGetCfgResult) {
    279                     throw new IllegalStateException(
    280                         String.format("Expected code %d, but got %d",
    281                                 NetdResponseCode.InterfaceGetCfgResult, code));
    282                 }
    283             } catch (NumberFormatException nfe) {
    284                 throw new IllegalStateException(
    285                         String.format("Invalid response from daemon (%s)", rsp));
    286             }
    287 
    288             cfg = new InterfaceConfiguration();
    289             cfg.hwAddr = st.nextToken(" ");
    290             try {
    291                 cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
    292             } catch (UnknownHostException uhe) {
    293                 Slog.e(TAG, "Failed to parse ipaddr", uhe);
    294                 cfg.ipAddr = 0;
    295             }
    296 
    297             try {
    298                 cfg.netmask = stringToIpAddr(st.nextToken(" "));
    299             } catch (UnknownHostException uhe) {
    300                 Slog.e(TAG, "Failed to parse netmask", uhe);
    301                 cfg.netmask = 0;
    302             }
    303             cfg.interfaceFlags = st.nextToken("]").trim() +"]";
    304         } catch (NoSuchElementException nsee) {
    305             throw new IllegalStateException(
    306                     String.format("Invalid response from daemon (%s)", rsp));
    307         }
    308         Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
    309         return cfg;
    310     }
    311 
    312     public void setInterfaceConfig(
    313             String iface, InterfaceConfiguration cfg) throws IllegalStateException {
    314         String cmd = String.format("interface setcfg %s %s %s %s", iface,
    315                 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
    316         try {
    317             mConnector.doCommand(cmd);
    318         } catch (NativeDaemonConnectorException e) {
    319             throw new IllegalStateException(
    320                     "Unable to communicate with native daemon to interface setcfg");
    321         }
    322     }
    323 
    324     public void shutdown() {
    325         if (mContext.checkCallingOrSelfPermission(
    326                 android.Manifest.permission.SHUTDOWN)
    327                 != PackageManager.PERMISSION_GRANTED) {
    328             throw new SecurityException("Requires SHUTDOWN permission");
    329         }
    330 
    331         Slog.d(TAG, "Shutting down");
    332     }
    333 
    334     public boolean getIpForwardingEnabled() throws IllegalStateException{
    335         mContext.enforceCallingOrSelfPermission(
    336                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    337 
    338         ArrayList<String> rsp;
    339         try {
    340             rsp = mConnector.doCommand("ipfwd status");
    341         } catch (NativeDaemonConnectorException e) {
    342             throw new IllegalStateException(
    343                     "Unable to communicate with native daemon to ipfwd status");
    344         }
    345 
    346         for (String line : rsp) {
    347             String[] tok = line.split(" ");
    348             if (tok.length < 3) {
    349                 Slog.e(TAG, "Malformed response from native daemon: " + line);
    350                 return false;
    351             }
    352 
    353             int code = Integer.parseInt(tok[0]);
    354             if (code == NetdResponseCode.IpFwdStatusResult) {
    355                 // 211 Forwarding <enabled/disabled>
    356                 return "enabled".equals(tok[2]);
    357             } else {
    358                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
    359             }
    360         }
    361         throw new IllegalStateException("Got an empty response");
    362     }
    363 
    364     public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
    365         mContext.enforceCallingOrSelfPermission(
    366                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    367         mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
    368     }
    369 
    370     public void startTethering(String[] dhcpRange)
    371              throws IllegalStateException {
    372         mContext.enforceCallingOrSelfPermission(
    373                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    374         // cmd is "tether start first_start first_stop second_start second_stop ..."
    375         // an odd number of addrs will fail
    376         String cmd = "tether start";
    377         for (String d : dhcpRange) {
    378             cmd += " " + d;
    379         }
    380 
    381         try {
    382             mConnector.doCommand(cmd);
    383         } catch (NativeDaemonConnectorException e) {
    384             throw new IllegalStateException("Unable to communicate to native daemon");
    385         }
    386     }
    387 
    388     public void stopTethering() throws IllegalStateException {
    389         mContext.enforceCallingOrSelfPermission(
    390                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    391         try {
    392             mConnector.doCommand("tether stop");
    393         } catch (NativeDaemonConnectorException e) {
    394             throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
    395         }
    396     }
    397 
    398     public boolean isTetheringStarted() throws IllegalStateException {
    399         mContext.enforceCallingOrSelfPermission(
    400                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    401 
    402         ArrayList<String> rsp;
    403         try {
    404             rsp = mConnector.doCommand("tether status");
    405         } catch (NativeDaemonConnectorException e) {
    406             throw new IllegalStateException(
    407                     "Unable to communicate to native daemon to get tether status");
    408         }
    409 
    410         for (String line : rsp) {
    411             String[] tok = line.split(" ");
    412             if (tok.length < 3) {
    413                 throw new IllegalStateException("Malformed response for tether status: " + line);
    414             }
    415             int code = Integer.parseInt(tok[0]);
    416             if (code == NetdResponseCode.TetherStatusResult) {
    417                 // XXX: Tethering services <started/stopped> <TBD>...
    418                 return "started".equals(tok[2]);
    419             } else {
    420                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
    421             }
    422         }
    423         throw new IllegalStateException("Got an empty response");
    424     }
    425 
    426     public void tetherInterface(String iface) throws IllegalStateException {
    427         mContext.enforceCallingOrSelfPermission(
    428                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    429         try {
    430             mConnector.doCommand("tether interface add " + iface);
    431         } catch (NativeDaemonConnectorException e) {
    432             throw new IllegalStateException(
    433                     "Unable to communicate to native daemon for adding tether interface");
    434         }
    435     }
    436 
    437     public void untetherInterface(String iface) {
    438         mContext.enforceCallingOrSelfPermission(
    439                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    440         try {
    441             mConnector.doCommand("tether interface remove " + iface);
    442         } catch (NativeDaemonConnectorException e) {
    443             throw new IllegalStateException(
    444                     "Unable to communicate to native daemon for removing tether interface");
    445         }
    446     }
    447 
    448     public String[] listTetheredInterfaces() throws IllegalStateException {
    449         mContext.enforceCallingOrSelfPermission(
    450                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    451         try {
    452             return mConnector.doListCommand(
    453                     "tether interface list", NetdResponseCode.TetherInterfaceListResult);
    454         } catch (NativeDaemonConnectorException e) {
    455             throw new IllegalStateException(
    456                     "Unable to communicate to native daemon for listing tether interfaces");
    457         }
    458     }
    459 
    460     public void setDnsForwarders(String[] dns) throws IllegalStateException {
    461         mContext.enforceCallingOrSelfPermission(
    462                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    463         try {
    464             String cmd = "tether dns set";
    465             for (String s : dns) {
    466                 cmd += " " + InetAddress.getByName(s).getHostAddress();
    467             }
    468             try {
    469                 mConnector.doCommand(cmd);
    470             } catch (NativeDaemonConnectorException e) {
    471                 throw new IllegalStateException(
    472                         "Unable to communicate to native daemon for setting tether dns");
    473             }
    474         } catch (UnknownHostException e) {
    475             throw new IllegalStateException("Error resolving dns name", e);
    476         }
    477     }
    478 
    479     public String[] getDnsForwarders() throws IllegalStateException {
    480         mContext.enforceCallingOrSelfPermission(
    481                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    482         try {
    483             return mConnector.doListCommand(
    484                     "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
    485         } catch (NativeDaemonConnectorException e) {
    486             throw new IllegalStateException(
    487                     "Unable to communicate to native daemon for listing tether dns");
    488         }
    489     }
    490 
    491     public void enableNat(String internalInterface, String externalInterface)
    492             throws IllegalStateException {
    493         mContext.enforceCallingOrSelfPermission(
    494                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    495         try {
    496             mConnector.doCommand(
    497                     String.format("nat enable %s %s", internalInterface, externalInterface));
    498         } catch (NativeDaemonConnectorException e) {
    499             throw new IllegalStateException(
    500                     "Unable to communicate to native daemon for enabling NAT interface");
    501         }
    502     }
    503 
    504     public void disableNat(String internalInterface, String externalInterface)
    505             throws IllegalStateException {
    506         mContext.enforceCallingOrSelfPermission(
    507                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    508         try {
    509             mConnector.doCommand(
    510                     String.format("nat disable %s %s", internalInterface, externalInterface));
    511         } catch (NativeDaemonConnectorException e) {
    512             throw new IllegalStateException(
    513                     "Unable to communicate to native daemon for disabling NAT interface");
    514         }
    515     }
    516 
    517     public String[] listTtys() throws IllegalStateException {
    518         mContext.enforceCallingOrSelfPermission(
    519                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    520         try {
    521             return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
    522         } catch (NativeDaemonConnectorException e) {
    523             throw new IllegalStateException(
    524                     "Unable to communicate to native daemon for listing TTYs");
    525         }
    526     }
    527 
    528     public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
    529             String dns2Addr) throws IllegalStateException {
    530         try {
    531             mContext.enforceCallingOrSelfPermission(
    532                     android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    533             mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
    534                     InetAddress.getByName(localAddr).getHostAddress(),
    535                     InetAddress.getByName(remoteAddr).getHostAddress(),
    536                     InetAddress.getByName(dns1Addr).getHostAddress(),
    537                     InetAddress.getByName(dns2Addr).getHostAddress()));
    538         } catch (UnknownHostException e) {
    539             throw new IllegalStateException("Error resolving addr", e);
    540         } catch (NativeDaemonConnectorException e) {
    541             throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
    542         }
    543     }
    544 
    545     public void detachPppd(String tty) throws IllegalStateException {
    546         mContext.enforceCallingOrSelfPermission(
    547                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    548         try {
    549             mConnector.doCommand(String.format("pppd detach %s", tty));
    550         } catch (NativeDaemonConnectorException e) {
    551             throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
    552         }
    553     }
    554 
    555     public void startUsbRNDIS() throws IllegalStateException {
    556         mContext.enforceCallingOrSelfPermission(
    557                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    558         try {
    559             mConnector.doCommand("usb startrndis");
    560         } catch (NativeDaemonConnectorException e) {
    561             throw new IllegalStateException(
    562                     "Error communicating to native daemon for starting RNDIS", e);
    563         }
    564     }
    565 
    566     public void stopUsbRNDIS() throws IllegalStateException {
    567         mContext.enforceCallingOrSelfPermission(
    568                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    569         try {
    570             mConnector.doCommand("usb stoprndis");
    571         } catch (NativeDaemonConnectorException e) {
    572             throw new IllegalStateException("Error communicating to native daemon", e);
    573         }
    574     }
    575 
    576     public boolean isUsbRNDISStarted() throws IllegalStateException {
    577         mContext.enforceCallingOrSelfPermission(
    578                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    579         ArrayList<String> rsp;
    580         try {
    581             rsp = mConnector.doCommand("usb rndisstatus");
    582         } catch (NativeDaemonConnectorException e) {
    583             throw new IllegalStateException(
    584                     "Error communicating to native daemon to check RNDIS status", e);
    585         }
    586 
    587         for (String line : rsp) {
    588             String []tok = line.split(" ");
    589             int code = Integer.parseInt(tok[0]);
    590             if (code == NetdResponseCode.UsbRNDISStatusResult) {
    591                 if (tok[3].equals("started"))
    592                     return true;
    593                 return false;
    594             } else {
    595                 throw new IllegalStateException(String.format("Unexpected response code %d", code));
    596             }
    597         }
    598         throw new IllegalStateException("Got an empty response");
    599     }
    600 
    601     public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
    602              throws IllegalStateException {
    603         mContext.enforceCallingOrSelfPermission(
    604                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    605         mContext.enforceCallingOrSelfPermission(
    606                 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
    607         try {
    608             mConnector.doCommand(String.format("softap stop " + wlanIface));
    609             mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
    610             mConnector.doCommand(String.format("softap start " + wlanIface));
    611             if (wifiConfig == null) {
    612                 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
    613             } else {
    614                 /**
    615                  * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
    616                  * argv1 - wlan interface
    617                  * argv2 - softap interface
    618                  * argv3 - SSID
    619                  * argv4 - Security
    620                  * argv5 - Key
    621                  * argv6 - Channel
    622                  * argv7 - Preamble
    623                  * argv8 - Max SCB
    624                  */
    625                 String str = String.format("softap set " + wlanIface + " " + softapIface +
    626                                            " %s %s %s", convertQuotedString(wifiConfig.SSID),
    627                                            wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
    628                                            "wpa2-psk" : "open",
    629                                            convertQuotedString(wifiConfig.preSharedKey));
    630                 mConnector.doCommand(str);
    631             }
    632             mConnector.doCommand(String.format("softap startap"));
    633         } catch (NativeDaemonConnectorException e) {
    634             throw new IllegalStateException("Error communicating to native daemon to start softap", e);
    635         }
    636     }
    637 
    638     private String convertQuotedString(String s) {
    639         if (s == null) {
    640             return s;
    641         }
    642         /* Replace \ with \\, then " with \" and add quotes at end */
    643         return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
    644     }
    645 
    646     public void stopAccessPoint() throws IllegalStateException {
    647         mContext.enforceCallingOrSelfPermission(
    648                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    649         mContext.enforceCallingOrSelfPermission(
    650                 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
    651         try {
    652             mConnector.doCommand("softap stopap");
    653         } catch (NativeDaemonConnectorException e) {
    654             throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
    655                     e);
    656         }
    657     }
    658 
    659     public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
    660             throws IllegalStateException {
    661         mContext.enforceCallingOrSelfPermission(
    662                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    663         mContext.enforceCallingOrSelfPermission(
    664             android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
    665         try {
    666             if (wifiConfig == null) {
    667                 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
    668             } else {
    669                 String str = String.format("softap set " + wlanIface + " " + softapIface
    670                         + " %s %s %s", convertQuotedString(wifiConfig.SSID),
    671                         wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open",
    672                         convertQuotedString(wifiConfig.preSharedKey));
    673                 mConnector.doCommand(str);
    674             }
    675         } catch (NativeDaemonConnectorException e) {
    676             throw new IllegalStateException("Error communicating to native daemon to set soft AP",
    677                     e);
    678         }
    679     }
    680 
    681     private long getInterfaceCounter(String iface, boolean rx) {
    682         mContext.enforceCallingOrSelfPermission(
    683                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    684         try {
    685             String rsp;
    686             try {
    687                 rsp = mConnector.doCommand(
    688                         String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
    689             } catch (NativeDaemonConnectorException e1) {
    690                 Slog.e(TAG, "Error communicating with native daemon", e1);
    691                 return -1;
    692             }
    693 
    694             String[] tok = rsp.split(" ");
    695             if (tok.length < 2) {
    696                 Slog.e(TAG, String.format("Malformed response for reading %s interface",
    697                         (rx ? "rx" : "tx")));
    698                 return -1;
    699             }
    700 
    701             int code;
    702             try {
    703                 code = Integer.parseInt(tok[0]);
    704             } catch (NumberFormatException nfe) {
    705                 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
    706                 return -1;
    707             }
    708             if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
    709                     !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
    710                 Slog.e(TAG, String.format("Unexpected response code %d", code));
    711                 return -1;
    712             }
    713             return Long.parseLong(tok[1]);
    714         } catch (Exception e) {
    715             Slog.e(TAG, String.format(
    716                     "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
    717         }
    718         return -1;
    719     }
    720 
    721     public long getInterfaceRxCounter(String iface) {
    722         return getInterfaceCounter(iface, true);
    723     }
    724 
    725     public long getInterfaceTxCounter(String iface) {
    726         return getInterfaceCounter(iface, false);
    727     }
    728 
    729     public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
    730         mContext.enforceCallingOrSelfPermission(
    731                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
    732         try {
    733             mConnector.doCommand(String.format(
    734                     "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
    735         } catch (NativeDaemonConnectorException e) {
    736             Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
    737         }
    738     }
    739 
    740     private int getInterfaceThrottle(String iface, boolean rx) {
    741         mContext.enforceCallingOrSelfPermission(
    742                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
    743         try {
    744             String rsp;
    745             try {
    746                 rsp = mConnector.doCommand(
    747                         String.format("interface getthrottle %s %s", iface,
    748                                 (rx ? "rx" : "tx"))).get(0);
    749             } catch (NativeDaemonConnectorException e) {
    750                 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
    751                 return -1;
    752             }
    753 
    754             String[] tok = rsp.split(" ");
    755             if (tok.length < 2) {
    756                 Slog.e(TAG, "Malformed response to getthrottle command");
    757                 return -1;
    758             }
    759 
    760             int code;
    761             try {
    762                 code = Integer.parseInt(tok[0]);
    763             } catch (NumberFormatException nfe) {
    764                 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
    765                 return -1;
    766             }
    767             if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
    768                     !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
    769                 Slog.e(TAG, String.format("Unexpected response code %d", code));
    770                 return -1;
    771             }
    772             return Integer.parseInt(tok[1]);
    773         } catch (Exception e) {
    774             Slog.e(TAG, String.format(
    775                     "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
    776         }
    777         return -1;
    778     }
    779 
    780     public int getInterfaceRxThrottle(String iface) {
    781         return getInterfaceThrottle(iface, true);
    782     }
    783 
    784     public int getInterfaceTxThrottle(String iface) {
    785         return getInterfaceThrottle(iface, false);
    786     }
    787 }
    788