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