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 17 package com.android.server; 18 19 import android.app.PendingIntent; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.res.Resources; 25 import android.content.pm.PackageManager; 26 import android.net.Uri; 27 import android.net.InterfaceConfiguration; 28 import android.net.INetworkManagementEventObserver; 29 import android.net.wifi.WifiConfiguration; 30 import android.net.wifi.WifiConfiguration.KeyMgmt; 31 import android.os.INetworkManagementService; 32 import android.os.Handler; 33 import android.os.SystemProperties; 34 import android.text.TextUtils; 35 import android.util.Log; 36 import android.util.Slog; 37 import java.util.ArrayList; 38 import java.util.StringTokenizer; 39 import android.provider.Settings; 40 import android.content.ContentResolver; 41 import android.database.ContentObserver; 42 43 import java.io.File; 44 import java.io.FileReader; 45 import java.lang.IllegalStateException; 46 47 import java.net.InetAddress; 48 import java.net.UnknownHostException; 49 50 /** 51 * @hide 52 */ 53 class 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 public static final int InterfaceRxCounterResult = 216; 69 public static final int InterfaceTxCounterResult = 217; 70 public static final int InterfaceRxThrottleResult = 218; 71 public static final int InterfaceTxThrottleResult = 219; 72 73 public static final int InterfaceChange = 600; 74 } 75 76 /** 77 * Binder context for this service 78 */ 79 private Context mContext; 80 81 /** 82 * connector object for communicating with netd 83 */ 84 private NativeDaemonConnector mConnector; 85 86 private ArrayList<INetworkManagementEventObserver> mObservers; 87 88 /** 89 * Constructs a new NetworkManagementService instance 90 * 91 * @param context Binder context for this service 92 */ 93 public NetworkManagementService(Context context) { 94 mContext = context; 95 96 mObservers = new ArrayList<INetworkManagementEventObserver>(); 97 98 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 99 return; 100 } 101 102 mConnector = new NativeDaemonConnector( 103 new NetdCallbackReceiver(), "netd", 10, "NetdConnector"); 104 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName()); 105 thread.start(); 106 } 107 108 public void registerObserver(INetworkManagementEventObserver obs) { 109 Slog.d(TAG, "Registering observer"); 110 mObservers.add(obs); 111 } 112 113 public void unregisterObserver(INetworkManagementEventObserver obs) { 114 Slog.d(TAG, "Unregistering observer"); 115 mObservers.remove(mObservers.indexOf(obs)); 116 } 117 118 /** 119 * Notify our observers of an interface link status change 120 */ 121 private void notifyInterfaceLinkStatusChanged(String iface, boolean link) { 122 for (INetworkManagementEventObserver obs : mObservers) { 123 try { 124 obs.interfaceLinkStatusChanged(iface, link); 125 } catch (Exception ex) { 126 Slog.w(TAG, "Observer notifier failed", ex); 127 } 128 } 129 } 130 131 /** 132 * Notify our observers of an interface addition. 133 */ 134 private void notifyInterfaceAdded(String iface) { 135 for (INetworkManagementEventObserver obs : mObservers) { 136 try { 137 obs.interfaceAdded(iface); 138 } catch (Exception ex) { 139 Slog.w(TAG, "Observer notifier failed", ex); 140 } 141 } 142 } 143 144 /** 145 * Notify our observers of an interface removal. 146 */ 147 private void notifyInterfaceRemoved(String iface) { 148 for (INetworkManagementEventObserver obs : mObservers) { 149 try { 150 obs.interfaceRemoved(iface); 151 } catch (Exception ex) { 152 Slog.w(TAG, "Observer notifier failed", ex); 153 } 154 } 155 } 156 157 158 // 159 // Netd Callback handling 160 // 161 162 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 163 public void onDaemonConnected() { 164 new Thread() { 165 public void run() { 166 } 167 }.start(); 168 } 169 public boolean onEvent(int code, String raw, String[] cooked) { 170 if (code == NetdResponseCode.InterfaceChange) { 171 /* 172 * a network interface change occured 173 * Format: "NNN Iface added <name>" 174 * "NNN Iface removed <name>" 175 * "NNN Iface changed <name> <up/down>" 176 */ 177 if (cooked.length < 4 || !cooked[1].equals("Iface")) { 178 throw new IllegalStateException( 179 String.format("Invalid event from daemon (%s)", raw)); 180 } 181 if (cooked[2].equals("added")) { 182 notifyInterfaceAdded(cooked[3]); 183 return true; 184 } else if (cooked[2].equals("removed")) { 185 notifyInterfaceRemoved(cooked[3]); 186 return true; 187 } else if (cooked[2].equals("changed") && cooked.length == 5) { 188 notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up")); 189 return true; 190 } 191 throw new IllegalStateException( 192 String.format("Invalid event from daemon (%s)", raw)); 193 } 194 return false; 195 } 196 } 197 198 private static int stringToIpAddr(String addrString) throws UnknownHostException { 199 try { 200 String[] parts = addrString.split("\\."); 201 if (parts.length != 4) { 202 throw new UnknownHostException(addrString); 203 } 204 205 int a = Integer.parseInt(parts[0]) ; 206 int b = Integer.parseInt(parts[1]) << 8; 207 int c = Integer.parseInt(parts[2]) << 16; 208 int d = Integer.parseInt(parts[3]) << 24; 209 210 return a | b | c | d; 211 } catch (NumberFormatException ex) { 212 throw new UnknownHostException(addrString); 213 } 214 } 215 216 public static String intToIpString(int i) { 217 return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." + 218 (i & 0xFF); 219 } 220 221 // 222 // INetworkManagementService members 223 // 224 225 public String[] listInterfaces() throws IllegalStateException { 226 mContext.enforceCallingOrSelfPermission( 227 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 228 229 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); 230 } 231 232 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { 233 String rsp = mConnector.doCommand("interface getcfg " + iface).get(0); 234 Slog.d(TAG, String.format("rsp <%s>", rsp)); 235 236 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] 237 StringTokenizer st = new StringTokenizer(rsp); 238 239 try { 240 int code = Integer.parseInt(st.nextToken(" ")); 241 if (code != NetdResponseCode.InterfaceGetCfgResult) { 242 throw new IllegalStateException( 243 String.format("Expected code %d, but got %d", 244 NetdResponseCode.InterfaceGetCfgResult, code)); 245 } 246 } catch (NumberFormatException nfe) { 247 throw new IllegalStateException( 248 String.format("Invalid response from daemon (%s)", rsp)); 249 } 250 251 InterfaceConfiguration cfg = new InterfaceConfiguration(); 252 cfg.hwAddr = st.nextToken(" "); 253 try { 254 cfg.ipAddr = stringToIpAddr(st.nextToken(" ")); 255 } catch (UnknownHostException uhe) { 256 Slog.e(TAG, "Failed to parse ipaddr", uhe); 257 cfg.ipAddr = 0; 258 } 259 260 try { 261 cfg.netmask = stringToIpAddr(st.nextToken(" ")); 262 } catch (UnknownHostException uhe) { 263 Slog.e(TAG, "Failed to parse netmask", uhe); 264 cfg.netmask = 0; 265 } 266 cfg.interfaceFlags = st.nextToken("]").trim() +"]"; 267 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); 268 return cfg; 269 } 270 271 public void setInterfaceConfig( 272 String iface, InterfaceConfiguration cfg) throws IllegalStateException { 273 String cmd = String.format("interface setcfg %s %s %s %s", iface, 274 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); 275 mConnector.doCommand(cmd); 276 } 277 278 public void shutdown() { 279 if (mContext.checkCallingOrSelfPermission( 280 android.Manifest.permission.SHUTDOWN) 281 != PackageManager.PERMISSION_GRANTED) { 282 throw new SecurityException("Requires SHUTDOWN permission"); 283 } 284 285 Slog.d(TAG, "Shutting down"); 286 } 287 288 public boolean getIpForwardingEnabled() throws IllegalStateException{ 289 mContext.enforceCallingOrSelfPermission( 290 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 291 292 ArrayList<String> rsp = mConnector.doCommand("ipfwd status"); 293 294 for (String line : rsp) { 295 String []tok = line.split(" "); 296 int code = Integer.parseInt(tok[0]); 297 if (code == NetdResponseCode.IpFwdStatusResult) { 298 // 211 Forwarding <enabled/disabled> 299 if (tok.length !=2) { 300 throw new IllegalStateException( 301 String.format("Malformatted list entry '%s'", line)); 302 } 303 if (tok[2].equals("enabled")) 304 return true; 305 return false; 306 } else { 307 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 308 } 309 } 310 throw new IllegalStateException("Got an empty response"); 311 } 312 313 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { 314 mContext.enforceCallingOrSelfPermission( 315 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 316 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); 317 } 318 319 public void startTethering(String[] dhcpRange) 320 throws IllegalStateException { 321 mContext.enforceCallingOrSelfPermission( 322 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 323 // cmd is "tether start first_start first_stop second_start second_stop ..." 324 // an odd number of addrs will fail 325 String cmd = "tether start"; 326 for (String d : dhcpRange) { 327 cmd += " " + d; 328 } 329 mConnector.doCommand(cmd); 330 } 331 332 public void stopTethering() throws IllegalStateException { 333 mContext.enforceCallingOrSelfPermission( 334 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 335 mConnector.doCommand("tether stop"); 336 } 337 338 public boolean isTetheringStarted() throws IllegalStateException { 339 mContext.enforceCallingOrSelfPermission( 340 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 341 342 ArrayList<String> rsp = mConnector.doCommand("tether status"); 343 344 for (String line : rsp) { 345 String []tok = line.split(" "); 346 int code = Integer.parseInt(tok[0]); 347 if (code == NetdResponseCode.TetherStatusResult) { 348 // XXX: Tethering services <started/stopped> <TBD>... 349 if (tok[2].equals("started")) 350 return true; 351 return false; 352 } else { 353 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 354 } 355 } 356 throw new IllegalStateException("Got an empty response"); 357 } 358 359 public void tetherInterface(String iface) throws IllegalStateException { 360 mContext.enforceCallingOrSelfPermission( 361 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 362 mConnector.doCommand("tether interface add " + iface); 363 } 364 365 public void untetherInterface(String iface) { 366 mContext.enforceCallingOrSelfPermission( 367 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 368 mConnector.doCommand("tether interface remove " + iface); 369 } 370 371 public String[] listTetheredInterfaces() throws IllegalStateException { 372 mContext.enforceCallingOrSelfPermission( 373 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 374 return mConnector.doListCommand( 375 "tether interface list", NetdResponseCode.TetherInterfaceListResult); 376 } 377 378 public void setDnsForwarders(String[] dns) throws IllegalStateException { 379 mContext.enforceCallingOrSelfPermission( 380 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 381 try { 382 String cmd = "tether dns set"; 383 for (String s : dns) { 384 cmd += " " + InetAddress.getByName(s).getHostAddress(); 385 } 386 mConnector.doCommand(cmd); 387 } catch (UnknownHostException e) { 388 throw new IllegalStateException("Error resolving dns name", e); 389 } 390 } 391 392 public String[] getDnsForwarders() throws IllegalStateException { 393 mContext.enforceCallingOrSelfPermission( 394 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 395 return mConnector.doListCommand( 396 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); 397 } 398 399 public void enableNat(String internalInterface, String externalInterface) 400 throws IllegalStateException { 401 mContext.enforceCallingOrSelfPermission( 402 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 403 mConnector.doCommand( 404 String.format("nat enable %s %s", internalInterface, externalInterface)); 405 } 406 407 public void disableNat(String internalInterface, String externalInterface) 408 throws IllegalStateException { 409 mContext.enforceCallingOrSelfPermission( 410 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 411 mConnector.doCommand( 412 String.format("nat disable %s %s", internalInterface, externalInterface)); 413 } 414 415 public String[] listTtys() throws IllegalStateException { 416 mContext.enforceCallingOrSelfPermission( 417 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 418 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); 419 } 420 421 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, 422 String dns2Addr) throws IllegalStateException { 423 try { 424 mContext.enforceCallingOrSelfPermission( 425 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 426 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, 427 InetAddress.getByName(localAddr).getHostAddress(), 428 InetAddress.getByName(remoteAddr).getHostAddress(), 429 InetAddress.getByName(dns1Addr).getHostAddress(), 430 InetAddress.getByName(dns2Addr).getHostAddress())); 431 } catch (UnknownHostException e) { 432 throw new IllegalStateException("Error resolving addr", e); 433 } 434 } 435 436 public void detachPppd(String tty) throws IllegalStateException { 437 mContext.enforceCallingOrSelfPermission( 438 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 439 mConnector.doCommand(String.format("pppd detach %s", tty)); 440 } 441 442 public void startUsbRNDIS() throws IllegalStateException { 443 mContext.enforceCallingOrSelfPermission( 444 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 445 mConnector.doCommand("usb startrndis"); 446 } 447 448 public void stopUsbRNDIS() throws IllegalStateException { 449 mContext.enforceCallingOrSelfPermission( 450 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 451 mConnector.doCommand("usb stoprndis"); 452 } 453 454 public boolean isUsbRNDISStarted() throws IllegalStateException { 455 mContext.enforceCallingOrSelfPermission( 456 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 457 ArrayList<String> rsp = mConnector.doCommand("usb rndisstatus"); 458 459 for (String line : rsp) { 460 String []tok = line.split(" "); 461 int code = Integer.parseInt(tok[0]); 462 if (code == NetdResponseCode.UsbRNDISStatusResult) { 463 if (tok[3].equals("started")) 464 return true; 465 return false; 466 } else { 467 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 468 } 469 } 470 throw new IllegalStateException("Got an empty response"); 471 } 472 473 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 474 throws IllegalStateException { 475 mContext.enforceCallingOrSelfPermission( 476 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 477 mContext.enforceCallingOrSelfPermission( 478 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 479 mConnector.doCommand(String.format("softap stop " + wlanIface)); 480 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); 481 mConnector.doCommand(String.format("softap start " + wlanIface)); 482 if (wifiConfig == null) { 483 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 484 } else { 485 /** 486 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] 487 * argv1 - wlan interface 488 * argv2 - softap interface 489 * argv3 - SSID 490 * argv4 - Security 491 * argv5 - Key 492 * argv6 - Channel 493 * argv7 - Preamble 494 * argv8 - Max SCB 495 */ 496 String str = String.format("softap set " + wlanIface + " " + softapIface + 497 " %s %s %s", convertQuotedString(wifiConfig.SSID), 498 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? 499 "wpa2-psk" : "open", 500 convertQuotedString(wifiConfig.preSharedKey)); 501 mConnector.doCommand(str); 502 } 503 mConnector.doCommand(String.format("softap startap")); 504 } 505 506 private String convertQuotedString(String s) { 507 if (s == null) { 508 return s; 509 } 510 /* Replace \ with \\, then " with \" and add quotes at end */ 511 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"'; 512 } 513 514 public void stopAccessPoint() throws IllegalStateException { 515 mContext.enforceCallingOrSelfPermission( 516 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 517 mContext.enforceCallingOrSelfPermission( 518 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 519 mConnector.doCommand("softap stopap"); 520 } 521 522 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 523 throws IllegalStateException { 524 mContext.enforceCallingOrSelfPermission( 525 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 526 mContext.enforceCallingOrSelfPermission( 527 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 528 if (wifiConfig == null) { 529 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 530 } else { 531 String str = String.format("softap set " + wlanIface + " " + softapIface + 532 " %s %s %s", convertQuotedString(wifiConfig.SSID), 533 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? 534 "wpa2-psk" : "open", 535 convertQuotedString(wifiConfig.preSharedKey)); 536 mConnector.doCommand(str); 537 } 538 } 539 540 private long getInterfaceCounter(String iface, boolean rx) { 541 mContext.enforceCallingOrSelfPermission( 542 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 543 try { 544 String rsp = mConnector.doCommand( 545 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); 546 String []tok = rsp.split(" "); 547 int code; 548 try { 549 code = Integer.parseInt(tok[0]); 550 } catch (NumberFormatException nfe) { 551 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 552 return -1; 553 } 554 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || ( 555 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) { 556 Slog.e(TAG, String.format("Unexpected response code %d", code)); 557 return -1; 558 } 559 return Long.parseLong(tok[1]); 560 } catch (Exception e) { 561 Slog.e(TAG, String.format( 562 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e); 563 } 564 return -1; 565 } 566 567 public long getInterfaceRxCounter(String iface) { 568 return getInterfaceCounter(iface, true); 569 } 570 571 public long getInterfaceTxCounter(String iface) { 572 return getInterfaceCounter(iface, false); 573 } 574 575 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { 576 mContext.enforceCallingOrSelfPermission( 577 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 578 mConnector.doCommand(String.format( 579 "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); 580 } 581 582 private int getInterfaceThrottle(String iface, boolean rx) { 583 mContext.enforceCallingOrSelfPermission( 584 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 585 try { 586 String rsp = mConnector.doCommand( 587 String.format("interface getthrottle %s %s", iface,(rx ? "rx" : "tx"))).get(0); 588 String []tok = rsp.split(" "); 589 int code; 590 try { 591 code = Integer.parseInt(tok[0]); 592 } catch (NumberFormatException nfe) { 593 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 594 return -1; 595 } 596 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || ( 597 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) { 598 Slog.e(TAG, String.format("Unexpected response code %d", code)); 599 return -1; 600 } 601 return Integer.parseInt(tok[1]); 602 } catch (Exception e) { 603 Slog.e(TAG, String.format( 604 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e); 605 } 606 return -1; 607 } 608 609 public int getInterfaceRxThrottle(String iface) { 610 return getInterfaceThrottle(iface, true); 611 } 612 613 public int getInterfaceTxThrottle(String iface) { 614 return getInterfaceThrottle(iface, false); 615 } 616 } 617