blob: b114ca2c5820b5d159fb384910d74e8d65fc5a90 [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;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080029import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiConfiguration.KeyMgmt;
San Mehat873f2142010-01-14 10:25:07 -080031import android.os.INetworkManagementService;
32import android.os.Handler;
Marco Nelissen62dbb222010-02-18 10:56:30 -080033import android.os.SystemProperties;
San Mehat873f2142010-01-14 10:25:07 -080034import android.text.TextUtils;
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -080035import android.util.Log;
Joe Onorato8a9b2202010-02-26 18:56:32 -080036import android.util.Slog;
San Mehat873f2142010-01-14 10:25:07 -080037import java.util.ArrayList;
San Mehated4fc8a2010-01-22 12:28:36 -080038import java.util.StringTokenizer;
San Mehat873f2142010-01-14 10:25:07 -080039import 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;
San Mehat72759df2010-01-19 13:50:37 -080061 public static final int TtyListResult = 113;
San Mehat873f2142010-01-14 10:25:07 -080062
63 public static final int TetherStatusResult = 210;
64 public static final int IpFwdStatusResult = 211;
San Mehated4fc8a2010-01-22 12:28:36 -080065 public static final int InterfaceGetCfgResult = 213;
Robert Greenwalte3253922010-02-18 09:23:25 -080066 public static final int SoftapStatusResult = 214;
67 public static final int UsbRNDISStatusResult = 215;
68
69 public static final int InterfaceChange = 600;
San Mehat873f2142010-01-14 10:25:07 -080070 }
71
72 /**
73 * Binder context for this service
74 */
75 private Context mContext;
76
77 /**
78 * connector object for communicating with netd
79 */
80 private NativeDaemonConnector mConnector;
81
San Mehat4d02d002010-01-22 16:07:46 -080082 private ArrayList<INetworkManagementEventObserver> mObservers;
83
San Mehat873f2142010-01-14 10:25:07 -080084 /**
85 * Constructs a new NetworkManagementService instance
86 *
87 * @param context Binder context for this service
88 */
San Mehatd1df8ac2010-01-26 06:17:26 -080089 public NetworkManagementService(Context context) {
San Mehat873f2142010-01-14 10:25:07 -080090 mContext = context;
91
San Mehat4d02d002010-01-22 16:07:46 -080092 mObservers = new ArrayList<INetworkManagementEventObserver>();
93
Marco Nelissen62dbb222010-02-18 10:56:30 -080094 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
95 return;
96 }
97
San Mehat873f2142010-01-14 10:25:07 -080098 mConnector = new NativeDaemonConnector(
99 new NetdCallbackReceiver(), "netd", 10, "NetdConnector");
100 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
101 thread.start();
102 }
103
San Mehat4d02d002010-01-22 16:07:46 -0800104 public void registerObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800105 Slog.d(TAG, "Registering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800106 mObservers.add(obs);
107 }
108
109 public void unregisterObserver(INetworkManagementEventObserver obs) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800110 Slog.d(TAG, "Unregistering observer");
San Mehat4d02d002010-01-22 16:07:46 -0800111 mObservers.remove(mObservers.indexOf(obs));
112 }
113
114 /**
115 * Notify our observers of an interface link status change
116 */
117 private void notifyInterfaceLinkStatusChanged(String iface, boolean link) {
118 for (INetworkManagementEventObserver obs : mObservers) {
119 try {
120 obs.interfaceLinkStatusChanged(iface, link);
121 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800122 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800123 }
124 }
125 }
126
127 /**
128 * Notify our observers of an interface addition.
129 */
130 private void notifyInterfaceAdded(String iface) {
131 for (INetworkManagementEventObserver obs : mObservers) {
132 try {
133 obs.interfaceAdded(iface);
134 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800135 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800136 }
137 }
138 }
139
140 /**
141 * Notify our observers of an interface removal.
142 */
143 private void notifyInterfaceRemoved(String iface) {
144 for (INetworkManagementEventObserver obs : mObservers) {
145 try {
146 obs.interfaceRemoved(iface);
147 } catch (Exception ex) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800148 Slog.w(TAG, "Observer notifier failed", ex);
San Mehat4d02d002010-01-22 16:07:46 -0800149 }
150 }
151 }
152
153
San Mehat873f2142010-01-14 10:25:07 -0800154 //
155 // Netd Callback handling
156 //
157
158 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
159 public void onDaemonConnected() {
160 new Thread() {
161 public void run() {
San Mehat873f2142010-01-14 10:25:07 -0800162 }
163 }.start();
164 }
165 public boolean onEvent(int code, String raw, String[] cooked) {
Robert Greenwalte3253922010-02-18 09:23:25 -0800166 if (code == NetdResponseCode.InterfaceChange) {
167 /*
168 * a network interface change occured
169 * Format: "NNN Iface added <name>"
170 * "NNN Iface removed <name>"
171 * "NNN Iface changed <name> <up/down>"
172 */
173 if (cooked.length < 4 || !cooked[1].equals("Iface")) {
174 throw new IllegalStateException(
175 String.format("Invalid event from daemon (%s)", raw));
176 }
177 if (cooked[2].equals("added")) {
178 notifyInterfaceAdded(cooked[3]);
179 return true;
180 } else if (cooked[2].equals("removed")) {
181 notifyInterfaceRemoved(cooked[3]);
182 return true;
183 } else if (cooked[2].equals("changed") && cooked.length == 5) {
184 notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up"));
185 return true;
186 }
187 throw new IllegalStateException(
188 String.format("Invalid event from daemon (%s)", raw));
189 }
190 return false;
San Mehat873f2142010-01-14 10:25:07 -0800191 }
192 }
193
San Mehated4fc8a2010-01-22 12:28:36 -0800194 private static int stringToIpAddr(String addrString) throws UnknownHostException {
195 try {
196 String[] parts = addrString.split("\\.");
197 if (parts.length != 4) {
198 throw new UnknownHostException(addrString);
199 }
200
201 int a = Integer.parseInt(parts[0]) ;
202 int b = Integer.parseInt(parts[1]) << 8;
203 int c = Integer.parseInt(parts[2]) << 16;
204 int d = Integer.parseInt(parts[3]) << 24;
205
206 return a | b | c | d;
207 } catch (NumberFormatException ex) {
208 throw new UnknownHostException(addrString);
209 }
210 }
211
212 public static String intToIpString(int i) {
213 return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." +
214 (i & 0xFF);
215 }
216
San Mehat873f2142010-01-14 10:25:07 -0800217 //
218 // INetworkManagementService members
219 //
220
221 public String[] listInterfaces() throws IllegalStateException {
222 mContext.enforceCallingOrSelfPermission(
223 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
224
San Mehated4fc8a2010-01-22 12:28:36 -0800225 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult);
226 }
227
228 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException {
229 String rsp = mConnector.doCommand("interface getcfg " + iface).get(0);
Joe Onorato8a9b2202010-02-26 18:56:32 -0800230 Slog.d(TAG, String.format("rsp <%s>", rsp));
San Mehated4fc8a2010-01-22 12:28:36 -0800231
232 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3]
233 StringTokenizer st = new StringTokenizer(rsp);
234
235 try {
236 int code = Integer.parseInt(st.nextToken(" "));
237 if (code != NetdResponseCode.InterfaceGetCfgResult) {
238 throw new IllegalStateException(
239 String.format("Expected code %d, but got %d",
240 NetdResponseCode.InterfaceGetCfgResult, code));
241 }
242 } catch (NumberFormatException nfe) {
243 throw new IllegalStateException(
244 String.format("Invalid response from daemon (%s)", rsp));
245 }
246
247 InterfaceConfiguration cfg = new InterfaceConfiguration();
248 cfg.hwAddr = st.nextToken(" ");
249 try {
250 cfg.ipAddr = stringToIpAddr(st.nextToken(" "));
251 } catch (UnknownHostException uhe) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800252 Slog.e(TAG, "Failed to parse ipaddr", uhe);
San Mehated4fc8a2010-01-22 12:28:36 -0800253 cfg.ipAddr = 0;
254 }
255
256 try {
257 cfg.netmask = stringToIpAddr(st.nextToken(" "));
258 } catch (UnknownHostException uhe) {
Joe Onorato8a9b2202010-02-26 18:56:32 -0800259 Slog.e(TAG, "Failed to parse netmask", uhe);
San Mehated4fc8a2010-01-22 12:28:36 -0800260 cfg.netmask = 0;
261 }
Robert Greenwalt65ae29b2010-02-18 11:25:54 -0800262 cfg.interfaceFlags = st.nextToken("]").trim() +"]";
Joe Onorato8a9b2202010-02-26 18:56:32 -0800263 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags));
San Mehated4fc8a2010-01-22 12:28:36 -0800264 return cfg;
265 }
266
267 public void setInterfaceConfig(
268 String iface, InterfaceConfiguration cfg) throws IllegalStateException {
Robert Greenwalt65ae29b2010-02-18 11:25:54 -0800269 String cmd = String.format("interface setcfg %s %s %s %s", iface,
San Mehated4fc8a2010-01-22 12:28:36 -0800270 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags);
271 mConnector.doCommand(cmd);
San Mehat873f2142010-01-14 10:25:07 -0800272 }
273
274 public void shutdown() {
275 if (mContext.checkCallingOrSelfPermission(
276 android.Manifest.permission.SHUTDOWN)
277 != PackageManager.PERMISSION_GRANTED) {
278 throw new SecurityException("Requires SHUTDOWN permission");
279 }
280
Joe Onorato8a9b2202010-02-26 18:56:32 -0800281 Slog.d(TAG, "Shutting down");
San Mehat873f2142010-01-14 10:25:07 -0800282 }
283
284 public boolean getIpForwardingEnabled() throws IllegalStateException{
285 mContext.enforceCallingOrSelfPermission(
286 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
287
288 ArrayList<String> rsp = mConnector.doCommand("ipfwd status");
289
290 for (String line : rsp) {
291 String []tok = line.split(" ");
292 int code = Integer.parseInt(tok[0]);
293 if (code == NetdResponseCode.IpFwdStatusResult) {
294 // 211 Forwarding <enabled/disabled>
295 if (tok.length !=2) {
296 throw new IllegalStateException(
297 String.format("Malformatted list entry '%s'", line));
298 }
299 if (tok[2].equals("enabled"))
300 return true;
301 return false;
302 } else {
303 throw new IllegalStateException(String.format("Unexpected response code %d", code));
304 }
305 }
306 throw new IllegalStateException("Got an empty response");
307 }
308
309 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException {
310 mContext.enforceCallingOrSelfPermission(
311 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
312 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis")));
313 }
314
315 public void startTethering(String dhcpRangeStart, String dhcpRangeEnd)
316 throws IllegalStateException {
317 mContext.enforceCallingOrSelfPermission(
318 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
319 mConnector.doCommand(String.format("tether start %s %s", dhcpRangeStart, dhcpRangeEnd));
320 }
321
322 public void stopTethering() throws IllegalStateException {
323 mContext.enforceCallingOrSelfPermission(
324 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
325 mConnector.doCommand("tether stop");
326 }
327
328 public boolean isTetheringStarted() throws IllegalStateException {
329 mContext.enforceCallingOrSelfPermission(
330 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
331
332 ArrayList<String> rsp = mConnector.doCommand("tether status");
333
334 for (String line : rsp) {
335 String []tok = line.split(" ");
336 int code = Integer.parseInt(tok[0]);
337 if (code == NetdResponseCode.TetherStatusResult) {
338 // XXX: Tethering services <started/stopped> <TBD>...
339 if (tok[2].equals("started"))
340 return true;
341 return false;
342 } else {
343 throw new IllegalStateException(String.format("Unexpected response code %d", code));
344 }
345 }
346 throw new IllegalStateException("Got an empty response");
347 }
348
349 public void tetherInterface(String iface) throws IllegalStateException {
350 mContext.enforceCallingOrSelfPermission(
351 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
352 mConnector.doCommand("tether interface add " + iface);
353 }
354
355 public void untetherInterface(String iface) {
356 mContext.enforceCallingOrSelfPermission(
357 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
358 mConnector.doCommand("tether interface remove " + iface);
359 }
360
361 public String[] listTetheredInterfaces() throws IllegalStateException {
362 mContext.enforceCallingOrSelfPermission(
363 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
San Mehat72759df2010-01-19 13:50:37 -0800364 return mConnector.doListCommand(
365 "tether interface list", NetdResponseCode.TetherInterfaceListResult);
San Mehat873f2142010-01-14 10:25:07 -0800366 }
367
368 public void setDnsForwarders(String[] dns) throws IllegalStateException {
369 mContext.enforceCallingOrSelfPermission(
370 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
371 try {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800372 String cmd = "tether dns set";
San Mehat873f2142010-01-14 10:25:07 -0800373 for (String s : dns) {
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800374 cmd += " " + InetAddress.getByName(s).getHostAddress();
San Mehat873f2142010-01-14 10:25:07 -0800375 }
376 mConnector.doCommand(cmd);
377 } catch (UnknownHostException e) {
378 throw new IllegalStateException("Error resolving dns name", e);
379 }
380 }
381
382 public String[] getDnsForwarders() throws IllegalStateException {
383 mContext.enforceCallingOrSelfPermission(
384 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
San Mehat72759df2010-01-19 13:50:37 -0800385 return mConnector.doListCommand(
386 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult);
San Mehat873f2142010-01-14 10:25:07 -0800387 }
388
389 public void enableNat(String internalInterface, String externalInterface)
390 throws IllegalStateException {
391 mContext.enforceCallingOrSelfPermission(
392 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
393 mConnector.doCommand(
394 String.format("nat enable %s %s", internalInterface, externalInterface));
395 }
396
397 public void disableNat(String internalInterface, String externalInterface)
398 throws IllegalStateException {
399 mContext.enforceCallingOrSelfPermission(
400 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
401 mConnector.doCommand(
402 String.format("nat disable %s %s", internalInterface, externalInterface));
403 }
San Mehat72759df2010-01-19 13:50:37 -0800404
405 public String[] listTtys() throws IllegalStateException {
406 mContext.enforceCallingOrSelfPermission(
407 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
408 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
409 }
410
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800411 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
412 String dns2Addr) throws IllegalStateException {
San Mehat72759df2010-01-19 13:50:37 -0800413 try {
414 mContext.enforceCallingOrSelfPermission(
415 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
Robert Greenwaltd0e18ff2010-01-26 11:40:34 -0800416 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
417 InetAddress.getByName(localAddr).getHostAddress(),
418 InetAddress.getByName(remoteAddr).getHostAddress(),
419 InetAddress.getByName(dns1Addr).getHostAddress(),
420 InetAddress.getByName(dns2Addr).getHostAddress()));
San Mehat72759df2010-01-19 13:50:37 -0800421 } catch (UnknownHostException e) {
422 throw new IllegalStateException("Error resolving addr", e);
423 }
424 }
425
426 public void detachPppd(String tty) throws IllegalStateException {
427 mContext.enforceCallingOrSelfPermission(
428 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
429 mConnector.doCommand(String.format("pppd detach %s", tty));
430 }
Robert Greenwaltce1200d2010-02-18 11:25:54 -0800431
432 public void startUsbRNDIS() throws IllegalStateException {
433 mContext.enforceCallingOrSelfPermission(
434 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
435 mConnector.doCommand("usb startrndis");
436 }
437
438 public void stopUsbRNDIS() throws IllegalStateException {
439 mContext.enforceCallingOrSelfPermission(
440 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
441 mConnector.doCommand("usb stoprndis");
442 }
443
444 public boolean isUsbRNDISStarted() throws IllegalStateException {
445 mContext.enforceCallingOrSelfPermission(
446 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
447 ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus");
448
449 for (String line : rsp) {
450 String []tok = line.split(" ");
451 int code = Integer.parseInt(tok[0]);
452 if (code == NetdResponseCode.UsbRNDISStatusResult) {
Mike Lockwood51cb9d52010-03-03 15:12:00 -0500453 if (tok[3].equals("started"))
Robert Greenwaltce1200d2010-02-18 11:25:54 -0800454 return true;
455 return false;
456 } else {
457 throw new IllegalStateException(String.format("Unexpected response code %d", code));
458 }
459 }
460 throw new IllegalStateException("Got an empty response");
461 }
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800462
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800463 public void startAccessPoint(WifiConfiguration wifiConfig, String intf)
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800464 throws IllegalStateException {
465 mContext.enforceCallingOrSelfPermission(
466 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
467 mContext.enforceCallingOrSelfPermission(
468 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800469 mConnector.doCommand(String.format("softap stop " + intf));
470 mConnector.doCommand(String.format("softap fwreload " + intf + " AP"));
471 mConnector.doCommand(String.format("softap start " + intf));
472 if (wifiConfig == null) {
473 mConnector.doCommand(String.format("softap set " + intf + " wl0.1"));
474 } else {
475 /**
476 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8]
477 * argv1 - wlan interface
478 * argv2 - softap interface
479 * argv3 - SSID
480 * argv4 - Security
481 * argv5 - Key
482 * argv6 - Channel
483 * argv7 - Preamble
484 * argv8 - Max SCB
485 *
486 * TODO: get a configurable softap interface from driver
487 */
488 String str = String.format("softap set " + intf + " wl0.1 %s %s %s", wifiConfig.SSID,
489 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ?
490 "wpa2-psk" : "open",
491 wifiConfig.preSharedKey);
492 mConnector.doCommand(str);
493 }
494 mConnector.doCommand(String.format("softap startap"));
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800495 }
496
497 public void stopAccessPoint() throws IllegalStateException {
498 mContext.enforceCallingOrSelfPermission(
499 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
500 mContext.enforceCallingOrSelfPermission(
501 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService");
Irfan Sheriff9ab518ad2010-03-12 15:48:17 -0800502 mConnector.doCommand("softap stopap");
Irfan Sheriff5321aef2010-02-12 12:35:59 -0800503 }
504
San Mehat873f2142010-01-14 10:25:07 -0800505}