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