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