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