blob: 1015028fc5668d7c895d01a562cddd5f9cc21547 [file] [log] [blame]
San Mehat873f2142010-01-14 10:25:07 -08001/*
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;
San Mehated4fc8a2010-01-22 12:28:36 -080027import android.net.InterfaceConfiguration;
San Mehat4d02d002010-01-22 16:07:46 -080028import android.net.INetworkManagementEventObserver;
San Mehat873f2142010-01-14 10:25:07 -080029import android.os.INetworkManagementService;
30import android.os.Handler;
Marco Nelissen62dbb222010-02-18 10:56:30 -080031import android.os.SystemProperties;
San Mehat873f2142010-01-14 10:25:07 -080032import android.text.TextUtils;
33import android.util.Log;
34import java.util.ArrayList;
San Mehated4fc8a2010-01-22 12:28:36 -080035import java.util.StringTokenizer;
San Mehat873f2142010-01-14 10:25:07 -080036import 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;
San Mehat72759df2010-01-19 13:50:37 -080058 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -080059
60 public static final int TetherStatusResult = 210;
61 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -080062 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -080063 public static final int SoftapStatusResult = 214;
64 public static final int UsbRNDISStatusResult = 215;
65
66 public static final int InterfaceChange = 600;
San Mehat873f2142010-01-14 10:25:07 -080067 }
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
San Mehat4d02d002010-01-22 16:07:46 -080079 private ArrayList<INetworkManagementEventObserver> mObservers;
80
San Mehat873f2142010-01-14 10:25:07 -080081 /**
82 * Constructs a new NetworkManagementService instance
83 *
84 * @param context Binder context for this service
85 */
San Mehatd1df8ac2010-01-26 06:17:26 -080086 public NetworkManagementService(Context context) {
San Mehat873f2142010-01-14 10:25:07 -080087 mContext = context;
88
San Mehat4d02d002010-01-22 16:07:46 -080089 mObservers = new ArrayList<INetworkManagementEventObserver>();
90
Marco Nelissen62dbb222010-02-18 10:56:30 -080091 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
92 return;
93 }
94
San Mehat873f2142010-01-14 10:25:07 -080095 mConnector = new NativeDaemonConnector(
96 new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
97 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
98 thread.start();
99 }
100
San Mehat4d02d002010-01-22 16:07:46 -0800101 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
San Mehat873f2142010-01-14 10:25:07 -0800151 //
152 // Netd Callback handling
153 //
154
155 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
156 public void onDaemonConnected() {
157 new Thread() {
158 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800159 }
160 }.start();
161 }
162 public boolean onEvent(int code, String raw, String[] cooked) {
Robert Greenwalte3253922010-02-18 09:23:25 -0800163 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;
San Mehat873f2142010-01-14 10:25:07 -0800188 }
189 }
190
San Mehated4fc8a2010-01-22 12:28:36 -0800191 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
San Mehat873f2142010-01-14 10:25:07 -0800214 //
215 // INetworkManagementService members
216 //
217
218 public String[] listInterfaces() throws IllegalStateException {
219 mContext.enforceCallingOrSelfPermission(
220 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
221
San Mehated4fc8a2010-01-22 12:28:36 -0800222 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);
San Mehat873f2142010-01-14 10:25:07 -0800269 }
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");
San Mehat72759df2010-01-19 13:50:37 -0800361 return mConnector.doListCommand(
362 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
San Mehat873f2142010-01-14 10:25:07 -0800363 }
364
365 public void setDnsForwarders(String[] dns) throws IllegalStateException {
366 mContext.enforceCallingOrSelfPermission(
367 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
368 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800369 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800370 for (String s : dns) {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800371 cmd += " " + InetAddress.getByName(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800372 }
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");
San Mehat72759df2010-01-19 13:50:37 -0800382 return mConnector.doListCommand(
383 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
San Mehat873f2142010-01-14 10:25:07 -0800384 }
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 }
San Mehat72759df2010-01-19 13:50:37 -0800401
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
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800408 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
409 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800410 try {
411 mContext.enforceCallingOrSelfPermission(
412 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800413 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()));
San Mehat72759df2010-01-19 13:50:37 -0800418 } 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 }
San Mehat873f2142010-01-14 10:25:07 -0800428}