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