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 android.app.PendingIntent; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.res.Resources; 25 import android.content.pm.PackageManager; 26 import android.net.Uri; 27 import android.net.InterfaceConfiguration; 28 import android.net.INetworkManagementEventObserver; 29 import android.net.wifi.WifiConfiguration; 30 import android.net.wifi.WifiConfiguration.KeyMgmt; 31 import android.os.INetworkManagementService; 32 import android.os.Handler; 33 import android.os.SystemProperties; 34 import android.text.TextUtils; 35 import android.util.Log; 36 import android.util.Slog; 37 import java.util.ArrayList; 38 import java.util.NoSuchElementException; 39 import java.util.StringTokenizer; 40 import android.provider.Settings; 41 import android.content.ContentResolver; 42 import android.database.ContentObserver; 43 44 import java.io.File; 45 import java.io.FileReader; 46 import java.lang.IllegalStateException; 47 48 import java.net.InetAddress; 49 import java.net.UnknownHostException; 50 import java.util.concurrent.CountDownLatch; 51 52 /** 53 * @hide 54 */ 55 class NetworkManagementService extends INetworkManagementService.Stub { 56 57 private static final String TAG = "NetworkManagmentService"; 58 private static final boolean DBG = false; 59 private static final String NETD_TAG = "NetdConnector"; 60 61 class NetdResponseCode { 62 public static final int InterfaceListResult = 110; 63 public static final int TetherInterfaceListResult = 111; 64 public static final int TetherDnsFwdTgtListResult = 112; 65 public static final int TtyListResult = 113; 66 67 public static final int TetherStatusResult = 210; 68 public static final int IpFwdStatusResult = 211; 69 public static final int InterfaceGetCfgResult = 213; 70 public static final int SoftapStatusResult = 214; 71 public static final int UsbRNDISStatusResult = 215; 72 public static final int InterfaceRxCounterResult = 216; 73 public static final int InterfaceTxCounterResult = 217; 74 public static final int InterfaceRxThrottleResult = 218; 75 public static final int InterfaceTxThrottleResult = 219; 76 77 public static final int InterfaceChange = 600; 78 } 79 80 /** 81 * Binder context for this service 82 */ 83 private Context mContext; 84 85 /** 86 * connector object for communicating with netd 87 */ 88 private NativeDaemonConnector mConnector; 89 90 private Thread mThread; 91 private final CountDownLatch mConnectedSignal = new CountDownLatch(1); 92 93 private ArrayList<INetworkManagementEventObserver> mObservers; 94 95 /** 96 * Constructs a new NetworkManagementService instance 97 * 98 * @param context Binder context for this service 99 */ 100 private NetworkManagementService(Context context) { 101 mContext = context; 102 mObservers = new ArrayList<INetworkManagementEventObserver>(); 103 104 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 105 return; 106 } 107 108 mConnector = new NativeDaemonConnector( 109 new NetdCallbackReceiver(), "netd", 10, NETD_TAG); 110 mThread = new Thread(mConnector, NETD_TAG); 111 } 112 113 public static NetworkManagementService create(Context context) throws InterruptedException { 114 NetworkManagementService service = new NetworkManagementService(context); 115 if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); 116 service.mThread.start(); 117 if (DBG) Slog.d(TAG, "Awaiting socket connection"); 118 service.mConnectedSignal.await(); 119 if (DBG) Slog.d(TAG, "Connected"); 120 return service; 121 } 122 123 public void registerObserver(INetworkManagementEventObserver obs) { 124 Slog.d(TAG, "Registering observer"); 125 mObservers.add(obs); 126 } 127 128 public void unregisterObserver(INetworkManagementEventObserver obs) { 129 Slog.d(TAG, "Unregistering observer"); 130 mObservers.remove(mObservers.indexOf(obs)); 131 } 132 133 /** 134 * Notify our observers of an interface link status change 135 */ 136 private void notifyInterfaceLinkStatusChanged(String iface, boolean link) { 137 for (INetworkManagementEventObserver obs : mObservers) { 138 try { 139 obs.interfaceLinkStatusChanged(iface, link); 140 } catch (Exception ex) { 141 Slog.w(TAG, "Observer notifier failed", ex); 142 } 143 } 144 } 145 146 /** 147 * Notify our observers of an interface addition. 148 */ 149 private void notifyInterfaceAdded(String iface) { 150 for (INetworkManagementEventObserver obs : mObservers) { 151 try { 152 obs.interfaceAdded(iface); 153 } catch (Exception ex) { 154 Slog.w(TAG, "Observer notifier failed", ex); 155 } 156 } 157 } 158 159 /** 160 * Notify our observers of an interface removal. 161 */ 162 private void notifyInterfaceRemoved(String iface) { 163 for (INetworkManagementEventObserver obs : mObservers) { 164 try { 165 obs.interfaceRemoved(iface); 166 } catch (Exception ex) { 167 Slog.w(TAG, "Observer notifier failed", ex); 168 } 169 } 170 } 171 172 /** 173 * Let us know the daemon is connected 174 */ 175 protected void onConnected() { 176 if (DBG) Slog.d(TAG, "onConnected"); 177 mConnectedSignal.countDown(); 178 } 179 180 181 // 182 // Netd Callback handling 183 // 184 185 class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 186 public void onDaemonConnected() { 187 NetworkManagementService.this.onConnected(); 188 new Thread() { 189 public void run() { 190 } 191 }.start(); 192 } 193 public boolean onEvent(int code, String raw, String[] cooked) { 194 if (code == NetdResponseCode.InterfaceChange) { 195 /* 196 * a network interface change occured 197 * Format: "NNN Iface added <name>" 198 * "NNN Iface removed <name>" 199 * "NNN Iface changed <name> <up/down>" 200 */ 201 if (cooked.length < 4 || !cooked[1].equals("Iface")) { 202 throw new IllegalStateException( 203 String.format("Invalid event from daemon (%s)", raw)); 204 } 205 if (cooked[2].equals("added")) { 206 notifyInterfaceAdded(cooked[3]); 207 return true; 208 } else if (cooked[2].equals("removed")) { 209 notifyInterfaceRemoved(cooked[3]); 210 return true; 211 } else if (cooked[2].equals("changed") && cooked.length == 5) { 212 notifyInterfaceLinkStatusChanged(cooked[3], cooked[4].equals("up")); 213 return true; 214 } 215 throw new IllegalStateException( 216 String.format("Invalid event from daemon (%s)", raw)); 217 } 218 return false; 219 } 220 } 221 222 private static int stringToIpAddr(String addrString) throws UnknownHostException { 223 try { 224 String[] parts = addrString.split("\\."); 225 if (parts.length != 4) { 226 throw new UnknownHostException(addrString); 227 } 228 229 int a = Integer.parseInt(parts[0]) ; 230 int b = Integer.parseInt(parts[1]) << 8; 231 int c = Integer.parseInt(parts[2]) << 16; 232 int d = Integer.parseInt(parts[3]) << 24; 233 234 return a | b | c | d; 235 } catch (NumberFormatException ex) { 236 throw new UnknownHostException(addrString); 237 } 238 } 239 240 public static String intToIpString(int i) { 241 return ((i >> 24 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ((i >> 8 ) & 0xFF) + "." + 242 (i & 0xFF); 243 } 244 245 // 246 // INetworkManagementService members 247 // 248 249 public String[] listInterfaces() throws IllegalStateException { 250 mContext.enforceCallingOrSelfPermission( 251 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 252 253 try { 254 return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); 255 } catch (NativeDaemonConnectorException e) { 256 throw new IllegalStateException( 257 "Cannot communicate with native daemon to list interfaces"); 258 } 259 } 260 261 public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { 262 String rsp; 263 try { 264 rsp = mConnector.doCommand("interface getcfg " + iface).get(0); 265 } catch (NativeDaemonConnectorException e) { 266 throw new IllegalStateException( 267 "Cannot communicate with native daemon to get interface config"); 268 } 269 Slog.d(TAG, String.format("rsp <%s>", rsp)); 270 271 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz.zzz.zzz.zzz [flag1 flag2 flag3] 272 StringTokenizer st = new StringTokenizer(rsp); 273 274 InterfaceConfiguration cfg; 275 try { 276 try { 277 int code = Integer.parseInt(st.nextToken(" ")); 278 if (code != NetdResponseCode.InterfaceGetCfgResult) { 279 throw new IllegalStateException( 280 String.format("Expected code %d, but got %d", 281 NetdResponseCode.InterfaceGetCfgResult, code)); 282 } 283 } catch (NumberFormatException nfe) { 284 throw new IllegalStateException( 285 String.format("Invalid response from daemon (%s)", rsp)); 286 } 287 288 cfg = new InterfaceConfiguration(); 289 cfg.hwAddr = st.nextToken(" "); 290 try { 291 cfg.ipAddr = stringToIpAddr(st.nextToken(" ")); 292 } catch (UnknownHostException uhe) { 293 Slog.e(TAG, "Failed to parse ipaddr", uhe); 294 cfg.ipAddr = 0; 295 } 296 297 try { 298 cfg.netmask = stringToIpAddr(st.nextToken(" ")); 299 } catch (UnknownHostException uhe) { 300 Slog.e(TAG, "Failed to parse netmask", uhe); 301 cfg.netmask = 0; 302 } 303 cfg.interfaceFlags = st.nextToken("]").trim() +"]"; 304 } catch (NoSuchElementException nsee) { 305 throw new IllegalStateException( 306 String.format("Invalid response from daemon (%s)", rsp)); 307 } 308 Slog.d(TAG, String.format("flags <%s>", cfg.interfaceFlags)); 309 return cfg; 310 } 311 312 public void setInterfaceConfig( 313 String iface, InterfaceConfiguration cfg) throws IllegalStateException { 314 String cmd = String.format("interface setcfg %s %s %s %s", iface, 315 intToIpString(cfg.ipAddr), intToIpString(cfg.netmask), cfg.interfaceFlags); 316 try { 317 mConnector.doCommand(cmd); 318 } catch (NativeDaemonConnectorException e) { 319 throw new IllegalStateException( 320 "Unable to communicate with native daemon to interface setcfg"); 321 } 322 } 323 324 public void shutdown() { 325 if (mContext.checkCallingOrSelfPermission( 326 android.Manifest.permission.SHUTDOWN) 327 != PackageManager.PERMISSION_GRANTED) { 328 throw new SecurityException("Requires SHUTDOWN permission"); 329 } 330 331 Slog.d(TAG, "Shutting down"); 332 } 333 334 public boolean getIpForwardingEnabled() throws IllegalStateException{ 335 mContext.enforceCallingOrSelfPermission( 336 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 337 338 ArrayList<String> rsp; 339 try { 340 rsp = mConnector.doCommand("ipfwd status"); 341 } catch (NativeDaemonConnectorException e) { 342 throw new IllegalStateException( 343 "Unable to communicate with native daemon to ipfwd status"); 344 } 345 346 for (String line : rsp) { 347 String[] tok = line.split(" "); 348 if (tok.length < 3) { 349 Slog.e(TAG, "Malformed response from native daemon: " + line); 350 return false; 351 } 352 353 int code = Integer.parseInt(tok[0]); 354 if (code == NetdResponseCode.IpFwdStatusResult) { 355 // 211 Forwarding <enabled/disabled> 356 return "enabled".equals(tok[2]); 357 } else { 358 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 359 } 360 } 361 throw new IllegalStateException("Got an empty response"); 362 } 363 364 public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { 365 mContext.enforceCallingOrSelfPermission( 366 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 367 mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); 368 } 369 370 public void startTethering(String[] dhcpRange) 371 throws IllegalStateException { 372 mContext.enforceCallingOrSelfPermission( 373 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 374 // cmd is "tether start first_start first_stop second_start second_stop ..." 375 // an odd number of addrs will fail 376 String cmd = "tether start"; 377 for (String d : dhcpRange) { 378 cmd += " " + d; 379 } 380 381 try { 382 mConnector.doCommand(cmd); 383 } catch (NativeDaemonConnectorException e) { 384 throw new IllegalStateException("Unable to communicate to native daemon"); 385 } 386 } 387 388 public void stopTethering() throws IllegalStateException { 389 mContext.enforceCallingOrSelfPermission( 390 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 391 try { 392 mConnector.doCommand("tether stop"); 393 } catch (NativeDaemonConnectorException e) { 394 throw new IllegalStateException("Unable to communicate to native daemon to stop tether"); 395 } 396 } 397 398 public boolean isTetheringStarted() throws IllegalStateException { 399 mContext.enforceCallingOrSelfPermission( 400 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 401 402 ArrayList<String> rsp; 403 try { 404 rsp = mConnector.doCommand("tether status"); 405 } catch (NativeDaemonConnectorException e) { 406 throw new IllegalStateException( 407 "Unable to communicate to native daemon to get tether status"); 408 } 409 410 for (String line : rsp) { 411 String[] tok = line.split(" "); 412 if (tok.length < 3) { 413 throw new IllegalStateException("Malformed response for tether status: " + line); 414 } 415 int code = Integer.parseInt(tok[0]); 416 if (code == NetdResponseCode.TetherStatusResult) { 417 // XXX: Tethering services <started/stopped> <TBD>... 418 return "started".equals(tok[2]); 419 } else { 420 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 421 } 422 } 423 throw new IllegalStateException("Got an empty response"); 424 } 425 426 public void tetherInterface(String iface) throws IllegalStateException { 427 mContext.enforceCallingOrSelfPermission( 428 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 429 try { 430 mConnector.doCommand("tether interface add " + iface); 431 } catch (NativeDaemonConnectorException e) { 432 throw new IllegalStateException( 433 "Unable to communicate to native daemon for adding tether interface"); 434 } 435 } 436 437 public void untetherInterface(String iface) { 438 mContext.enforceCallingOrSelfPermission( 439 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 440 try { 441 mConnector.doCommand("tether interface remove " + iface); 442 } catch (NativeDaemonConnectorException e) { 443 throw new IllegalStateException( 444 "Unable to communicate to native daemon for removing tether interface"); 445 } 446 } 447 448 public String[] listTetheredInterfaces() throws IllegalStateException { 449 mContext.enforceCallingOrSelfPermission( 450 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 451 try { 452 return mConnector.doListCommand( 453 "tether interface list", NetdResponseCode.TetherInterfaceListResult); 454 } catch (NativeDaemonConnectorException e) { 455 throw new IllegalStateException( 456 "Unable to communicate to native daemon for listing tether interfaces"); 457 } 458 } 459 460 public void setDnsForwarders(String[] dns) throws IllegalStateException { 461 mContext.enforceCallingOrSelfPermission( 462 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 463 try { 464 String cmd = "tether dns set"; 465 for (String s : dns) { 466 cmd += " " + InetAddress.getByName(s).getHostAddress(); 467 } 468 try { 469 mConnector.doCommand(cmd); 470 } catch (NativeDaemonConnectorException e) { 471 throw new IllegalStateException( 472 "Unable to communicate to native daemon for setting tether dns"); 473 } 474 } catch (UnknownHostException e) { 475 throw new IllegalStateException("Error resolving dns name", e); 476 } 477 } 478 479 public String[] getDnsForwarders() throws IllegalStateException { 480 mContext.enforceCallingOrSelfPermission( 481 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 482 try { 483 return mConnector.doListCommand( 484 "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); 485 } catch (NativeDaemonConnectorException e) { 486 throw new IllegalStateException( 487 "Unable to communicate to native daemon for listing tether dns"); 488 } 489 } 490 491 public void enableNat(String internalInterface, String externalInterface) 492 throws IllegalStateException { 493 mContext.enforceCallingOrSelfPermission( 494 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 495 try { 496 mConnector.doCommand( 497 String.format("nat enable %s %s", internalInterface, externalInterface)); 498 } catch (NativeDaemonConnectorException e) { 499 throw new IllegalStateException( 500 "Unable to communicate to native daemon for enabling NAT interface"); 501 } 502 } 503 504 public void disableNat(String internalInterface, String externalInterface) 505 throws IllegalStateException { 506 mContext.enforceCallingOrSelfPermission( 507 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 508 try { 509 mConnector.doCommand( 510 String.format("nat disable %s %s", internalInterface, externalInterface)); 511 } catch (NativeDaemonConnectorException e) { 512 throw new IllegalStateException( 513 "Unable to communicate to native daemon for disabling NAT interface"); 514 } 515 } 516 517 public String[] listTtys() throws IllegalStateException { 518 mContext.enforceCallingOrSelfPermission( 519 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 520 try { 521 return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); 522 } catch (NativeDaemonConnectorException e) { 523 throw new IllegalStateException( 524 "Unable to communicate to native daemon for listing TTYs"); 525 } 526 } 527 528 public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, 529 String dns2Addr) throws IllegalStateException { 530 try { 531 mContext.enforceCallingOrSelfPermission( 532 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 533 mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, 534 InetAddress.getByName(localAddr).getHostAddress(), 535 InetAddress.getByName(remoteAddr).getHostAddress(), 536 InetAddress.getByName(dns1Addr).getHostAddress(), 537 InetAddress.getByName(dns2Addr).getHostAddress())); 538 } catch (UnknownHostException e) { 539 throw new IllegalStateException("Error resolving addr", e); 540 } catch (NativeDaemonConnectorException e) { 541 throw new IllegalStateException("Error communicating to native daemon to attach pppd", e); 542 } 543 } 544 545 public void detachPppd(String tty) throws IllegalStateException { 546 mContext.enforceCallingOrSelfPermission( 547 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 548 try { 549 mConnector.doCommand(String.format("pppd detach %s", tty)); 550 } catch (NativeDaemonConnectorException e) { 551 throw new IllegalStateException("Error communicating to native daemon to detach pppd", e); 552 } 553 } 554 555 public void startUsbRNDIS() throws IllegalStateException { 556 mContext.enforceCallingOrSelfPermission( 557 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 558 try { 559 mConnector.doCommand("usb startrndis"); 560 } catch (NativeDaemonConnectorException e) { 561 throw new IllegalStateException( 562 "Error communicating to native daemon for starting RNDIS", e); 563 } 564 } 565 566 public void stopUsbRNDIS() throws IllegalStateException { 567 mContext.enforceCallingOrSelfPermission( 568 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 569 try { 570 mConnector.doCommand("usb stoprndis"); 571 } catch (NativeDaemonConnectorException e) { 572 throw new IllegalStateException("Error communicating to native daemon", e); 573 } 574 } 575 576 public boolean isUsbRNDISStarted() throws IllegalStateException { 577 mContext.enforceCallingOrSelfPermission( 578 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 579 ArrayList<String> rsp; 580 try { 581 rsp = mConnector.doCommand("usb rndisstatus"); 582 } catch (NativeDaemonConnectorException e) { 583 throw new IllegalStateException( 584 "Error communicating to native daemon to check RNDIS status", e); 585 } 586 587 for (String line : rsp) { 588 String []tok = line.split(" "); 589 int code = Integer.parseInt(tok[0]); 590 if (code == NetdResponseCode.UsbRNDISStatusResult) { 591 if (tok[3].equals("started")) 592 return true; 593 return false; 594 } else { 595 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 596 } 597 } 598 throw new IllegalStateException("Got an empty response"); 599 } 600 601 public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 602 throws IllegalStateException { 603 mContext.enforceCallingOrSelfPermission( 604 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 605 mContext.enforceCallingOrSelfPermission( 606 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 607 try { 608 mConnector.doCommand(String.format("softap stop " + wlanIface)); 609 mConnector.doCommand(String.format("softap fwreload " + wlanIface + " AP")); 610 mConnector.doCommand(String.format("softap start " + wlanIface)); 611 if (wifiConfig == null) { 612 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 613 } else { 614 /** 615 * softap set arg1 arg2 arg3 [arg4 arg5 arg6 arg7 arg8] 616 * argv1 - wlan interface 617 * argv2 - softap interface 618 * argv3 - SSID 619 * argv4 - Security 620 * argv5 - Key 621 * argv6 - Channel 622 * argv7 - Preamble 623 * argv8 - Max SCB 624 */ 625 String str = String.format("softap set " + wlanIface + " " + softapIface + 626 " %s %s %s", convertQuotedString(wifiConfig.SSID), 627 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? 628 "wpa2-psk" : "open", 629 convertQuotedString(wifiConfig.preSharedKey)); 630 mConnector.doCommand(str); 631 } 632 mConnector.doCommand(String.format("softap startap")); 633 } catch (NativeDaemonConnectorException e) { 634 throw new IllegalStateException("Error communicating to native daemon to start softap", e); 635 } 636 } 637 638 private String convertQuotedString(String s) { 639 if (s == null) { 640 return s; 641 } 642 /* Replace \ with \\, then " with \" and add quotes at end */ 643 return '"' + s.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\\\"") + '"'; 644 } 645 646 public void stopAccessPoint() throws IllegalStateException { 647 mContext.enforceCallingOrSelfPermission( 648 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 649 mContext.enforceCallingOrSelfPermission( 650 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 651 try { 652 mConnector.doCommand("softap stopap"); 653 } catch (NativeDaemonConnectorException e) { 654 throw new IllegalStateException("Error communicating to native daemon to stop soft AP", 655 e); 656 } 657 } 658 659 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) 660 throws IllegalStateException { 661 mContext.enforceCallingOrSelfPermission( 662 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 663 mContext.enforceCallingOrSelfPermission( 664 android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); 665 try { 666 if (wifiConfig == null) { 667 mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); 668 } else { 669 String str = String.format("softap set " + wlanIface + " " + softapIface 670 + " %s %s %s", convertQuotedString(wifiConfig.SSID), 671 wifiConfig.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ? "wpa2-psk" : "open", 672 convertQuotedString(wifiConfig.preSharedKey)); 673 mConnector.doCommand(str); 674 } 675 } catch (NativeDaemonConnectorException e) { 676 throw new IllegalStateException("Error communicating to native daemon to set soft AP", 677 e); 678 } 679 } 680 681 private long getInterfaceCounter(String iface, boolean rx) { 682 mContext.enforceCallingOrSelfPermission( 683 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 684 try { 685 String rsp; 686 try { 687 rsp = mConnector.doCommand( 688 String.format("interface read%scounter %s", (rx ? "rx" : "tx"), iface)).get(0); 689 } catch (NativeDaemonConnectorException e1) { 690 Slog.e(TAG, "Error communicating with native daemon", e1); 691 return -1; 692 } 693 694 String[] tok = rsp.split(" "); 695 if (tok.length < 2) { 696 Slog.e(TAG, String.format("Malformed response for reading %s interface", 697 (rx ? "rx" : "tx"))); 698 return -1; 699 } 700 701 int code; 702 try { 703 code = Integer.parseInt(tok[0]); 704 } catch (NumberFormatException nfe) { 705 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 706 return -1; 707 } 708 if ((rx && code != NetdResponseCode.InterfaceRxCounterResult) || ( 709 !rx && code != NetdResponseCode.InterfaceTxCounterResult)) { 710 Slog.e(TAG, String.format("Unexpected response code %d", code)); 711 return -1; 712 } 713 return Long.parseLong(tok[1]); 714 } catch (Exception e) { 715 Slog.e(TAG, String.format( 716 "Failed to read interface %s counters", (rx ? "rx" : "tx")), e); 717 } 718 return -1; 719 } 720 721 public long getInterfaceRxCounter(String iface) { 722 return getInterfaceCounter(iface, true); 723 } 724 725 public long getInterfaceTxCounter(String iface) { 726 return getInterfaceCounter(iface, false); 727 } 728 729 public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { 730 mContext.enforceCallingOrSelfPermission( 731 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); 732 try { 733 mConnector.doCommand(String.format( 734 "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); 735 } catch (NativeDaemonConnectorException e) { 736 Slog.e(TAG, "Error communicating with native daemon to set throttle", e); 737 } 738 } 739 740 private int getInterfaceThrottle(String iface, boolean rx) { 741 mContext.enforceCallingOrSelfPermission( 742 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); 743 try { 744 String rsp; 745 try { 746 rsp = mConnector.doCommand( 747 String.format("interface getthrottle %s %s", iface, 748 (rx ? "rx" : "tx"))).get(0); 749 } catch (NativeDaemonConnectorException e) { 750 Slog.e(TAG, "Error communicating with native daemon to getthrottle", e); 751 return -1; 752 } 753 754 String[] tok = rsp.split(" "); 755 if (tok.length < 2) { 756 Slog.e(TAG, "Malformed response to getthrottle command"); 757 return -1; 758 } 759 760 int code; 761 try { 762 code = Integer.parseInt(tok[0]); 763 } catch (NumberFormatException nfe) { 764 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 765 return -1; 766 } 767 if ((rx && code != NetdResponseCode.InterfaceRxThrottleResult) || ( 768 !rx && code != NetdResponseCode.InterfaceTxThrottleResult)) { 769 Slog.e(TAG, String.format("Unexpected response code %d", code)); 770 return -1; 771 } 772 return Integer.parseInt(tok[1]); 773 } catch (Exception e) { 774 Slog.e(TAG, String.format( 775 "Failed to read interface %s throttle value", (rx ? "rx" : "tx")), e); 776 } 777 return -1; 778 } 779 780 public int getInterfaceRxThrottle(String iface) { 781 return getInterfaceThrottle(iface, true); 782 } 783 784 public int getInterfaceTxThrottle(String iface) { 785 return getInterfaceThrottle(iface, false); 786 } 787 } 788