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