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