Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server;
     18 
     19 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
     20 import static android.Manifest.permission.DUMP;
     21 import static android.Manifest.permission.SHUTDOWN;
     22 import static android.net.NetworkStats.SET_DEFAULT;
     23 import static android.net.NetworkStats.TAG_NONE;
     24 import static android.net.NetworkStats.UID_ALL;
     25 import static android.net.TrafficStats.UID_TETHERING;
     26 import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
     27 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
     28 import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
     29 import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
     30 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
     31 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
     32 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
     33 import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsResult;
     34 import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
     35 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
     36 
     37 import android.content.Context;
     38 import android.net.INetworkManagementEventObserver;
     39 import android.net.InterfaceConfiguration;
     40 import android.net.LinkAddress;
     41 import android.net.NetworkStats;
     42 import android.net.NetworkUtils;
     43 import android.net.RouteInfo;
     44 import android.net.wifi.WifiConfiguration;
     45 import android.net.wifi.WifiConfiguration.KeyMgmt;
     46 import android.os.Binder;
     47 import android.os.Handler;
     48 import android.os.INetworkManagementService;
     49 import android.os.Process;
     50 import android.os.RemoteCallbackList;
     51 import android.os.RemoteException;
     52 import android.os.SystemClock;
     53 import android.os.SystemProperties;
     54 import android.util.Log;
     55 import android.util.Slog;
     56 import android.util.SparseBooleanArray;
     57 
     58 import com.android.internal.net.NetworkStatsFactory;
     59 import com.android.internal.util.Preconditions;
     60 import com.android.server.NativeDaemonConnector.Command;
     61 import com.android.server.NativeDaemonConnector.SensitiveArg;
     62 import com.android.server.net.LockdownVpnTracker;
     63 import com.google.android.collect.Maps;
     64 
     65 import java.io.BufferedReader;
     66 import java.io.DataInputStream;
     67 import java.io.File;
     68 import java.io.FileDescriptor;
     69 import java.io.FileInputStream;
     70 import java.io.IOException;
     71 import java.io.InputStreamReader;
     72 import java.io.PrintWriter;
     73 import java.net.Inet4Address;
     74 import java.net.InetAddress;
     75 import java.net.InterfaceAddress;
     76 import java.net.NetworkInterface;
     77 import java.net.SocketException;
     78 import java.util.ArrayList;
     79 import java.util.Collection;
     80 import java.util.HashMap;
     81 import java.util.Map;
     82 import java.util.NoSuchElementException;
     83 import java.util.StringTokenizer;
     84 import java.util.concurrent.CountDownLatch;
     85 
     86 /**
     87  * @hide
     88  */
     89 public class NetworkManagementService extends INetworkManagementService.Stub
     90         implements Watchdog.Monitor {
     91     private static final String TAG = "NetworkManagementService";
     92     private static final boolean DBG = false;
     93     private static final String NETD_TAG = "NetdConnector";
     94 
     95     private static final String ADD = "add";
     96     private static final String REMOVE = "remove";
     97 
     98     private static final String ALLOW = "allow";
     99     private static final String DENY = "deny";
    100 
    101     private static final String DEFAULT = "default";
    102     private static final String SECONDARY = "secondary";
    103 
    104     /**
    105      * Name representing {@link #setGlobalAlert(long)} limit when delivered to
    106      * {@link INetworkManagementEventObserver#limitReached(String, String)}.
    107      */
    108     public static final String LIMIT_GLOBAL_ALERT = "globalAlert";
    109 
    110     class NetdResponseCode {
    111         /* Keep in sync with system/netd/ResponseCode.h */
    112         public static final int InterfaceListResult       = 110;
    113         public static final int TetherInterfaceListResult = 111;
    114         public static final int TetherDnsFwdTgtListResult = 112;
    115         public static final int TtyListResult             = 113;
    116 
    117         public static final int TetherStatusResult        = 210;
    118         public static final int IpFwdStatusResult         = 211;
    119         public static final int InterfaceGetCfgResult     = 213;
    120         public static final int SoftapStatusResult        = 214;
    121         public static final int InterfaceRxCounterResult  = 216;
    122         public static final int InterfaceTxCounterResult  = 217;
    123         public static final int QuotaCounterResult        = 220;
    124         public static final int TetheringStatsResult      = 221;
    125         public static final int DnsProxyQueryResult       = 222;
    126         public static final int ClatdStatusResult         = 223;
    127 
    128         public static final int InterfaceChange           = 600;
    129         public static final int BandwidthControl          = 601;
    130         public static final int InterfaceClassActivity    = 613;
    131     }
    132 
    133     /**
    134      * Binder context for this service
    135      */
    136     private Context mContext;
    137 
    138     /**
    139      * connector object for communicating with netd
    140      */
    141     private NativeDaemonConnector mConnector;
    142 
    143     private final Handler mMainHandler = new Handler();
    144 
    145     private Thread mThread;
    146     private CountDownLatch mConnectedSignal = new CountDownLatch(1);
    147 
    148     private final RemoteCallbackList<INetworkManagementEventObserver> mObservers =
    149             new RemoteCallbackList<INetworkManagementEventObserver>();
    150 
    151     private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
    152 
    153     private Object mQuotaLock = new Object();
    154     /** Set of interfaces with active quotas. */
    155     private HashMap<String, Long> mActiveQuotas = Maps.newHashMap();
    156     /** Set of interfaces with active alerts. */
    157     private HashMap<String, Long> mActiveAlerts = Maps.newHashMap();
    158     /** Set of UIDs with active reject rules. */
    159     private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
    160 
    161     private Object mIdleTimerLock = new Object();
    162     /** Set of interfaces with active idle timers. */
    163     private static class IdleTimerParams {
    164         public final int timeout;
    165         public final String label;
    166         public int networkCount;
    167 
    168         IdleTimerParams(int timeout, String label) {
    169             this.timeout = timeout;
    170             this.label = label;
    171             this.networkCount = 1;
    172         }
    173     }
    174     private HashMap<String, IdleTimerParams> mActiveIdleTimers = Maps.newHashMap();
    175 
    176     private volatile boolean mBandwidthControlEnabled;
    177     private volatile boolean mFirewallEnabled;
    178 
    179     /**
    180      * Constructs a new NetworkManagementService instance
    181      *
    182      * @param context  Binder context for this service
    183      */
    184     private NetworkManagementService(Context context) {
    185         mContext = context;
    186 
    187         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
    188             return;
    189         }
    190 
    191         mConnector = new NativeDaemonConnector(
    192                 new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
    193         mThread = new Thread(mConnector, NETD_TAG);
    194 
    195         // Add ourself to the Watchdog monitors.
    196         Watchdog.getInstance().addMonitor(this);
    197     }
    198 
    199     public static NetworkManagementService create(Context context) throws InterruptedException {
    200         final NetworkManagementService service = new NetworkManagementService(context);
    201         final CountDownLatch connectedSignal = service.mConnectedSignal;
    202         if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
    203         service.mThread.start();
    204         if (DBG) Slog.d(TAG, "Awaiting socket connection");
    205         connectedSignal.await();
    206         if (DBG) Slog.d(TAG, "Connected");
    207         return service;
    208     }
    209 
    210     public void systemReady() {
    211         prepareNativeDaemon();
    212         if (DBG) Slog.d(TAG, "Prepared");
    213     }
    214 
    215     @Override
    216     public void registerObserver(INetworkManagementEventObserver observer) {
    217         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    218         mObservers.register(observer);
    219     }
    220 
    221     @Override
    222     public void unregisterObserver(INetworkManagementEventObserver observer) {
    223         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    224         mObservers.unregister(observer);
    225     }
    226 
    227     /**
    228      * Notify our observers of an interface status change
    229      */
    230     private void notifyInterfaceStatusChanged(String iface, boolean up) {
    231         final int length = mObservers.beginBroadcast();
    232         for (int i = 0; i < length; i++) {
    233             try {
    234                 mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
    235             } catch (RemoteException e) {
    236             } catch (RuntimeException e) {
    237             }
    238         }
    239         mObservers.finishBroadcast();
    240     }
    241 
    242     /**
    243      * Notify our observers of an interface link state change
    244      * (typically, an Ethernet cable has been plugged-in or unplugged).
    245      */
    246     private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
    247         final int length = mObservers.beginBroadcast();
    248         for (int i = 0; i < length; i++) {
    249             try {
    250                 mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
    251             } catch (RemoteException e) {
    252             } catch (RuntimeException e) {
    253             }
    254         }
    255         mObservers.finishBroadcast();
    256     }
    257 
    258     /**
    259      * Notify our observers of an interface addition.
    260      */
    261     private void notifyInterfaceAdded(String iface) {
    262         final int length = mObservers.beginBroadcast();
    263         for (int i = 0; i < length; i++) {
    264             try {
    265                 mObservers.getBroadcastItem(i).interfaceAdded(iface);
    266             } catch (RemoteException e) {
    267             } catch (RuntimeException e) {
    268             }
    269         }
    270         mObservers.finishBroadcast();
    271     }
    272 
    273     /**
    274      * Notify our observers of an interface removal.
    275      */
    276     private void notifyInterfaceRemoved(String iface) {
    277         // netd already clears out quota and alerts for removed ifaces; update
    278         // our sanity-checking state.
    279         mActiveAlerts.remove(iface);
    280         mActiveQuotas.remove(iface);
    281 
    282         final int length = mObservers.beginBroadcast();
    283         for (int i = 0; i < length; i++) {
    284             try {
    285                 mObservers.getBroadcastItem(i).interfaceRemoved(iface);
    286             } catch (RemoteException e) {
    287             } catch (RuntimeException e) {
    288             }
    289         }
    290         mObservers.finishBroadcast();
    291     }
    292 
    293     /**
    294      * Notify our observers of a limit reached.
    295      */
    296     private void notifyLimitReached(String limitName, String iface) {
    297         final int length = mObservers.beginBroadcast();
    298         for (int i = 0; i < length; i++) {
    299             try {
    300                 mObservers.getBroadcastItem(i).limitReached(limitName, iface);
    301             } catch (RemoteException e) {
    302             } catch (RuntimeException e) {
    303             }
    304         }
    305         mObservers.finishBroadcast();
    306     }
    307 
    308     /**
    309      * Notify our observers of a change in the data activity state of the interface
    310      */
    311     private void notifyInterfaceClassActivity(String label, boolean active) {
    312         final int length = mObservers.beginBroadcast();
    313         for (int i = 0; i < length; i++) {
    314             try {
    315                 mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
    316             } catch (RemoteException e) {
    317             } catch (RuntimeException e) {
    318             }
    319         }
    320         mObservers.finishBroadcast();
    321     }
    322 
    323     /**
    324      * Prepare native daemon once connected, enabling modules and pushing any
    325      * existing in-memory rules.
    326      */
    327     private void prepareNativeDaemon() {
    328         mBandwidthControlEnabled = false;
    329 
    330         // only enable bandwidth control when support exists
    331         final boolean hasKernelSupport = new File("/proc/net/xt_qtaguid/ctrl").exists();
    332         if (hasKernelSupport) {
    333             Slog.d(TAG, "enabling bandwidth control");
    334             try {
    335                 mConnector.execute("bandwidth", "enable");
    336                 mBandwidthControlEnabled = true;
    337             } catch (NativeDaemonConnectorException e) {
    338                 Log.wtf(TAG, "problem enabling bandwidth controls", e);
    339             }
    340         } else {
    341             Slog.d(TAG, "not enabling bandwidth control");
    342         }
    343 
    344         SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0");
    345 
    346         // push any existing quota or UID rules
    347         synchronized (mQuotaLock) {
    348             int size = mActiveQuotas.size();
    349             if (size > 0) {
    350                 Slog.d(TAG, "pushing " + size + " active quota rules");
    351                 final HashMap<String, Long> activeQuotas = mActiveQuotas;
    352                 mActiveQuotas = Maps.newHashMap();
    353                 for (Map.Entry<String, Long> entry : activeQuotas.entrySet()) {
    354                     setInterfaceQuota(entry.getKey(), entry.getValue());
    355                 }
    356             }
    357 
    358             size = mActiveAlerts.size();
    359             if (size > 0) {
    360                 Slog.d(TAG, "pushing " + size + " active alert rules");
    361                 final HashMap<String, Long> activeAlerts = mActiveAlerts;
    362                 mActiveAlerts = Maps.newHashMap();
    363                 for (Map.Entry<String, Long> entry : activeAlerts.entrySet()) {
    364                     setInterfaceAlert(entry.getKey(), entry.getValue());
    365                 }
    366             }
    367 
    368             size = mUidRejectOnQuota.size();
    369             if (size > 0) {
    370                 Slog.d(TAG, "pushing " + size + " active uid rules");
    371                 final SparseBooleanArray uidRejectOnQuota = mUidRejectOnQuota;
    372                 mUidRejectOnQuota = new SparseBooleanArray();
    373                 for (int i = 0; i < uidRejectOnQuota.size(); i++) {
    374                     setUidNetworkRules(uidRejectOnQuota.keyAt(i), uidRejectOnQuota.valueAt(i));
    375                 }
    376             }
    377         }
    378 
    379         // TODO: Push any existing firewall state
    380         setFirewallEnabled(mFirewallEnabled || LockdownVpnTracker.isEnabled());
    381     }
    382 
    383     //
    384     // Netd Callback handling
    385     //
    386 
    387     private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
    388         @Override
    389         public void onDaemonConnected() {
    390             // event is dispatched from internal NDC thread, so we prepare the
    391             // daemon back on main thread.
    392             if (mConnectedSignal != null) {
    393                 mConnectedSignal.countDown();
    394                 mConnectedSignal = null;
    395             } else {
    396                 mMainHandler.post(new Runnable() {
    397                     @Override
    398                     public void run() {
    399                         prepareNativeDaemon();
    400                     }
    401                 });
    402             }
    403         }
    404 
    405         @Override
    406         public boolean onEvent(int code, String raw, String[] cooked) {
    407             switch (code) {
    408             case NetdResponseCode.InterfaceChange:
    409                     /*
    410                      * a network interface change occured
    411                      * Format: "NNN Iface added <name>"
    412                      *         "NNN Iface removed <name>"
    413                      *         "NNN Iface changed <name> <up/down>"
    414                      *         "NNN Iface linkstatus <name> <up/down>"
    415                      */
    416                     if (cooked.length < 4 || !cooked[1].equals("Iface")) {
    417                         throw new IllegalStateException(
    418                                 String.format("Invalid event from daemon (%s)", raw));
    419                     }
    420                     if (cooked[2].equals("added")) {
    421                         notifyInterfaceAdded(cooked[3]);
    422                         return true;
    423                     } else if (cooked[2].equals("removed")) {
    424                         notifyInterfaceRemoved(cooked[3]);
    425                         return true;
    426                     } else if (cooked[2].equals("changed") && cooked.length == 5) {
    427                         notifyInterfaceStatusChanged(cooked[3], cooked[4].equals("up"));
    428                         return true;
    429                     } else if (cooked[2].equals("linkstate") && cooked.length == 5) {
    430                         notifyInterfaceLinkStateChanged(cooked[3], cooked[4].equals("up"));
    431                         return true;
    432                     }
    433                     throw new IllegalStateException(
    434                             String.format("Invalid event from daemon (%s)", raw));
    435                     // break;
    436             case NetdResponseCode.BandwidthControl:
    437                     /*
    438                      * Bandwidth control needs some attention
    439                      * Format: "NNN limit alert <alertName> <ifaceName>"
    440                      */
    441                     if (cooked.length < 5 || !cooked[1].equals("limit")) {
    442                         throw new IllegalStateException(
    443                                 String.format("Invalid event from daemon (%s)", raw));
    444                     }
    445                     if (cooked[2].equals("alert")) {
    446                         notifyLimitReached(cooked[3], cooked[4]);
    447                         return true;
    448                     }
    449                     throw new IllegalStateException(
    450                             String.format("Invalid event from daemon (%s)", raw));
    451                     // break;
    452             case NetdResponseCode.InterfaceClassActivity:
    453                     /*
    454                      * An network interface class state changed (active/idle)
    455                      * Format: "NNN IfaceClass <active/idle> <label>"
    456                      */
    457                     if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
    458                         throw new IllegalStateException(
    459                                 String.format("Invalid event from daemon (%s)", raw));
    460                     }
    461                     boolean isActive = cooked[2].equals("active");
    462                     notifyInterfaceClassActivity(cooked[3], isActive);
    463                     return true;
    464                     // break;
    465             default: break;
    466             }
    467             return false;
    468         }
    469     }
    470 
    471 
    472     //
    473     // INetworkManagementService members
    474     //
    475 
    476     @Override
    477     public String[] listInterfaces() {
    478         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    479         try {
    480             return NativeDaemonEvent.filterMessageList(
    481                     mConnector.executeForList("interface", "list"), InterfaceListResult);
    482         } catch (NativeDaemonConnectorException e) {
    483             throw e.rethrowAsParcelableException();
    484         }
    485     }
    486 
    487     @Override
    488     public InterfaceConfiguration getInterfaceConfig(String iface) {
    489         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    490 
    491         final NativeDaemonEvent event;
    492         try {
    493             event = mConnector.execute("interface", "getcfg", iface);
    494         } catch (NativeDaemonConnectorException e) {
    495             throw e.rethrowAsParcelableException();
    496         }
    497 
    498         event.checkCode(InterfaceGetCfgResult);
    499 
    500         // Rsp: 213 xx:xx:xx:xx:xx:xx yyy.yyy.yyy.yyy zzz flag1 flag2 flag3
    501         final StringTokenizer st = new StringTokenizer(event.getMessage());
    502 
    503         InterfaceConfiguration cfg;
    504         try {
    505             cfg = new InterfaceConfiguration();
    506             cfg.setHardwareAddress(st.nextToken(" "));
    507             InetAddress addr = null;
    508             int prefixLength = 0;
    509             try {
    510                 addr = NetworkUtils.numericToInetAddress(st.nextToken());
    511             } catch (IllegalArgumentException iae) {
    512                 Slog.e(TAG, "Failed to parse ipaddr", iae);
    513             }
    514 
    515             try {
    516                 prefixLength = Integer.parseInt(st.nextToken());
    517             } catch (NumberFormatException nfe) {
    518                 Slog.e(TAG, "Failed to parse prefixLength", nfe);
    519             }
    520 
    521             cfg.setLinkAddress(new LinkAddress(addr, prefixLength));
    522             while (st.hasMoreTokens()) {
    523                 cfg.setFlag(st.nextToken());
    524             }
    525         } catch (NoSuchElementException nsee) {
    526             throw new IllegalStateException("Invalid response from daemon: " + event);
    527         }
    528         return cfg;
    529     }
    530 
    531     @Override
    532     public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
    533         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    534         LinkAddress linkAddr = cfg.getLinkAddress();
    535         if (linkAddr == null || linkAddr.getAddress() == null) {
    536             throw new IllegalStateException("Null LinkAddress given");
    537         }
    538 
    539         final Command cmd = new Command("interface", "setcfg", iface,
    540                 linkAddr.getAddress().getHostAddress(),
    541                 linkAddr.getNetworkPrefixLength());
    542         for (String flag : cfg.getFlags()) {
    543             cmd.appendArg(flag);
    544         }
    545 
    546         try {
    547             mConnector.execute(cmd);
    548         } catch (NativeDaemonConnectorException e) {
    549             throw e.rethrowAsParcelableException();
    550         }
    551     }
    552 
    553     @Override
    554     public void setInterfaceDown(String iface) {
    555         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    556         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
    557         ifcg.setInterfaceDown();
    558         setInterfaceConfig(iface, ifcg);
    559     }
    560 
    561     @Override
    562     public void setInterfaceUp(String iface) {
    563         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    564         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
    565         ifcg.setInterfaceUp();
    566         setInterfaceConfig(iface, ifcg);
    567     }
    568 
    569     @Override
    570     public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
    571         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    572         try {
    573             mConnector.execute(
    574                     "interface", "ipv6privacyextensions", iface, enable ? "enable" : "disable");
    575         } catch (NativeDaemonConnectorException e) {
    576             throw e.rethrowAsParcelableException();
    577         }
    578     }
    579 
    580     /* TODO: This is right now a IPv4 only function. Works for wifi which loses its
    581        IPv6 addresses on interface down, but we need to do full clean up here */
    582     @Override
    583     public void clearInterfaceAddresses(String iface) {
    584         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    585         try {
    586             mConnector.execute("interface", "clearaddrs", iface);
    587         } catch (NativeDaemonConnectorException e) {
    588             throw e.rethrowAsParcelableException();
    589         }
    590     }
    591 
    592     @Override
    593     public void enableIpv6(String iface) {
    594         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    595         try {
    596             mConnector.execute("interface", "ipv6", iface, "enable");
    597         } catch (NativeDaemonConnectorException e) {
    598             throw e.rethrowAsParcelableException();
    599         }
    600     }
    601 
    602     @Override
    603     public void disableIpv6(String iface) {
    604         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    605         try {
    606             mConnector.execute("interface", "ipv6", iface, "disable");
    607         } catch (NativeDaemonConnectorException e) {
    608             throw e.rethrowAsParcelableException();
    609         }
    610     }
    611 
    612     @Override
    613     public void addRoute(String interfaceName, RouteInfo route) {
    614         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    615         modifyRoute(interfaceName, ADD, route, DEFAULT);
    616     }
    617 
    618     @Override
    619     public void removeRoute(String interfaceName, RouteInfo route) {
    620         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    621         modifyRoute(interfaceName, REMOVE, route, DEFAULT);
    622     }
    623 
    624     @Override
    625     public void addSecondaryRoute(String interfaceName, RouteInfo route) {
    626         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    627         modifyRoute(interfaceName, ADD, route, SECONDARY);
    628     }
    629 
    630     @Override
    631     public void removeSecondaryRoute(String interfaceName, RouteInfo route) {
    632         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    633         modifyRoute(interfaceName, REMOVE, route, SECONDARY);
    634     }
    635 
    636     private void modifyRoute(String interfaceName, String action, RouteInfo route, String type) {
    637         final Command cmd = new Command("interface", "route", action, interfaceName, type);
    638 
    639         // create triplet: dest-ip-addr prefixlength gateway-ip-addr
    640         final LinkAddress la = route.getDestination();
    641         cmd.appendArg(la.getAddress().getHostAddress());
    642         cmd.appendArg(la.getNetworkPrefixLength());
    643 
    644         if (route.getGateway() == null) {
    645             if (la.getAddress() instanceof Inet4Address) {
    646                 cmd.appendArg("0.0.0.0");
    647             } else {
    648                 cmd.appendArg("::0");
    649             }
    650         } else {
    651             cmd.appendArg(route.getGateway().getHostAddress());
    652         }
    653 
    654         try {
    655             mConnector.execute(cmd);
    656         } catch (NativeDaemonConnectorException e) {
    657             throw e.rethrowAsParcelableException();
    658         }
    659     }
    660 
    661     private ArrayList<String> readRouteList(String filename) {
    662         FileInputStream fstream = null;
    663         ArrayList<String> list = new ArrayList<String>();
    664 
    665         try {
    666             fstream = new FileInputStream(filename);
    667             DataInputStream in = new DataInputStream(fstream);
    668             BufferedReader br = new BufferedReader(new InputStreamReader(in));
    669             String s;
    670 
    671             // throw away the title line
    672 
    673             while (((s = br.readLine()) != null) && (s.length() != 0)) {
    674                 list.add(s);
    675             }
    676         } catch (IOException ex) {
    677             // return current list, possibly empty
    678         } finally {
    679             if (fstream != null) {
    680                 try {
    681                     fstream.close();
    682                 } catch (IOException ex) {}
    683             }
    684         }
    685 
    686         return list;
    687     }
    688 
    689     @Override
    690     public RouteInfo[] getRoutes(String interfaceName) {
    691         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    692         ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>();
    693 
    694         // v4 routes listed as:
    695         // iface dest-addr gateway-addr flags refcnt use metric netmask mtu window IRTT
    696         for (String s : readRouteList("/proc/net/route")) {
    697             String[] fields = s.split("\t");
    698 
    699             if (fields.length > 7) {
    700                 String iface = fields[0];
    701 
    702                 if (interfaceName.equals(iface)) {
    703                     String dest = fields[1];
    704                     String gate = fields[2];
    705                     String flags = fields[3]; // future use?
    706                     String mask = fields[7];
    707                     try {
    708                         // address stored as a hex string, ex: 0014A8C0
    709                         InetAddress destAddr =
    710                                 NetworkUtils.intToInetAddress((int)Long.parseLong(dest, 16));
    711                         int prefixLength =
    712                                 NetworkUtils.netmaskIntToPrefixLength(
    713                                 (int)Long.parseLong(mask, 16));
    714                         LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
    715 
    716                         // address stored as a hex string, ex 0014A8C0
    717                         InetAddress gatewayAddr =
    718                                 NetworkUtils.intToInetAddress((int)Long.parseLong(gate, 16));
    719 
    720                         RouteInfo route = new RouteInfo(linkAddress, gatewayAddr);
    721                         routes.add(route);
    722                     } catch (Exception e) {
    723                         Log.e(TAG, "Error parsing route " + s + " : " + e);
    724                         continue;
    725                     }
    726                 }
    727             }
    728         }
    729 
    730         // v6 routes listed as:
    731         // dest-addr prefixlength ?? ?? gateway-addr ?? ?? ?? ?? iface
    732         for (String s : readRouteList("/proc/net/ipv6_route")) {
    733             String[]fields = s.split("\\s+");
    734             if (fields.length > 9) {
    735                 String iface = fields[9].trim();
    736                 if (interfaceName.equals(iface)) {
    737                     String dest = fields[0];
    738                     String prefix = fields[1];
    739                     String gate = fields[4];
    740 
    741                     try {
    742                         // prefix length stored as a hex string, ex 40
    743                         int prefixLength = Integer.parseInt(prefix, 16);
    744 
    745                         // address stored as a 32 char hex string
    746                         // ex fe800000000000000000000000000000
    747                         InetAddress destAddr = NetworkUtils.hexToInet6Address(dest);
    748                         LinkAddress linkAddress = new LinkAddress(destAddr, prefixLength);
    749 
    750                         InetAddress gateAddr = NetworkUtils.hexToInet6Address(gate);
    751 
    752                         RouteInfo route = new RouteInfo(linkAddress, gateAddr);
    753                         routes.add(route);
    754                     } catch (Exception e) {
    755                         Log.e(TAG, "Error parsing route " + s + " : " + e);
    756                         continue;
    757                     }
    758                 }
    759             }
    760         }
    761         return routes.toArray(new RouteInfo[routes.size()]);
    762     }
    763 
    764     @Override
    765     public void shutdown() {
    766         // TODO: remove from aidl if nobody calls externally
    767         mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG);
    768 
    769         Slog.d(TAG, "Shutting down");
    770     }
    771 
    772     @Override
    773     public boolean getIpForwardingEnabled() throws IllegalStateException{
    774         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    775 
    776         final NativeDaemonEvent event;
    777         try {
    778             event = mConnector.execute("ipfwd", "status");
    779         } catch (NativeDaemonConnectorException e) {
    780             throw e.rethrowAsParcelableException();
    781         }
    782 
    783         // 211 Forwarding enabled
    784         event.checkCode(IpFwdStatusResult);
    785         return event.getMessage().endsWith("enabled");
    786     }
    787 
    788     @Override
    789     public void setIpForwardingEnabled(boolean enable) {
    790         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    791         try {
    792             mConnector.execute("ipfwd", enable ? "enable" : "disable");
    793         } catch (NativeDaemonConnectorException e) {
    794             throw e.rethrowAsParcelableException();
    795         }
    796     }
    797 
    798     @Override
    799     public void startTethering(String[] dhcpRange) {
    800         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    801         // cmd is "tether start first_start first_stop second_start second_stop ..."
    802         // an odd number of addrs will fail
    803 
    804         final Command cmd = new Command("tether", "start");
    805         for (String d : dhcpRange) {
    806             cmd.appendArg(d);
    807         }
    808 
    809         try {
    810             mConnector.execute(cmd);
    811         } catch (NativeDaemonConnectorException e) {
    812             throw e.rethrowAsParcelableException();
    813         }
    814     }
    815 
    816     @Override
    817     public void stopTethering() {
    818         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    819         try {
    820             mConnector.execute("tether", "stop");
    821         } catch (NativeDaemonConnectorException e) {
    822             throw e.rethrowAsParcelableException();
    823         }
    824     }
    825 
    826     @Override
    827     public boolean isTetheringStarted() {
    828         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    829 
    830         final NativeDaemonEvent event;
    831         try {
    832             event = mConnector.execute("tether", "status");
    833         } catch (NativeDaemonConnectorException e) {
    834             throw e.rethrowAsParcelableException();
    835         }
    836 
    837         // 210 Tethering services started
    838         event.checkCode(TetherStatusResult);
    839         return event.getMessage().endsWith("started");
    840     }
    841 
    842     @Override
    843     public void tetherInterface(String iface) {
    844         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    845         try {
    846             mConnector.execute("tether", "interface", "add", iface);
    847         } catch (NativeDaemonConnectorException e) {
    848             throw e.rethrowAsParcelableException();
    849         }
    850     }
    851 
    852     @Override
    853     public void untetherInterface(String iface) {
    854         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    855         try {
    856             mConnector.execute("tether", "interface", "remove", iface);
    857         } catch (NativeDaemonConnectorException e) {
    858             throw e.rethrowAsParcelableException();
    859         }
    860     }
    861 
    862     @Override
    863     public String[] listTetheredInterfaces() {
    864         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    865         try {
    866             return NativeDaemonEvent.filterMessageList(
    867                     mConnector.executeForList("tether", "interface", "list"),
    868                     TetherInterfaceListResult);
    869         } catch (NativeDaemonConnectorException e) {
    870             throw e.rethrowAsParcelableException();
    871         }
    872     }
    873 
    874     @Override
    875     public void setDnsForwarders(String[] dns) {
    876         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    877 
    878         final Command cmd = new Command("tether", "dns", "set");
    879         for (String s : dns) {
    880             cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress());
    881         }
    882 
    883         try {
    884             mConnector.execute(cmd);
    885         } catch (NativeDaemonConnectorException e) {
    886             throw e.rethrowAsParcelableException();
    887         }
    888     }
    889 
    890     @Override
    891     public String[] getDnsForwarders() {
    892         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    893         try {
    894             return NativeDaemonEvent.filterMessageList(
    895                     mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult);
    896         } catch (NativeDaemonConnectorException e) {
    897             throw e.rethrowAsParcelableException();
    898         }
    899     }
    900 
    901     private void modifyNat(String action, String internalInterface, String externalInterface)
    902             throws SocketException {
    903         final Command cmd = new Command("nat", action, internalInterface, externalInterface);
    904 
    905         final NetworkInterface internalNetworkInterface = NetworkInterface.getByName(
    906                 internalInterface);
    907         if (internalNetworkInterface == null) {
    908             cmd.appendArg("0");
    909         } else {
    910             Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
    911                     .getInterfaceAddresses();
    912             cmd.appendArg(interfaceAddresses.size());
    913             for (InterfaceAddress ia : interfaceAddresses) {
    914                 InetAddress addr = NetworkUtils.getNetworkPart(
    915                         ia.getAddress(), ia.getNetworkPrefixLength());
    916                 cmd.appendArg(addr.getHostAddress() + "/" + ia.getNetworkPrefixLength());
    917             }
    918         }
    919 
    920         try {
    921             mConnector.execute(cmd);
    922         } catch (NativeDaemonConnectorException e) {
    923             throw e.rethrowAsParcelableException();
    924         }
    925     }
    926 
    927     @Override
    928     public void enableNat(String internalInterface, String externalInterface) {
    929         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    930         try {
    931             modifyNat("enable", internalInterface, externalInterface);
    932         } catch (SocketException e) {
    933             throw new IllegalStateException(e);
    934         }
    935     }
    936 
    937     @Override
    938     public void disableNat(String internalInterface, String externalInterface) {
    939         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    940         try {
    941             modifyNat("disable", internalInterface, externalInterface);
    942         } catch (SocketException e) {
    943             throw new IllegalStateException(e);
    944         }
    945     }
    946 
    947     @Override
    948     public String[] listTtys() {
    949         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    950         try {
    951             return NativeDaemonEvent.filterMessageList(
    952                     mConnector.executeForList("list_ttys"), TtyListResult);
    953         } catch (NativeDaemonConnectorException e) {
    954             throw e.rethrowAsParcelableException();
    955         }
    956     }
    957 
    958     @Override
    959     public void attachPppd(
    960             String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) {
    961         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    962         try {
    963             mConnector.execute("pppd", "attach", tty,
    964                     NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
    965                     NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
    966                     NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
    967                     NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress());
    968         } catch (NativeDaemonConnectorException e) {
    969             throw e.rethrowAsParcelableException();
    970         }
    971     }
    972 
    973     @Override
    974     public void detachPppd(String tty) {
    975         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    976         try {
    977             mConnector.execute("pppd", "detach", tty);
    978         } catch (NativeDaemonConnectorException e) {
    979             throw e.rethrowAsParcelableException();
    980         }
    981     }
    982 
    983     @Override
    984     public void startAccessPoint(
    985             WifiConfiguration wifiConfig, String wlanIface) {
    986         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    987         try {
    988             wifiFirmwareReload(wlanIface, "AP");
    989             if (wifiConfig == null) {
    990                 mConnector.execute("softap", "set", wlanIface);
    991             } else {
    992                 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
    993                         getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey));
    994             }
    995             mConnector.execute("softap", "startap");
    996         } catch (NativeDaemonConnectorException e) {
    997             throw e.rethrowAsParcelableException();
    998         }
    999     }
   1000 
   1001     private static String getSecurityType(WifiConfiguration wifiConfig) {
   1002         switch (wifiConfig.getAuthType()) {
   1003             case KeyMgmt.WPA_PSK:
   1004                 return "wpa-psk";
   1005             case KeyMgmt.WPA2_PSK:
   1006                 return "wpa2-psk";
   1007             default:
   1008                 return "open";
   1009         }
   1010     }
   1011 
   1012     /* @param mode can be "AP", "STA" or "P2P" */
   1013     @Override
   1014     public void wifiFirmwareReload(String wlanIface, String mode) {
   1015         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1016         try {
   1017             mConnector.execute("softap", "fwreload", wlanIface, mode);
   1018         } catch (NativeDaemonConnectorException e) {
   1019             throw e.rethrowAsParcelableException();
   1020         }
   1021     }
   1022 
   1023     @Override
   1024     public void stopAccessPoint(String wlanIface) {
   1025         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1026         try {
   1027             mConnector.execute("softap", "stopap");
   1028             wifiFirmwareReload(wlanIface, "STA");
   1029         } catch (NativeDaemonConnectorException e) {
   1030             throw e.rethrowAsParcelableException();
   1031         }
   1032     }
   1033 
   1034     @Override
   1035     public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface) {
   1036         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1037         try {
   1038             if (wifiConfig == null) {
   1039                 mConnector.execute("softap", "set", wlanIface);
   1040             } else {
   1041                 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
   1042                         getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey));
   1043             }
   1044         } catch (NativeDaemonConnectorException e) {
   1045             throw e.rethrowAsParcelableException();
   1046         }
   1047     }
   1048 
   1049     @Override
   1050     public void addIdleTimer(String iface, int timeout, String label) {
   1051         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1052 
   1053         if (DBG) Slog.d(TAG, "Adding idletimer");
   1054 
   1055         synchronized (mIdleTimerLock) {
   1056             IdleTimerParams params = mActiveIdleTimers.get(iface);
   1057             if (params != null) {
   1058                 // the interface already has idletimer, update network count
   1059                 params.networkCount++;
   1060                 return;
   1061             }
   1062 
   1063             try {
   1064                 mConnector.execute("idletimer", "add", iface, Integer.toString(timeout), label);
   1065             } catch (NativeDaemonConnectorException e) {
   1066                 throw e.rethrowAsParcelableException();
   1067             }
   1068             mActiveIdleTimers.put(iface, new IdleTimerParams(timeout, label));
   1069         }
   1070     }
   1071 
   1072     @Override
   1073     public void removeIdleTimer(String iface) {
   1074         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1075 
   1076         if (DBG) Slog.d(TAG, "Removing idletimer");
   1077 
   1078         synchronized (mIdleTimerLock) {
   1079             IdleTimerParams params = mActiveIdleTimers.get(iface);
   1080             if (params == null || --(params.networkCount) > 0) {
   1081                 return;
   1082             }
   1083 
   1084             try {
   1085                 mConnector.execute("idletimer", "remove", iface,
   1086                         Integer.toString(params.timeout), params.label);
   1087             } catch (NativeDaemonConnectorException e) {
   1088                 throw e.rethrowAsParcelableException();
   1089             }
   1090             mActiveIdleTimers.remove(iface);
   1091         }
   1092     }
   1093 
   1094     @Override
   1095     public NetworkStats getNetworkStatsSummaryDev() {
   1096         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1097         try {
   1098             return mStatsFactory.readNetworkStatsSummaryDev();
   1099         } catch (IOException e) {
   1100             throw new IllegalStateException(e);
   1101         }
   1102     }
   1103 
   1104     @Override
   1105     public NetworkStats getNetworkStatsSummaryXt() {
   1106         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1107         try {
   1108             return mStatsFactory.readNetworkStatsSummaryXt();
   1109         } catch (IOException e) {
   1110             throw new IllegalStateException(e);
   1111         }
   1112     }
   1113 
   1114     @Override
   1115     public NetworkStats getNetworkStatsDetail() {
   1116         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1117         try {
   1118             return mStatsFactory.readNetworkStatsDetail(UID_ALL);
   1119         } catch (IOException e) {
   1120             throw new IllegalStateException(e);
   1121         }
   1122     }
   1123 
   1124     @Override
   1125     public void setInterfaceQuota(String iface, long quotaBytes) {
   1126         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1127 
   1128         // silently discard when control disabled
   1129         // TODO: eventually migrate to be always enabled
   1130         if (!mBandwidthControlEnabled) return;
   1131 
   1132         synchronized (mQuotaLock) {
   1133             if (mActiveQuotas.containsKey(iface)) {
   1134                 throw new IllegalStateException("iface " + iface + " already has quota");
   1135             }
   1136 
   1137             try {
   1138                 // TODO: support quota shared across interfaces
   1139                 mConnector.execute("bandwidth", "setiquota", iface, quotaBytes);
   1140                 mActiveQuotas.put(iface, quotaBytes);
   1141             } catch (NativeDaemonConnectorException e) {
   1142                 throw e.rethrowAsParcelableException();
   1143             }
   1144         }
   1145     }
   1146 
   1147     @Override
   1148     public void removeInterfaceQuota(String iface) {
   1149         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1150 
   1151         // silently discard when control disabled
   1152         // TODO: eventually migrate to be always enabled
   1153         if (!mBandwidthControlEnabled) return;
   1154 
   1155         synchronized (mQuotaLock) {
   1156             if (!mActiveQuotas.containsKey(iface)) {
   1157                 // TODO: eventually consider throwing
   1158                 return;
   1159             }
   1160 
   1161             mActiveQuotas.remove(iface);
   1162             mActiveAlerts.remove(iface);
   1163 
   1164             try {
   1165                 // TODO: support quota shared across interfaces
   1166                 mConnector.execute("bandwidth", "removeiquota", iface);
   1167             } catch (NativeDaemonConnectorException e) {
   1168                 throw e.rethrowAsParcelableException();
   1169             }
   1170         }
   1171     }
   1172 
   1173     @Override
   1174     public void setInterfaceAlert(String iface, long alertBytes) {
   1175         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1176 
   1177         // silently discard when control disabled
   1178         // TODO: eventually migrate to be always enabled
   1179         if (!mBandwidthControlEnabled) return;
   1180 
   1181         // quick sanity check
   1182         if (!mActiveQuotas.containsKey(iface)) {
   1183             throw new IllegalStateException("setting alert requires existing quota on iface");
   1184         }
   1185 
   1186         synchronized (mQuotaLock) {
   1187             if (mActiveAlerts.containsKey(iface)) {
   1188                 throw new IllegalStateException("iface " + iface + " already has alert");
   1189             }
   1190 
   1191             try {
   1192                 // TODO: support alert shared across interfaces
   1193                 mConnector.execute("bandwidth", "setinterfacealert", iface, alertBytes);
   1194                 mActiveAlerts.put(iface, alertBytes);
   1195             } catch (NativeDaemonConnectorException e) {
   1196                 throw e.rethrowAsParcelableException();
   1197             }
   1198         }
   1199     }
   1200 
   1201     @Override
   1202     public void removeInterfaceAlert(String iface) {
   1203         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1204 
   1205         // silently discard when control disabled
   1206         // TODO: eventually migrate to be always enabled
   1207         if (!mBandwidthControlEnabled) return;
   1208 
   1209         synchronized (mQuotaLock) {
   1210             if (!mActiveAlerts.containsKey(iface)) {
   1211                 // TODO: eventually consider throwing
   1212                 return;
   1213             }
   1214 
   1215             try {
   1216                 // TODO: support alert shared across interfaces
   1217                 mConnector.execute("bandwidth", "removeinterfacealert", iface);
   1218                 mActiveAlerts.remove(iface);
   1219             } catch (NativeDaemonConnectorException e) {
   1220                 throw e.rethrowAsParcelableException();
   1221             }
   1222         }
   1223     }
   1224 
   1225     @Override
   1226     public void setGlobalAlert(long alertBytes) {
   1227         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1228 
   1229         // silently discard when control disabled
   1230         // TODO: eventually migrate to be always enabled
   1231         if (!mBandwidthControlEnabled) return;
   1232 
   1233         try {
   1234             mConnector.execute("bandwidth", "setglobalalert", alertBytes);
   1235         } catch (NativeDaemonConnectorException e) {
   1236             throw e.rethrowAsParcelableException();
   1237         }
   1238     }
   1239 
   1240     @Override
   1241     public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
   1242         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1243 
   1244         // silently discard when control disabled
   1245         // TODO: eventually migrate to be always enabled
   1246         if (!mBandwidthControlEnabled) return;
   1247 
   1248         synchronized (mQuotaLock) {
   1249             final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
   1250             if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
   1251                 // TODO: eventually consider throwing
   1252                 return;
   1253             }
   1254 
   1255             try {
   1256                 mConnector.execute("bandwidth",
   1257                         rejectOnQuotaInterfaces ? "addnaughtyapps" : "removenaughtyapps", uid);
   1258                 if (rejectOnQuotaInterfaces) {
   1259                     mUidRejectOnQuota.put(uid, true);
   1260                 } else {
   1261                     mUidRejectOnQuota.delete(uid);
   1262                 }
   1263             } catch (NativeDaemonConnectorException e) {
   1264                 throw e.rethrowAsParcelableException();
   1265             }
   1266         }
   1267     }
   1268 
   1269     @Override
   1270     public boolean isBandwidthControlEnabled() {
   1271         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1272         return mBandwidthControlEnabled;
   1273     }
   1274 
   1275     @Override
   1276     public NetworkStats getNetworkStatsUidDetail(int uid) {
   1277         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1278         try {
   1279             return mStatsFactory.readNetworkStatsDetail(uid);
   1280         } catch (IOException e) {
   1281             throw new IllegalStateException(e);
   1282         }
   1283     }
   1284 
   1285     @Override
   1286     public NetworkStats getNetworkStatsTethering(String[] ifacePairs) {
   1287         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1288 
   1289         if (ifacePairs.length % 2 != 0) {
   1290             throw new IllegalArgumentException(
   1291                     "unexpected ifacePairs; length=" + ifacePairs.length);
   1292         }
   1293 
   1294         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
   1295         for (int i = 0; i < ifacePairs.length; i += 2) {
   1296             final String ifaceIn = ifacePairs[i];
   1297             final String ifaceOut = ifacePairs[i + 1];
   1298             if (ifaceIn != null && ifaceOut != null) {
   1299                 stats.combineValues(getNetworkStatsTethering(ifaceIn, ifaceOut));
   1300             }
   1301         }
   1302         return stats;
   1303     }
   1304 
   1305     private NetworkStats.Entry getNetworkStatsTethering(String ifaceIn, String ifaceOut) {
   1306         final NativeDaemonEvent event;
   1307         try {
   1308             event = mConnector.execute("bandwidth", "gettetherstats", ifaceIn, ifaceOut);
   1309         } catch (NativeDaemonConnectorException e) {
   1310             throw e.rethrowAsParcelableException();
   1311         }
   1312 
   1313         event.checkCode(TetheringStatsResult);
   1314 
   1315         // 221 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
   1316         final StringTokenizer tok = new StringTokenizer(event.getMessage());
   1317         tok.nextToken();
   1318         tok.nextToken();
   1319 
   1320         try {
   1321             final NetworkStats.Entry entry = new NetworkStats.Entry();
   1322             entry.iface = ifaceIn;
   1323             entry.uid = UID_TETHERING;
   1324             entry.set = SET_DEFAULT;
   1325             entry.tag = TAG_NONE;
   1326             entry.rxBytes = Long.parseLong(tok.nextToken());
   1327             entry.rxPackets = Long.parseLong(tok.nextToken());
   1328             entry.txBytes = Long.parseLong(tok.nextToken());
   1329             entry.txPackets = Long.parseLong(tok.nextToken());
   1330             return entry;
   1331         } catch (NumberFormatException e) {
   1332             throw new IllegalStateException(
   1333                     "problem parsing tethering stats for " + ifaceIn + " " + ifaceOut + ": " + e);
   1334         }
   1335     }
   1336 
   1337     @Override
   1338     public void setDefaultInterfaceForDns(String iface) {
   1339         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1340         try {
   1341             mConnector.execute("resolver", "setdefaultif", iface);
   1342         } catch (NativeDaemonConnectorException e) {
   1343             throw e.rethrowAsParcelableException();
   1344         }
   1345     }
   1346 
   1347     @Override
   1348     public void setDnsServersForInterface(String iface, String[] servers, String domains) {
   1349         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1350 
   1351         final Command cmd = new Command("resolver", "setifdns", iface,
   1352                 (domains == null ? "" : domains));
   1353 
   1354         for (String s : servers) {
   1355             InetAddress a = NetworkUtils.numericToInetAddress(s);
   1356             if (a.isAnyLocalAddress() == false) {
   1357                 cmd.appendArg(a.getHostAddress());
   1358             }
   1359         }
   1360 
   1361         try {
   1362             mConnector.execute(cmd);
   1363         } catch (NativeDaemonConnectorException e) {
   1364             throw e.rethrowAsParcelableException();
   1365         }
   1366     }
   1367 
   1368     @Override
   1369     public void flushDefaultDnsCache() {
   1370         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1371         try {
   1372             mConnector.execute("resolver", "flushdefaultif");
   1373         } catch (NativeDaemonConnectorException e) {
   1374             throw e.rethrowAsParcelableException();
   1375         }
   1376     }
   1377 
   1378     @Override
   1379     public void flushInterfaceDnsCache(String iface) {
   1380         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1381         try {
   1382             mConnector.execute("resolver", "flushif", iface);
   1383         } catch (NativeDaemonConnectorException e) {
   1384             throw e.rethrowAsParcelableException();
   1385         }
   1386     }
   1387 
   1388     @Override
   1389     public void setFirewallEnabled(boolean enabled) {
   1390         enforceSystemUid();
   1391         try {
   1392             mConnector.execute("firewall", enabled ? "enable" : "disable");
   1393             mFirewallEnabled = enabled;
   1394         } catch (NativeDaemonConnectorException e) {
   1395             throw e.rethrowAsParcelableException();
   1396         }
   1397     }
   1398 
   1399     @Override
   1400     public boolean isFirewallEnabled() {
   1401         enforceSystemUid();
   1402         return mFirewallEnabled;
   1403     }
   1404 
   1405     @Override
   1406     public void setFirewallInterfaceRule(String iface, boolean allow) {
   1407         enforceSystemUid();
   1408         Preconditions.checkState(mFirewallEnabled);
   1409         final String rule = allow ? ALLOW : DENY;
   1410         try {
   1411             mConnector.execute("firewall", "set_interface_rule", iface, rule);
   1412         } catch (NativeDaemonConnectorException e) {
   1413             throw e.rethrowAsParcelableException();
   1414         }
   1415     }
   1416 
   1417     @Override
   1418     public void setFirewallEgressSourceRule(String addr, boolean allow) {
   1419         enforceSystemUid();
   1420         Preconditions.checkState(mFirewallEnabled);
   1421         final String rule = allow ? ALLOW : DENY;
   1422         try {
   1423             mConnector.execute("firewall", "set_egress_source_rule", addr, rule);
   1424         } catch (NativeDaemonConnectorException e) {
   1425             throw e.rethrowAsParcelableException();
   1426         }
   1427     }
   1428 
   1429     @Override
   1430     public void setFirewallEgressDestRule(String addr, int port, boolean allow) {
   1431         enforceSystemUid();
   1432         Preconditions.checkState(mFirewallEnabled);
   1433         final String rule = allow ? ALLOW : DENY;
   1434         try {
   1435             mConnector.execute("firewall", "set_egress_dest_rule", addr, port, rule);
   1436         } catch (NativeDaemonConnectorException e) {
   1437             throw e.rethrowAsParcelableException();
   1438         }
   1439     }
   1440 
   1441     @Override
   1442     public void setFirewallUidRule(int uid, boolean allow) {
   1443         enforceSystemUid();
   1444         Preconditions.checkState(mFirewallEnabled);
   1445         final String rule = allow ? ALLOW : DENY;
   1446         try {
   1447             mConnector.execute("firewall", "set_uid_rule", uid, rule);
   1448         } catch (NativeDaemonConnectorException e) {
   1449             throw e.rethrowAsParcelableException();
   1450         }
   1451     }
   1452 
   1453     private static void enforceSystemUid() {
   1454         final int uid = Binder.getCallingUid();
   1455         if (uid != Process.SYSTEM_UID) {
   1456             throw new SecurityException("Only available to AID_SYSTEM");
   1457         }
   1458     }
   1459 
   1460     @Override
   1461     public void setDnsInterfaceForPid(String iface, int pid) throws IllegalStateException {
   1462         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1463         try {
   1464             mConnector.execute("resolver", "setifaceforpid", iface, pid);
   1465         } catch (NativeDaemonConnectorException e) {
   1466             throw new IllegalStateException(
   1467                     "Error communicating with native deamon to set interface for pid" + iface, e);
   1468         }
   1469     }
   1470 
   1471     @Override
   1472     public void clearDnsInterfaceForPid(int pid) throws IllegalStateException {
   1473         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1474         try {
   1475             mConnector.execute("resolver", "clearifaceforpid", pid);
   1476         } catch (NativeDaemonConnectorException e) {
   1477             throw new IllegalStateException(
   1478                     "Error communicating with native deamon to clear interface for pid " + pid, e);
   1479         }
   1480     }
   1481 
   1482     @Override
   1483     public void startClatd(String interfaceName) throws IllegalStateException {
   1484         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1485 
   1486         try {
   1487             mConnector.execute("clatd", "start", interfaceName);
   1488         } catch (NativeDaemonConnectorException e) {
   1489             throw e.rethrowAsParcelableException();
   1490         }
   1491     }
   1492 
   1493     @Override
   1494     public void stopClatd() throws IllegalStateException {
   1495         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1496 
   1497         try {
   1498             mConnector.execute("clatd", "stop");
   1499         } catch (NativeDaemonConnectorException e) {
   1500             throw e.rethrowAsParcelableException();
   1501         }
   1502     }
   1503 
   1504     @Override
   1505     public boolean isClatdStarted() {
   1506         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
   1507 
   1508         final NativeDaemonEvent event;
   1509         try {
   1510             event = mConnector.execute("clatd", "status");
   1511         } catch (NativeDaemonConnectorException e) {
   1512             throw e.rethrowAsParcelableException();
   1513         }
   1514 
   1515         event.checkCode(ClatdStatusResult);
   1516         return event.getMessage().endsWith("started");
   1517     }
   1518 
   1519     /** {@inheritDoc} */
   1520     @Override
   1521     public void monitor() {
   1522         if (mConnector != null) {
   1523             mConnector.monitor();
   1524         }
   1525     }
   1526 
   1527     @Override
   1528     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1529         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
   1530 
   1531         pw.println("NetworkManagementService NativeDaemonConnector Log:");
   1532         mConnector.dump(fd, pw, args);
   1533         pw.println();
   1534 
   1535         pw.print("Bandwidth control enabled: "); pw.println(mBandwidthControlEnabled);
   1536 
   1537         synchronized (mQuotaLock) {
   1538             pw.print("Active quota ifaces: "); pw.println(mActiveQuotas.toString());
   1539             pw.print("Active alert ifaces: "); pw.println(mActiveAlerts.toString());
   1540         }
   1541 
   1542         synchronized (mUidRejectOnQuota) {
   1543             pw.print("UID reject on quota ifaces: [");
   1544             final int size = mUidRejectOnQuota.size();
   1545             for (int i = 0; i < size; i++) {
   1546                 pw.print(mUidRejectOnQuota.keyAt(i));
   1547                 if (i < size - 1) pw.print(",");
   1548             }
   1549             pw.println("]");
   1550         }
   1551 
   1552         pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
   1553     }
   1554 }
   1555