NetworkManagementService.java revision 81d5ad515a9379432b2907aa9bcc830303202c84
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;
50import java.util.concurrent.CountDownLatch;
51
52/**
53 * @hide
54 */
55class 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
223    //
224    // INetworkManagementService members
225    //
226
227    public String[] listInterfaces() throws IllegalStateException {
228        mContext.enforceCallingOrSelfPermission(
229                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
230
231        try {
232            return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
233        } catch (NativeDaemonConnectorException e) {
234            throw new IllegalStateException(
235                    "Cannot communicate with native daemon to list interfaces");
236        }
237    }
238
239    public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
240        String rsp;
241        try {
242            rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
243        } catch (NativeDaemonConnectorException e) {
244            throw new IllegalStateException(
245                    "Cannot communicate with native daemon to get interface config");
246        }
247        Slog.d(TAG, String.format("rsp <%s>", rsp));
248
249        // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
250        StringTokenizer st = new StringTokenizer(rsp);
251
252        InterfaceConfiguration cfg;
253        try {
254            try {
255                int code = Integer.parseInt(st.nextToken(" "));
256                if (code != NetdResponseCode.InterfaceGetCfgResult) {
257                    throw new IllegalStateException(
258                        String.format("Expected code %d, but got %d",
259                                NetdResponseCode.InterfaceGetCfgResult, code));
260                }
261            } catch (NumberFormatException nfe) {
262                throw new IllegalStateException(
263                        String.format("Invalid response from daemon (%s)", rsp));
264            }
265
266            cfg = new InterfaceConfiguration();
267            cfg.hwAddr = st.nextToken(" ");
268            try {
269                cfg.addr = InetAddress.getByName(st.nextToken(" "));
270            } catch (UnknownHostException uhe) {
271                Slog.e(TAG, "Failed to parse ipaddr", uhe);
272            }
273
274            try {
275                cfg.mask = InetAddress.getByName(st.nextToken(" "));
276            } catch (UnknownHostException uhe) {
277                Slog.e(TAG, "Failed to parse netmask", uhe);
278            }
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                cfg.addr.getHostAddress(), cfg.mask.getHostAddress(),
293                cfg.interfaceFlags);
294        try {
295            mConnector.doCommand(cmd);
296        } catch (NativeDaemonConnectorException e) {
297            throw new IllegalStateException(
298                    "Unable to communicate with native daemon to interface setcfg - " + e);
299        }
300    }
301
302    public void shutdown() {
303        if (mContext.checkCallingOrSelfPermission(
304                android.Manifest.permission.SHUTDOWN)
305                != PackageManager.PERMISSION_GRANTED) {
306            throw new SecurityException("Requires SHUTDOWN permission");
307        }
308
309        Slog.d(TAG, "Shutting down");
310    }
311
312    public boolean getIpForwardingEnabled() throws IllegalStateException{
313        mContext.enforceCallingOrSelfPermission(
314                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
315
316        ArrayList<String> rsp;
317        try {
318            rsp = mConnector.doCommand("ipfwd status");
319        } catch (NativeDaemonConnectorException e) {
320            throw new IllegalStateException(
321                    "Unable to communicate with native daemon to ipfwd status");
322        }
323
324        for (String line : rsp) {
325            String[] tok = line.split(" ");
326            if (tok.length < 3) {
327                Slog.e(TAG, "Malformed response from native daemon: " + line);
328                return false;
329            }
330
331            int code = Integer.parseInt(tok[0]);
332            if (code == NetdResponseCode.IpFwdStatusResult) {
333                // 211 Forwarding <enabled/disabled>
334                return "enabled".equals(tok[2]);
335            } else {
336                throw new IllegalStateException(String.format("Unexpected response code %d", code));
337            }
338        }
339        throw new IllegalStateException("Got an empty response");
340    }
341
342    public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
343        mContext.enforceCallingOrSelfPermission(
344                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
345        mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
346    }
347
348    public void startTethering(String[] dhcpRange)
349             throws IllegalStateException {
350        mContext.enforceCallingOrSelfPermission(
351                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
352        // cmd is "tether start first_start first_stop second_start second_stop ..."
353        // an odd number of addrs will fail
354        String cmd = "tether start";
355        for (String d : dhcpRange) {
356            cmd += " " + d;
357        }
358
359        try {
360            mConnector.doCommand(cmd);
361        } catch (NativeDaemonConnectorException e) {
362            throw new IllegalStateException("Unable to communicate to native daemon");
363        }
364    }
365
366    public void stopTethering() throws IllegalStateException {
367        mContext.enforceCallingOrSelfPermission(
368                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
369        try {
370            mConnector.doCommand("tether stop");
371        } catch (NativeDaemonConnectorException e) {
372            throw new IllegalStateException("Unable to communicate to native daemon to stop tether");
373        }
374    }
375
376    public boolean isTetheringStarted() throws IllegalStateException {
377        mContext.enforceCallingOrSelfPermission(
378                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
379
380        ArrayList<String> rsp;
381        try {
382            rsp = mConnector.doCommand("tether status");
383        } catch (NativeDaemonConnectorException e) {
384            throw new IllegalStateException(
385                    "Unable to communicate to native daemon to get tether status");
386        }
387
388        for (String line : rsp) {
389            String[] tok = line.split(" ");
390            if (tok.length < 3) {
391                throw new IllegalStateException("Malformed response for tether status: " + line);
392            }
393            int code = Integer.parseInt(tok[0]);
394            if (code == NetdResponseCode.TetherStatusResult) {
395                // XXX: Tethering services <started/stopped> <TBD>...
396                return "started".equals(tok[2]);
397            } else {
398                throw new IllegalStateException(String.format("Unexpected response code %d", code));
399            }
400        }
401        throw new IllegalStateException("Got an empty response");
402    }
403
404    public void tetherInterface(String iface) throws IllegalStateException {
405        mContext.enforceCallingOrSelfPermission(
406                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
407        try {
408            mConnector.doCommand("tether interface add " + iface);
409        } catch (NativeDaemonConnectorException e) {
410            throw new IllegalStateException(
411                    "Unable to communicate to native daemon for adding tether interface");
412        }
413    }
414
415    public void untetherInterface(String iface) {
416        mContext.enforceCallingOrSelfPermission(
417                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
418        try {
419            mConnector.doCommand("tether interface remove " + iface);
420        } catch (NativeDaemonConnectorException e) {
421            throw new IllegalStateException(
422                    "Unable to communicate to native daemon for removing tether interface");
423        }
424    }
425
426    public String[] listTetheredInterfaces() throws IllegalStateException {
427        mContext.enforceCallingOrSelfPermission(
428                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
429        try {
430            return mConnector.doListCommand(
431                    "tether interface list", NetdResponseCode.TetherInterfaceListResult);
432        } catch (NativeDaemonConnectorException e) {
433            throw new IllegalStateException(
434                    "Unable to communicate to native daemon for listing tether interfaces");
435        }
436    }
437
438    public void setDnsForwarders(String[] dns) throws IllegalStateException {
439        mContext.enforceCallingOrSelfPermission(
440                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
441        try {
442            String cmd = "tether dns set";
443            for (String s : dns) {
444                cmd += " " + InetAddress.getByName(s).getHostAddress();
445            }
446            try {
447                mConnector.doCommand(cmd);
448            } catch (NativeDaemonConnectorException e) {
449                throw new IllegalStateException(
450                        "Unable to communicate to native daemon for setting tether dns");
451            }
452        } catch (UnknownHostException e) {
453            throw new IllegalStateException("Error resolving dns name", e);
454        }
455    }
456
457    public String[] getDnsForwarders() throws IllegalStateException {
458        mContext.enforceCallingOrSelfPermission(
459                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
460        try {
461            return mConnector.doListCommand(
462                    "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
463        } catch (NativeDaemonConnectorException e) {
464            throw new IllegalStateException(
465                    "Unable to communicate to native daemon for listing tether dns");
466        }
467    }
468
469    public void enableNat(String internalInterface, String externalInterface)
470            throws IllegalStateException {
471        mContext.enforceCallingOrSelfPermission(
472                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
473        try {
474            mConnector.doCommand(
475                    String.format("nat enable %s %s", internalInterface, externalInterface));
476        } catch (NativeDaemonConnectorException e) {
477            throw new IllegalStateException(
478                    "Unable to communicate to native daemon for enabling NAT interface");
479        }
480    }
481
482    public void disableNat(String internalInterface, String externalInterface)
483            throws IllegalStateException {
484        mContext.enforceCallingOrSelfPermission(
485                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
486        try {
487            mConnector.doCommand(
488                    String.format("nat disable %s %s", internalInterface, externalInterface));
489        } catch (NativeDaemonConnectorException e) {
490            throw new IllegalStateException(
491                    "Unable to communicate to native daemon for disabling NAT interface");
492        }
493    }
494
495    public String[] listTtys() throws IllegalStateException {
496        mContext.enforceCallingOrSelfPermission(
497                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
498        try {
499            return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
500        } catch (NativeDaemonConnectorException e) {
501            throw new IllegalStateException(
502                    "Unable to communicate to native daemon for listing TTYs");
503        }
504    }
505
506    public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
507            String dns2Addr) throws IllegalStateException {
508        try {
509            mContext.enforceCallingOrSelfPermission(
510                    android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
511            mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
512                    InetAddress.getByName(localAddr).getHostAddress(),
513                    InetAddress.getByName(remoteAddr).getHostAddress(),
514                    InetAddress.getByName(dns1Addr).getHostAddress(),
515                    InetAddress.getByName(dns2Addr).getHostAddress()));
516        } catch (UnknownHostException e) {
517            throw new IllegalStateException("Error resolving addr", e);
518        } catch (NativeDaemonConnectorException e) {
519            throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
520        }
521    }
522
523    public void detachPppd(String tty) throws IllegalStateException {
524        mContext.enforceCallingOrSelfPermission(
525                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
526        try {
527            mConnector.doCommand(String.format("pppd detach %s", tty));
528        } catch (NativeDaemonConnectorException e) {
529            throw new IllegalStateException("Error communicating to native daemon to detach pppd", e);
530        }
531    }
532
533    public void startUsbRNDIS() throws IllegalStateException {
534        mContext.enforceCallingOrSelfPermission(
535                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
536        try {
537            mConnector.doCommand("usb startrndis");
538        } catch (NativeDaemonConnectorException e) {
539            throw new IllegalStateException(
540                    "Error communicating to native daemon for starting RNDIS", e);
541        }
542    }
543
544    public void stopUsbRNDIS() throws IllegalStateException {
545        mContext.enforceCallingOrSelfPermission(
546                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
547        try {
548            mConnector.doCommand("usb stoprndis");
549        } catch (NativeDaemonConnectorException e) {
550            throw new IllegalStateException("Error communicating to native daemon", e);
551        }
552    }
553
554    public boolean isUsbRNDISStarted() throws IllegalStateException {
555        mContext.enforceCallingOrSelfPermission(
556                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
557        ArrayList<String> rsp;
558        try {
559            rsp = mConnector.doCommand("usb rndisstatus");
560        } catch (NativeDaemonConnectorException e) {
561            throw new IllegalStateException(
562                    "Error communicating to native daemon to check RNDIS status", e);
563        }
564
565        for (String line : rsp) {
566            String []tok = line.split(" ");
567            int code = Integer.parseInt(tok[0]);
568            if (code == NetdResponseCode.UsbRNDISStatusResult) {
569                if (tok[3].equals("started"))
570                    return true;
571                return false;
572            } else {
573                throw new IllegalStateException(String.format("Unexpected response code %d", code));
574            }
575        }
576        throw new IllegalStateException("Got an empty response");
577    }
578
579    public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
580             throws IllegalStateException {
581        mContext.enforceCallingOrSelfPermission(
582                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
583        mContext.enforceCallingOrSelfPermission(
584                android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
585        try {
586            mConnector.doCommand(String.format("softap stop " + wlanIface));
587            mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP"));
588            mConnector.doCommand(String.format("softap start " + wlanIface));
589            if (wifiConfig == null) {
590                mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
591            } else {
592                /**
593                 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
594                 * argv1 - wlan interface
595                 * argv2 - softap interface
596                 * argv3 - SSID
597                 * argv4 - Security
598                 * argv5 - Key
599                 * argv6 - Channel
600                 * argv7 - Preamble
601                 * argv8 - Max SCB
602                 */
603                String str = String.format("softap set " + wlanIface + " " + softapIface +
604                                           " %s %s %s", convertQuotedString(wifiConfig.SSID),
605                                           wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
606                                           "wpa2-psk" : "open",
607                                           convertQuotedString(wifiConfig.preSharedKey));
608                mConnector.doCommand(str);
609            }
610            mConnector.doCommand(String.format("softap startap"));
611        } catch (NativeDaemonConnectorException e) {
612            throw new IllegalStateException("Error communicating to native daemon to start softap", e);
613        }
614    }
615
616    private String convertQuotedString(String s) {
617        if (s == null) {
618            return s;
619        }
620        /* Replace \ with \\, then " with \" and add quotes at end */
621        return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"';
622    }
623
624    public void stopAccessPoint() throws IllegalStateException {
625        mContext.enforceCallingOrSelfPermission(
626                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
627        mContext.enforceCallingOrSelfPermission(
628                android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
629        try {
630            mConnector.doCommand("softap stopap");
631        } catch (NativeDaemonConnectorException e) {
632            throw new IllegalStateException("Error communicating to native daemon to stop soft AP",
633                    e);
634        }
635    }
636
637    public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface)
638            throws IllegalStateException {
639        mContext.enforceCallingOrSelfPermission(
640                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
641        mContext.enforceCallingOrSelfPermission(
642            android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
643        try {
644            if (wifiConfig == null) {
645                mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface));
646            } else {
647                String str = String.format("softap set " + wlanIface + " " + softapIface
648                        + " %s %s %s", convertQuotedString(wifiConfig.SSID),
649                        wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open",
650                        convertQuotedString(wifiConfig.preSharedKey));
651                mConnector.doCommand(str);
652            }
653        } catch (NativeDaemonConnectorException e) {
654            throw new IllegalStateException("Error communicating to native daemon to set soft AP",
655                    e);
656        }
657    }
658
659    private long getInterfaceCounter(String iface, boolean rx) {
660        mContext.enforceCallingOrSelfPermission(
661                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
662        try {
663            String rsp;
664            try {
665                rsp = mConnector.doCommand(
666                        String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0);
667            } catch (NativeDaemonConnectorException e1) {
668                Slog.e(TAG, "Error communicating with native daemon", e1);
669                return -1;
670            }
671
672            String[] tok = rsp.split(" ");
673            if (tok.length < 2) {
674                Slog.e(TAG, String.format("Malformed response for reading %s interface",
675                        (rx ? "rx" : "tx")));
676                return -1;
677            }
678
679            int code;
680            try {
681                code = Integer.parseInt(tok[0]);
682            } catch (NumberFormatException nfe) {
683                Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
684                return -1;
685            }
686            if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || (
687                    !rx && code != NetdResponseCode.InterfaceTxCounterResult)) {
688                Slog.e(TAG, String.format("Unexpected response code %d", code));
689                return -1;
690            }
691            return Long.parseLong(tok[1]);
692        } catch (Exception e) {
693            Slog.e(TAG, String.format(
694                    "Failed to read interface %s counters", (rx ? "rx" : "tx")), e);
695        }
696        return -1;
697    }
698
699    public long getInterfaceRxCounter(String iface) {
700        return getInterfaceCounter(iface, true);
701    }
702
703    public long getInterfaceTxCounter(String iface) {
704        return getInterfaceCounter(iface, false);
705    }
706
707    public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
708        mContext.enforceCallingOrSelfPermission(
709                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
710        try {
711            mConnector.doCommand(String.format(
712                    "interface setthrottle %s %d %d", iface, rxKbps, txKbps));
713        } catch (NativeDaemonConnectorException e) {
714            Slog.e(TAG, "Error communicating with native daemon to set throttle", e);
715        }
716    }
717
718    private int getInterfaceThrottle(String iface, boolean rx) {
719        mContext.enforceCallingOrSelfPermission(
720                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
721        try {
722            String rsp;
723            try {
724                rsp = mConnector.doCommand(
725                        String.format("interface getthrottle %s %s", iface,
726                                (rx ? "rx" : "tx"))).get(0);
727            } catch (NativeDaemonConnectorException e) {
728                Slog.e(TAG, "Error communicating with native daemon to getthrottle", e);
729                return -1;
730            }
731
732            String[] tok = rsp.split(" ");
733            if (tok.length < 2) {
734                Slog.e(TAG, "Malformed response to getthrottle command");
735                return -1;
736            }
737
738            int code;
739            try {
740                code = Integer.parseInt(tok[0]);
741            } catch (NumberFormatException nfe) {
742                Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
743                return -1;
744            }
745            if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || (
746                    !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) {
747                Slog.e(TAG, String.format("Unexpected response code %d", code));
748                return -1;
749            }
750            return Integer.parseInt(tok[1]);
751        } catch (Exception e) {
752            Slog.e(TAG, String.format(
753                    "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e);
754        }
755        return -1;
756    }
757
758    public int getInterfaceRxThrottle(String iface) {
759        return getInterfaceThrottle(iface, true);
760    }
761
762    public int getInterfaceTxThrottle(String iface) {
763        return getInterfaceThrottle(iface, false);
764    }
765}
766