NetworkManagementService.java revision ce1200d42c46ae5d3ec637587b07dfdc02ad21c0
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.os.INetworkManagementService;
30import android.os.Handler;
31import android.os.SystemProperties;
32import android.text.TextUtils;
33import android.util.Log;
34import java.util.ArrayList;
35import java.util.StringTokenizer;
36import android.provider.Settings;
37import android.content.ContentResolver;
38import android.database.ContentObserver;
39
40import java.io.File;
41import java.io.FileReader;
42import java.lang.IllegalStateException;
43
44import java.net.InetAddress;
45import java.net.UnknownHostException;
46
47/**
48 * @hide
49 */
50class NetworkManagementService extends INetworkManagementService.Stub {
51
52    private static final String TAG = "NetworkManagmentService";
53
54    class NetdResponseCode {
55        public static final int InterfaceListResult       = 110;
56        public static final int TetherInterfaceListResult = 111;
57        public static final int TetherDnsFwdTgtListResult = 112;
58        public static final int TtyListResult             = 113;
59
60        public static final int TetherStatusResult        = 210;
61        public static final int IpFwdStatusResult         = 211;
62        public static final int InterfaceGetCfgResult     = 213;
63        public static final int SoftapStatusResult        = 214;
64        public static final int UsbRNDISStatusResult      = 215;
65
66        public static final int InterfaceChange           = 600;
67    }
68
69    /**
70     * Binder context for this service
71     */
72    private Context mContext;
73
74    /**
75     * connector object for communicating with netd
76     */
77    private NativeDaemonConnector mConnector;
78
79    private ArrayList<INetworkManagementEventObserver> mObservers;
80
81    /**
82     * Constructs a new NetworkManagementService instance
83     *
84     * @param context  Binder context for this service
85     */
86    public NetworkManagementService(Context context) {
87        mContext = context;
88
89        mObservers = new ArrayList<INetworkManagementEventObserver>();
90
91        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
92            return;
93        }
94
95        mConnector = new NativeDaemonConnector(
96                new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
97        Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
98        thread.start();
99    }
100
101    public void registerObserver(INetworkManagementEventObserver obs) {
102        Log.d(TAG, "Registering observer");
103        mObservers.add(obs);
104    }
105
106    public void unregisterObserver(INetworkManagementEventObserver obs) {
107        Log.d(TAG, "Unregistering observer");
108        mObservers.remove(mObservers.indexOf(obs));
109    }
110
111    /**
112     * Notify our observers of an interface link status change
113     */
114    private void notifyInterfaceLinkStatusChanged(String iface, boolean link) {
115        for (INetworkManagementEventObserver obs : mObservers) {
116            try {
117                obs.interfaceLinkStatusChanged(iface, link);
118            } catch (Exception ex) {
119                Log.w(TAG, "Observer notifier failed", ex);
120            }
121        }
122    }
123
124    /**
125     * Notify our observers of an interface addition.
126     */
127    private void notifyInterfaceAdded(String iface) {
128        for (INetworkManagementEventObserver obs : mObservers) {
129            try {
130                obs.interfaceAdded(iface);
131            } catch (Exception ex) {
132                Log.w(TAG, "Observer notifier failed", ex);
133            }
134        }
135    }
136
137    /**
138     * Notify our observers of an interface removal.
139     */
140    private void notifyInterfaceRemoved(String iface) {
141        for (INetworkManagementEventObserver obs : mObservers) {
142            try {
143                obs.interfaceRemoved(iface);
144            } catch (Exception ex) {
145                Log.w(TAG, "Observer notifier failed", ex);
146            }
147        }
148    }
149
150
151    //
152    // Netd Callback handling
153    //
154
155    class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
156        public void onDaemonConnected() {
157            new Thread() {
158                public void run() {
159                }
160            }.start();
161        }
162        public boolean onEvent(int code, String raw, String[] cooked) {
163            if (code == NetdResponseCode.InterfaceChange) {
164                /*
165                 * a network interface change occured
166                 * Format: "NNN Iface added <name>"
167                 *         "NNN Iface removed <name>"
168                 *         "NNN Iface changed <name> <up/down>"
169                 */
170                if (cooked.length < 4 || !cooked[1].equals("Iface")) {
171                    throw new IllegalStateException(
172                            String.format("Invalid event from daemon (%s)", raw));
173                }
174                if (cooked[2].equals("added")) {
175                    notifyInterfaceAdded(cooked[3]);
176                    return true;
177                } else if (cooked[2].equals("removed")) {
178                    notifyInterfaceRemoved(cooked[3]);
179                    return true;
180                } else if (cooked[2].equals("changed") && cooked.length == 5) {
181                    notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up"));
182                    return true;
183                }
184                throw new IllegalStateException(
185                        String.format("Invalid event from daemon (%s)", raw));
186            }
187            return false;
188        }
189    }
190
191    private static int stringToIpAddr(String addrString) throws UnknownHostException {
192        try {
193            String[] parts = addrString.split("\\.");
194            if (parts.length != 4) {
195                throw new UnknownHostException(addrString);
196            }
197
198            int a = Integer.parseInt(parts[0])      ;
199            int b = Integer.parseInt(parts[1]) <<  8;
200            int c = Integer.parseInt(parts[2]) << 16;
201            int d = Integer.parseInt(parts[3]) << 24;
202
203            return a | b | c | d;
204        } catch (NumberFormatException ex) {
205            throw new UnknownHostException(addrString);
206        }
207    }
208
209    public static String intToIpString(int i) {
210        return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >>  8 ) & 0xFF) + "." +
211               (i & 0xFF);
212    }
213
214    //
215    // INetworkManagementService members
216    //
217
218    public String[] listInterfaces() throws IllegalStateException {
219        mContext.enforceCallingOrSelfPermission(
220                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
221
222        return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
223    }
224
225    public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
226        String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
227        Log.d(TAG, String.format("rsp <%s>", rsp));
228
229        // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
230        StringTokenizer st = new StringTokenizer(rsp);
231
232        try {
233            int code = Integer.parseInt(st.nextToken(" "));
234            if (code != NetdResponseCode.InterfaceGetCfgResult) {
235                throw new IllegalStateException(
236                    String.format("Expected code %d, but got %d",
237                            NetdResponseCode.InterfaceGetCfgResult, code));
238            }
239        } catch (NumberFormatException nfe) {
240            throw new IllegalStateException(
241                    String.format("Invalid response from daemon (%s)", rsp));
242        }
243
244        InterfaceConfiguration cfg = new InterfaceConfiguration();
245        cfg.hwAddr = st.nextToken(" ");
246        try {
247            cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
248        } catch (UnknownHostException uhe) {
249            Log.e(TAG, "Failed to parse ipaddr", uhe);
250            cfg.ipAddr = 0;
251        }
252
253        try {
254            cfg.netmask = stringToIpAddr(st.nextToken(" "));
255        } catch (UnknownHostException uhe) {
256            Log.e(TAG, "Failed to parse netmask", uhe);
257            cfg.netmask = 0;
258        }
259        cfg.interfaceFlags = st.nextToken("]");
260        Log.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
261        return cfg;
262    }
263
264    public void setInterfaceConfig(
265            String iface, InterfaceConfiguration cfg) throws IllegalStateException {
266        String cmd = String.format("interface setcfg %s %s %s", iface,
267                intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
268        mConnector.doCommand(cmd);
269    }
270
271    public void shutdown() {
272        if (mContext.checkCallingOrSelfPermission(
273                android.Manifest.permission.SHUTDOWN)
274                != PackageManager.PERMISSION_GRANTED) {
275            throw new SecurityException("Requires SHUTDOWN permission");
276        }
277
278        Log.d(TAG, "Shutting down");
279    }
280
281    public boolean getIpForwardingEnabled() throws IllegalStateException{
282        mContext.enforceCallingOrSelfPermission(
283                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
284
285        ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
286
287        for (String line : rsp) {
288            String []tok = line.split(" ");
289            int code = Integer.parseInt(tok[0]);
290            if (code == NetdResponseCode.IpFwdStatusResult) {
291                // 211 Forwarding <enabled/disabled>
292                if (tok.length !=2) {
293                    throw new IllegalStateException(
294                            String.format("Malformatted list entry '%s'", line));
295                }
296                if (tok[2].equals("enabled"))
297                    return true;
298                return false;
299            } else {
300                throw new IllegalStateException(String.format("Unexpected response code %d", code));
301            }
302        }
303        throw new IllegalStateException("Got an empty response");
304    }
305
306    public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
307        mContext.enforceCallingOrSelfPermission(
308                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
309        mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
310    }
311
312    public void startTethering(String dhcpRangeStart, String dhcpRangeEnd)
313             throws IllegalStateException {
314        mContext.enforceCallingOrSelfPermission(
315                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
316        mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd));
317    }
318
319    public void stopTethering() throws IllegalStateException {
320        mContext.enforceCallingOrSelfPermission(
321                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
322        mConnector.doCommand("tether stop");
323    }
324
325    public boolean isTetheringStarted() throws IllegalStateException {
326        mContext.enforceCallingOrSelfPermission(
327                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
328
329        ArrayList<String> rsp = mConnector.doCommand("tether status");
330
331        for (String line : rsp) {
332            String []tok = line.split(" ");
333            int code = Integer.parseInt(tok[0]);
334            if (code == NetdResponseCode.TetherStatusResult) {
335                // XXX: Tethering services <started/stopped> <TBD>...
336                if (tok[2].equals("started"))
337                    return true;
338                return false;
339            } else {
340                throw new IllegalStateException(String.format("Unexpected response code %d", code));
341            }
342        }
343        throw new IllegalStateException("Got an empty response");
344    }
345
346    public void tetherInterface(String iface) throws IllegalStateException {
347        mContext.enforceCallingOrSelfPermission(
348                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
349        mConnector.doCommand("tether interface add " + iface);
350    }
351
352    public void untetherInterface(String iface) {
353        mContext.enforceCallingOrSelfPermission(
354                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
355        mConnector.doCommand("tether interface remove " + iface);
356    }
357
358    public String[] listTetheredInterfaces() throws IllegalStateException {
359        mContext.enforceCallingOrSelfPermission(
360                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
361        return mConnector.doListCommand(
362                "tether interface list", NetdResponseCode.TetherInterfaceListResult);
363    }
364
365    public void setDnsForwarders(String[] dns) throws IllegalStateException {
366        mContext.enforceCallingOrSelfPermission(
367                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
368        try {
369            String cmd = "tether dns set";
370            for (String s : dns) {
371                cmd += " " + InetAddress.getByName(s).getHostAddress();
372            }
373            mConnector.doCommand(cmd);
374        } catch (UnknownHostException e) {
375            throw new IllegalStateException("Error resolving dns name", e);
376        }
377    }
378
379    public String[] getDnsForwarders() throws IllegalStateException {
380        mContext.enforceCallingOrSelfPermission(
381                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
382        return mConnector.doListCommand(
383                "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
384    }
385
386    public void enableNat(String internalInterface, String externalInterface)
387            throws IllegalStateException {
388        mContext.enforceCallingOrSelfPermission(
389                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
390        mConnector.doCommand(
391                String.format("nat enable %s %s", internalInterface, externalInterface));
392    }
393
394    public void disableNat(String internalInterface, String externalInterface)
395            throws IllegalStateException {
396        mContext.enforceCallingOrSelfPermission(
397                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
398        mConnector.doCommand(
399                String.format("nat disable %s %s", internalInterface, externalInterface));
400    }
401
402    public String[] listTtys() throws IllegalStateException {
403        mContext.enforceCallingOrSelfPermission(
404                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
405        return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
406    }
407
408    public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
409            String dns2Addr) throws IllegalStateException {
410        try {
411            mContext.enforceCallingOrSelfPermission(
412                    android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
413            mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
414                    InetAddress.getByName(localAddr).getHostAddress(),
415                    InetAddress.getByName(remoteAddr).getHostAddress(),
416                    InetAddress.getByName(dns1Addr).getHostAddress(),
417                    InetAddress.getByName(dns2Addr).getHostAddress()));
418        } catch (UnknownHostException e) {
419            throw new IllegalStateException("Error resolving addr", e);
420        }
421    }
422
423    public void detachPppd(String tty) throws IllegalStateException {
424        mContext.enforceCallingOrSelfPermission(
425                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
426        mConnector.doCommand(String.format("pppd detach %s", tty));
427    }
428
429    public void startUsbRNDIS() throws IllegalStateException {
430        mContext.enforceCallingOrSelfPermission(
431                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
432        mConnector.doCommand("usb startrndis");
433    }
434
435    public void stopUsbRNDIS() throws IllegalStateException {
436        mContext.enforceCallingOrSelfPermission(
437                android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
438        mConnector.doCommand("usb stoprndis");
439    }
440
441    public boolean isUsbRNDISStarted() throws IllegalStateException {
442        mContext.enforceCallingOrSelfPermission(
443                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
444        ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus");
445
446        for (String line : rsp) {
447            String []tok = line.split(" ");
448            int code = Integer.parseInt(tok[0]);
449            if (code == NetdResponseCode.UsbRNDISStatusResult) {
450                if (tok[2].equals("started"))
451                    return true;
452                return false;
453            } else {
454                throw new IllegalStateException(String.format("Unexpected response code %d", code));
455            }
456        }
457        throw new IllegalStateException("Got an empty response");
458    }
459}
460