NetworkManagementService.java revision c2f54c267b896cd1799d82be81e904a2b56c2f26
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 69 public static final int InterfaceChange = 600; 70 } 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 82 private ArrayList<INetworkManagementEventObserver> mObservers; 83 84 /** 85 * Constructs a new NetworkManagementService instance 86 * 87 * @param context Binder context for this service 88 */ 89 public NetworkManagementService(Context context) { 90 mContext = context; 91 92 mObservers = new ArrayList<INetworkManagementEventObserver>(); 93 94 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 95 return; 96 } 97 98 mConnector = new NativeDaemonConnector( 99 new NetdCallbackReceiver(), "netd", 10, "NetdConnector"); 100 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); 101 thread.start(); 102 } 103 104 public void registerObserver(INetworkManagementEventObserver obs) { 105 Slog.d(TAG, "Registering observer"); 106 mObservers.add(obs); 107 } 108 109 public void unregisterObserver(INetworkManagementEventObserver obs) { 110 Slog.d(TAG, "Unregistering observer"); 111 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) { 122 Slog.w(TAG, "Observer notifier failed", ex); 123 } 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) { 135 Slog.w(TAG, "Observer notifier failed", ex); 136 } 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) { 148 Slog.w(TAG, "Observer notifier failed", ex); 149 } 150 } 151 } 152 153 154 // 155 // Netd Callback handling 156 // 157 158 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 159 public void onDaemonConnected() { 160 new Thread() { 161 public void run() { 162 } 163 }.start(); 164 } 165 public boolean onEvent(int code, String raw, String[] cooked) { 166 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; 191 } 192 } 193 194 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 217 // 218 // INetworkManagementService members 219 // 220 221 public String[] listInterfaces() throws IllegalStateException { 222 mContext.enforceCallingOrSelfPermission( 223 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 224 225 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); 230 Slog.d(TAG, String.format("rsp <%s>", rsp)); 231 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) { 252 Slog.e(TAG, "Failed to parse ipaddr", uhe); 253 cfg.ipAddr = 0; 254 } 255 256 try { 257 cfg.netmask = stringToIpAddr(st.nextToken(" ")); 258 } catch (UnknownHostException uhe) { 259 Slog.e(TAG, "Failed to parse netmask", uhe); 260 cfg.netmask = 0; 261 } 262 cfg.interfaceFlags = st.nextToken("]").trim() +"]"; 263 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); 264 return cfg; 265 } 266 267 public void setInterfaceConfig( 268 String iface, InterfaceConfiguration cfg) throws IllegalStateException { 269 String cmd = String.format("interface setcfg %s %s %s %s", iface, 270 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); 271 mConnector.doCommand(cmd); 272 } 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 281 Slog.d(TAG, "Shutting down"); 282 } 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"); 364 return mConnector.doListCommand( 365 "tether interface list", NetdResponseCode.TetherInterfaceListResult); 366 } 367 368 public void setDnsForwarders(String[] dns) throws IllegalStateException { 369 mContext.enforceCallingOrSelfPermission( 370 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 371 try { 372 String cmd = "tether dns set"; 373 for (String s : dns) { 374 cmd += " " + InetAddress.getByName(s).getHostAddress(); 375 } 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"); 385 return mConnector.doListCommand( 386 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); 387 } 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 } 404 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 411 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, 412 String dns2Addr) throws IllegalStateException { 413 try { 414 mContext.enforceCallingOrSelfPermission( 415 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 416 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())); 421 } 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 } 431 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) { 453 if (tok[3].equals("started")) 454 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 } 462 463 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 464 throws IllegalStateException { 465 mContext.enforceCallingOrSelfPermission( 466 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 467 mContext.enforceCallingOrSelfPermission( 468 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 469 mConnector.doCommand(String.format("softap stop " + wlanIface)); 470 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); 471 mConnector.doCommand(String.format("softap start " + wlanIface)); 472 if (wifiConfig == null) { 473 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 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 String str = String.format("softap set " + wlanIface + " " + softapIface + " %s %s %s", 487 wifiConfig.SSID, 488 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? 489 "wpa2-psk" : "open", 490 wifiConfig.preSharedKey); 491 mConnector.doCommand(str); 492 } 493 mConnector.doCommand(String.format("softap startap")); 494 } 495 496 public void stopAccessPoint() throws IllegalStateException { 497 mContext.enforceCallingOrSelfPermission( 498 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 499 mContext.enforceCallingOrSelfPermission( 500 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 501 mConnector.doCommand("softap stopap"); 502 } 503 504 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 505 throws IllegalStateException { 506 mContext.enforceCallingOrSelfPermission( 507 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 508 mContext.enforceCallingOrSelfPermission( 509 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 510 if (wifiConfig == null) { 511 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 512 } else { 513 String str = String.format("softap set " + wlanIface + " " + softapIface + 514 " %s %s %s", wifiConfig.SSID, 515 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? 516 "wpa2-psk" : "open", 517 wifiConfig.preSharedKey); 518 mConnector.doCommand(str); 519 } 520 } 521} 522