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