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