Home | History | Annotate | Download | only in tethering
      1 /*
      2  * Copyright (C) 2016 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.connectivity.tethering;
     18 
     19 import static android.net.util.NetworkConstants.asByte;
     20 import static android.net.util.NetworkConstants.FF;
     21 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
     22 
     23 import android.net.ConnectivityManager;
     24 import android.net.INetd;
     25 import android.net.INetworkStatsService;
     26 import android.net.InterfaceConfiguration;
     27 import android.net.IpPrefix;
     28 import android.net.LinkAddress;
     29 import android.net.LinkProperties;
     30 import android.net.NetworkUtils;
     31 import android.net.RouteInfo;
     32 import android.net.ip.InterfaceController;
     33 import android.net.ip.RouterAdvertisementDaemon;
     34 import android.net.ip.RouterAdvertisementDaemon.RaParams;
     35 import android.net.util.InterfaceParams;
     36 import android.net.util.InterfaceSet;
     37 import android.net.util.SharedLog;
     38 import android.os.INetworkManagementService;
     39 import android.os.Looper;
     40 import android.os.Message;
     41 import android.os.RemoteException;
     42 import android.os.ServiceSpecificException;
     43 import android.util.Log;
     44 import android.util.Slog;
     45 import android.util.SparseArray;
     46 
     47 import com.android.internal.util.MessageUtils;
     48 import com.android.internal.util.Protocol;
     49 import com.android.internal.util.State;
     50 import com.android.internal.util.StateMachine;
     51 
     52 import java.net.Inet6Address;
     53 import java.net.InetAddress;
     54 import java.net.UnknownHostException;
     55 import java.util.ArrayList;
     56 import java.util.HashSet;
     57 import java.util.Objects;
     58 import java.util.Random;
     59 import java.util.Set;
     60 
     61 /**
     62  * Provides the interface to IP-layer serving functionality for a given network
     63  * interface, e.g. for tethering or "local-only hotspot" mode.
     64  *
     65  * @hide
     66  */
     67 public class TetherInterfaceStateMachine extends StateMachine {
     68     private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
     69     private static final byte DOUG_ADAMS = (byte) 42;
     70 
     71     private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
     72     private static final int USB_PREFIX_LENGTH = 24;
     73     private static final String WIFI_HOST_IFACE_ADDR = "192.168.43.1";
     74     private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24;
     75 
     76     private final static String TAG = "TetherInterfaceSM";
     77     private final static boolean DBG = false;
     78     private final static boolean VDBG = false;
     79     private static final Class[] messageClasses = {
     80             TetherInterfaceStateMachine.class
     81     };
     82     private static final SparseArray<String> sMagicDecoderRing =
     83             MessageUtils.findMessageNames(messageClasses);
     84 
     85     private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
     86     // request from the user that it wants to tether
     87     public static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
     88     // request from the user that it wants to untether
     89     public static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
     90     // notification that this interface is down
     91     public static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
     92     // notification from the master SM that it had trouble enabling IP Forwarding
     93     public static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
     94     // notification from the master SM that it had trouble disabling IP Forwarding
     95     public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
     96     // notification from the master SM that it had trouble starting tethering
     97     public static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
     98     // notification from the master SM that it had trouble stopping tethering
     99     public static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
    100     // notification from the master SM that it had trouble setting the DNS forwarders
    101     public static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
    102     // the upstream connection has changed
    103     public static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;
    104     // new IPv6 tethering parameters need to be processed
    105     public static final int CMD_IPV6_TETHER_UPDATE          = BASE_IFACE + 13;
    106 
    107     private final State mInitialState;
    108     private final State mLocalHotspotState;
    109     private final State mTetheredState;
    110     private final State mUnavailableState;
    111 
    112     private final SharedLog mLog;
    113     private final INetworkManagementService mNMService;
    114     private final INetd mNetd;
    115     private final INetworkStatsService mStatsService;
    116     private final IControlsTethering mTetherController;
    117     private final InterfaceController mInterfaceCtrl;
    118 
    119     private final String mIfaceName;
    120     private final int mInterfaceType;
    121     private final LinkProperties mLinkProperties;
    122 
    123     private final TetheringDependencies mDeps;
    124 
    125     private int mLastError;
    126     private int mServingMode;
    127     private InterfaceSet mUpstreamIfaceSet;  // may change over time
    128     private InterfaceParams mInterfaceParams;
    129     // TODO: De-duplicate this with mLinkProperties above. Currently, these link
    130     // properties are those selected by the IPv6TetheringCoordinator and relayed
    131     // to us. By comparison, mLinkProperties contains the addresses and directly
    132     // connected routes that have been formed from these properties iff. we have
    133     // succeeded in configuring them and are able to announce them within Router
    134     // Advertisements (otherwise, we do not add them to mLinkProperties at all).
    135     private LinkProperties mLastIPv6LinkProperties;
    136     private RouterAdvertisementDaemon mRaDaemon;
    137     private RaParams mLastRaParams;
    138 
    139     public TetherInterfaceStateMachine(
    140             String ifaceName, Looper looper, int interfaceType, SharedLog log,
    141             INetworkManagementService nMService, INetworkStatsService statsService,
    142             IControlsTethering tetherController,
    143             TetheringDependencies deps) {
    144         super(ifaceName, looper);
    145         mLog = log.forSubComponent(ifaceName);
    146         mNMService = nMService;
    147         mNetd = deps.getNetdService();
    148         mStatsService = statsService;
    149         mTetherController = tetherController;
    150         mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
    151         mIfaceName = ifaceName;
    152         mInterfaceType = interfaceType;
    153         mLinkProperties = new LinkProperties();
    154         mDeps = deps;
    155         resetLinkProperties();
    156         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
    157         mServingMode = IControlsTethering.STATE_AVAILABLE;
    158 
    159         mInitialState = new InitialState();
    160         mLocalHotspotState = new LocalHotspotState();
    161         mTetheredState = new TetheredState();
    162         mUnavailableState = new UnavailableState();
    163         addState(mInitialState);
    164         addState(mLocalHotspotState);
    165         addState(mTetheredState);
    166         addState(mUnavailableState);
    167 
    168         setInitialState(mInitialState);
    169     }
    170 
    171     public String interfaceName() { return mIfaceName; }
    172 
    173     public int interfaceType() { return mInterfaceType; }
    174 
    175     public int lastError() { return mLastError; }
    176 
    177     public int servingMode() { return mServingMode; }
    178 
    179     public LinkProperties linkProperties() { return new LinkProperties(mLinkProperties); }
    180 
    181     public void stop() { sendMessage(CMD_INTERFACE_DOWN); }
    182 
    183     public void unwanted() { sendMessage(CMD_TETHER_UNREQUESTED); }
    184 
    185     /**
    186      * Internals.
    187      */
    188 
    189     private boolean startIPv4() { return configureIPv4(true); }
    190 
    191     private void stopIPv4() {
    192         configureIPv4(false);
    193         // NOTE: All of configureIPv4() will be refactored out of existence
    194         // into calls to InterfaceController, shared with startIPv4().
    195         mInterfaceCtrl.clearIPv4Address();
    196     }
    197 
    198     // TODO: Refactor this in terms of calls to InterfaceController.
    199     private boolean configureIPv4(boolean enabled) {
    200         if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
    201 
    202         // TODO: Replace this hard-coded information with dynamically selected
    203         // config passed down to us by a higher layer IP-coordinating element.
    204         String ipAsString = null;
    205         int prefixLen = 0;
    206         if (mInterfaceType == ConnectivityManager.TETHERING_USB) {
    207             ipAsString = USB_NEAR_IFACE_ADDR;
    208             prefixLen = USB_PREFIX_LENGTH;
    209         } else if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
    210             ipAsString = getRandomWifiIPv4Address();
    211             prefixLen = WIFI_HOST_IFACE_PREFIX_LENGTH;
    212         } else {
    213             // Nothing to do, BT does this elsewhere.
    214             return true;
    215         }
    216 
    217         final LinkAddress linkAddr;
    218         try {
    219             final InterfaceConfiguration ifcg = mNMService.getInterfaceConfig(mIfaceName);
    220             if (ifcg == null) {
    221                 mLog.e("Received null interface config");
    222                 return false;
    223             }
    224 
    225             InetAddress addr = NetworkUtils.numericToInetAddress(ipAsString);
    226             linkAddr = new LinkAddress(addr, prefixLen);
    227             ifcg.setLinkAddress(linkAddr);
    228             if (mInterfaceType == ConnectivityManager.TETHERING_WIFI) {
    229                 // The WiFi stack has ownership of the interface up/down state.
    230                 // It is unclear whether the Bluetooth or USB stacks will manage their own
    231                 // state.
    232                 ifcg.ignoreInterfaceUpDownStatus();
    233             } else {
    234                 if (enabled) {
    235                     ifcg.setInterfaceUp();
    236                 } else {
    237                     ifcg.setInterfaceDown();
    238                 }
    239             }
    240             ifcg.clearFlag("running");
    241             mNMService.setInterfaceConfig(mIfaceName, ifcg);
    242         } catch (Exception e) {
    243             mLog.e("Error configuring interface " + e);
    244             return false;
    245         }
    246 
    247         // Directly-connected route.
    248         final RouteInfo route = new RouteInfo(linkAddr);
    249         if (enabled) {
    250             mLinkProperties.addLinkAddress(linkAddr);
    251             mLinkProperties.addRoute(route);
    252         } else {
    253             mLinkProperties.removeLinkAddress(linkAddr);
    254             mLinkProperties.removeRoute(route);
    255         }
    256         return true;
    257     }
    258 
    259     private String getRandomWifiIPv4Address() {
    260         try {
    261             byte[] bytes = NetworkUtils.numericToInetAddress(WIFI_HOST_IFACE_ADDR).getAddress();
    262             bytes[3] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1), FF);
    263             return InetAddress.getByAddress(bytes).getHostAddress();
    264         } catch (Exception e) {
    265             return WIFI_HOST_IFACE_ADDR;
    266         }
    267     }
    268 
    269     private boolean startIPv6() {
    270         mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
    271         if (mInterfaceParams == null) {
    272             mLog.e("Failed to find InterfaceParams");
    273             stopIPv6();
    274             return false;
    275         }
    276 
    277         mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
    278         if (!mRaDaemon.start()) {
    279             stopIPv6();
    280             return false;
    281         }
    282 
    283         return true;
    284     }
    285 
    286     private void stopIPv6() {
    287         mInterfaceParams = null;
    288         setRaParams(null);
    289 
    290         if (mRaDaemon != null) {
    291             mRaDaemon.stop();
    292             mRaDaemon = null;
    293         }
    294     }
    295 
    296     // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
    297     // LinkProperties. These have extraneous data filtered out and only the
    298     // necessary prefixes included (per its prefix distribution policy).
    299     //
    300     // TODO: Evaluate using a data structure than is more directly suited to
    301     // communicating only the relevant information.
    302     private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
    303         if (mRaDaemon == null) return;
    304 
    305         // Avoid unnecessary work on spurious updates.
    306         if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
    307             return;
    308         }
    309 
    310         RaParams params = null;
    311 
    312         if (v6only != null) {
    313             params = new RaParams();
    314             params.mtu = v6only.getMtu();
    315             params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
    316 
    317             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
    318                 if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
    319 
    320                 final IpPrefix prefix = new IpPrefix(
    321                         linkAddr.getAddress(), linkAddr.getPrefixLength());
    322                 params.prefixes.add(prefix);
    323 
    324                 final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
    325                 if (dnsServer != null) {
    326                     params.dnses.add(dnsServer);
    327                 }
    328             }
    329         }
    330         // If v6only is null, we pass in null to setRaParams(), which handles
    331         // deprecation of any existing RA data.
    332 
    333         setRaParams(params);
    334         mLastIPv6LinkProperties = v6only;
    335     }
    336 
    337     private void configureLocalIPv6Routes(
    338             HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
    339         // [1] Remove the routes that are deprecated.
    340         if (!deprecatedPrefixes.isEmpty()) {
    341             final ArrayList<RouteInfo> toBeRemoved =
    342                     getLocalRoutesFor(mIfaceName, deprecatedPrefixes);
    343             try {
    344                 final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
    345                 if (removalFailures > 0) {
    346                     mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
    347                             removalFailures));
    348                 }
    349             } catch (RemoteException e) {
    350                 mLog.e("Failed to remove IPv6 routes from local table: " + e);
    351             }
    352 
    353             for (RouteInfo route : toBeRemoved) mLinkProperties.removeRoute(route);
    354         }
    355 
    356         // [2] Add only the routes that have not previously been added.
    357         if (newPrefixes != null && !newPrefixes.isEmpty()) {
    358             HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
    359             if (mLastRaParams != null) {
    360                 addedPrefixes.removeAll(mLastRaParams.prefixes);
    361             }
    362 
    363             if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
    364                 // We need to be able to send unicast RAs, and clients might
    365                 // like to ping the default router's link-local address.  Note
    366                 // that we never remove the link-local route from the network
    367                 // until Tethering disables tethering on the interface. We
    368                 // only need to add the link-local prefix once, but in the
    369                 // event we add it more than once netd silently ignores EEXIST.
    370                 addedPrefixes.add(LINK_LOCAL_PREFIX);
    371             }
    372 
    373             if (!addedPrefixes.isEmpty()) {
    374                 final ArrayList<RouteInfo> toBeAdded =
    375                         getLocalRoutesFor(mIfaceName, addedPrefixes);
    376                 try {
    377                     // It's safe to call addInterfaceToLocalNetwork() even if
    378                     // the interface is already in the local_network. Note also
    379                     // that adding routes that already exist does not cause an
    380                     // error (EEXIST is silently ignored).
    381                     mNMService.addInterfaceToLocalNetwork(mIfaceName, toBeAdded);
    382                 } catch (RemoteException e) {
    383                     mLog.e("Failed to add IPv6 routes to local table: " + e);
    384                 }
    385 
    386                 for (RouteInfo route : toBeAdded) mLinkProperties.addRoute(route);
    387             }
    388         }
    389     }
    390 
    391     private void configureLocalIPv6Dns(
    392             HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
    393         // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located?
    394         if (mNetd == null) {
    395             if (newDnses != null) newDnses.clear();
    396             mLog.e("No netd service instance available; not setting local IPv6 addresses");
    397             return;
    398         }
    399 
    400         // [1] Remove deprecated local DNS IP addresses.
    401         if (!deprecatedDnses.isEmpty()) {
    402             for (Inet6Address dns : deprecatedDnses) {
    403                 if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) {
    404                     mLog.e("Failed to remove local dns IP " + dns);
    405                 }
    406 
    407                 mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
    408             }
    409         }
    410 
    411         // [2] Add only the local DNS IP addresses that have not previously been added.
    412         if (newDnses != null && !newDnses.isEmpty()) {
    413             final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
    414             if (mLastRaParams != null) {
    415                 addedDnses.removeAll(mLastRaParams.dnses);
    416             }
    417 
    418             for (Inet6Address dns : addedDnses) {
    419                 if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) {
    420                     mLog.e("Failed to add local dns IP " + dns);
    421                     newDnses.remove(dns);
    422                 }
    423 
    424                 mLinkProperties.addLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
    425             }
    426         }
    427 
    428         try {
    429             mNetd.tetherApplyDnsInterfaces();
    430         } catch (ServiceSpecificException | RemoteException e) {
    431             mLog.e("Failed to update local DNS caching server");
    432             if (newDnses != null) newDnses.clear();
    433         }
    434     }
    435 
    436     private void setRaParams(RaParams newParams) {
    437         if (mRaDaemon != null) {
    438             final RaParams deprecatedParams =
    439                     RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
    440 
    441             configureLocalIPv6Routes(deprecatedParams.prefixes,
    442                     (newParams != null) ? newParams.prefixes : null);
    443 
    444             configureLocalIPv6Dns(deprecatedParams.dnses,
    445                     (newParams != null) ? newParams.dnses : null);
    446 
    447             mRaDaemon.buildNewRa(deprecatedParams, newParams);
    448         }
    449 
    450         mLastRaParams = newParams;
    451     }
    452 
    453     private void logMessage(State state, int what) {
    454         mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
    455     }
    456 
    457     private void sendInterfaceState(int newInterfaceState) {
    458         mServingMode = newInterfaceState;
    459         mTetherController.updateInterfaceState(
    460                 TetherInterfaceStateMachine.this, newInterfaceState, mLastError);
    461         sendLinkProperties();
    462     }
    463 
    464     private void sendLinkProperties() {
    465         mTetherController.updateLinkProperties(
    466                 TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties));
    467     }
    468 
    469     private void resetLinkProperties() {
    470         mLinkProperties.clear();
    471         mLinkProperties.setInterfaceName(mIfaceName);
    472     }
    473 
    474     class InitialState extends State {
    475         @Override
    476         public void enter() {
    477             sendInterfaceState(IControlsTethering.STATE_AVAILABLE);
    478         }
    479 
    480         @Override
    481         public boolean processMessage(Message message) {
    482             logMessage(this, message.what);
    483             switch (message.what) {
    484                 case CMD_TETHER_REQUESTED:
    485                     mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
    486                     switch (message.arg1) {
    487                         case IControlsTethering.STATE_LOCAL_ONLY:
    488                             transitionTo(mLocalHotspotState);
    489                             break;
    490                         case IControlsTethering.STATE_TETHERED:
    491                             transitionTo(mTetheredState);
    492                             break;
    493                         default:
    494                             mLog.e("Invalid tethering interface serving state specified.");
    495                     }
    496                     break;
    497                 case CMD_INTERFACE_DOWN:
    498                     transitionTo(mUnavailableState);
    499                     break;
    500                 case CMD_IPV6_TETHER_UPDATE:
    501                     updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
    502                     break;
    503                 default:
    504                     return NOT_HANDLED;
    505             }
    506             return HANDLED;
    507         }
    508     }
    509 
    510     class BaseServingState extends State {
    511         @Override
    512         public void enter() {
    513             if (!startIPv4()) {
    514                 mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
    515                 return;
    516             }
    517 
    518             try {
    519                 mNMService.tetherInterface(mIfaceName);
    520             } catch (Exception e) {
    521                 mLog.e("Error Tethering: " + e);
    522                 mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
    523                 return;
    524             }
    525 
    526             if (!startIPv6()) {
    527                 mLog.e("Failed to startIPv6");
    528                 // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
    529                 return;
    530             }
    531         }
    532 
    533         @Override
    534         public void exit() {
    535             // Note that at this point, we're leaving the tethered state.  We can fail any
    536             // of these operations, but it doesn't really change that we have to try them
    537             // all in sequence.
    538             stopIPv6();
    539 
    540             try {
    541                 mNMService.untetherInterface(mIfaceName);
    542             } catch (Exception e) {
    543                 mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
    544                 mLog.e("Failed to untether interface: " + e);
    545             }
    546 
    547             stopIPv4();
    548 
    549             resetLinkProperties();
    550         }
    551 
    552         @Override
    553         public boolean processMessage(Message message) {
    554             logMessage(this, message.what);
    555             switch (message.what) {
    556                 case CMD_TETHER_UNREQUESTED:
    557                     transitionTo(mInitialState);
    558                     if (DBG) Log.d(TAG, "Untethered (unrequested)" + mIfaceName);
    559                     break;
    560                 case CMD_INTERFACE_DOWN:
    561                     transitionTo(mUnavailableState);
    562                     if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
    563                     break;
    564                 case CMD_IPV6_TETHER_UPDATE:
    565                     updateUpstreamIPv6LinkProperties((LinkProperties) message.obj);
    566                     sendLinkProperties();
    567                     break;
    568                 case CMD_IP_FORWARDING_ENABLE_ERROR:
    569                 case CMD_IP_FORWARDING_DISABLE_ERROR:
    570                 case CMD_START_TETHERING_ERROR:
    571                 case CMD_STOP_TETHERING_ERROR:
    572                 case CMD_SET_DNS_FORWARDERS_ERROR:
    573                     mLastError = ConnectivityManager.TETHER_ERROR_MASTER_ERROR;
    574                     transitionTo(mInitialState);
    575                     break;
    576                 default:
    577                     return false;
    578             }
    579             return true;
    580         }
    581     }
    582 
    583     // Handling errors in BaseServingState.enter() by transitioning is
    584     // problematic because transitioning during a multi-state jump yields
    585     // a Log.wtf(). Ultimately, there should be only one ServingState,
    586     // and forwarding and NAT rules should be handled by a coordinating
    587     // functional element outside of TetherInterfaceStateMachine.
    588     class LocalHotspotState extends BaseServingState {
    589         @Override
    590         public void enter() {
    591             super.enter();
    592             if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
    593                 transitionTo(mInitialState);
    594             }
    595 
    596             if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
    597             sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY);
    598         }
    599 
    600         @Override
    601         public boolean processMessage(Message message) {
    602             if (super.processMessage(message)) return true;
    603 
    604             logMessage(this, message.what);
    605             switch (message.what) {
    606                 case CMD_TETHER_REQUESTED:
    607                     mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
    608                     break;
    609                 case CMD_TETHER_CONNECTION_CHANGED:
    610                     // Ignored in local hotspot state.
    611                     break;
    612                 default:
    613                     return false;
    614             }
    615             return true;
    616         }
    617     }
    618 
    619     // Handling errors in BaseServingState.enter() by transitioning is
    620     // problematic because transitioning during a multi-state jump yields
    621     // a Log.wtf(). Ultimately, there should be only one ServingState,
    622     // and forwarding and NAT rules should be handled by a coordinating
    623     // functional element outside of TetherInterfaceStateMachine.
    624     class TetheredState extends BaseServingState {
    625         @Override
    626         public void enter() {
    627             super.enter();
    628             if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
    629                 transitionTo(mInitialState);
    630             }
    631 
    632             if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
    633             sendInterfaceState(IControlsTethering.STATE_TETHERED);
    634         }
    635 
    636         @Override
    637         public void exit() {
    638             cleanupUpstream();
    639             super.exit();
    640         }
    641 
    642         private void cleanupUpstream() {
    643             if (mUpstreamIfaceSet == null) return;
    644 
    645             for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
    646             mUpstreamIfaceSet = null;
    647         }
    648 
    649         private void cleanupUpstreamInterface(String upstreamIface) {
    650             // Note that we don't care about errors here.
    651             // Sometimes interfaces are gone before we get
    652             // to remove their rules, which generates errors.
    653             // Just do the best we can.
    654             try {
    655                 // About to tear down NAT; gather remaining statistics.
    656                 mStatsService.forceUpdate();
    657             } catch (Exception e) {
    658                 if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
    659             }
    660             try {
    661                 mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface);
    662             } catch (Exception e) {
    663                 if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString());
    664             }
    665             try {
    666                 mNMService.disableNat(mIfaceName, upstreamIface);
    667             } catch (Exception e) {
    668                 if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
    669             }
    670         }
    671 
    672         @Override
    673         public boolean processMessage(Message message) {
    674             if (super.processMessage(message)) return true;
    675 
    676             logMessage(this, message.what);
    677             switch (message.what) {
    678                 case CMD_TETHER_REQUESTED:
    679                     mLog.e("CMD_TETHER_REQUESTED while already tethering.");
    680                     break;
    681                 case CMD_TETHER_CONNECTION_CHANGED:
    682                     final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
    683                     if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
    684                         if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
    685                         break;
    686                     }
    687 
    688                     if (newUpstreamIfaceSet == null) {
    689                         cleanupUpstream();
    690                         break;
    691                     }
    692 
    693                     for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
    694                         cleanupUpstreamInterface(removed);
    695                     }
    696 
    697                     final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
    698                     // This makes the call to cleanupUpstream() in the error
    699                     // path for any interface neatly cleanup all the interfaces.
    700                     mUpstreamIfaceSet = newUpstreamIfaceSet;
    701 
    702                     for (String ifname : added) {
    703                         try {
    704                             mNMService.enableNat(mIfaceName, ifname);
    705                             mNMService.startInterfaceForwarding(mIfaceName, ifname);
    706                         } catch (Exception e) {
    707                             mLog.e("Exception enabling NAT: " + e);
    708                             cleanupUpstream();
    709                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
    710                             transitionTo(mInitialState);
    711                             return true;
    712                         }
    713                     }
    714                     break;
    715                 default:
    716                     return false;
    717             }
    718             return true;
    719         }
    720 
    721         private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
    722             if (mUpstreamIfaceSet == null && newIfaces == null) return true;
    723             if (mUpstreamIfaceSet != null && newIfaces != null) {
    724                 return mUpstreamIfaceSet.equals(newIfaces);
    725             }
    726             return false;
    727         }
    728 
    729         private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
    730             if (mUpstreamIfaceSet == null) return new HashSet<>();
    731 
    732             final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
    733             removed.removeAll(newIfaces.ifnames);
    734             return removed;
    735         }
    736 
    737         private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
    738             final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
    739             if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
    740             return added;
    741         }
    742     }
    743 
    744     /**
    745      * This state is terminal for the per interface state machine.  At this
    746      * point, the master state machine should have removed this interface
    747      * specific state machine from its list of possible recipients of
    748      * tethering requests.  The state machine itself will hang around until
    749      * the garbage collector finds it.
    750      */
    751     class UnavailableState extends State {
    752         @Override
    753         public void enter() {
    754             mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
    755             sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE);
    756         }
    757     }
    758 
    759     // Accumulate routes representing "prefixes to be assigned to the local
    760     // interface", for subsequent modification of local_network routing.
    761     private static ArrayList<RouteInfo> getLocalRoutesFor(
    762             String ifname, HashSet<IpPrefix> prefixes) {
    763         final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
    764         for (IpPrefix ipp : prefixes) {
    765             localRoutes.add(new RouteInfo(ipp, null, ifname));
    766         }
    767         return localRoutes;
    768     }
    769 
    770     // Given a prefix like 2001:db8::/64 return an address like 2001:db8::1.
    771     private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
    772         final byte[] dnsBytes = localPrefix.getRawAddress();
    773         dnsBytes[dnsBytes.length - 1] = getRandomSanitizedByte(DOUG_ADAMS, asByte(0), asByte(1));
    774         try {
    775             return Inet6Address.getByAddress(null, dnsBytes, 0);
    776         } catch (UnknownHostException e) {
    777             Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
    778             return null;
    779         }
    780     }
    781 
    782     private static byte getRandomSanitizedByte(byte dflt, byte... excluded) {
    783         final byte random = (byte) (new Random()).nextInt();
    784         for (int value : excluded) {
    785             if (random == value) return dflt;
    786         }
    787         return random;
    788     }
    789 }
    790