NetworkManagementService.java revision b51645ee2c7608f0b1a50d09f203cf5323b0b02d
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.Manifest.permission.MANAGE_NETWORK_POLICY; 20import static android.net.NetworkStats.IFACE_ALL; 21import static android.net.NetworkStats.SET_DEFAULT; 22import static android.net.NetworkStats.TAG_NONE; 23import static android.net.NetworkStats.UID_ALL; 24import static android.provider.Settings.Secure.NETSTATS_ENABLED; 25import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; 26import static com.android.server.NetworkManagementSocketTagger.kernelToTag; 27 28import android.content.Context; 29import android.content.pm.PackageManager; 30import android.net.INetworkManagementEventObserver; 31import android.net.InterfaceConfiguration; 32import android.net.LinkAddress; 33import android.net.NetworkStats; 34import android.net.NetworkUtils; 35import android.net.RouteInfo; 36import android.net.wifi.WifiConfiguration; 37import android.net.wifi.WifiConfiguration.KeyMgmt; 38import android.os.Binder; 39import android.os.INetworkManagementService; 40import android.os.SystemClock; 41import android.os.SystemProperties; 42import android.provider.Settings; 43import android.util.Log; 44import android.util.Slog; 45import android.util.SparseBooleanArray; 46 47import com.google.android.collect.Lists; 48import com.google.android.collect.Maps; 49import com.google.android.collect.Sets; 50 51import java.io.BufferedReader; 52import java.io.DataInputStream; 53import java.io.File; 54import java.io.FileInputStream; 55import java.io.FileReader; 56import java.io.IOException; 57import java.io.InputStreamReader; 58import java.net.Inet4Address; 59import java.net.InetAddress; 60import java.util.ArrayList; 61import java.util.HashMap; 62import java.util.HashSet; 63import java.util.NoSuchElementException; 64import java.util.StringTokenizer; 65import java.util.concurrent.CountDownLatch; 66 67import libcore.io.IoUtils; 68 69/** 70 * @hide 71 */ 72class NetworkManagementService extends INetworkManagementService.Stub implements Watchdog.Monitor { 73 private static final String TAG = "NetworkManagementService"; 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 /** Path to {@code /proc/uid_stat}. */ 81 @Deprecated 82 private final File mStatsUid; 83 /** Path to {@code /proc/net/dev}. */ 84 private final File mStatsIface; 85 /** Path to {@code /proc/net/xt_qtaguid/stats}. */ 86 private final File mStatsXtUid; 87 /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */ 88 private final File mStatsXtIface; 89 90 /** {@link #mStatsXtUid} headers. */ 91 private static final String KEY_IFACE = "iface"; 92 private static final String KEY_UID = "uid_tag_int"; 93 private static final String KEY_COUNTER_SET = "cnt_set"; 94 private static final String KEY_TAG_HEX = "acct_tag_hex"; 95 private static final String KEY_RX_BYTES = "rx_bytes"; 96 private static final String KEY_RX_PACKETS = "rx_packets"; 97 private static final String KEY_TX_BYTES = "tx_bytes"; 98 private static final String KEY_TX_PACKETS = "tx_packets"; 99 100 class NetdResponseCode { 101 /* Keep in sync with system/netd/ResponseCode.h */ 102 public static final int InterfaceListResult = 110; 103 public static final int TetherInterfaceListResult = 111; 104 public static final int TetherDnsFwdTgtListResult = 112; 105 public static final int TtyListResult = 113; 106 107 public static final int TetherStatusResult = 210; 108 public static final int IpFwdStatusResult = 211; 109 public static final int InterfaceGetCfgResult = 213; 110 public static final int SoftapStatusResult = 214; 111 public static final int InterfaceRxCounterResult = 216; 112 public static final int InterfaceTxCounterResult = 217; 113 public static final int InterfaceRxThrottleResult = 218; 114 public static final int InterfaceTxThrottleResult = 219; 115 116 public static final int InterfaceChange = 600; 117 public static final int BandwidthControl = 601; 118 } 119 120 /** 121 * Binder context for this service 122 */ 123 private Context mContext; 124 125 /** 126 * connector object for communicating with netd 127 */ 128 private NativeDaemonConnector mConnector; 129 130 private Thread mThread; 131 private final CountDownLatch mConnectedSignal = new CountDownLatch(1); 132 133 // TODO: replace with RemoteCallbackList 134 private ArrayList<INetworkManagementEventObserver> mObservers; 135 136 private Object mQuotaLock = new Object(); 137 /** Set of interfaces with active quotas. */ 138 private HashSet<String> mActiveQuotaIfaces = Sets.newHashSet(); 139 /** Set of interfaces with active alerts. */ 140 private HashSet<String> mActiveAlertIfaces = Sets.newHashSet(); 141 /** Set of UIDs with active reject rules. */ 142 private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); 143 144 private volatile boolean mBandwidthControlEnabled; 145 146 /** 147 * Constructs a new NetworkManagementService instance 148 * 149 * @param context Binder context for this service 150 */ 151 private NetworkManagementService(Context context, File procRoot) { 152 mContext = context; 153 mObservers = new ArrayList<INetworkManagementEventObserver>(); 154 155 mStatsUid = new File(procRoot, "uid_stat"); 156 mStatsIface = new File(procRoot, "net/dev"); 157 mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); 158 mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat"); 159 160 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 161 return; 162 } 163 164 mConnector = new NativeDaemonConnector( 165 new NetdCallbackReceiver(), "netd", 10, NETD_TAG); 166 mThread = new Thread(mConnector, NETD_TAG); 167 168 // Add ourself to the Watchdog monitors. 169 Watchdog.getInstance().addMonitor(this); 170 } 171 172 public static NetworkManagementService create(Context context) throws InterruptedException { 173 NetworkManagementService service = new NetworkManagementService( 174 context, new File("/proc/")); 175 if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); 176 service.mThread.start(); 177 if (DBG) Slog.d(TAG, "Awaiting socket connection"); 178 service.mConnectedSignal.await(); 179 if (DBG) Slog.d(TAG, "Connected"); 180 return service; 181 } 182 183 // @VisibleForTesting 184 public static NetworkManagementService createForTest( 185 Context context, File procRoot, boolean bandwidthControlEnabled) { 186 // TODO: eventually connect with mock netd 187 final NetworkManagementService service = new NetworkManagementService(context, procRoot); 188 service.mBandwidthControlEnabled = bandwidthControlEnabled; 189 return service; 190 } 191 192 public void systemReady() { 193 // only enable bandwidth control when support exists, and requested by 194 // system setting. 195 final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists(); 196 final boolean shouldEnable = 197 Settings.Secure.getInt(mContext.getContentResolver(), NETSTATS_ENABLED, 1) != 0; 198 199 if (hasKernelSupport && shouldEnable) { 200 Slog.d(TAG, "enabling bandwidth control"); 201 try { 202 mConnector.doCommand("bandwidth enable"); 203 mBandwidthControlEnabled = true; 204 } catch (NativeDaemonConnectorException e) { 205 Slog.e(TAG, "problem enabling bandwidth controls", e); 206 } 207 } else { 208 Slog.d(TAG, "not enabling bandwidth control"); 209 } 210 211 SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0"); 212 } 213 214 public void registerObserver(INetworkManagementEventObserver obs) { 215 Slog.d(TAG, "Registering observer"); 216 mObservers.add(obs); 217 } 218 219 public void unregisterObserver(INetworkManagementEventObserver obs) { 220 Slog.d(TAG, "Unregistering observer"); 221 mObservers.remove(mObservers.indexOf(obs)); 222 } 223 224 /** 225 * Notify our observers of an interface status change 226 */ 227 private void notifyInterfaceStatusChanged(String iface, boolean up) { 228 for (INetworkManagementEventObserver obs : mObservers) { 229 try { 230 obs.interfaceStatusChanged(iface, up); 231 } catch (Exception ex) { 232 Slog.w(TAG, "Observer notifier failed", ex); 233 } 234 } 235 } 236 237 /** 238 * Notify our observers of an interface link state change 239 * (typically, an Ethernet cable has been plugged-in or unplugged). 240 */ 241 private void notifyInterfaceLinkStateChanged(String iface, boolean up) { 242 for (INetworkManagementEventObserver obs : mObservers) { 243 try { 244 obs.interfaceLinkStateChanged(iface, up); 245 } catch (Exception ex) { 246 Slog.w(TAG, "Observer notifier failed", ex); 247 } 248 } 249 } 250 251 /** 252 * Notify our observers of an interface addition. 253 */ 254 private void notifyInterfaceAdded(String iface) { 255 for (INetworkManagementEventObserver obs : mObservers) { 256 try { 257 obs.interfaceAdded(iface); 258 } catch (Exception ex) { 259 Slog.w(TAG, "Observer notifier failed", ex); 260 } 261 } 262 } 263 264 /** 265 * Notify our observers of an interface removal. 266 */ 267 private void notifyInterfaceRemoved(String iface) { 268 for (INetworkManagementEventObserver obs : mObservers) { 269 try { 270 obs.interfaceRemoved(iface); 271 } catch (Exception ex) { 272 Slog.w(TAG, "Observer notifier failed", ex); 273 } 274 } 275 } 276 277 /** 278 * Notify our observers of a limit reached. 279 */ 280 private void notifyLimitReached(String limitName, String iface) { 281 for (INetworkManagementEventObserver obs : mObservers) { 282 try { 283 obs.limitReached(limitName, iface); 284 Slog.d(TAG, "Observer notified limit reached for " + limitName + " " + iface); 285 } catch (Exception ex) { 286 Slog.w(TAG, "Observer notifier failed", ex); 287 } 288 } 289 } 290 291 /** 292 * Let us know the daemon is connected 293 */ 294 protected void onDaemonConnected() { 295 if (DBG) Slog.d(TAG, "onConnected"); 296 mConnectedSignal.countDown(); 297 } 298 299 300 // 301 // Netd Callback handling 302 // 303 304 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 305 /** {@inheritDoc} */ 306 public void onDaemonConnected() { 307 NetworkManagementService.this.onDaemonConnected(); 308 } 309 310 /** {@inheritDoc} */ 311 public boolean onEvent(int code, String raw, String[] cooked) { 312 switch (code) { 313 case NetdResponseCode.InterfaceChange: 314 /* 315 * a network interface change occured 316 * Format: "NNN Iface added <name>" 317 * "NNN Iface removed <name>" 318 * "NNN Iface changed <name> <up/down>" 319 * "NNN Iface linkstatus <name> <up/down>" 320 */ 321 if (cooked.length < 4 || !cooked[1].equals("Iface")) { 322 throw new IllegalStateException( 323 String.format("Invalid event from daemon (%s)", raw)); 324 } 325 if (cooked[2].equals("added")) { 326 notifyInterfaceAdded(cooked[3]); 327 return true; 328 } else if (cooked[2].equals("removed")) { 329 notifyInterfaceRemoved(cooked[3]); 330 return true; 331 } else if (cooked[2].equals("changed") && cooked.length == 5) { 332 notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up")); 333 return true; 334 } else if (cooked[2].equals("linkstate") && cooked.length == 5) { 335 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up")); 336 return true; 337 } 338 throw new IllegalStateException( 339 String.format("Invalid event from daemon (%s)", raw)); 340 // break; 341 case NetdResponseCode.BandwidthControl: 342 /* 343 * Bandwidth control needs some attention 344 * Format: "NNN limit alert <alertName> <ifaceName>" 345 */ 346 if (cooked.length < 5 || !cooked[1].equals("limit")) { 347 throw new IllegalStateException( 348 String.format("Invalid event from daemon (%s)", raw)); 349 } 350 if (cooked[2].equals("alert")) { 351 notifyLimitReached(cooked[3], cooked[4]); 352 return true; 353 } 354 throw new IllegalStateException( 355 String.format("Invalid event from daemon (%s)", raw)); 356 // break; 357 default: break; 358 } 359 return false; 360 } 361 } 362 363 364 // 365 // INetworkManagementService members 366 // 367 368 public String[] listInterfaces() throws IllegalStateException { 369 mContext.enforceCallingOrSelfPermission( 370 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 371 372 try { 373 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); 374 } catch (NativeDaemonConnectorException e) { 375 throw new IllegalStateException( 376 "Cannot communicate with native daemon to list interfaces"); 377 } 378 } 379 380 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { 381 String rsp; 382 try { 383 rsp = mConnector.doCommand("interface getcfg " + iface).get(0); 384 } catch (NativeDaemonConnectorException e) { 385 throw new IllegalStateException( 386 "Cannot communicate with native daemon to get interface config"); 387 } 388 Slog.d(TAG, String.format("rsp <%s>", rsp)); 389 390 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz [flag1 flag2 flag3] 391 StringTokenizer st = new StringTokenizer(rsp); 392 393 InterfaceConfiguration cfg; 394 try { 395 try { 396 int code = Integer.parseInt(st.nextToken(" ")); 397 if (code != NetdResponseCode.InterfaceGetCfgResult) { 398 throw new IllegalStateException( 399 String.format("Expected code %d, but got %d", 400 NetdResponseCode.InterfaceGetCfgResult, code)); 401 } 402 } catch (NumberFormatException nfe) { 403 throw new IllegalStateException( 404 String.format("Invalid response from daemon (%s)", rsp)); 405 } 406 407 cfg = new InterfaceConfiguration(); 408 cfg.hwAddr = st.nextToken(" "); 409 InetAddress addr = null; 410 int prefixLength = 0; 411 try { 412 addr = NetworkUtils.numericToInetAddress(st.nextToken(" ")); 413 } catch (IllegalArgumentException iae) { 414 Slog.e(TAG, "Failed to parse ipaddr", iae); 415 } 416 417 try { 418 prefixLength = Integer.parseInt(st.nextToken(" ")); 419 } catch (NumberFormatException nfe) { 420 Slog.e(TAG, "Failed to parse prefixLength", nfe); 421 } 422 423 cfg.addr = new LinkAddress(addr, prefixLength); 424 cfg.interfaceFlags = st.nextToken("]").trim() +"]"; 425 } catch (NoSuchElementException nsee) { 426 throw new IllegalStateException( 427 String.format("Invalid response from daemon (%s)", rsp)); 428 } 429 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); 430 return cfg; 431 } 432 433 public void setInterfaceConfig( 434 String iface, InterfaceConfiguration cfg) throws IllegalStateException { 435 LinkAddress linkAddr = cfg.addr; 436 if (linkAddr == null || linkAddr.getAddress() == null) { 437 throw new IllegalStateException("Null LinkAddress given"); 438 } 439 String cmd = String.format("interface setcfg %s %s %d %s", iface, 440 linkAddr.getAddress().getHostAddress(), 441 linkAddr.getNetworkPrefixLength(), 442 cfg.interfaceFlags); 443 try { 444 mConnector.doCommand(cmd); 445 } catch (NativeDaemonConnectorException e) { 446 throw new IllegalStateException( 447 "Unable to communicate with native daemon to interface setcfg - " + e); 448 } 449 } 450 451 public void setInterfaceDown(String iface) throws IllegalStateException { 452 try { 453 InterfaceConfiguration ifcg = getInterfaceConfig(iface); 454 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); 455 setInterfaceConfig(iface, ifcg); 456 } catch (NativeDaemonConnectorException e) { 457 throw new IllegalStateException( 458 "Unable to communicate with native daemon for interface down - " + e); 459 } 460 } 461 462 public void setInterfaceUp(String iface) throws IllegalStateException { 463 try { 464 InterfaceConfiguration ifcg = getInterfaceConfig(iface); 465 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); 466 setInterfaceConfig(iface, ifcg); 467 } catch (NativeDaemonConnectorException e) { 468 throw new IllegalStateException( 469 "Unable to communicate with native daemon for interface up - " + e); 470 } 471 } 472 473 /* TODO: This is right now a IPv4 only function. Works for wifi which loses its 474 IPv6 addresses on interface down, but we need to do full clean up here */ 475 public void clearInterfaceAddresses(String iface) throws IllegalStateException { 476 String cmd = String.format("interface clearaddrs %s", iface); 477 try { 478 mConnector.doCommand(cmd); 479 } catch (NativeDaemonConnectorException e) { 480 throw new IllegalStateException( 481 "Unable to communicate with native daemon to interface clearallips - " + e); 482 } 483 } 484 485 public void addRoute(String interfaceName, RouteInfo route) { 486 modifyRoute(interfaceName, ADD, route); 487 } 488 489 public void removeRoute(String interfaceName, RouteInfo route) { 490 modifyRoute(interfaceName, REMOVE, route); 491 } 492 493 private void modifyRoute(String interfaceName, int action, RouteInfo route) { 494 ArrayList<String> rsp; 495 496 StringBuilder cmd; 497 498 switch (action) { 499 case ADD: 500 { 501 cmd = new StringBuilder("interface route add " + interfaceName); 502 break; 503 } 504 case REMOVE: 505 { 506 cmd = new StringBuilder("interface route remove " + interfaceName); 507 break; 508 } 509 default: 510 throw new IllegalStateException("Unknown action type " + action); 511 } 512 513 // create triplet: dest-ip-addr prefixlength gateway-ip-addr 514 LinkAddress la = route.getDestination(); 515 cmd.append(' '); 516 cmd.append(la.getAddress().getHostAddress()); 517 cmd.append(' '); 518 cmd.append(la.getNetworkPrefixLength()); 519 cmd.append(' '); 520 if (route.getGateway() == null) { 521 if (la.getAddress() instanceof Inet4Address) { 522 cmd.append("0.0.0.0"); 523 } else { 524 cmd.append ("::0"); 525 } 526 } else { 527 cmd.append(route.getGateway().getHostAddress()); 528 } 529 try { 530 rsp = mConnector.doCommand(cmd.toString()); 531 } catch (NativeDaemonConnectorException e) { 532 throw new IllegalStateException( 533 "Unable to communicate with native dameon to add routes - " 534 + e); 535 } 536 537 for (String line : rsp) { 538 Log.v(TAG, "add route response is " + line); 539 } 540 } 541 542 private ArrayList<String> readRouteList(String filename) { 543 FileInputStream fstream = null; 544 ArrayList<String> list = new ArrayList<String>(); 545 546 try { 547 fstream = new FileInputStream(filename); 548 DataInputStream in = new DataInputStream(fstream); 549 BufferedReader br = new BufferedReader(new InputStreamReader(in)); 550 String s; 551 552 // throw away the title line 553 554 while (((s = br.readLine()) != null) && (s.length() != 0)) { 555 list.add(s); 556 } 557 } catch (IOException ex) { 558 // return current list, possibly empty 559 } finally { 560 if (fstream != null) { 561 try { 562 fstream.close(); 563 } catch (IOException ex) {} 564 } 565 } 566 567 return list; 568 } 569 570 public RouteInfo[] getRoutes(String interfaceName) { 571 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>(); 572 573 // v4 routes listed as: 574 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT 575 for (String s : readRouteList("/proc/net/route")) { 576 String[] fields = s.split("\t"); 577 578 if (fields.length > 7) { 579 String iface = fields[0]; 580 581 if (interfaceName.equals(iface)) { 582 String dest = fields[1]; 583 String gate = fields[2]; 584 String flags = fields[3]; // future use? 585 String mask = fields[7]; 586 try { 587 // address stored as a hex string, ex: 0014A8C0 588 InetAddress destAddr = 589 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16)); 590 int prefixLength = 591 NetworkUtils.netmaskIntToPrefixLength( 592 (int)Long.parseLong(mask, 16)); 593 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength); 594 595 // address stored as a hex string, ex 0014A8C0 596 InetAddress gatewayAddr = 597 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16)); 598 599 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr); 600 routes.add(route); 601 } catch (Exception e) { 602 Log.e(TAG, "Error parsing route " + s + " : " + e); 603 continue; 604 } 605 } 606 } 607 } 608 609 // v6 routes listed as: 610 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface 611 for (String s : readRouteList("/proc/net/ipv6_route")) { 612 String[]fields = s.split("\\s+"); 613 if (fields.length > 9) { 614 String iface = fields[9].trim(); 615 if (interfaceName.equals(iface)) { 616 String dest = fields[0]; 617 String prefix = fields[1]; 618 String gate = fields[4]; 619 620 try { 621 // prefix length stored as a hex string, ex 40 622 int prefixLength = Integer.parseInt(prefix, 16); 623 624 // address stored as a 32 char hex string 625 // ex fe800000000000000000000000000000 626 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest); 627 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength); 628 629 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate); 630 631 RouteInfo route = new RouteInfo(linkAddress, gateAddr); 632 routes.add(route); 633 } catch (Exception e) { 634 Log.e(TAG, "Error parsing route " + s + " : " + e); 635 continue; 636 } 637 } 638 } 639 } 640 return (RouteInfo[]) routes.toArray(new RouteInfo[0]); 641 } 642 643 public void shutdown() { 644 if (mContext.checkCallingOrSelfPermission( 645 android.Manifest.permission.SHUTDOWN) 646 != PackageManager.PERMISSION_GRANTED) { 647 throw new SecurityException("Requires SHUTDOWN permission"); 648 } 649 650 Slog.d(TAG, "Shutting down"); 651 } 652 653 public boolean getIpForwardingEnabled() throws IllegalStateException{ 654 mContext.enforceCallingOrSelfPermission( 655 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 656 657 ArrayList<String> rsp; 658 try { 659 rsp = mConnector.doCommand("ipfwd status"); 660 } catch (NativeDaemonConnectorException e) { 661 throw new IllegalStateException( 662 "Unable to communicate with native daemon to ipfwd status"); 663 } 664 665 for (String line : rsp) { 666 String[] tok = line.split(" "); 667 if (tok.length < 3) { 668 Slog.e(TAG, "Malformed response from native daemon: " + line); 669 return false; 670 } 671 672 int code = Integer.parseInt(tok[0]); 673 if (code == NetdResponseCode.IpFwdStatusResult) { 674 // 211 Forwarding <enabled/disabled> 675 return "enabled".equals(tok[2]); 676 } else { 677 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 678 } 679 } 680 throw new IllegalStateException("Got an empty response"); 681 } 682 683 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { 684 mContext.enforceCallingOrSelfPermission( 685 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 686 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); 687 } 688 689 public void startTethering(String[] dhcpRange) 690 throws IllegalStateException { 691 mContext.enforceCallingOrSelfPermission( 692 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 693 // cmd is "tether start first_start first_stop second_start second_stop ..." 694 // an odd number of addrs will fail 695 String cmd = "tether start"; 696 for (String d : dhcpRange) { 697 cmd += " " + d; 698 } 699 700 try { 701 mConnector.doCommand(cmd); 702 } catch (NativeDaemonConnectorException e) { 703 throw new IllegalStateException("Unable to communicate to native daemon"); 704 } 705 } 706 707 public void stopTethering() throws IllegalStateException { 708 mContext.enforceCallingOrSelfPermission( 709 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 710 try { 711 mConnector.doCommand("tether stop"); 712 } catch (NativeDaemonConnectorException e) { 713 throw new IllegalStateException("Unable to communicate to native daemon to stop tether"); 714 } 715 } 716 717 public boolean isTetheringStarted() throws IllegalStateException { 718 mContext.enforceCallingOrSelfPermission( 719 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 720 721 ArrayList<String> rsp; 722 try { 723 rsp = mConnector.doCommand("tether status"); 724 } catch (NativeDaemonConnectorException e) { 725 throw new IllegalStateException( 726 "Unable to communicate to native daemon to get tether status"); 727 } 728 729 for (String line : rsp) { 730 String[] tok = line.split(" "); 731 if (tok.length < 3) { 732 throw new IllegalStateException("Malformed response for tether status: " + line); 733 } 734 int code = Integer.parseInt(tok[0]); 735 if (code == NetdResponseCode.TetherStatusResult) { 736 // XXX: Tethering services <started/stopped> <TBD>... 737 return "started".equals(tok[2]); 738 } else { 739 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 740 } 741 } 742 throw new IllegalStateException("Got an empty response"); 743 } 744 745 public void tetherInterface(String iface) throws IllegalStateException { 746 mContext.enforceCallingOrSelfPermission( 747 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 748 try { 749 mConnector.doCommand("tether interface add " + iface); 750 } catch (NativeDaemonConnectorException e) { 751 throw new IllegalStateException( 752 "Unable to communicate to native daemon for adding tether interface"); 753 } 754 } 755 756 public void untetherInterface(String iface) { 757 mContext.enforceCallingOrSelfPermission( 758 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 759 try { 760 mConnector.doCommand("tether interface remove " + iface); 761 } catch (NativeDaemonConnectorException e) { 762 throw new IllegalStateException( 763 "Unable to communicate to native daemon for removing tether interface"); 764 } 765 } 766 767 public String[] listTetheredInterfaces() throws IllegalStateException { 768 mContext.enforceCallingOrSelfPermission( 769 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 770 try { 771 return mConnector.doListCommand( 772 "tether interface list", NetdResponseCode.TetherInterfaceListResult); 773 } catch (NativeDaemonConnectorException e) { 774 throw new IllegalStateException( 775 "Unable to communicate to native daemon for listing tether interfaces"); 776 } 777 } 778 779 public void setDnsForwarders(String[] dns) throws IllegalStateException { 780 mContext.enforceCallingOrSelfPermission( 781 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 782 try { 783 String cmd = "tether dns set"; 784 for (String s : dns) { 785 cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress(); 786 } 787 try { 788 mConnector.doCommand(cmd); 789 } catch (NativeDaemonConnectorException e) { 790 throw new IllegalStateException( 791 "Unable to communicate to native daemon for setting tether dns"); 792 } 793 } catch (IllegalArgumentException e) { 794 throw new IllegalStateException("Error resolving dns name", e); 795 } 796 } 797 798 public String[] getDnsForwarders() throws IllegalStateException { 799 mContext.enforceCallingOrSelfPermission( 800 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 801 try { 802 return mConnector.doListCommand( 803 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); 804 } catch (NativeDaemonConnectorException e) { 805 throw new IllegalStateException( 806 "Unable to communicate to native daemon for listing tether dns"); 807 } 808 } 809 810 public void enableNat(String internalInterface, String externalInterface) 811 throws IllegalStateException { 812 mContext.enforceCallingOrSelfPermission( 813 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 814 try { 815 mConnector.doCommand( 816 String.format("nat enable %s %s", internalInterface, externalInterface)); 817 } catch (NativeDaemonConnectorException e) { 818 throw new IllegalStateException( 819 "Unable to communicate to native daemon for enabling NAT interface"); 820 } 821 } 822 823 public void disableNat(String internalInterface, String externalInterface) 824 throws IllegalStateException { 825 mContext.enforceCallingOrSelfPermission( 826 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 827 try { 828 mConnector.doCommand( 829 String.format("nat disable %s %s", internalInterface, externalInterface)); 830 } catch (NativeDaemonConnectorException e) { 831 throw new IllegalStateException( 832 "Unable to communicate to native daemon for disabling NAT interface"); 833 } 834 } 835 836 public String[] listTtys() throws IllegalStateException { 837 mContext.enforceCallingOrSelfPermission( 838 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 839 try { 840 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); 841 } catch (NativeDaemonConnectorException e) { 842 throw new IllegalStateException( 843 "Unable to communicate to native daemon for listing TTYs"); 844 } 845 } 846 847 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, 848 String dns2Addr) throws IllegalStateException { 849 try { 850 mContext.enforceCallingOrSelfPermission( 851 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 852 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, 853 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(), 854 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(), 855 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(), 856 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress())); 857 } catch (IllegalArgumentException e) { 858 throw new IllegalStateException("Error resolving addr", e); 859 } catch (NativeDaemonConnectorException e) { 860 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e); 861 } 862 } 863 864 public void detachPppd(String tty) throws IllegalStateException { 865 mContext.enforceCallingOrSelfPermission( 866 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 867 try { 868 mConnector.doCommand(String.format("pppd detach %s", tty)); 869 } catch (NativeDaemonConnectorException e) { 870 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e); 871 } 872 } 873 874 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 875 throws IllegalStateException { 876 mContext.enforceCallingOrSelfPermission( 877 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 878 mContext.enforceCallingOrSelfPermission( 879 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 880 try { 881 wifiFirmwareReload(wlanIface, "AP"); 882 mConnector.doCommand(String.format("softap start " + wlanIface)); 883 if (wifiConfig == null) { 884 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 885 } else { 886 /** 887 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] 888 * argv1 - wlan interface 889 * argv2 - softap interface 890 * argv3 - SSID 891 * argv4 - Security 892 * argv5 - Key 893 * argv6 - Channel 894 * argv7 - Preamble 895 * argv8 - Max SCB 896 */ 897 String str = String.format("softap set " + wlanIface + " " + softapIface + 898 " %s %s %s", convertQuotedString(wifiConfig.SSID), 899 getSecurityType(wifiConfig), 900 convertQuotedString(wifiConfig.preSharedKey)); 901 mConnector.doCommand(str); 902 } 903 mConnector.doCommand(String.format("softap startap")); 904 } catch (NativeDaemonConnectorException e) { 905 throw new IllegalStateException("Error communicating to native daemon to start softap", e); 906 } 907 } 908 909 private String convertQuotedString(String s) { 910 if (s == null) { 911 return s; 912 } 913 /* Replace \ with \\, then " with \" and add quotes at end */ 914 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"'; 915 } 916 917 private String getSecurityType(WifiConfiguration wifiConfig) { 918 switch (wifiConfig.getAuthType()) { 919 case KeyMgmt.WPA_PSK: 920 return "wpa-psk"; 921 case KeyMgmt.WPA2_PSK: 922 return "wpa2-psk"; 923 default: 924 return "open"; 925 } 926 } 927 928 /* @param mode can be "AP", "STA" or "P2P" */ 929 public void wifiFirmwareReload(String wlanIface, String mode) throws IllegalStateException { 930 mContext.enforceCallingOrSelfPermission( 931 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 932 mContext.enforceCallingOrSelfPermission( 933 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 934 935 try { 936 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " " + mode)); 937 } catch (NativeDaemonConnectorException e) { 938 throw new IllegalStateException("Error communicating to native daemon ", e); 939 } 940 } 941 942 public void stopAccessPoint(String wlanIface) throws IllegalStateException { 943 mContext.enforceCallingOrSelfPermission( 944 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 945 mContext.enforceCallingOrSelfPermission( 946 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 947 try { 948 mConnector.doCommand("softap stopap"); 949 mConnector.doCommand("softap stop " + wlanIface); 950 wifiFirmwareReload(wlanIface, "STA"); 951 } catch (NativeDaemonConnectorException e) { 952 throw new IllegalStateException("Error communicating to native daemon to stop soft AP", 953 e); 954 } 955 } 956 957 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 958 throws IllegalStateException { 959 mContext.enforceCallingOrSelfPermission( 960 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 961 mContext.enforceCallingOrSelfPermission( 962 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 963 try { 964 if (wifiConfig == null) { 965 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 966 } else { 967 String str = String.format("softap set " + wlanIface + " " + softapIface 968 + " %s %s %s", convertQuotedString(wifiConfig.SSID), 969 getSecurityType(wifiConfig), 970 convertQuotedString(wifiConfig.preSharedKey)); 971 mConnector.doCommand(str); 972 } 973 } catch (NativeDaemonConnectorException e) { 974 throw new IllegalStateException("Error communicating to native daemon to set soft AP", 975 e); 976 } 977 } 978 979 private long getInterfaceCounter(String iface, boolean rx) { 980 mContext.enforceCallingOrSelfPermission( 981 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 982 try { 983 String rsp; 984 try { 985 rsp = mConnector.doCommand( 986 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); 987 } catch (NativeDaemonConnectorException e1) { 988 Slog.e(TAG, "Error communicating with native daemon", e1); 989 return -1; 990 } 991 992 String[] tok = rsp.split(" "); 993 if (tok.length < 2) { 994 Slog.e(TAG, String.format("Malformed response for reading %s interface", 995 (rx ? "rx" : "tx"))); 996 return -1; 997 } 998 999 int code; 1000 try { 1001 code = Integer.parseInt(tok[0]); 1002 } catch (NumberFormatException nfe) { 1003 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 1004 return -1; 1005 } 1006 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || ( 1007 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) { 1008 Slog.e(TAG, String.format("Unexpected response code %d", code)); 1009 return -1; 1010 } 1011 return Long.parseLong(tok[1]); 1012 } catch (Exception e) { 1013 Slog.e(TAG, String.format( 1014 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e); 1015 } 1016 return -1; 1017 } 1018 1019 @Override 1020 public NetworkStats getNetworkStatsSummary() { 1021 mContext.enforceCallingOrSelfPermission( 1022 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 1023 1024 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6); 1025 final NetworkStats.Entry entry = new NetworkStats.Entry(); 1026 1027 final HashSet<String> activeIfaces = Sets.newHashSet(); 1028 final ArrayList<String> values = Lists.newArrayList(); 1029 1030 BufferedReader reader = null; 1031 try { 1032 reader = new BufferedReader(new FileReader(mStatsIface)); 1033 1034 // skip first two header lines 1035 reader.readLine(); 1036 reader.readLine(); 1037 1038 // parse remaining lines 1039 String line; 1040 while ((line = reader.readLine()) != null) { 1041 splitLine(line, values); 1042 1043 try { 1044 entry.iface = values.get(0); 1045 entry.uid = UID_ALL; 1046 entry.set = SET_DEFAULT; 1047 entry.tag = TAG_NONE; 1048 entry.rxBytes = Long.parseLong(values.get(1)); 1049 entry.rxPackets = Long.parseLong(values.get(2)); 1050 entry.txBytes = Long.parseLong(values.get(9)); 1051 entry.txPackets = Long.parseLong(values.get(10)); 1052 1053 activeIfaces.add(entry.iface); 1054 stats.addValues(entry); 1055 } catch (NumberFormatException e) { 1056 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); 1057 } 1058 } 1059 } catch (IOException e) { 1060 Slog.w(TAG, "problem parsing stats: " + e); 1061 } finally { 1062 IoUtils.closeQuietly(reader); 1063 } 1064 1065 if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces); 1066 1067 // splice in stats from any disabled ifaces 1068 if (mBandwidthControlEnabled) { 1069 final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface)); 1070 xtIfaces.removeAll(activeIfaces); 1071 1072 for (String iface : xtIfaces) { 1073 final File ifacePath = new File(mStatsXtIface, iface); 1074 1075 entry.iface = iface; 1076 entry.uid = UID_ALL; 1077 entry.set = SET_DEFAULT; 1078 entry.tag = TAG_NONE; 1079 entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes")); 1080 entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets")); 1081 entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes")); 1082 entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets")); 1083 1084 stats.addValues(entry); 1085 } 1086 1087 if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces); 1088 } 1089 1090 return stats; 1091 } 1092 1093 @Override 1094 public NetworkStats getNetworkStatsDetail() { 1095 mContext.enforceCallingOrSelfPermission( 1096 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 1097 1098 if (mBandwidthControlEnabled) { 1099 return getNetworkStatsDetailNetfilter(UID_ALL); 1100 } else { 1101 return getNetworkStatsDetailUidstat(UID_ALL); 1102 } 1103 } 1104 1105 @Override 1106 public void setInterfaceQuota(String iface, long quotaBytes) { 1107 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1108 1109 // silently discard when control disabled 1110 // TODO: eventually migrate to be always enabled 1111 if (!mBandwidthControlEnabled) return; 1112 1113 synchronized (mQuotaLock) { 1114 if (mActiveQuotaIfaces.contains(iface)) { 1115 throw new IllegalStateException("iface " + iface + " already has quota"); 1116 } 1117 1118 final StringBuilder command = new StringBuilder(); 1119 command.append("bandwidth setiquota ").append(iface).append(" ").append(quotaBytes); 1120 1121 try { 1122 // TODO: support quota shared across interfaces 1123 mConnector.doCommand(command.toString()); 1124 mActiveQuotaIfaces.add(iface); 1125 } catch (NativeDaemonConnectorException e) { 1126 throw new IllegalStateException("Error communicating to native daemon", e); 1127 } 1128 } 1129 } 1130 1131 @Override 1132 public void removeInterfaceQuota(String iface) { 1133 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1134 1135 // silently discard when control disabled 1136 // TODO: eventually migrate to be always enabled 1137 if (!mBandwidthControlEnabled) return; 1138 1139 synchronized (mQuotaLock) { 1140 if (!mActiveQuotaIfaces.contains(iface)) { 1141 // TODO: eventually consider throwing 1142 return; 1143 } 1144 1145 final StringBuilder command = new StringBuilder(); 1146 command.append("bandwidth removeiquota ").append(iface); 1147 1148 try { 1149 // TODO: support quota shared across interfaces 1150 mConnector.doCommand(command.toString()); 1151 mActiveQuotaIfaces.remove(iface); 1152 mActiveAlertIfaces.remove(iface); 1153 } catch (NativeDaemonConnectorException e) { 1154 throw new IllegalStateException("Error communicating to native daemon", e); 1155 } 1156 } 1157 } 1158 1159 @Override 1160 public void setInterfaceAlert(String iface, long alertBytes) { 1161 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1162 1163 // silently discard when control disabled 1164 // TODO: eventually migrate to be always enabled 1165 if (!mBandwidthControlEnabled) return; 1166 1167 // quick sanity check 1168 if (!mActiveQuotaIfaces.contains(iface)) { 1169 throw new IllegalStateException("setting alert requires existing quota on iface"); 1170 } 1171 1172 synchronized (mQuotaLock) { 1173 if (mActiveAlertIfaces.contains(iface)) { 1174 throw new IllegalStateException("iface " + iface + " already has alert"); 1175 } 1176 1177 final StringBuilder command = new StringBuilder(); 1178 command.append("bandwidth setinterfacealert ").append(iface).append(" ").append( 1179 alertBytes); 1180 1181 try { 1182 // TODO: support alert shared across interfaces 1183 mConnector.doCommand(command.toString()); 1184 mActiveAlertIfaces.add(iface); 1185 } catch (NativeDaemonConnectorException e) { 1186 throw new IllegalStateException("Error communicating to native daemon", e); 1187 } 1188 } 1189 } 1190 1191 @Override 1192 public void removeInterfaceAlert(String iface) { 1193 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1194 1195 // silently discard when control disabled 1196 // TODO: eventually migrate to be always enabled 1197 if (!mBandwidthControlEnabled) return; 1198 1199 synchronized (mQuotaLock) { 1200 if (!mActiveAlertIfaces.contains(iface)) { 1201 // TODO: eventually consider throwing 1202 return; 1203 } 1204 1205 final StringBuilder command = new StringBuilder(); 1206 command.append("bandwidth removeinterfacealert ").append(iface); 1207 1208 try { 1209 // TODO: support alert shared across interfaces 1210 mConnector.doCommand(command.toString()); 1211 mActiveAlertIfaces.remove(iface); 1212 } catch (NativeDaemonConnectorException e) { 1213 throw new IllegalStateException("Error communicating to native daemon", e); 1214 } 1215 } 1216 } 1217 1218 @Override 1219 public void setGlobalAlert(long alertBytes) { 1220 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1221 1222 // silently discard when control disabled 1223 // TODO: eventually migrate to be always enabled 1224 if (!mBandwidthControlEnabled) return; 1225 1226 final StringBuilder command = new StringBuilder(); 1227 command.append("bandwidth setglobalalert ").append(alertBytes); 1228 1229 try { 1230 mConnector.doCommand(command.toString()); 1231 } catch (NativeDaemonConnectorException e) { 1232 throw new IllegalStateException("Error communicating to native daemon", e); 1233 } 1234 } 1235 1236 @Override 1237 public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { 1238 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1239 1240 // silently discard when control disabled 1241 // TODO: eventually migrate to be always enabled 1242 if (!mBandwidthControlEnabled) return; 1243 1244 synchronized (mUidRejectOnQuota) { 1245 final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false); 1246 if (oldRejectOnQuota == rejectOnQuotaInterfaces) { 1247 // TODO: eventually consider throwing 1248 return; 1249 } 1250 1251 final StringBuilder command = new StringBuilder(); 1252 command.append("bandwidth"); 1253 if (rejectOnQuotaInterfaces) { 1254 command.append(" addnaughtyapps"); 1255 } else { 1256 command.append(" removenaughtyapps"); 1257 } 1258 command.append(" ").append(uid); 1259 1260 try { 1261 mConnector.doCommand(command.toString()); 1262 if (rejectOnQuotaInterfaces) { 1263 mUidRejectOnQuota.put(uid, true); 1264 } else { 1265 mUidRejectOnQuota.delete(uid); 1266 } 1267 } catch (NativeDaemonConnectorException e) { 1268 throw new IllegalStateException("Error communicating to native daemon", e); 1269 } 1270 } 1271 } 1272 1273 @Override 1274 public boolean isBandwidthControlEnabled() { 1275 mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); 1276 return mBandwidthControlEnabled; 1277 } 1278 1279 @Override 1280 public NetworkStats getNetworkStatsUidDetail(int uid) { 1281 if (Binder.getCallingUid() != uid) { 1282 mContext.enforceCallingOrSelfPermission( 1283 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 1284 } 1285 1286 if (mBandwidthControlEnabled) { 1287 return getNetworkStatsDetailNetfilter(uid); 1288 } else { 1289 return getNetworkStatsDetailUidstat(uid); 1290 } 1291 } 1292 1293 /** 1294 * Build {@link NetworkStats} with detailed UID statistics. 1295 */ 1296 private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) { 1297 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24); 1298 final NetworkStats.Entry entry = new NetworkStats.Entry(); 1299 1300 // TODO: remove knownLines check once 5087722 verified 1301 final HashSet<String> knownLines = Sets.newHashSet(); 1302 1303 final ArrayList<String> keys = Lists.newArrayList(); 1304 final ArrayList<String> values = Lists.newArrayList(); 1305 final HashMap<String, String> parsed = Maps.newHashMap(); 1306 1307 BufferedReader reader = null; 1308 try { 1309 reader = new BufferedReader(new FileReader(mStatsXtUid)); 1310 1311 // parse first line as header 1312 String line = reader.readLine(); 1313 splitLine(line, keys); 1314 1315 // parse remaining lines 1316 while ((line = reader.readLine()) != null) { 1317 splitLine(line, values); 1318 parseLine(keys, values, parsed); 1319 1320 if (!knownLines.add(line)) { 1321 throw new IllegalStateException("encountered duplicate proc entry"); 1322 } 1323 1324 try { 1325 entry.iface = parsed.get(KEY_IFACE); 1326 entry.uid = getParsedInt(parsed, KEY_UID); 1327 entry.set = getParsedInt(parsed, KEY_COUNTER_SET); 1328 entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX)); 1329 entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES); 1330 entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS); 1331 entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES); 1332 entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS); 1333 1334 if (limitUid == UID_ALL || limitUid == entry.uid) { 1335 stats.addValues(entry); 1336 } 1337 } catch (NumberFormatException e) { 1338 Slog.w(TAG, "problem parsing stats row '" + line + "': " + e); 1339 } 1340 } 1341 } catch (IOException e) { 1342 Slog.w(TAG, "problem parsing stats: " + e); 1343 } finally { 1344 IoUtils.closeQuietly(reader); 1345 } 1346 1347 return stats; 1348 } 1349 1350 private static int getParsedInt(HashMap<String, String> parsed, String key) { 1351 final String value = parsed.get(key); 1352 return value != null ? Integer.parseInt(value) : 0; 1353 } 1354 1355 private static long getParsedLong(HashMap<String, String> parsed, String key) { 1356 final String value = parsed.get(key); 1357 return value != null ? Long.parseLong(value) : 0; 1358 } 1359 1360 /** 1361 * Build {@link NetworkStats} with detailed UID statistics. 1362 * 1363 * @deprecated since this uses older "uid_stat" data, and doesn't provide 1364 * tag-level granularity or additional variables. 1365 */ 1366 @Deprecated 1367 private NetworkStats getNetworkStatsDetailUidstat(int limitUid) { 1368 final String[] knownUids; 1369 if (limitUid == UID_ALL) { 1370 knownUids = fileListWithoutNull(mStatsUid); 1371 } else { 1372 knownUids = new String[] { String.valueOf(limitUid) }; 1373 } 1374 1375 final NetworkStats stats = new NetworkStats( 1376 SystemClock.elapsedRealtime(), knownUids.length); 1377 final NetworkStats.Entry entry = new NetworkStats.Entry(); 1378 for (String uid : knownUids) { 1379 final int uidInt = Integer.parseInt(uid); 1380 final File uidPath = new File(mStatsUid, uid); 1381 1382 entry.iface = IFACE_ALL; 1383 entry.uid = uidInt; 1384 entry.tag = TAG_NONE; 1385 entry.rxBytes = readSingleLongFromFile(new File(uidPath, "tcp_rcv")); 1386 entry.rxPackets = readSingleLongFromFile(new File(uidPath, "tcp_rcv_pkt")); 1387 entry.txBytes = readSingleLongFromFile(new File(uidPath, "tcp_snd")); 1388 entry.txPackets = readSingleLongFromFile(new File(uidPath, "tcp_snd_pkt")); 1389 1390 stats.addValues(entry); 1391 } 1392 1393 return stats; 1394 } 1395 1396 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { 1397 mContext.enforceCallingOrSelfPermission( 1398 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 1399 try { 1400 mConnector.doCommand(String.format( 1401 "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); 1402 } catch (NativeDaemonConnectorException e) { 1403 Slog.e(TAG, "Error communicating with native daemon to set throttle", e); 1404 } 1405 } 1406 1407 private int getInterfaceThrottle(String iface, boolean rx) { 1408 mContext.enforceCallingOrSelfPermission( 1409 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 1410 try { 1411 String rsp; 1412 try { 1413 rsp = mConnector.doCommand( 1414 String.format("interface getthrottle %s %s", iface, 1415 (rx ? "rx" : "tx"))).get(0); 1416 } catch (NativeDaemonConnectorException e) { 1417 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e); 1418 return -1; 1419 } 1420 1421 String[] tok = rsp.split(" "); 1422 if (tok.length < 2) { 1423 Slog.e(TAG, "Malformed response to getthrottle command"); 1424 return -1; 1425 } 1426 1427 int code; 1428 try { 1429 code = Integer.parseInt(tok[0]); 1430 } catch (NumberFormatException nfe) { 1431 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 1432 return -1; 1433 } 1434 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || ( 1435 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) { 1436 Slog.e(TAG, String.format("Unexpected response code %d", code)); 1437 return -1; 1438 } 1439 return Integer.parseInt(tok[1]); 1440 } catch (Exception e) { 1441 Slog.e(TAG, String.format( 1442 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e); 1443 } 1444 return -1; 1445 } 1446 1447 public int getInterfaceRxThrottle(String iface) { 1448 return getInterfaceThrottle(iface, true); 1449 } 1450 1451 public int getInterfaceTxThrottle(String iface) { 1452 return getInterfaceThrottle(iface, false); 1453 } 1454 1455 /** 1456 * Split given line into {@link ArrayList}. 1457 */ 1458 private static void splitLine(String line, ArrayList<String> outSplit) { 1459 outSplit.clear(); 1460 1461 final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:"); 1462 while (t.hasMoreTokens()) { 1463 outSplit.add(t.nextToken()); 1464 } 1465 } 1466 1467 /** 1468 * Zip the two given {@link ArrayList} as key and value pairs into 1469 * {@link HashMap}. 1470 */ 1471 private static void parseLine( 1472 ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) { 1473 outParsed.clear(); 1474 1475 final int size = Math.min(keys.size(), values.size()); 1476 for (int i = 0; i < size; i++) { 1477 outParsed.put(keys.get(i), values.get(i)); 1478 } 1479 } 1480 1481 /** 1482 * Utility method to read a single plain-text {@link Long} from the given 1483 * {@link File}, usually from a {@code /proc/} filesystem. 1484 */ 1485 private static long readSingleLongFromFile(File file) { 1486 try { 1487 final byte[] buffer = IoUtils.readFileAsByteArray(file.toString()); 1488 return Long.parseLong(new String(buffer).trim()); 1489 } catch (NumberFormatException e) { 1490 return -1; 1491 } catch (IOException e) { 1492 return -1; 1493 } 1494 } 1495 1496 /** 1497 * Wrapper for {@link File#list()} that returns empty array instead of 1498 * {@code null}. 1499 */ 1500 private static String[] fileListWithoutNull(File file) { 1501 final String[] list = file.list(); 1502 return list != null ? list : new String[0]; 1503 } 1504 1505 public void setDefaultInterfaceForDns(String iface) throws IllegalStateException { 1506 mContext.enforceCallingOrSelfPermission( 1507 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 1508 try { 1509 String cmd = "resolver setdefaultif " + iface; 1510 1511 mConnector.doCommand(cmd); 1512 } catch (NativeDaemonConnectorException e) { 1513 throw new IllegalStateException( 1514 "Error communicating with native daemon to set default interface", e); 1515 } 1516 } 1517 1518 public void setDnsServersForInterface(String iface, String[] servers) 1519 throws IllegalStateException { 1520 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE, 1521 "NetworkManagementService"); 1522 try { 1523 String cmd = "resolver setifdns " + iface; 1524 for (String s : servers) { 1525 InetAddress a = NetworkUtils.numericToInetAddress(s); 1526 if (a.isAnyLocalAddress() == false) { 1527 cmd += " " + a.getHostAddress(); 1528 } 1529 } 1530 mConnector.doCommand(cmd); 1531 } catch (IllegalArgumentException e) { 1532 throw new IllegalStateException("Error setting dnsn for interface", e); 1533 } catch (NativeDaemonConnectorException e) { 1534 throw new IllegalStateException( 1535 "Error communicating with native daemon to set dns for interface", e); 1536 } 1537 } 1538 1539 public void flushDefaultDnsCache() throws IllegalStateException { 1540 mContext.enforceCallingOrSelfPermission( 1541 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 1542 try { 1543 String cmd = "resolver flushdefaultif"; 1544 1545 mConnector.doCommand(cmd); 1546 } catch (NativeDaemonConnectorException e) { 1547 throw new IllegalStateException( 1548 "Error communicating with native deamon to flush default interface", e); 1549 } 1550 } 1551 1552 public void flushInterfaceDnsCache(String iface) throws IllegalStateException { 1553 mContext.enforceCallingOrSelfPermission( 1554 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 1555 try { 1556 String cmd = "resolver flushif " + iface; 1557 1558 mConnector.doCommand(cmd); 1559 } catch (NativeDaemonConnectorException e) { 1560 throw new IllegalStateException( 1561 "Error communicating with native daemon to flush interface " + iface, e); 1562 } 1563 } 1564 1565 /** {@inheritDoc} */ 1566 public void monitor() { 1567 if (mConnector != null) { 1568 mConnector.monitor(); 1569 } 1570 } 1571} 1572