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