NetworkManagementService.java revision 5c3816f2ff3d40136da1e40eec792d8faf0e75dd
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.NetworkStats; 27import android.net.Uri; 28import android.net.InterfaceConfiguration; 29import android.net.INetworkManagementEventObserver; 30import android.net.LinkAddress; 31import android.net.NetworkUtils; 32import android.net.RouteInfo; 33import android.net.wifi.WifiConfiguration; 34import android.net.wifi.WifiConfiguration.KeyMgmt; 35import android.os.INetworkManagementService; 36import android.os.Handler; 37import android.os.RemoteException; 38import android.os.SystemClock; 39import android.os.SystemProperties; 40import android.text.TextUtils; 41import android.util.Log; 42import android.util.Slog; 43import java.util.ArrayList; 44import java.util.NoSuchElementException; 45import java.util.StringTokenizer; 46import android.provider.Settings; 47import android.content.ContentResolver; 48import android.database.ContentObserver; 49 50import java.io.BufferedReader; 51import java.io.DataInputStream; 52import java.io.File; 53import java.io.FileInputStream; 54import java.io.FileReader; 55import java.io.IOException; 56import java.io.InputStream; 57import java.io.InputStreamReader; 58import java.io.RandomAccessFile; 59import java.io.Reader; 60import java.lang.IllegalStateException; 61import java.net.InetAddress; 62import java.net.Inet4Address; 63import java.net.UnknownHostException; 64import java.util.concurrent.CountDownLatch; 65 66import libcore.io.IoUtils; 67 68/** 69 * @hide 70 */ 71class NetworkManagementService extends INetworkManagementService.Stub { 72 73 private static final String TAG = "NetworkManagmentService"; 74 private static final boolean DBG = false; 75 private static final String NETD_TAG = "NetdConnector"; 76 77 private static final int ADD = 1; 78 private static final int REMOVE = 2; 79 80 class NetdResponseCode { 81 public static final int InterfaceListResult = 110; 82 public static final int TetherInterfaceListResult = 111; 83 public static final int TetherDnsFwdTgtListResult = 112; 84 public static final int TtyListResult = 113; 85 86 public static final int TetherStatusResult = 210; 87 public static final int IpFwdStatusResult = 211; 88 public static final int InterfaceGetCfgResult = 213; 89 public static final int SoftapStatusResult = 214; 90 public static final int UsbRNDISStatusResult = 215; 91 public static final int InterfaceRxCounterResult = 216; 92 public static final int InterfaceTxCounterResult = 217; 93 public static final int InterfaceRxThrottleResult = 218; 94 public static final int InterfaceTxThrottleResult = 219; 95 96 public static final int InterfaceChange = 600; 97 } 98 99 /** 100 * Binder context for this service 101 */ 102 private Context mContext; 103 104 /** 105 * connector object for communicating with netd 106 */ 107 private NativeDaemonConnector mConnector; 108 109 private Thread mThread; 110 private final CountDownLatch mConnectedSignal = new CountDownLatch(1); 111 112 private ArrayList<INetworkManagementEventObserver> mObservers; 113 114 /** 115 * Constructs a new NetworkManagementService instance 116 * 117 * @param context Binder context for this service 118 */ 119 private NetworkManagementService(Context context) { 120 mContext = context; 121 mObservers = new ArrayList<INetworkManagementEventObserver>(); 122 123 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 124 return; 125 } 126 127 mConnector = new NativeDaemonConnector( 128 new NetdCallbackReceiver(), "netd", 10, NETD_TAG); 129 mThread = new Thread(mConnector, NETD_TAG); 130 } 131 132 public static NetworkManagementService create(Context context) throws InterruptedException { 133 NetworkManagementService service = new NetworkManagementService(context); 134 if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); 135 service.mThread.start(); 136 if (DBG) Slog.d(TAG, "Awaiting socket connection"); 137 service.mConnectedSignal.await(); 138 if (DBG) Slog.d(TAG, "Connected"); 139 return service; 140 } 141 142 public void registerObserver(INetworkManagementEventObserver obs) { 143 Slog.d(TAG, "Registering observer"); 144 mObservers.add(obs); 145 } 146 147 public void unregisterObserver(INetworkManagementEventObserver obs) { 148 Slog.d(TAG, "Unregistering observer"); 149 mObservers.remove(mObservers.indexOf(obs)); 150 } 151 152 /** 153 * Notify our observers of an interface link status change 154 */ 155 private void notifyInterfaceLinkStatusChanged(String iface, boolean link) { 156 for (INetworkManagementEventObserver obs : mObservers) { 157 try { 158 obs.interfaceLinkStatusChanged(iface, link); 159 } catch (Exception ex) { 160 Slog.w(TAG, "Observer notifier failed", ex); 161 } 162 } 163 } 164 165 /** 166 * Notify our observers of an interface addition. 167 */ 168 private void notifyInterfaceAdded(String iface) { 169 for (INetworkManagementEventObserver obs : mObservers) { 170 try { 171 obs.interfaceAdded(iface); 172 } catch (Exception ex) { 173 Slog.w(TAG, "Observer notifier failed", ex); 174 } 175 } 176 } 177 178 /** 179 * Notify our observers of an interface removal. 180 */ 181 private void notifyInterfaceRemoved(String iface) { 182 for (INetworkManagementEventObserver obs : mObservers) { 183 try { 184 obs.interfaceRemoved(iface); 185 } catch (Exception ex) { 186 Slog.w(TAG, "Observer notifier failed", ex); 187 } 188 } 189 } 190 191 /** 192 * Let us know the daemon is connected 193 */ 194 protected void onConnected() { 195 if (DBG) Slog.d(TAG, "onConnected"); 196 mConnectedSignal.countDown(); 197 } 198 199 200 // 201 // Netd Callback handling 202 // 203 204 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 205 public void onDaemonConnected() { 206 NetworkManagementService.this.onConnected(); 207 new Thread() { 208 public void run() { 209 } 210 }.start(); 211 } 212 public boolean onEvent(int code, String raw, String[] cooked) { 213 if (code == NetdResponseCode.InterfaceChange) { 214 /* 215 * a network interface change occured 216 * Format: "NNN Iface added <name>" 217 * "NNN Iface removed <name>" 218 * "NNN Iface changed <name> <up/down>" 219 */ 220 if (cooked.length < 4 || !cooked[1].equals("Iface")) { 221 throw new IllegalStateException( 222 String.format("Invalid event from daemon (%s)", raw)); 223 } 224 if (cooked[2].equals("added")) { 225 notifyInterfaceAdded(cooked[3]); 226 return true; 227 } else if (cooked[2].equals("removed")) { 228 notifyInterfaceRemoved(cooked[3]); 229 return true; 230 } else if (cooked[2].equals("changed") && cooked.length == 5) { 231 notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up")); 232 return true; 233 } 234 throw new IllegalStateException( 235 String.format("Invalid event from daemon (%s)", raw)); 236 } 237 return false; 238 } 239 } 240 241 242 // 243 // INetworkManagementService members 244 // 245 246 public String[] listInterfaces() throws IllegalStateException { 247 mContext.enforceCallingOrSelfPermission( 248 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 249 250 try { 251 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); 252 } catch (NativeDaemonConnectorException e) { 253 throw new IllegalStateException( 254 "Cannot communicate with native daemon to list interfaces"); 255 } 256 } 257 258 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { 259 String rsp; 260 try { 261 rsp = mConnector.doCommand("interface getcfg " + iface).get(0); 262 } catch (NativeDaemonConnectorException e) { 263 throw new IllegalStateException( 264 "Cannot communicate with native daemon to get interface config"); 265 } 266 Slog.d(TAG, String.format("rsp <%s>", rsp)); 267 268 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3] 269 StringTokenizer st = new StringTokenizer(rsp); 270 271 InterfaceConfiguration cfg; 272 try { 273 try { 274 int code = Integer.parseInt(st.nextToken(" ")); 275 if (code != NetdResponseCode.InterfaceGetCfgResult) { 276 throw new IllegalStateException( 277 String.format("Expected code %d, but got %d", 278 NetdResponseCode.InterfaceGetCfgResult, code)); 279 } 280 } catch (NumberFormatException nfe) { 281 throw new IllegalStateException( 282 String.format("Invalid response from daemon (%s)", rsp)); 283 } 284 285 cfg = new InterfaceConfiguration(); 286 cfg.hwAddr = st.nextToken(" "); 287 InetAddress addr = null; 288 int prefixLength = 0; 289 try { 290 addr = NetworkUtils.numericToInetAddress(st.nextToken(" ")); 291 } catch (IllegalArgumentException iae) { 292 Slog.e(TAG, "Failed to parse ipaddr", iae); 293 } 294 295 try { 296 prefixLength = Integer.parseInt(st.nextToken(" ")); 297 } catch (NumberFormatException nfe) { 298 Slog.e(TAG, "Failed to parse prefixLength", nfe); 299 } 300 301 cfg.addr = new LinkAddress(addr, prefixLength); 302 cfg.interfaceFlags = st.nextToken("]").trim() +"]"; 303 } catch (NoSuchElementException nsee) { 304 throw new IllegalStateException( 305 String.format("Invalid response from daemon (%s)", rsp)); 306 } 307 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); 308 return cfg; 309 } 310 311 public void setInterfaceConfig( 312 String iface, InterfaceConfiguration cfg) throws IllegalStateException { 313 LinkAddress linkAddr = cfg.addr; 314 if (linkAddr == null || linkAddr.getAddress() == null) { 315 throw new IllegalStateException("Null LinkAddress given"); 316 } 317 String cmd = String.format("interface setcfg %s %s %d %s", iface, 318 linkAddr.getAddress().getHostAddress(), 319 linkAddr.getNetworkPrefixLength(), 320 cfg.interfaceFlags); 321 try { 322 mConnector.doCommand(cmd); 323 } catch (NativeDaemonConnectorException e) { 324 throw new IllegalStateException( 325 "Unable to communicate with native daemon to interface setcfg - " + e); 326 } 327 } 328 329 public void addRoute(String interfaceName, RouteInfo route) { 330 modifyRoute(interfaceName, ADD, route); 331 } 332 333 public void removeRoute(String interfaceName, RouteInfo route) { 334 modifyRoute(interfaceName, REMOVE, route); 335 } 336 337 private void modifyRoute(String interfaceName, int action, RouteInfo route) { 338 ArrayList<String> rsp; 339 340 StringBuilder cmd; 341 342 switch (action) { 343 case ADD: 344 { 345 cmd = new StringBuilder("interface route add " + interfaceName); 346 break; 347 } 348 case REMOVE: 349 { 350 cmd = new StringBuilder("interface route remove " + interfaceName); 351 break; 352 } 353 default: 354 throw new IllegalStateException("Unknown action type " + action); 355 } 356 357 // create triplet: dest-ip-addr prefixlength gateway-ip-addr 358 LinkAddress la = route.getDestination(); 359 cmd.append(' '); 360 cmd.append(la.getAddress().getHostAddress()); 361 cmd.append(' '); 362 cmd.append(la.getNetworkPrefixLength()); 363 cmd.append(' '); 364 if (route.getGateway() == null) { 365 if (la.getAddress() instanceof Inet4Address) { 366 cmd.append("0.0.0.0"); 367 } else { 368 cmd.append ("::0"); 369 } 370 } else { 371 cmd.append(route.getGateway().getHostAddress()); 372 } 373 try { 374 rsp = mConnector.doCommand(cmd.toString()); 375 } catch (NativeDaemonConnectorException e) { 376 throw new IllegalStateException( 377 "Unable to communicate with native dameon to add routes - " 378 + e); 379 } 380 381 for (String line : rsp) { 382 Log.v(TAG, "add route response is " + line); 383 } 384 } 385 386 private ArrayList<String> readRouteList(String filename) { 387 FileInputStream fstream = null; 388 ArrayList<String> list = new ArrayList<String>(); 389 390 try { 391 fstream = new FileInputStream(filename); 392 DataInputStream in = new DataInputStream(fstream); 393 BufferedReader br = new BufferedReader(new InputStreamReader(in)); 394 String s; 395 396 // throw away the title line 397 398 while (((s = br.readLine()) != null) && (s.length() != 0)) { 399 list.add(s); 400 } 401 } catch (IOException ex) { 402 // return current list, possibly empty 403 } finally { 404 if (fstream != null) { 405 try { 406 fstream.close(); 407 } catch (IOException ex) {} 408 } 409 } 410 411 return list; 412 } 413 414 public RouteInfo[] getRoutes(String interfaceName) { 415 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>(); 416 417 // v4 routes listed as: 418 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT 419 for (String s : readRouteList("/proc/net/route")) { 420 String[] fields = s.split("\t"); 421 422 if (fields.length > 7) { 423 String iface = fields[0]; 424 425 if (interfaceName.equals(iface)) { 426 String dest = fields[1]; 427 String gate = fields[2]; 428 String flags = fields[3]; // future use? 429 String mask = fields[7]; 430 try { 431 // address stored as a hex string, ex: 0014A8C0 432 InetAddress destAddr = 433 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16)); 434 int prefixLength = 435 NetworkUtils.netmaskIntToPrefixLength( 436 (int)Long.parseLong(mask, 16)); 437 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength); 438 439 // address stored as a hex string, ex 0014A8C0 440 InetAddress gatewayAddr = 441 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16)); 442 443 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr); 444 routes.add(route); 445 } catch (Exception e) { 446 Log.e(TAG, "Error parsing route " + s + " : " + e); 447 continue; 448 } 449 } 450 } 451 } 452 453 // v6 routes listed as: 454 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface 455 for (String s : readRouteList("/proc/net/ipv6_route")) { 456 String[]fields = s.split("\\s+"); 457 if (fields.length > 9) { 458 String iface = fields[9].trim(); 459 if (interfaceName.equals(iface)) { 460 String dest = fields[0]; 461 String prefix = fields[1]; 462 String gate = fields[4]; 463 464 try { 465 // prefix length stored as a hex string, ex 40 466 int prefixLength = Integer.parseInt(prefix, 16); 467 468 // address stored as a 32 char hex string 469 // ex fe800000000000000000000000000000 470 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest); 471 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength); 472 473 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate); 474 475 RouteInfo route = new RouteInfo(linkAddress, gateAddr); 476 routes.add(route); 477 } catch (Exception e) { 478 Log.e(TAG, "Error parsing route " + s + " : " + e); 479 continue; 480 } 481 } 482 } 483 } 484 return (RouteInfo[]) routes.toArray(new RouteInfo[0]); 485 } 486 487 public void shutdown() { 488 if (mContext.checkCallingOrSelfPermission( 489 android.Manifest.permission.SHUTDOWN) 490 != PackageManager.PERMISSION_GRANTED) { 491 throw new SecurityException("Requires SHUTDOWN permission"); 492 } 493 494 Slog.d(TAG, "Shutting down"); 495 } 496 497 public boolean getIpForwardingEnabled() throws IllegalStateException{ 498 mContext.enforceCallingOrSelfPermission( 499 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 500 501 ArrayList<String> rsp; 502 try { 503 rsp = mConnector.doCommand("ipfwd status"); 504 } catch (NativeDaemonConnectorException e) { 505 throw new IllegalStateException( 506 "Unable to communicate with native daemon to ipfwd status"); 507 } 508 509 for (String line : rsp) { 510 String[] tok = line.split(" "); 511 if (tok.length < 3) { 512 Slog.e(TAG, "Malformed response from native daemon: " + line); 513 return false; 514 } 515 516 int code = Integer.parseInt(tok[0]); 517 if (code == NetdResponseCode.IpFwdStatusResult) { 518 // 211 Forwarding <enabled/disabled> 519 return "enabled".equals(tok[2]); 520 } else { 521 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 522 } 523 } 524 throw new IllegalStateException("Got an empty response"); 525 } 526 527 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { 528 mContext.enforceCallingOrSelfPermission( 529 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 530 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); 531 } 532 533 public void startTethering(String[] dhcpRange) 534 throws IllegalStateException { 535 mContext.enforceCallingOrSelfPermission( 536 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 537 // cmd is "tether start first_start first_stop second_start second_stop ..." 538 // an odd number of addrs will fail 539 String cmd = "tether start"; 540 for (String d : dhcpRange) { 541 cmd += " " + d; 542 } 543 544 try { 545 mConnector.doCommand(cmd); 546 } catch (NativeDaemonConnectorException e) { 547 throw new IllegalStateException("Unable to communicate to native daemon"); 548 } 549 } 550 551 public void stopTethering() throws IllegalStateException { 552 mContext.enforceCallingOrSelfPermission( 553 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 554 try { 555 mConnector.doCommand("tether stop"); 556 } catch (NativeDaemonConnectorException e) { 557 throw new IllegalStateException("Unable to communicate to native daemon to stop tether"); 558 } 559 } 560 561 public boolean isTetheringStarted() throws IllegalStateException { 562 mContext.enforceCallingOrSelfPermission( 563 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 564 565 ArrayList<String> rsp; 566 try { 567 rsp = mConnector.doCommand("tether status"); 568 } catch (NativeDaemonConnectorException e) { 569 throw new IllegalStateException( 570 "Unable to communicate to native daemon to get tether status"); 571 } 572 573 for (String line : rsp) { 574 String[] tok = line.split(" "); 575 if (tok.length < 3) { 576 throw new IllegalStateException("Malformed response for tether status: " + line); 577 } 578 int code = Integer.parseInt(tok[0]); 579 if (code == NetdResponseCode.TetherStatusResult) { 580 // XXX: Tethering services <started/stopped> <TBD>... 581 return "started".equals(tok[2]); 582 } else { 583 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 584 } 585 } 586 throw new IllegalStateException("Got an empty response"); 587 } 588 589 public void tetherInterface(String iface) throws IllegalStateException { 590 mContext.enforceCallingOrSelfPermission( 591 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 592 try { 593 mConnector.doCommand("tether interface add " + iface); 594 } catch (NativeDaemonConnectorException e) { 595 throw new IllegalStateException( 596 "Unable to communicate to native daemon for adding tether interface"); 597 } 598 } 599 600 public void untetherInterface(String iface) { 601 mContext.enforceCallingOrSelfPermission( 602 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 603 try { 604 mConnector.doCommand("tether interface remove " + iface); 605 } catch (NativeDaemonConnectorException e) { 606 throw new IllegalStateException( 607 "Unable to communicate to native daemon for removing tether interface"); 608 } 609 } 610 611 public String[] listTetheredInterfaces() throws IllegalStateException { 612 mContext.enforceCallingOrSelfPermission( 613 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 614 try { 615 return mConnector.doListCommand( 616 "tether interface list", NetdResponseCode.TetherInterfaceListResult); 617 } catch (NativeDaemonConnectorException e) { 618 throw new IllegalStateException( 619 "Unable to communicate to native daemon for listing tether interfaces"); 620 } 621 } 622 623 public void setDnsForwarders(String[] dns) throws IllegalStateException { 624 mContext.enforceCallingOrSelfPermission( 625 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 626 try { 627 String cmd = "tether dns set"; 628 for (String s : dns) { 629 cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress(); 630 } 631 try { 632 mConnector.doCommand(cmd); 633 } catch (NativeDaemonConnectorException e) { 634 throw new IllegalStateException( 635 "Unable to communicate to native daemon for setting tether dns"); 636 } 637 } catch (IllegalArgumentException e) { 638 throw new IllegalStateException("Error resolving dns name", e); 639 } 640 } 641 642 public String[] getDnsForwarders() throws IllegalStateException { 643 mContext.enforceCallingOrSelfPermission( 644 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 645 try { 646 return mConnector.doListCommand( 647 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); 648 } catch (NativeDaemonConnectorException e) { 649 throw new IllegalStateException( 650 "Unable to communicate to native daemon for listing tether dns"); 651 } 652 } 653 654 public void enableNat(String internalInterface, String externalInterface) 655 throws IllegalStateException { 656 mContext.enforceCallingOrSelfPermission( 657 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 658 try { 659 mConnector.doCommand( 660 String.format("nat enable %s %s", internalInterface, externalInterface)); 661 } catch (NativeDaemonConnectorException e) { 662 throw new IllegalStateException( 663 "Unable to communicate to native daemon for enabling NAT interface"); 664 } 665 } 666 667 public void disableNat(String internalInterface, String externalInterface) 668 throws IllegalStateException { 669 mContext.enforceCallingOrSelfPermission( 670 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 671 try { 672 mConnector.doCommand( 673 String.format("nat disable %s %s", internalInterface, externalInterface)); 674 } catch (NativeDaemonConnectorException e) { 675 throw new IllegalStateException( 676 "Unable to communicate to native daemon for disabling NAT interface"); 677 } 678 } 679 680 public String[] listTtys() throws IllegalStateException { 681 mContext.enforceCallingOrSelfPermission( 682 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 683 try { 684 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); 685 } catch (NativeDaemonConnectorException e) { 686 throw new IllegalStateException( 687 "Unable to communicate to native daemon for listing TTYs"); 688 } 689 } 690 691 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, 692 String dns2Addr) throws IllegalStateException { 693 try { 694 mContext.enforceCallingOrSelfPermission( 695 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 696 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, 697 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(), 698 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(), 699 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(), 700 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress())); 701 } catch (IllegalArgumentException e) { 702 throw new IllegalStateException("Error resolving addr", e); 703 } catch (NativeDaemonConnectorException e) { 704 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e); 705 } 706 } 707 708 public void detachPppd(String tty) throws IllegalStateException { 709 mContext.enforceCallingOrSelfPermission( 710 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 711 try { 712 mConnector.doCommand(String.format("pppd detach %s", tty)); 713 } catch (NativeDaemonConnectorException e) { 714 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e); 715 } 716 } 717 718 public void startUsbRNDIS() throws IllegalStateException { 719 mContext.enforceCallingOrSelfPermission( 720 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 721 try { 722 mConnector.doCommand("usb startrndis"); 723 } catch (NativeDaemonConnectorException e) { 724 throw new IllegalStateException( 725 "Error communicating to native daemon for starting RNDIS", e); 726 } 727 } 728 729 public void stopUsbRNDIS() throws IllegalStateException { 730 mContext.enforceCallingOrSelfPermission( 731 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 732 try { 733 mConnector.doCommand("usb stoprndis"); 734 } catch (NativeDaemonConnectorException e) { 735 throw new IllegalStateException("Error communicating to native daemon", e); 736 } 737 } 738 739 public boolean isUsbRNDISStarted() throws IllegalStateException { 740 mContext.enforceCallingOrSelfPermission( 741 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 742 ArrayList<String> rsp; 743 try { 744 rsp = mConnector.doCommand("usb rndisstatus"); 745 } catch (NativeDaemonConnectorException e) { 746 throw new IllegalStateException( 747 "Error communicating to native daemon to check RNDIS status", e); 748 } 749 750 for (String line : rsp) { 751 String []tok = line.split(" "); 752 int code = Integer.parseInt(tok[0]); 753 if (code == NetdResponseCode.UsbRNDISStatusResult) { 754 if (tok[3].equals("started")) 755 return true; 756 return false; 757 } else { 758 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 759 } 760 } 761 throw new IllegalStateException("Got an empty response"); 762 } 763 764 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 765 throws IllegalStateException { 766 mContext.enforceCallingOrSelfPermission( 767 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 768 mContext.enforceCallingOrSelfPermission( 769 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 770 try { 771 mConnector.doCommand(String.format("softap stop " + wlanIface)); 772 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); 773 mConnector.doCommand(String.format("softap start " + wlanIface)); 774 if (wifiConfig == null) { 775 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 776 } else { 777 /** 778 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] 779 * argv1 - wlan interface 780 * argv2 - softap interface 781 * argv3 - SSID 782 * argv4 - Security 783 * argv5 - Key 784 * argv6 - Channel 785 * argv7 - Preamble 786 * argv8 - Max SCB 787 */ 788 String str = String.format("softap set " + wlanIface + " " + softapIface + 789 " %s %s %s", convertQuotedString(wifiConfig.SSID), 790 getSecurityType(wifiConfig), 791 convertQuotedString(wifiConfig.preSharedKey)); 792 mConnector.doCommand(str); 793 } 794 mConnector.doCommand(String.format("softap startap")); 795 } catch (NativeDaemonConnectorException e) { 796 throw new IllegalStateException("Error communicating to native daemon to start softap", e); 797 } 798 } 799 800 private String convertQuotedString(String s) { 801 if (s == null) { 802 return s; 803 } 804 /* Replace \ with \\, then " with \" and add quotes at end */ 805 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"'; 806 } 807 808 private String getSecurityType(WifiConfiguration wifiConfig) { 809 switch (wifiConfig.getAuthType()) { 810 case KeyMgmt.WPA_PSK: 811 return "wpa-psk"; 812 case KeyMgmt.WPA2_PSK: 813 return "wpa2-psk"; 814 default: 815 return "open"; 816 } 817 } 818 819 public void stopAccessPoint() throws IllegalStateException { 820 mContext.enforceCallingOrSelfPermission( 821 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 822 mContext.enforceCallingOrSelfPermission( 823 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 824 try { 825 mConnector.doCommand("softap stopap"); 826 } catch (NativeDaemonConnectorException e) { 827 throw new IllegalStateException("Error communicating to native daemon to stop soft AP", 828 e); 829 } 830 } 831 832 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 833 throws IllegalStateException { 834 mContext.enforceCallingOrSelfPermission( 835 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 836 mContext.enforceCallingOrSelfPermission( 837 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 838 try { 839 if (wifiConfig == null) { 840 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 841 } else { 842 String str = String.format("softap set " + wlanIface + " " + softapIface 843 + " %s %s %s", convertQuotedString(wifiConfig.SSID), 844 getSecurityType(wifiConfig), 845 convertQuotedString(wifiConfig.preSharedKey)); 846 mConnector.doCommand(str); 847 } 848 } catch (NativeDaemonConnectorException e) { 849 throw new IllegalStateException("Error communicating to native daemon to set soft AP", 850 e); 851 } 852 } 853 854 private long getInterfaceCounter(String iface, boolean rx) { 855 mContext.enforceCallingOrSelfPermission( 856 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 857 try { 858 String rsp; 859 try { 860 rsp = mConnector.doCommand( 861 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); 862 } catch (NativeDaemonConnectorException e1) { 863 Slog.e(TAG, "Error communicating with native daemon", e1); 864 return -1; 865 } 866 867 String[] tok = rsp.split(" "); 868 if (tok.length < 2) { 869 Slog.e(TAG, String.format("Malformed response for reading %s interface", 870 (rx ? "rx" : "tx"))); 871 return -1; 872 } 873 874 int code; 875 try { 876 code = Integer.parseInt(tok[0]); 877 } catch (NumberFormatException nfe) { 878 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 879 return -1; 880 } 881 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || ( 882 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) { 883 Slog.e(TAG, String.format("Unexpected response code %d", code)); 884 return -1; 885 } 886 return Long.parseLong(tok[1]); 887 } catch (Exception e) { 888 Slog.e(TAG, String.format( 889 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e); 890 } 891 return -1; 892 } 893 894 /** {@inheritDoc} */ 895 public NetworkStats getNetworkStatsSummary() { 896 mContext.enforceCallingOrSelfPermission( 897 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 898 899 final String[] ifaces = listInterfaces(); 900 final NetworkStats.Builder stats = new NetworkStats.Builder( 901 SystemClock.elapsedRealtime(), ifaces.length); 902 903 for (String iface : ifaces) { 904 final long rx = getInterfaceCounter(iface, true); 905 final long tx = getInterfaceCounter(iface, false); 906 stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx); 907 } 908 909 return stats.build(); 910 } 911 912 /** {@inheritDoc} */ 913 public NetworkStats getNetworkStatsDetail() { 914 mContext.enforceCallingOrSelfPermission( 915 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 916 917 final File procPath = new File("/proc/uid_stat"); 918 final String[] knownUids = procPath.list(); 919 final NetworkStats.Builder stats = new NetworkStats.Builder( 920 SystemClock.elapsedRealtime(), knownUids.length); 921 922 // TODO: kernel module will provide interface-level stats in future 923 // TODO: migrate these stats to come across netd in bulk, instead of all 924 // these individual file reads. 925 for (String uid : knownUids) { 926 final File uidPath = new File(procPath, uid); 927 final int rx = readSingleIntFromFile(new File(uidPath, "tcp_rcv")); 928 final int tx = readSingleIntFromFile(new File(uidPath, "tcp_snd")); 929 930 final int uidInt = Integer.parseInt(uid); 931 stats.addEntry(NetworkStats.IFACE_ALL, uidInt, rx, tx); 932 } 933 934 return stats.build(); 935 } 936 937 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { 938 mContext.enforceCallingOrSelfPermission( 939 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 940 try { 941 mConnector.doCommand(String.format( 942 "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); 943 } catch (NativeDaemonConnectorException e) { 944 Slog.e(TAG, "Error communicating with native daemon to set throttle", e); 945 } 946 } 947 948 private int getInterfaceThrottle(String iface, boolean rx) { 949 mContext.enforceCallingOrSelfPermission( 950 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 951 try { 952 String rsp; 953 try { 954 rsp = mConnector.doCommand( 955 String.format("interface getthrottle %s %s", iface, 956 (rx ? "rx" : "tx"))).get(0); 957 } catch (NativeDaemonConnectorException e) { 958 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e); 959 return -1; 960 } 961 962 String[] tok = rsp.split(" "); 963 if (tok.length < 2) { 964 Slog.e(TAG, "Malformed response to getthrottle command"); 965 return -1; 966 } 967 968 int code; 969 try { 970 code = Integer.parseInt(tok[0]); 971 } catch (NumberFormatException nfe) { 972 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 973 return -1; 974 } 975 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || ( 976 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) { 977 Slog.e(TAG, String.format("Unexpected response code %d", code)); 978 return -1; 979 } 980 return Integer.parseInt(tok[1]); 981 } catch (Exception e) { 982 Slog.e(TAG, String.format( 983 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e); 984 } 985 return -1; 986 } 987 988 public int getInterfaceRxThrottle(String iface) { 989 return getInterfaceThrottle(iface, true); 990 } 991 992 public int getInterfaceTxThrottle(String iface) { 993 return getInterfaceThrottle(iface, false); 994 } 995 996 /** 997 * Utility method to read a single plain-text {@link Integer} from the given 998 * {@link File}, usually from a {@code /proc/} filesystem. 999 */ 1000 private static int readSingleIntFromFile(File file) { 1001 RandomAccessFile f = null; 1002 try { 1003 f = new RandomAccessFile(file, "r"); 1004 byte[] buffer = new byte[(int) f.length()]; 1005 f.readFully(buffer); 1006 return Integer.parseInt(new String(buffer).trim()); 1007 } catch (NumberFormatException e) { 1008 return -1; 1009 } catch (IOException e) { 1010 return -1; 1011 } finally { 1012 IoUtils.closeQuietly(f); 1013 } 1014 } 1015} 1016