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