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