NetworkManagementService.java revision 4590e52f3d0558e01322fe4dd55bb612afdfb079
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.CONNECTIVITY_INTERNAL; 20import static android.Manifest.permission.DUMP; 21import static android.Manifest.permission.SHUTDOWN; 22import static android.net.NetworkStats.SET_DEFAULT; 23import static android.net.NetworkStats.TAG_ALL; 24import static android.net.NetworkStats.TAG_NONE; 25import static android.net.NetworkStats.UID_ALL; 26import static android.net.TrafficStats.UID_TETHERING; 27import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult; 28import static com.android.server.NetworkManagementService.NetdResponseCode.GetMarkResult; 29import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult; 30import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult; 31import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult; 32import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult; 33import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult; 34import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult; 35import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult; 36import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult; 37import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; 38 39import android.content.Context; 40import android.net.ConnectivityManager; 41import android.net.INetworkManagementEventObserver; 42import android.net.InterfaceConfiguration; 43import android.net.LinkAddress; 44import android.net.NetworkStats; 45import android.net.NetworkUtils; 46import android.net.RouteInfo; 47import android.net.wifi.WifiConfiguration; 48import android.net.wifi.WifiConfiguration.KeyMgmt; 49import android.os.BatteryStats; 50import android.os.Binder; 51import android.os.Handler; 52import android.os.INetworkActivityListener; 53import android.os.INetworkManagementService; 54import android.os.PowerManager; 55import android.os.Process; 56import android.os.RemoteCallbackList; 57import android.os.RemoteException; 58import android.os.ServiceManager; 59import android.os.SystemClock; 60import android.os.SystemProperties; 61import android.util.Log; 62import android.util.Slog; 63import android.util.SparseBooleanArray; 64 65import com.android.internal.app.IBatteryStats; 66import com.android.internal.net.NetworkStatsFactory; 67import com.android.internal.util.Preconditions; 68import com.android.server.NativeDaemonConnector.Command; 69import com.android.server.NativeDaemonConnector.SensitiveArg; 70import com.android.server.net.LockdownVpnTracker; 71import com.google.android.collect.Maps; 72 73import java.io.BufferedReader; 74import java.io.DataInputStream; 75import java.io.File; 76import java.io.FileDescriptor; 77import java.io.FileInputStream; 78import java.io.IOException; 79import java.io.InputStreamReader; 80import java.io.PrintWriter; 81import java.net.Inet4Address; 82import java.net.InetAddress; 83import java.net.InterfaceAddress; 84import java.net.NetworkInterface; 85import java.net.SocketException; 86import java.util.ArrayList; 87import java.util.HashMap; 88import java.util.List; 89import java.util.Map; 90import java.util.NoSuchElementException; 91import java.util.StringTokenizer; 92import java.util.concurrent.CountDownLatch; 93 94/** 95 * @hide 96 */ 97public class NetworkManagementService extends INetworkManagementService.Stub 98 implements Watchdog.Monitor { 99 private static final String TAG = "NetworkManagementService"; 100 private static final boolean DBG = false; 101 private static final String NETD_TAG = "NetdConnector"; 102 private static final String NETD_SOCKET_NAME = "netd"; 103 104 private static final String ADD = "add"; 105 private static final String REMOVE = "remove"; 106 107 private static final String ALLOW = "allow"; 108 private static final String DENY = "deny"; 109 110 private static final String DEFAULT = "default"; 111 private static final String SECONDARY = "secondary"; 112 113 /** 114 * Name representing {@link #setGlobalAlert(long)} limit when delivered to 115 * {@link INetworkManagementEventObserver#limitReached(String, String)}. 116 */ 117 public static final String LIMIT_GLOBAL_ALERT = "globalAlert"; 118 119 class NetdResponseCode { 120 /* Keep in sync with system/netd/ResponseCode.h */ 121 public static final int InterfaceListResult = 110; 122 public static final int TetherInterfaceListResult = 111; 123 public static final int TetherDnsFwdTgtListResult = 112; 124 public static final int TtyListResult = 113; 125 public static final int TetheringStatsListResult = 114; 126 127 public static final int TetherStatusResult = 210; 128 public static final int IpFwdStatusResult = 211; 129 public static final int InterfaceGetCfgResult = 213; 130 public static final int SoftapStatusResult = 214; 131 public static final int InterfaceRxCounterResult = 216; 132 public static final int InterfaceTxCounterResult = 217; 133 public static final int QuotaCounterResult = 220; 134 public static final int TetheringStatsResult = 221; 135 public static final int DnsProxyQueryResult = 222; 136 public static final int ClatdStatusResult = 223; 137 public static final int GetMarkResult = 225; 138 139 public static final int InterfaceChange = 600; 140 public static final int BandwidthControl = 601; 141 public static final int InterfaceClassActivity = 613; 142 public static final int InterfaceAddressChange = 614; 143 public static final int InterfaceDnsServerInfo = 615; 144 } 145 146 /** 147 * Binder context for this service 148 */ 149 private Context mContext; 150 151 /** 152 * connector object for communicating with netd 153 */ 154 private NativeDaemonConnector mConnector; 155 156 private final Handler mMainHandler = new Handler(); 157 158 private IBatteryStats mBatteryStats; 159 160 private Thread mThread; 161 private CountDownLatch mConnectedSignal = new CountDownLatch(1); 162 163 private final RemoteCallbackList<INetworkManagementEventObserver> mObservers = 164 new RemoteCallbackList<INetworkManagementEventObserver>(); 165 166 private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory(); 167 168 private Object mQuotaLock = new Object(); 169 /** Set of interfaces with active quotas. */ 170 private HashMap<String, Long> mActiveQuotas = Maps.newHashMap(); 171 /** Set of interfaces with active alerts. */ 172 private HashMap<String, Long> mActiveAlerts = Maps.newHashMap(); 173 /** Set of UIDs with active reject rules. */ 174 private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray(); 175 176 private Object mIdleTimerLock = new Object(); 177 /** Set of interfaces with active idle timers. */ 178 private static class IdleTimerParams { 179 public final int timeout; 180 public final int type; 181 public int networkCount; 182 183 IdleTimerParams(int timeout, int type) { 184 this.timeout = timeout; 185 this.type = type; 186 this.networkCount = 1; 187 } 188 } 189 private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap(); 190 191 private volatile boolean mBandwidthControlEnabled; 192 private volatile boolean mFirewallEnabled; 193 194 private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners = 195 new RemoteCallbackList<INetworkActivityListener>(); 196 private boolean mNetworkActive; 197 198 /** 199 * Constructs a new NetworkManagementService instance 200 * 201 * @param context Binder context for this service 202 */ 203 private NetworkManagementService(Context context, String socket) { 204 mContext = context; 205 206 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 207 return; 208 } 209 210 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 211 // Don't need this wake lock, since we now have a time stamp for when 212 // the network actually went inactive. (It might be nice to still do this, 213 // but I don't want to do it through the power manager because that pollutes the 214 // battery stats history with pointless noise.) 215 PowerManager.WakeLock wl = null; //pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NETD_TAG); 216 217 mConnector = new NativeDaemonConnector( 218 new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160, wl); 219 mThread = new Thread(mConnector, NETD_TAG); 220 221 // Add ourself to the Watchdog monitors. 222 Watchdog.getInstance().addMonitor(this); 223 } 224 225 static NetworkManagementService create(Context context, 226 String socket) throws InterruptedException { 227 final NetworkManagementService service = new NetworkManagementService(context, socket); 228 final CountDownLatch connectedSignal = service.mConnectedSignal; 229 if (DBG) Slog.d(TAG, "Creating NetworkManagementService"); 230 service.mThread.start(); 231 if (DBG) Slog.d(TAG, "Awaiting socket connection"); 232 connectedSignal.await(); 233 if (DBG) Slog.d(TAG, "Connected"); 234 return service; 235 } 236 237 public static NetworkManagementService create(Context context) throws InterruptedException { 238 return create(context, NETD_SOCKET_NAME); 239 } 240 241 public void systemReady() { 242 prepareNativeDaemon(); 243 if (DBG) Slog.d(TAG, "Prepared"); 244 } 245 246 private IBatteryStats getBatteryStats() { 247 synchronized (this) { 248 if (mBatteryStats != null) { 249 return mBatteryStats; 250 } 251 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService( 252 BatteryStats.SERVICE_NAME)); 253 return mBatteryStats; 254 } 255 } 256 257 @Override 258 public void registerObserver(INetworkManagementEventObserver observer) { 259 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 260 mObservers.register(observer); 261 } 262 263 @Override 264 public void unregisterObserver(INetworkManagementEventObserver observer) { 265 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 266 mObservers.unregister(observer); 267 } 268 269 /** 270 * Notify our observers of an interface status change 271 */ 272 private void notifyInterfaceStatusChanged(String iface, boolean up) { 273 final int length = mObservers.beginBroadcast(); 274 for (int i = 0; i < length; i++) { 275 try { 276 mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up); 277 } catch (RemoteException e) { 278 } catch (RuntimeException e) { 279 } 280 } 281 mObservers.finishBroadcast(); 282 } 283 284 /** 285 * Notify our observers of an interface link state change 286 * (typically, an Ethernet cable has been plugged-in or unplugged). 287 */ 288 private void notifyInterfaceLinkStateChanged(String iface, boolean up) { 289 final int length = mObservers.beginBroadcast(); 290 for (int i = 0; i < length; i++) { 291 try { 292 mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up); 293 } catch (RemoteException e) { 294 } catch (RuntimeException e) { 295 } 296 } 297 mObservers.finishBroadcast(); 298 } 299 300 /** 301 * Notify our observers of an interface addition. 302 */ 303 private void notifyInterfaceAdded(String iface) { 304 final int length = mObservers.beginBroadcast(); 305 for (int i = 0; i < length; i++) { 306 try { 307 mObservers.getBroadcastItem(i).interfaceAdded(iface); 308 } catch (RemoteException e) { 309 } catch (RuntimeException e) { 310 } 311 } 312 mObservers.finishBroadcast(); 313 } 314 315 /** 316 * Notify our observers of an interface removal. 317 */ 318 private void notifyInterfaceRemoved(String iface) { 319 // netd already clears out quota and alerts for removed ifaces; update 320 // our sanity-checking state. 321 mActiveAlerts.remove(iface); 322 mActiveQuotas.remove(iface); 323 324 final int length = mObservers.beginBroadcast(); 325 for (int i = 0; i < length; i++) { 326 try { 327 mObservers.getBroadcastItem(i).interfaceRemoved(iface); 328 } catch (RemoteException e) { 329 } catch (RuntimeException e) { 330 } 331 } 332 mObservers.finishBroadcast(); 333 } 334 335 /** 336 * Notify our observers of a limit reached. 337 */ 338 private void notifyLimitReached(String limitName, String iface) { 339 final int length = mObservers.beginBroadcast(); 340 for (int i = 0; i < length; i++) { 341 try { 342 mObservers.getBroadcastItem(i).limitReached(limitName, iface); 343 } catch (RemoteException e) { 344 } catch (RuntimeException e) { 345 } 346 } 347 mObservers.finishBroadcast(); 348 } 349 350 /** 351 * Notify our observers of a change in the data activity state of the interface 352 */ 353 private void notifyInterfaceClassActivity(int type, boolean active, long tsNanos) { 354 try { 355 getBatteryStats().noteDataConnectionActive(type, active, tsNanos); 356 } catch (RemoteException e) { 357 } 358 359 final int length = mObservers.beginBroadcast(); 360 for (int i = 0; i < length; i++) { 361 try { 362 mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged( 363 Integer.toString(type), active, tsNanos); 364 } catch (RemoteException e) { 365 } catch (RuntimeException e) { 366 } 367 } 368 mObservers.finishBroadcast(); 369 370 boolean report = false; 371 synchronized (mIdleTimerLock) { 372 if (mActiveIdleTimers.isEmpty()) { 373 // If there are no idle times, we are not monitoring activity, so we 374 // are always considered active. 375 active = true; 376 } 377 if (mNetworkActive != active) { 378 mNetworkActive = active; 379 report = active; 380 } 381 } 382 if (report) { 383 reportNetworkActive(); 384 } 385 } 386 387 /** 388 * Prepare native daemon once connected, enabling modules and pushing any 389 * existing in-memory rules. 390 */ 391 private void prepareNativeDaemon() { 392 mBandwidthControlEnabled = false; 393 394 // only enable bandwidth control when support exists 395 final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists(); 396 if (hasKernelSupport) { 397 Slog.d(TAG, "enabling bandwidth control"); 398 try { 399 mConnector.execute("bandwidth", "enable"); 400 mBandwidthControlEnabled = true; 401 } catch (NativeDaemonConnectorException e) { 402 Log.wtf(TAG, "problem enabling bandwidth controls", e); 403 } 404 } else { 405 Slog.d(TAG, "not enabling bandwidth control"); 406 } 407 408 SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0"); 409 410 if (mBandwidthControlEnabled) { 411 try { 412 getBatteryStats().noteNetworkStatsEnabled(); 413 } catch (RemoteException e) { 414 } 415 } 416 417 // push any existing quota or UID rules 418 synchronized (mQuotaLock) { 419 int size = mActiveQuotas.size(); 420 if (size > 0) { 421 Slog.d(TAG, "pushing " + size + " active quota rules"); 422 final HashMap<String, Long> activeQuotas = mActiveQuotas; 423 mActiveQuotas = Maps.newHashMap(); 424 for (Map.Entry<String, Long> entry : activeQuotas.entrySet()) { 425 setInterfaceQuota(entry.getKey(), entry.getValue()); 426 } 427 } 428 429 size = mActiveAlerts.size(); 430 if (size > 0) { 431 Slog.d(TAG, "pushing " + size + " active alert rules"); 432 final HashMap<String, Long> activeAlerts = mActiveAlerts; 433 mActiveAlerts = Maps.newHashMap(); 434 for (Map.Entry<String, Long> entry : activeAlerts.entrySet()) { 435 setInterfaceAlert(entry.getKey(), entry.getValue()); 436 } 437 } 438 439 size = mUidRejectOnQuota.size(); 440 if (size > 0) { 441 Slog.d(TAG, "pushing " + size + " active uid rules"); 442 final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota; 443 mUidRejectOnQuota = new SparseBooleanArray(); 444 for (int i = 0; i < uidRejectOnQuota.size(); i++) { 445 setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i)); 446 } 447 } 448 } 449 450 // TODO: Push any existing firewall state 451 setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled()); 452 } 453 454 /** 455 * Notify our observers of a new or updated interface address. 456 */ 457 private void notifyAddressUpdated(String iface, LinkAddress address) { 458 final int length = mObservers.beginBroadcast(); 459 for (int i = 0; i < length; i++) { 460 try { 461 mObservers.getBroadcastItem(i).addressUpdated(iface, address); 462 } catch (RemoteException e) { 463 } catch (RuntimeException e) { 464 } 465 } 466 mObservers.finishBroadcast(); 467 } 468 469 /** 470 * Notify our observers of a deleted interface address. 471 */ 472 private void notifyAddressRemoved(String iface, LinkAddress address) { 473 final int length = mObservers.beginBroadcast(); 474 for (int i = 0; i < length; i++) { 475 try { 476 mObservers.getBroadcastItem(i).addressRemoved(iface, address); 477 } catch (RemoteException e) { 478 } catch (RuntimeException e) { 479 } 480 } 481 mObservers.finishBroadcast(); 482 } 483 484 /** 485 * Notify our observers of DNS server information received. 486 */ 487 private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { 488 final int length = mObservers.beginBroadcast(); 489 for (int i = 0; i < length; i++) { 490 try { 491 mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime, addresses); 492 } catch (RemoteException e) { 493 } catch (RuntimeException e) { 494 } 495 } 496 mObservers.finishBroadcast(); 497 } 498 499 // 500 // Netd Callback handling 501 // 502 503 private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks { 504 @Override 505 public void onDaemonConnected() { 506 // event is dispatched from internal NDC thread, so we prepare the 507 // daemon back on main thread. 508 if (mConnectedSignal != null) { 509 mConnectedSignal.countDown(); 510 mConnectedSignal = null; 511 } else { 512 mMainHandler.post(new Runnable() { 513 @Override 514 public void run() { 515 prepareNativeDaemon(); 516 } 517 }); 518 } 519 } 520 521 @Override 522 public boolean onCheckHoldWakeLock(int code) { 523 return code == NetdResponseCode.InterfaceClassActivity; 524 } 525 526 @Override 527 public boolean onEvent(int code, String raw, String[] cooked) { 528 String errorMessage = String.format("Invalid event from daemon (%s)", raw); 529 switch (code) { 530 case NetdResponseCode.InterfaceChange: 531 /* 532 * a network interface change occured 533 * Format: "NNN Iface added <name>" 534 * "NNN Iface removed <name>" 535 * "NNN Iface changed <name> <up/down>" 536 * "NNN Iface linkstatus <name> <up/down>" 537 */ 538 if (cooked.length < 4 || !cooked[1].equals("Iface")) { 539 throw new IllegalStateException(errorMessage); 540 } 541 if (cooked[2].equals("added")) { 542 notifyInterfaceAdded(cooked[3]); 543 return true; 544 } else if (cooked[2].equals("removed")) { 545 notifyInterfaceRemoved(cooked[3]); 546 return true; 547 } else if (cooked[2].equals("changed") && cooked.length == 5) { 548 notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up")); 549 return true; 550 } else if (cooked[2].equals("linkstate") && cooked.length == 5) { 551 notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up")); 552 return true; 553 } 554 throw new IllegalStateException(errorMessage); 555 // break; 556 case NetdResponseCode.BandwidthControl: 557 /* 558 * Bandwidth control needs some attention 559 * Format: "NNN limit alert <alertName> <ifaceName>" 560 */ 561 if (cooked.length < 5 || !cooked[1].equals("limit")) { 562 throw new IllegalStateException(errorMessage); 563 } 564 if (cooked[2].equals("alert")) { 565 notifyLimitReached(cooked[3], cooked[4]); 566 return true; 567 } 568 throw new IllegalStateException(errorMessage); 569 // break; 570 case NetdResponseCode.InterfaceClassActivity: 571 /* 572 * An network interface class state changed (active/idle) 573 * Format: "NNN IfaceClass <active/idle> <label>" 574 */ 575 if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) { 576 throw new IllegalStateException(errorMessage); 577 } 578 long timestampNanos = 0; 579 if (cooked.length == 5) { 580 try { 581 timestampNanos = Long.parseLong(cooked[4]); 582 } catch(NumberFormatException ne) {} 583 } 584 boolean isActive = cooked[2].equals("active"); 585 notifyInterfaceClassActivity(Integer.parseInt(cooked[3]), 586 isActive, timestampNanos); 587 return true; 588 // break; 589 case NetdResponseCode.InterfaceAddressChange: 590 /* 591 * A network address change occurred 592 * Format: "NNN Address updated <addr> <iface> <flags> <scope>" 593 * "NNN Address removed <addr> <iface> <flags> <scope>" 594 */ 595 if (cooked.length < 7 || !cooked[1].equals("Address")) { 596 throw new IllegalStateException(errorMessage); 597 } 598 599 String iface = cooked[4]; 600 LinkAddress address; 601 try { 602 int flags = Integer.parseInt(cooked[5]); 603 int scope = Integer.parseInt(cooked[6]); 604 address = new LinkAddress(cooked[3], flags, scope); 605 } catch(NumberFormatException e) { // Non-numeric lifetime or scope. 606 throw new IllegalStateException(errorMessage, e); 607 } catch(IllegalArgumentException e) { // Malformed/invalid IP address. 608 throw new IllegalStateException(errorMessage, e); 609 } 610 611 if (cooked[2].equals("updated")) { 612 notifyAddressUpdated(iface, address); 613 } else { 614 notifyAddressRemoved(iface, address); 615 } 616 return true; 617 // break; 618 case NetdResponseCode.InterfaceDnsServerInfo: 619 /* 620 * Information about available DNS servers has been received. 621 * Format: "NNN DnsInfo servers <interface> <lifetime> <servers>" 622 */ 623 long lifetime; // Actually a 32-bit unsigned integer. 624 625 if (cooked.length == 6 && 626 cooked[1].equals("DnsInfo") && 627 cooked[2].equals("servers")) { 628 try { 629 lifetime = Long.parseLong(cooked[4]); 630 } catch (NumberFormatException e) { 631 throw new IllegalStateException(errorMessage); 632 } 633 String[] servers = cooked[5].split(","); 634 notifyInterfaceDnsServerInfo(cooked[3], lifetime, servers); 635 } 636 return true; 637 // break; 638 default: break; 639 } 640 return false; 641 } 642 } 643 644 645 // 646 // INetworkManagementService members 647 // 648 649 @Override 650 public String[] listInterfaces() { 651 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 652 try { 653 return NativeDaemonEvent.filterMessageList( 654 mConnector.executeForList("interface", "list"), InterfaceListResult); 655 } catch (NativeDaemonConnectorException e) { 656 throw e.rethrowAsParcelableException(); 657 } 658 } 659 660 @Override 661 public InterfaceConfiguration getInterfaceConfig(String iface) { 662 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 663 664 final NativeDaemonEvent event; 665 try { 666 event = mConnector.execute("interface", "getcfg", iface); 667 } catch (NativeDaemonConnectorException e) { 668 throw e.rethrowAsParcelableException(); 669 } 670 671 event.checkCode(InterfaceGetCfgResult); 672 673 // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3 674 final StringTokenizer st = new StringTokenizer(event.getMessage()); 675 676 InterfaceConfiguration cfg; 677 try { 678 cfg = new InterfaceConfiguration(); 679 cfg.setHardwareAddress(st.nextToken(" ")); 680 InetAddress addr = null; 681 int prefixLength = 0; 682 try { 683 addr = NetworkUtils.numericToInetAddress(st.nextToken()); 684 } catch (IllegalArgumentException iae) { 685 Slog.e(TAG, "Failed to parse ipaddr", iae); 686 } 687 688 try { 689 prefixLength = Integer.parseInt(st.nextToken()); 690 } catch (NumberFormatException nfe) { 691 Slog.e(TAG, "Failed to parse prefixLength", nfe); 692 } 693 694 cfg.setLinkAddress(new LinkAddress(addr, prefixLength)); 695 while (st.hasMoreTokens()) { 696 cfg.setFlag(st.nextToken()); 697 } 698 } catch (NoSuchElementException nsee) { 699 throw new IllegalStateException("Invalid response from daemon: " + event); 700 } 701 return cfg; 702 } 703 704 @Override 705 public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) { 706 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 707 LinkAddress linkAddr = cfg.getLinkAddress(); 708 if (linkAddr == null || linkAddr.getAddress() == null) { 709 throw new IllegalStateException("Null LinkAddress given"); 710 } 711 712 final Command cmd = new Command("interface", "setcfg", iface, 713 linkAddr.getAddress().getHostAddress(), 714 linkAddr.getNetworkPrefixLength()); 715 for (String flag : cfg.getFlags()) { 716 cmd.appendArg(flag); 717 } 718 719 try { 720 mConnector.execute(cmd); 721 } catch (NativeDaemonConnectorException e) { 722 throw e.rethrowAsParcelableException(); 723 } 724 } 725 726 @Override 727 public void setInterfaceDown(String iface) { 728 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 729 final InterfaceConfiguration ifcg = getInterfaceConfig(iface); 730 ifcg.setInterfaceDown(); 731 setInterfaceConfig(iface, ifcg); 732 } 733 734 @Override 735 public void setInterfaceUp(String iface) { 736 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 737 final InterfaceConfiguration ifcg = getInterfaceConfig(iface); 738 ifcg.setInterfaceUp(); 739 setInterfaceConfig(iface, ifcg); 740 } 741 742 @Override 743 public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) { 744 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 745 try { 746 mConnector.execute( 747 "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable"); 748 } catch (NativeDaemonConnectorException e) { 749 throw e.rethrowAsParcelableException(); 750 } 751 } 752 753 /* TODO: This is right now a IPv4 only function. Works for wifi which loses its 754 IPv6 addresses on interface down, but we need to do full clean up here */ 755 @Override 756 public void clearInterfaceAddresses(String iface) { 757 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 758 try { 759 mConnector.execute("interface", "clearaddrs", iface); 760 } catch (NativeDaemonConnectorException e) { 761 throw e.rethrowAsParcelableException(); 762 } 763 } 764 765 @Override 766 public void enableIpv6(String iface) { 767 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 768 try { 769 mConnector.execute("interface", "ipv6", iface, "enable"); 770 } catch (NativeDaemonConnectorException e) { 771 throw e.rethrowAsParcelableException(); 772 } 773 } 774 775 @Override 776 public void disableIpv6(String iface) { 777 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 778 try { 779 mConnector.execute("interface", "ipv6", iface, "disable"); 780 } catch (NativeDaemonConnectorException e) { 781 throw e.rethrowAsParcelableException(); 782 } 783 } 784 785 @Override 786 public void addRoute(String interfaceName, RouteInfo route) { 787 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 788 modifyRoute(interfaceName, ADD, route, DEFAULT); 789 } 790 791 @Override 792 public void removeRoute(String interfaceName, RouteInfo route) { 793 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 794 modifyRoute(interfaceName, REMOVE, route, DEFAULT); 795 } 796 797 @Override 798 public void addSecondaryRoute(String interfaceName, RouteInfo route) { 799 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 800 modifyRoute(interfaceName, ADD, route, SECONDARY); 801 } 802 803 @Override 804 public void removeSecondaryRoute(String interfaceName, RouteInfo route) { 805 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 806 modifyRoute(interfaceName, REMOVE, route, SECONDARY); 807 } 808 809 private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) { 810 final Command cmd = new Command("interface", "route", action, interfaceName, type); 811 812 // create triplet: dest-ip-addr prefixlength gateway-ip-addr 813 final LinkAddress la = route.getDestination(); 814 cmd.appendArg(la.getAddress().getHostAddress()); 815 cmd.appendArg(la.getNetworkPrefixLength()); 816 817 if (route.getGateway() == null) { 818 if (la.getAddress() instanceof Inet4Address) { 819 cmd.appendArg("0.0.0.0"); 820 } else { 821 cmd.appendArg("::0"); 822 } 823 } else { 824 cmd.appendArg(route.getGateway().getHostAddress()); 825 } 826 827 try { 828 mConnector.execute(cmd); 829 } catch (NativeDaemonConnectorException e) { 830 throw e.rethrowAsParcelableException(); 831 } 832 } 833 834 private ArrayList<String> readRouteList(String filename) { 835 FileInputStream fstream = null; 836 ArrayList<String> list = new ArrayList<String>(); 837 838 try { 839 fstream = new FileInputStream(filename); 840 DataInputStream in = new DataInputStream(fstream); 841 BufferedReader br = new BufferedReader(new InputStreamReader(in)); 842 String s; 843 844 // throw away the title line 845 846 while (((s = br.readLine()) != null) && (s.length() != 0)) { 847 list.add(s); 848 } 849 } catch (IOException ex) { 850 // return current list, possibly empty 851 } finally { 852 if (fstream != null) { 853 try { 854 fstream.close(); 855 } catch (IOException ex) {} 856 } 857 } 858 859 return list; 860 } 861 862 @Override 863 public RouteInfo[] getRoutes(String interfaceName) { 864 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 865 ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>(); 866 867 // v4 routes listed as: 868 // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT 869 for (String s : readRouteList("/proc/net/route")) { 870 String[] fields = s.split("\t"); 871 872 if (fields.length > 7) { 873 String iface = fields[0]; 874 875 if (interfaceName.equals(iface)) { 876 String dest = fields[1]; 877 String gate = fields[2]; 878 String flags = fields[3]; // future use? 879 String mask = fields[7]; 880 try { 881 // address stored as a hex string, ex: 0014A8C0 882 InetAddress destAddr = 883 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16)); 884 int prefixLength = 885 NetworkUtils.netmaskIntToPrefixLength( 886 (int)Long.parseLong(mask, 16)); 887 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength); 888 889 // address stored as a hex string, ex 0014A8C0 890 InetAddress gatewayAddr = 891 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16)); 892 893 RouteInfo route = new RouteInfo(linkAddress, gatewayAddr); 894 routes.add(route); 895 } catch (Exception e) { 896 Log.e(TAG, "Error parsing route " + s + " : " + e); 897 continue; 898 } 899 } 900 } 901 } 902 903 // v6 routes listed as: 904 // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface 905 for (String s : readRouteList("/proc/net/ipv6_route")) { 906 String[]fields = s.split("\\s+"); 907 if (fields.length > 9) { 908 String iface = fields[9].trim(); 909 if (interfaceName.equals(iface)) { 910 String dest = fields[0]; 911 String prefix = fields[1]; 912 String gate = fields[4]; 913 914 try { 915 // prefix length stored as a hex string, ex 40 916 int prefixLength = Integer.parseInt(prefix, 16); 917 918 // address stored as a 32 char hex string 919 // ex fe800000000000000000000000000000 920 InetAddress destAddr = NetworkUtils.hexToInet6Address(dest); 921 LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength); 922 923 InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate); 924 925 RouteInfo route = new RouteInfo(linkAddress, gateAddr); 926 routes.add(route); 927 } catch (Exception e) { 928 Log.e(TAG, "Error parsing route " + s + " : " + e); 929 continue; 930 } 931 } 932 } 933 } 934 return routes.toArray(new RouteInfo[routes.size()]); 935 } 936 937 @Override 938 public void setMtu(String iface, int mtu) { 939 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 940 941 final NativeDaemonEvent event; 942 try { 943 event = mConnector.execute("interface", "setmtu", iface, mtu); 944 } catch (NativeDaemonConnectorException e) { 945 throw e.rethrowAsParcelableException(); 946 } 947 } 948 949 @Override 950 public void shutdown() { 951 // TODO: remove from aidl if nobody calls externally 952 mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG); 953 954 Slog.d(TAG, "Shutting down"); 955 } 956 957 @Override 958 public boolean getIpForwardingEnabled() throws IllegalStateException{ 959 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 960 961 final NativeDaemonEvent event; 962 try { 963 event = mConnector.execute("ipfwd", "status"); 964 } catch (NativeDaemonConnectorException e) { 965 throw e.rethrowAsParcelableException(); 966 } 967 968 // 211 Forwarding enabled 969 event.checkCode(IpFwdStatusResult); 970 return event.getMessage().endsWith("enabled"); 971 } 972 973 @Override 974 public void setIpForwardingEnabled(boolean enable) { 975 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 976 try { 977 mConnector.execute("ipfwd", enable ? "enable" : "disable"); 978 } catch (NativeDaemonConnectorException e) { 979 throw e.rethrowAsParcelableException(); 980 } 981 } 982 983 @Override 984 public void startTethering(String[] dhcpRange) { 985 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 986 // cmd is "tether start first_start first_stop second_start second_stop ..." 987 // an odd number of addrs will fail 988 989 final Command cmd = new Command("tether", "start"); 990 for (String d : dhcpRange) { 991 cmd.appendArg(d); 992 } 993 994 try { 995 mConnector.execute(cmd); 996 } catch (NativeDaemonConnectorException e) { 997 throw e.rethrowAsParcelableException(); 998 } 999 } 1000 1001 @Override 1002 public void stopTethering() { 1003 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1004 try { 1005 mConnector.execute("tether", "stop"); 1006 } catch (NativeDaemonConnectorException e) { 1007 throw e.rethrowAsParcelableException(); 1008 } 1009 } 1010 1011 @Override 1012 public boolean isTetheringStarted() { 1013 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1014 1015 final NativeDaemonEvent event; 1016 try { 1017 event = mConnector.execute("tether", "status"); 1018 } catch (NativeDaemonConnectorException e) { 1019 throw e.rethrowAsParcelableException(); 1020 } 1021 1022 // 210 Tethering services started 1023 event.checkCode(TetherStatusResult); 1024 return event.getMessage().endsWith("started"); 1025 } 1026 1027 @Override 1028 public void tetherInterface(String iface) { 1029 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1030 try { 1031 mConnector.execute("tether", "interface", "add", iface); 1032 } catch (NativeDaemonConnectorException e) { 1033 throw e.rethrowAsParcelableException(); 1034 } 1035 } 1036 1037 @Override 1038 public void untetherInterface(String iface) { 1039 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1040 try { 1041 mConnector.execute("tether", "interface", "remove", iface); 1042 } catch (NativeDaemonConnectorException e) { 1043 throw e.rethrowAsParcelableException(); 1044 } 1045 } 1046 1047 @Override 1048 public String[] listTetheredInterfaces() { 1049 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1050 try { 1051 return NativeDaemonEvent.filterMessageList( 1052 mConnector.executeForList("tether", "interface", "list"), 1053 TetherInterfaceListResult); 1054 } catch (NativeDaemonConnectorException e) { 1055 throw e.rethrowAsParcelableException(); 1056 } 1057 } 1058 1059 @Override 1060 public void setDnsForwarders(String[] dns) { 1061 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1062 1063 final Command cmd = new Command("tether", "dns", "set"); 1064 for (String s : dns) { 1065 cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress()); 1066 } 1067 1068 try { 1069 mConnector.execute(cmd); 1070 } catch (NativeDaemonConnectorException e) { 1071 throw e.rethrowAsParcelableException(); 1072 } 1073 } 1074 1075 @Override 1076 public String[] getDnsForwarders() { 1077 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1078 try { 1079 return NativeDaemonEvent.filterMessageList( 1080 mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult); 1081 } catch (NativeDaemonConnectorException e) { 1082 throw e.rethrowAsParcelableException(); 1083 } 1084 } 1085 1086 private List<InterfaceAddress> excludeLinkLocal(List<InterfaceAddress> addresses) { 1087 ArrayList<InterfaceAddress> filtered = new ArrayList<InterfaceAddress>(addresses.size()); 1088 for (InterfaceAddress ia : addresses) { 1089 if (!ia.getAddress().isLinkLocalAddress()) 1090 filtered.add(ia); 1091 } 1092 return filtered; 1093 } 1094 1095 private void modifyNat(String action, String internalInterface, String externalInterface) 1096 throws SocketException { 1097 final Command cmd = new Command("nat", action, internalInterface, externalInterface); 1098 1099 final NetworkInterface internalNetworkInterface = NetworkInterface.getByName( 1100 internalInterface); 1101 if (internalNetworkInterface == null) { 1102 cmd.appendArg("0"); 1103 } else { 1104 // Don't touch link-local routes, as link-local addresses aren't routable, 1105 // kernel creates link-local routes on all interfaces automatically 1106 List<InterfaceAddress> interfaceAddresses = excludeLinkLocal( 1107 internalNetworkInterface.getInterfaceAddresses()); 1108 cmd.appendArg(interfaceAddresses.size()); 1109 for (InterfaceAddress ia : interfaceAddresses) { 1110 InetAddress addr = NetworkUtils.getNetworkPart( 1111 ia.getAddress(), ia.getNetworkPrefixLength()); 1112 cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength()); 1113 } 1114 } 1115 1116 try { 1117 mConnector.execute(cmd); 1118 } catch (NativeDaemonConnectorException e) { 1119 throw e.rethrowAsParcelableException(); 1120 } 1121 } 1122 1123 @Override 1124 public void enableNat(String internalInterface, String externalInterface) { 1125 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1126 try { 1127 modifyNat("enable", internalInterface, externalInterface); 1128 } catch (SocketException e) { 1129 throw new IllegalStateException(e); 1130 } 1131 } 1132 1133 @Override 1134 public void disableNat(String internalInterface, String externalInterface) { 1135 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1136 try { 1137 modifyNat("disable", internalInterface, externalInterface); 1138 } catch (SocketException e) { 1139 throw new IllegalStateException(e); 1140 } 1141 } 1142 1143 @Override 1144 public String[] listTtys() { 1145 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1146 try { 1147 return NativeDaemonEvent.filterMessageList( 1148 mConnector.executeForList("list_ttys"), TtyListResult); 1149 } catch (NativeDaemonConnectorException e) { 1150 throw e.rethrowAsParcelableException(); 1151 } 1152 } 1153 1154 @Override 1155 public void attachPppd( 1156 String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) { 1157 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1158 try { 1159 mConnector.execute("pppd", "attach", tty, 1160 NetworkUtils.numericToInetAddress(localAddr).getHostAddress(), 1161 NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(), 1162 NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(), 1163 NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()); 1164 } catch (NativeDaemonConnectorException e) { 1165 throw e.rethrowAsParcelableException(); 1166 } 1167 } 1168 1169 @Override 1170 public void detachPppd(String tty) { 1171 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1172 try { 1173 mConnector.execute("pppd", "detach", tty); 1174 } catch (NativeDaemonConnectorException e) { 1175 throw e.rethrowAsParcelableException(); 1176 } 1177 } 1178 1179 @Override 1180 public void startAccessPoint( 1181 WifiConfiguration wifiConfig, String wlanIface) { 1182 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1183 try { 1184 wifiFirmwareReload(wlanIface, "AP"); 1185 if (wifiConfig == null) { 1186 mConnector.execute("softap", "set", wlanIface); 1187 } else { 1188 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, 1189 "broadcast", "6", getSecurityType(wifiConfig), 1190 new SensitiveArg(wifiConfig.preSharedKey)); 1191 } 1192 mConnector.execute("softap", "startap"); 1193 } catch (NativeDaemonConnectorException e) { 1194 throw e.rethrowAsParcelableException(); 1195 } 1196 } 1197 1198 private static String getSecurityType(WifiConfiguration wifiConfig) { 1199 switch (wifiConfig.getAuthType()) { 1200 case KeyMgmt.WPA_PSK: 1201 return "wpa-psk"; 1202 case KeyMgmt.WPA2_PSK: 1203 return "wpa2-psk"; 1204 default: 1205 return "open"; 1206 } 1207 } 1208 1209 /* @param mode can be "AP", "STA" or "P2P" */ 1210 @Override 1211 public void wifiFirmwareReload(String wlanIface, String mode) { 1212 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1213 try { 1214 mConnector.execute("softap", "fwreload", wlanIface, mode); 1215 } catch (NativeDaemonConnectorException e) { 1216 throw e.rethrowAsParcelableException(); 1217 } 1218 } 1219 1220 @Override 1221 public void stopAccessPoint(String wlanIface) { 1222 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1223 try { 1224 mConnector.execute("softap", "stopap"); 1225 wifiFirmwareReload(wlanIface, "STA"); 1226 } catch (NativeDaemonConnectorException e) { 1227 throw e.rethrowAsParcelableException(); 1228 } 1229 } 1230 1231 @Override 1232 public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) { 1233 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1234 try { 1235 if (wifiConfig == null) { 1236 mConnector.execute("softap", "set", wlanIface); 1237 } else { 1238 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID, 1239 "broadcast", "6", getSecurityType(wifiConfig), 1240 new SensitiveArg(wifiConfig.preSharedKey)); 1241 } 1242 } catch (NativeDaemonConnectorException e) { 1243 throw e.rethrowAsParcelableException(); 1244 } 1245 } 1246 1247 @Override 1248 public void addIdleTimer(String iface, int timeout, final int type) { 1249 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1250 1251 if (DBG) Slog.d(TAG, "Adding idletimer"); 1252 1253 synchronized (mIdleTimerLock) { 1254 IdleTimerParams params = mActiveIdleTimers.get(iface); 1255 if (params != null) { 1256 // the interface already has idletimer, update network count 1257 params.networkCount++; 1258 return; 1259 } 1260 1261 try { 1262 mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), 1263 Integer.toString(type)); 1264 } catch (NativeDaemonConnectorException e) { 1265 throw e.rethrowAsParcelableException(); 1266 } 1267 mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, type)); 1268 1269 // Networks start up. 1270 if (ConnectivityManager.isNetworkTypeMobile(type)) { 1271 mNetworkActive = false; 1272 } 1273 mMainHandler.post(new Runnable() { 1274 @Override public void run() { 1275 notifyInterfaceClassActivity(type, true, SystemClock.elapsedRealtimeNanos()); 1276 } 1277 }); 1278 } 1279 } 1280 1281 @Override 1282 public void removeIdleTimer(String iface) { 1283 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1284 1285 if (DBG) Slog.d(TAG, "Removing idletimer"); 1286 1287 synchronized (mIdleTimerLock) { 1288 final IdleTimerParams params = mActiveIdleTimers.get(iface); 1289 if (params == null || --(params.networkCount) > 0) { 1290 return; 1291 } 1292 1293 try { 1294 mConnector.execute("idletimer", "remove", iface, 1295 Integer.toString(params.timeout), Integer.toString(params.type)); 1296 } catch (NativeDaemonConnectorException e) { 1297 throw e.rethrowAsParcelableException(); 1298 } 1299 mActiveIdleTimers.remove(iface); 1300 mMainHandler.post(new Runnable() { 1301 @Override public void run() { 1302 notifyInterfaceClassActivity(params.type, false, 1303 SystemClock.elapsedRealtimeNanos()); 1304 } 1305 }); 1306 } 1307 } 1308 1309 @Override 1310 public NetworkStats getNetworkStatsSummaryDev() { 1311 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1312 try { 1313 return mStatsFactory.readNetworkStatsSummaryDev(); 1314 } catch (IOException e) { 1315 throw new IllegalStateException(e); 1316 } 1317 } 1318 1319 @Override 1320 public NetworkStats getNetworkStatsSummaryXt() { 1321 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1322 try { 1323 return mStatsFactory.readNetworkStatsSummaryXt(); 1324 } catch (IOException e) { 1325 throw new IllegalStateException(e); 1326 } 1327 } 1328 1329 @Override 1330 public NetworkStats getNetworkStatsDetail() { 1331 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1332 try { 1333 return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null); 1334 } catch (IOException e) { 1335 throw new IllegalStateException(e); 1336 } 1337 } 1338 1339 @Override 1340 public void setInterfaceQuota(String iface, long quotaBytes) { 1341 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1342 1343 // silently discard when control disabled 1344 // TODO: eventually migrate to be always enabled 1345 if (!mBandwidthControlEnabled) return; 1346 1347 synchronized (mQuotaLock) { 1348 if (mActiveQuotas.containsKey(iface)) { 1349 throw new IllegalStateException("iface " + iface + " already has quota"); 1350 } 1351 1352 try { 1353 // TODO: support quota shared across interfaces 1354 mConnector.execute("bandwidth", "setiquota", iface, quotaBytes); 1355 mActiveQuotas.put(iface, quotaBytes); 1356 } catch (NativeDaemonConnectorException e) { 1357 throw e.rethrowAsParcelableException(); 1358 } 1359 } 1360 } 1361 1362 @Override 1363 public void removeInterfaceQuota(String iface) { 1364 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1365 1366 // silently discard when control disabled 1367 // TODO: eventually migrate to be always enabled 1368 if (!mBandwidthControlEnabled) return; 1369 1370 synchronized (mQuotaLock) { 1371 if (!mActiveQuotas.containsKey(iface)) { 1372 // TODO: eventually consider throwing 1373 return; 1374 } 1375 1376 mActiveQuotas.remove(iface); 1377 mActiveAlerts.remove(iface); 1378 1379 try { 1380 // TODO: support quota shared across interfaces 1381 mConnector.execute("bandwidth", "removeiquota", iface); 1382 } catch (NativeDaemonConnectorException e) { 1383 throw e.rethrowAsParcelableException(); 1384 } 1385 } 1386 } 1387 1388 @Override 1389 public void setInterfaceAlert(String iface, long alertBytes) { 1390 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1391 1392 // silently discard when control disabled 1393 // TODO: eventually migrate to be always enabled 1394 if (!mBandwidthControlEnabled) return; 1395 1396 // quick sanity check 1397 if (!mActiveQuotas.containsKey(iface)) { 1398 throw new IllegalStateException("setting alert requires existing quota on iface"); 1399 } 1400 1401 synchronized (mQuotaLock) { 1402 if (mActiveAlerts.containsKey(iface)) { 1403 throw new IllegalStateException("iface " + iface + " already has alert"); 1404 } 1405 1406 try { 1407 // TODO: support alert shared across interfaces 1408 mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes); 1409 mActiveAlerts.put(iface, alertBytes); 1410 } catch (NativeDaemonConnectorException e) { 1411 throw e.rethrowAsParcelableException(); 1412 } 1413 } 1414 } 1415 1416 @Override 1417 public void removeInterfaceAlert(String iface) { 1418 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1419 1420 // silently discard when control disabled 1421 // TODO: eventually migrate to be always enabled 1422 if (!mBandwidthControlEnabled) return; 1423 1424 synchronized (mQuotaLock) { 1425 if (!mActiveAlerts.containsKey(iface)) { 1426 // TODO: eventually consider throwing 1427 return; 1428 } 1429 1430 try { 1431 // TODO: support alert shared across interfaces 1432 mConnector.execute("bandwidth", "removeinterfacealert", iface); 1433 mActiveAlerts.remove(iface); 1434 } catch (NativeDaemonConnectorException e) { 1435 throw e.rethrowAsParcelableException(); 1436 } 1437 } 1438 } 1439 1440 @Override 1441 public void setGlobalAlert(long alertBytes) { 1442 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1443 1444 // silently discard when control disabled 1445 // TODO: eventually migrate to be always enabled 1446 if (!mBandwidthControlEnabled) return; 1447 1448 try { 1449 mConnector.execute("bandwidth", "setglobalalert", alertBytes); 1450 } catch (NativeDaemonConnectorException e) { 1451 throw e.rethrowAsParcelableException(); 1452 } 1453 } 1454 1455 @Override 1456 public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) { 1457 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1458 1459 // silently discard when control disabled 1460 // TODO: eventually migrate to be always enabled 1461 if (!mBandwidthControlEnabled) return; 1462 1463 synchronized (mQuotaLock) { 1464 final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false); 1465 if (oldRejectOnQuota == rejectOnQuotaInterfaces) { 1466 // TODO: eventually consider throwing 1467 return; 1468 } 1469 1470 try { 1471 mConnector.execute("bandwidth", 1472 rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid); 1473 if (rejectOnQuotaInterfaces) { 1474 mUidRejectOnQuota.put(uid, true); 1475 } else { 1476 mUidRejectOnQuota.delete(uid); 1477 } 1478 } catch (NativeDaemonConnectorException e) { 1479 throw e.rethrowAsParcelableException(); 1480 } 1481 } 1482 } 1483 1484 @Override 1485 public boolean isBandwidthControlEnabled() { 1486 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1487 return mBandwidthControlEnabled; 1488 } 1489 1490 @Override 1491 public NetworkStats getNetworkStatsUidDetail(int uid) { 1492 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1493 try { 1494 return mStatsFactory.readNetworkStatsDetail(uid, null, TAG_ALL, null); 1495 } catch (IOException e) { 1496 throw new IllegalStateException(e); 1497 } 1498 } 1499 1500 @Override 1501 public NetworkStats getNetworkStatsTethering() { 1502 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1503 1504 final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1); 1505 try { 1506 final NativeDaemonEvent[] events = mConnector.executeForList( 1507 "bandwidth", "gettetherstats"); 1508 for (NativeDaemonEvent event : events) { 1509 if (event.getCode() != TetheringStatsListResult) continue; 1510 1511 // 114 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets 1512 final StringTokenizer tok = new StringTokenizer(event.getMessage()); 1513 try { 1514 final String ifaceIn = tok.nextToken(); 1515 final String ifaceOut = tok.nextToken(); 1516 1517 final NetworkStats.Entry entry = new NetworkStats.Entry(); 1518 entry.iface = ifaceOut; 1519 entry.uid = UID_TETHERING; 1520 entry.set = SET_DEFAULT; 1521 entry.tag = TAG_NONE; 1522 entry.rxBytes = Long.parseLong(tok.nextToken()); 1523 entry.rxPackets = Long.parseLong(tok.nextToken()); 1524 entry.txBytes = Long.parseLong(tok.nextToken()); 1525 entry.txPackets = Long.parseLong(tok.nextToken()); 1526 stats.combineValues(entry); 1527 } catch (NoSuchElementException e) { 1528 throw new IllegalStateException("problem parsing tethering stats: " + event); 1529 } catch (NumberFormatException e) { 1530 throw new IllegalStateException("problem parsing tethering stats: " + event); 1531 } 1532 } 1533 } catch (NativeDaemonConnectorException e) { 1534 throw e.rethrowAsParcelableException(); 1535 } 1536 return stats; 1537 } 1538 1539 @Override 1540 public void setDefaultInterfaceForDns(String iface) { 1541 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1542 try { 1543 mConnector.execute("resolver", "setdefaultif", iface); 1544 } catch (NativeDaemonConnectorException e) { 1545 throw e.rethrowAsParcelableException(); 1546 } 1547 } 1548 1549 @Override 1550 public void setDnsServersForInterface(String iface, String[] servers, String domains) { 1551 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1552 1553 final Command cmd = new Command("resolver", "setifdns", iface, 1554 (domains == null ? "" : domains)); 1555 1556 for (String s : servers) { 1557 InetAddress a = NetworkUtils.numericToInetAddress(s); 1558 if (a.isAnyLocalAddress() == false) { 1559 cmd.appendArg(a.getHostAddress()); 1560 } 1561 } 1562 1563 try { 1564 mConnector.execute(cmd); 1565 } catch (NativeDaemonConnectorException e) { 1566 throw e.rethrowAsParcelableException(); 1567 } 1568 } 1569 1570 @Override 1571 public void setUidRangeRoute(String iface, int uid_start, int uid_end) { 1572 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1573 try { 1574 mConnector.execute("interface", "fwmark", 1575 "uid", "add", iface, uid_start, uid_end); 1576 } catch (NativeDaemonConnectorException e) { 1577 throw e.rethrowAsParcelableException(); 1578 } 1579 } 1580 1581 @Override 1582 public void clearUidRangeRoute(String iface, int uid_start, int uid_end) { 1583 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1584 try { 1585 mConnector.execute("interface", "fwmark", 1586 "uid", "remove", iface, uid_start, uid_end); 1587 } catch (NativeDaemonConnectorException e) { 1588 throw e.rethrowAsParcelableException(); 1589 } 1590 } 1591 1592 @Override 1593 public void setMarkedForwarding(String iface) { 1594 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1595 try { 1596 mConnector.execute("interface", "fwmark", "rule", "add", iface); 1597 } catch (NativeDaemonConnectorException e) { 1598 throw e.rethrowAsParcelableException(); 1599 } 1600 } 1601 1602 @Override 1603 public void clearMarkedForwarding(String iface) { 1604 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1605 try { 1606 mConnector.execute("interface", "fwmark", "rule", "remove", iface); 1607 } catch (NativeDaemonConnectorException e) { 1608 throw e.rethrowAsParcelableException(); 1609 } 1610 } 1611 1612 @Override 1613 public int getMarkForUid(int uid) { 1614 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1615 final NativeDaemonEvent event; 1616 try { 1617 event = mConnector.execute("interface", "fwmark", "get", "mark", uid); 1618 } catch (NativeDaemonConnectorException e) { 1619 throw e.rethrowAsParcelableException(); 1620 } 1621 event.checkCode(GetMarkResult); 1622 return Integer.parseInt(event.getMessage()); 1623 } 1624 1625 @Override 1626 public int getMarkForProtect() { 1627 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1628 final NativeDaemonEvent event; 1629 try { 1630 event = mConnector.execute("interface", "fwmark", "get", "protect"); 1631 } catch (NativeDaemonConnectorException e) { 1632 throw e.rethrowAsParcelableException(); 1633 } 1634 event.checkCode(GetMarkResult); 1635 return Integer.parseInt(event.getMessage()); 1636 } 1637 1638 @Override 1639 public void setMarkedForwardingRoute(String iface, RouteInfo route) { 1640 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1641 try { 1642 LinkAddress dest = route.getDestination(); 1643 mConnector.execute("interface", "fwmark", "route", "add", iface, 1644 dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength()); 1645 } catch (NativeDaemonConnectorException e) { 1646 throw e.rethrowAsParcelableException(); 1647 } 1648 } 1649 1650 @Override 1651 public void clearMarkedForwardingRoute(String iface, RouteInfo route) { 1652 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1653 try { 1654 LinkAddress dest = route.getDestination(); 1655 mConnector.execute("interface", "fwmark", "route", "remove", iface, 1656 dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength()); 1657 } catch (NativeDaemonConnectorException e) { 1658 throw e.rethrowAsParcelableException(); 1659 } 1660 } 1661 1662 @Override 1663 public void setHostExemption(LinkAddress host) { 1664 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1665 try { 1666 mConnector.execute("interface", "fwmark", "exempt", "add", host); 1667 } catch (NativeDaemonConnectorException e) { 1668 throw e.rethrowAsParcelableException(); 1669 } 1670 } 1671 1672 @Override 1673 public void clearHostExemption(LinkAddress host) { 1674 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1675 try { 1676 mConnector.execute("interface", "fwmark", "exempt", "remove", host); 1677 } catch (NativeDaemonConnectorException e) { 1678 throw e.rethrowAsParcelableException(); 1679 } 1680 } 1681 1682 @Override 1683 public void setDnsInterfaceForUidRange(String iface, int uid_start, int uid_end) { 1684 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1685 try { 1686 mConnector.execute("resolver", "setifaceforuidrange", iface, uid_start, uid_end); 1687 } catch (NativeDaemonConnectorException e) { 1688 throw e.rethrowAsParcelableException(); 1689 } 1690 } 1691 1692 @Override 1693 public void clearDnsInterfaceForUidRange(int uid_start, int uid_end) { 1694 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1695 try { 1696 mConnector.execute("resolver", "clearifaceforuidrange", uid_start, uid_end); 1697 } catch (NativeDaemonConnectorException e) { 1698 throw e.rethrowAsParcelableException(); 1699 } 1700 } 1701 1702 @Override 1703 public void clearDnsInterfaceMaps() { 1704 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1705 try { 1706 mConnector.execute("resolver", "clearifacemapping"); 1707 } catch (NativeDaemonConnectorException e) { 1708 throw e.rethrowAsParcelableException(); 1709 } 1710 } 1711 1712 1713 @Override 1714 public void flushDefaultDnsCache() { 1715 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1716 try { 1717 mConnector.execute("resolver", "flushdefaultif"); 1718 } catch (NativeDaemonConnectorException e) { 1719 throw e.rethrowAsParcelableException(); 1720 } 1721 } 1722 1723 @Override 1724 public void flushInterfaceDnsCache(String iface) { 1725 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1726 try { 1727 mConnector.execute("resolver", "flushif", iface); 1728 } catch (NativeDaemonConnectorException e) { 1729 throw e.rethrowAsParcelableException(); 1730 } 1731 } 1732 1733 @Override 1734 public void setFirewallEnabled(boolean enabled) { 1735 enforceSystemUid(); 1736 try { 1737 mConnector.execute("firewall", enabled ? "enable" : "disable"); 1738 mFirewallEnabled = enabled; 1739 } catch (NativeDaemonConnectorException e) { 1740 throw e.rethrowAsParcelableException(); 1741 } 1742 } 1743 1744 @Override 1745 public boolean isFirewallEnabled() { 1746 enforceSystemUid(); 1747 return mFirewallEnabled; 1748 } 1749 1750 @Override 1751 public void setFirewallInterfaceRule(String iface, boolean allow) { 1752 enforceSystemUid(); 1753 Preconditions.checkState(mFirewallEnabled); 1754 final String rule = allow ? ALLOW : DENY; 1755 try { 1756 mConnector.execute("firewall", "set_interface_rule", iface, rule); 1757 } catch (NativeDaemonConnectorException e) { 1758 throw e.rethrowAsParcelableException(); 1759 } 1760 } 1761 1762 @Override 1763 public void setFirewallEgressSourceRule(String addr, boolean allow) { 1764 enforceSystemUid(); 1765 Preconditions.checkState(mFirewallEnabled); 1766 final String rule = allow ? ALLOW : DENY; 1767 try { 1768 mConnector.execute("firewall", "set_egress_source_rule", addr, rule); 1769 } catch (NativeDaemonConnectorException e) { 1770 throw e.rethrowAsParcelableException(); 1771 } 1772 } 1773 1774 @Override 1775 public void setFirewallEgressDestRule(String addr, int port, boolean allow) { 1776 enforceSystemUid(); 1777 Preconditions.checkState(mFirewallEnabled); 1778 final String rule = allow ? ALLOW : DENY; 1779 try { 1780 mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule); 1781 } catch (NativeDaemonConnectorException e) { 1782 throw e.rethrowAsParcelableException(); 1783 } 1784 } 1785 1786 @Override 1787 public void setFirewallUidRule(int uid, boolean allow) { 1788 enforceSystemUid(); 1789 Preconditions.checkState(mFirewallEnabled); 1790 final String rule = allow ? ALLOW : DENY; 1791 try { 1792 mConnector.execute("firewall", "set_uid_rule", uid, rule); 1793 } catch (NativeDaemonConnectorException e) { 1794 throw e.rethrowAsParcelableException(); 1795 } 1796 } 1797 1798 private static void enforceSystemUid() { 1799 final int uid = Binder.getCallingUid(); 1800 if (uid != Process.SYSTEM_UID) { 1801 throw new SecurityException("Only available to AID_SYSTEM"); 1802 } 1803 } 1804 1805 @Override 1806 public void setDnsInterfaceForPid(String iface, int pid) throws IllegalStateException { 1807 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1808 try { 1809 mConnector.execute("resolver", "setifaceforpid", iface, pid); 1810 } catch (NativeDaemonConnectorException e) { 1811 throw new IllegalStateException( 1812 "Error communicating with native deamon to set interface for pid" + iface, e); 1813 } 1814 } 1815 1816 @Override 1817 public void clearDnsInterfaceForPid(int pid) throws IllegalStateException { 1818 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1819 try { 1820 mConnector.execute("resolver", "clearifaceforpid", pid); 1821 } catch (NativeDaemonConnectorException e) { 1822 throw new IllegalStateException( 1823 "Error communicating with native deamon to clear interface for pid " + pid, e); 1824 } 1825 } 1826 1827 @Override 1828 public void startClatd(String interfaceName) throws IllegalStateException { 1829 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1830 1831 try { 1832 mConnector.execute("clatd", "start", interfaceName); 1833 } catch (NativeDaemonConnectorException e) { 1834 throw e.rethrowAsParcelableException(); 1835 } 1836 } 1837 1838 @Override 1839 public void stopClatd() throws IllegalStateException { 1840 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1841 1842 try { 1843 mConnector.execute("clatd", "stop"); 1844 } catch (NativeDaemonConnectorException e) { 1845 throw e.rethrowAsParcelableException(); 1846 } 1847 } 1848 1849 @Override 1850 public boolean isClatdStarted() { 1851 mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 1852 1853 final NativeDaemonEvent event; 1854 try { 1855 event = mConnector.execute("clatd", "status"); 1856 } catch (NativeDaemonConnectorException e) { 1857 throw e.rethrowAsParcelableException(); 1858 } 1859 1860 event.checkCode(ClatdStatusResult); 1861 return event.getMessage().endsWith("started"); 1862 } 1863 1864 @Override 1865 public void registerNetworkActivityListener(INetworkActivityListener listener) { 1866 mNetworkActivityListeners.register(listener); 1867 } 1868 1869 @Override 1870 public void unregisterNetworkActivityListener(INetworkActivityListener listener) { 1871 mNetworkActivityListeners.unregister(listener); 1872 } 1873 1874 @Override 1875 public boolean isNetworkActive() { 1876 synchronized (mNetworkActivityListeners) { 1877 return mNetworkActive || mActiveIdleTimers.isEmpty(); 1878 } 1879 } 1880 1881 private void reportNetworkActive() { 1882 final int length = mNetworkActivityListeners.beginBroadcast(); 1883 for (int i = 0; i < length; i++) { 1884 try { 1885 mNetworkActivityListeners.getBroadcastItem(i).onNetworkActive(); 1886 } catch (RemoteException e) { 1887 } catch (RuntimeException e) { 1888 } 1889 } 1890 mNetworkActivityListeners.finishBroadcast(); 1891 } 1892 1893 /** {@inheritDoc} */ 1894 @Override 1895 public void monitor() { 1896 if (mConnector != null) { 1897 mConnector.monitor(); 1898 } 1899 } 1900 1901 @Override 1902 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1903 mContext.enforceCallingOrSelfPermission(DUMP, TAG); 1904 1905 pw.println("NetworkManagementService NativeDaemonConnector Log:"); 1906 mConnector.dump(fd, pw, args); 1907 pw.println(); 1908 1909 pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled); 1910 1911 synchronized (mQuotaLock) { 1912 pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString()); 1913 pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString()); 1914 } 1915 1916 synchronized (mUidRejectOnQuota) { 1917 pw.print("UID reject on quota ifaces: ["); 1918 final int size = mUidRejectOnQuota.size(); 1919 for (int i = 0; i < size; i++) { 1920 pw.print(mUidRejectOnQuota.keyAt(i)); 1921 if (i < size - 1) pw.print(","); 1922 } 1923 pw.println("]"); 1924 } 1925 1926 synchronized (mIdleTimerLock) { 1927 pw.println("Idle timers:"); 1928 for (HashMap.Entry<String, IdleTimerParams> ent : mActiveIdleTimers.entrySet()) { 1929 pw.print(" "); pw.print(ent.getKey()); pw.println(":"); 1930 IdleTimerParams params = ent.getValue(); 1931 pw.print(" timeout="); pw.print(params.timeout); 1932 pw.print(" type="); pw.print(params.type); 1933 pw.print(" networkCount="); pw.println(params.networkCount); 1934 } 1935 } 1936 1937 pw.print("Firewall enabled: "); pw.println(mFirewallEnabled); 1938 } 1939} 1940