Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3f93b02 authored by Christopher Wiley's avatar Christopher Wiley
Browse files

Extract TetherInterfaceSM to its own class.

Attempt to keep all existing logic in place, except:

 + Marked a constructor as public, rather than default visible.
 + Added TAG, DBG, VDBG, and decoder ringer statics.
 + Moved static constants related to USB IPs into TetherInterfaceSM.

Bug: 28833951
Test: WiFi Tethering works on angler.

Change-Id: Id961220a9045832354cfe7381e5e9c0d8f54bf90
parent 9a509ca7
Loading
Loading
Loading
Loading
+1 −439
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.TetherInterfaceSM;
import com.android.server.net.BaseNetworkObserver;

import java.io.FileDescriptor;
@@ -135,9 +136,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
    private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
            .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));

    private static final String USB_NEAR_IFACE_ADDR      = "192.168.42.129";
    private static final int USB_PREFIX_LENGTH        = 24;

    // USB is  192.168.42.1 and 255.255.255.0
    // Wifi is 192.168.43.1 and 255.255.255.0
    // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
@@ -980,442 +978,6 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
        }
    }

    /**
     * @hide
     *
     * Tracks the eligibility of a given network interface for tethering.
     */
    public static class TetherInterfaceSM extends StateMachine {
        private static final int BASE_IFACE              = Protocol.BASE_TETHERING + 100;
        // notification from the master SM that it's not in tether mode
        static final int CMD_TETHER_MODE_DEAD            = BASE_IFACE + 1;
        // request from the user that it wants to tether
        static final int CMD_TETHER_REQUESTED            = BASE_IFACE + 2;
        // request from the user that it wants to untether
        static final int CMD_TETHER_UNREQUESTED          = BASE_IFACE + 3;
        // notification that this interface is down
        static final int CMD_INTERFACE_DOWN              = BASE_IFACE + 4;
        // notification that this interface is up
        static final int CMD_INTERFACE_UP                = BASE_IFACE + 5;
        // notification from the master SM that it had an error turning on cellular dun
        static final int CMD_CELL_DUN_ERROR              = BASE_IFACE + 6;
        // notification from the master SM that it had trouble enabling IP Forwarding
        static final int CMD_IP_FORWARDING_ENABLE_ERROR  = BASE_IFACE + 7;
        // notification from the master SM that it had trouble disabling IP Forwarding
        static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
        // notification from the master SM that it had trouble starting tethering
        static final int CMD_START_TETHERING_ERROR       = BASE_IFACE + 9;
        // notification from the master SM that it had trouble stopping tethering
        static final int CMD_STOP_TETHERING_ERROR        = BASE_IFACE + 10;
        // notification from the master SM that it had trouble setting the DNS forwarders
        static final int CMD_SET_DNS_FORWARDERS_ERROR    = BASE_IFACE + 11;
        // the upstream connection has changed
        static final int CMD_TETHER_CONNECTION_CHANGED   = BASE_IFACE + 12;

        private final State mInitialState;
        private final State mStartingState;
        private final State mTetheredState;
        private final State mUnavailableState;

        private final INetworkManagementService mNMService;
        private final INetworkStatsService mStatsService;
        private final IControlsTethering mTetherController;

        private final boolean mUsb;
        private final String mIfaceName;

        private final Object mMutex;  // Protects the fields below.
        private boolean mAvailable;
        private boolean mTethered;
        private int mLastError;
        private String mMyUpstreamIfaceName;  // may change over time

        TetherInterfaceSM(String ifaceName, Looper looper, boolean usb, Object mutex,
                        INetworkManagementService nMService, INetworkStatsService statsService,
                        IControlsTethering tetherController) {
            super(ifaceName, looper);
            mNMService = nMService;
            mStatsService = statsService;
            mTetherController = tetherController;
            mIfaceName = ifaceName;
            mUsb = usb;
            mMutex = mutex;
            setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);

            mInitialState = new InitialState();
            addState(mInitialState);
            mStartingState = new StartingState();
            addState(mStartingState);
            mTetheredState = new TetheredState();
            addState(mTetheredState);
            mUnavailableState = new UnavailableState();
            addState(mUnavailableState);

            setInitialState(mInitialState);
        }

        @Override
        public String toString() {
            String res = new String();
            res += mIfaceName + " - ";
            IState current = getCurrentState();
            if (current == mInitialState) res += "InitialState";
            if (current == mStartingState) res += "StartingState";
            if (current == mTetheredState) res += "TetheredState";
            if (current == mUnavailableState) res += "UnavailableState";
            if (isAvailable()) res += " - Available";
            if (isTethered()) res += " - Tethered";
            res += " - lastError =" + getLastError();
            return res;
        }

        public int getLastError() {
            synchronized (mMutex) {
                return mLastError;
            }
        }

        private void setLastError(int error) {
            synchronized (mMutex) {
                mLastError = error;

                if (isErrored()) {
                    if (mUsb) {
                        // note everything's been unwound by this point so nothing to do on
                        // further error..
                        configureUsbIface(false, mIfaceName);
                    }
                }
            }
        }

        public boolean isAvailable() {
            synchronized (mMutex) {
                return mAvailable;
            }
        }

        private void setAvailable(boolean available) {
            synchronized (mMutex) {
                mAvailable = available;
            }
        }

        public boolean isTethered() {
            synchronized (mMutex) {
                return mTethered;
            }
        }

        private void setTethered(boolean tethered) {
            synchronized (mMutex) {
                mTethered = tethered;
            }
        }

        public boolean isErrored() {
            synchronized (mMutex) {
                return (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR);
            }
        }

        // configured when we start tethering and unconfig'd on error or conclusion
        private boolean configureUsbIface(boolean enabled, String iface) {
            if (VDBG) Log.d(TAG, "configureUsbIface(" + enabled + ")");

            InterfaceConfiguration ifcg = null;
            try {
                ifcg = mNMService.getInterfaceConfig(iface);
                if (ifcg != null) {
                    InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
                    ifcg.setLinkAddress(new LinkAddress(addr, USB_PREFIX_LENGTH));
                    if (enabled) {
                        ifcg.setInterfaceUp();
                    } else {
                        ifcg.setInterfaceDown();
                    }
                    ifcg.clearFlag("running");
                    mNMService.setInterfaceConfig(iface, ifcg);
                }
            } catch (Exception e) {
                Log.e(TAG, "Error configuring interface " + iface, e);
                return false;
            }

            return true;
        }

        private void maybeLogMessage(State state, int what) {
            if (DBG) {
                Log.d(TAG, state.getName() + " got " +
                        sMagicDecoderRing.get(what, Integer.toString(what)));
            }
        }

        class InitialState extends State {
            @Override
            public void enter() {
                setAvailable(true);
                setTethered(false);
                mTetherController.sendTetherStateChangedBroadcast();
            }

            @Override
            public boolean processMessage(Message message) {
                maybeLogMessage(this, message.what);
                boolean retValue = true;
                switch (message.what) {
                    case CMD_TETHER_REQUESTED:
                        setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
                        mTetherController.notifyInterfaceTetheringReadiness(true, TetherInterfaceSM.this);
                        transitionTo(mStartingState);
                        break;
                    case CMD_INTERFACE_DOWN:
                        transitionTo(mUnavailableState);
                        break;
                    default:
                        retValue = false;
                        break;
                }
                return retValue;
            }
        }

        class StartingState extends State {
            @Override
            public void enter() {
                setAvailable(false);
                if (mUsb) {
                    if (!configureUsbIface(true, mIfaceName)) {
                        mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
                        setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);

                        transitionTo(mInitialState);
                        return;
                    }
                }
                mTetherController.sendTetherStateChangedBroadcast();

                // Skipping StartingState
                transitionTo(mTetheredState);
            }
            @Override
            public boolean processMessage(Message message) {
                maybeLogMessage(this, message.what);
                boolean retValue = true;
                switch (message.what) {
                    // maybe a parent class?
                    case CMD_TETHER_UNREQUESTED:
                        mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
                        if (mUsb) {
                            if (!configureUsbIface(false, mIfaceName)) {
                                setLastErrorAndTransitionToInitialState(
                                    ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                                break;
                            }
                        }
                        transitionTo(mInitialState);
                        break;
                    case CMD_CELL_DUN_ERROR:
                    case CMD_IP_FORWARDING_ENABLE_ERROR:
                    case CMD_IP_FORWARDING_DISABLE_ERROR:
                    case CMD_START_TETHERING_ERROR:
                    case CMD_STOP_TETHERING_ERROR:
                    case CMD_SET_DNS_FORWARDERS_ERROR:
                        setLastErrorAndTransitionToInitialState(
                                ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                        break;
                    case CMD_INTERFACE_DOWN:
                        mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
                        transitionTo(mUnavailableState);
                        break;
                    default:
                        retValue = false;
                }
                return retValue;
            }
        }

        class TetheredState extends State {
            @Override
            public void enter() {
                try {
                    mNMService.tetherInterface(mIfaceName);
                } catch (Exception e) {
                    Log.e(TAG, "Error Tethering: " + e.toString());
                    setLastError(ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR);

                    try {
                        mNMService.untetherInterface(mIfaceName);
                    } catch (Exception ee) {
                        Log.e(TAG, "Error untethering after failure!" + ee.toString());
                    }
                    transitionTo(mInitialState);
                    return;
                }
                if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
                setAvailable(false);
                setTethered(true);
                mTetherController.sendTetherStateChangedBroadcast();
            }

            private void cleanupUpstream() {
                if (mMyUpstreamIfaceName != null) {
                    // note that we don't care about errors here.
                    // sometimes interfaces are gone before we get
                    // to remove their rules, which generates errors.
                    // just do the best we can.
                    try {
                        // about to tear down NAT; gather remaining statistics
                        mStatsService.forceUpdate();
                    } catch (Exception e) {
                        if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString());
                    }
                    try {
                        mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName);
                    } catch (Exception e) {
                        if (VDBG) Log.e(
                                TAG, "Exception in removeInterfaceForward: " + e.toString());
                    }
                    try {
                        mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName);
                    } catch (Exception e) {
                        if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString());
                    }
                    mMyUpstreamIfaceName = null;
                }
                return;
            }

            @Override
            public boolean processMessage(Message message) {
                maybeLogMessage(this, message.what);
                boolean retValue = true;
                boolean error = false;
                switch (message.what) {
                    case CMD_TETHER_UNREQUESTED:
                    case CMD_INTERFACE_DOWN:
                        cleanupUpstream();
                        try {
                            mNMService.untetherInterface(mIfaceName);
                        } catch (Exception e) {
                            setLastErrorAndTransitionToInitialState(
                                    ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
                            break;
                        }
                        mTetherController.notifyInterfaceTetheringReadiness(false, TetherInterfaceSM.this);
                        if (message.what == CMD_TETHER_UNREQUESTED) {
                            if (mUsb) {
                                if (!configureUsbIface(false, mIfaceName)) {
                                    setLastError(
                                            ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                                }
                            }
                            transitionTo(mInitialState);
                        } else if (message.what == CMD_INTERFACE_DOWN) {
                            transitionTo(mUnavailableState);
                        }
                        if (DBG) Log.d(TAG, "Untethered " + mIfaceName);
                        break;
                    case CMD_TETHER_CONNECTION_CHANGED:
                        String newUpstreamIfaceName = (String)(message.obj);
                        if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
                                (mMyUpstreamIfaceName != null &&
                                mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
                            if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
                            break;
                        }
                        cleanupUpstream();
                        if (newUpstreamIfaceName != null) {
                            try {
                                mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
                                mNMService.startInterfaceForwarding(mIfaceName,
                                        newUpstreamIfaceName);
                            } catch (Exception e) {
                                Log.e(TAG, "Exception enabling Nat: " + e.toString());
                                try {
                                    mNMService.disableNat(mIfaceName, newUpstreamIfaceName);
                                } catch (Exception ee) {}
                                try {
                                    mNMService.untetherInterface(mIfaceName);
                                } catch (Exception ee) {}

                                setLastError(ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR);
                                transitionTo(mInitialState);
                                return true;
                            }
                        }
                        mMyUpstreamIfaceName = newUpstreamIfaceName;
                        break;
                    case CMD_CELL_DUN_ERROR:
                    case CMD_IP_FORWARDING_ENABLE_ERROR:
                    case CMD_IP_FORWARDING_DISABLE_ERROR:
                    case CMD_START_TETHERING_ERROR:
                    case CMD_STOP_TETHERING_ERROR:
                    case CMD_SET_DNS_FORWARDERS_ERROR:
                        error = true;
                        // fall through
                    case CMD_TETHER_MODE_DEAD:
                        cleanupUpstream();
                        try {
                            mNMService.untetherInterface(mIfaceName);
                        } catch (Exception e) {
                            setLastErrorAndTransitionToInitialState(
                                    ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR);
                            break;
                        }
                        if (error) {
                            setLastErrorAndTransitionToInitialState(
                                    ConnectivityManager.TETHER_ERROR_MASTER_ERROR);
                            break;
                        }
                        if (DBG) Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
                        mTetherController.sendTetherStateChangedBroadcast();
                        if (mUsb) {
                            if (!configureUsbIface(false, mIfaceName)) {
                                setLastError(ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR);
                            }
                        }
                        transitionTo(mInitialState);
                        break;
                    default:
                        retValue = false;
                        break;
                }
                return retValue;
            }
        }

        class UnavailableState extends State {
            @Override
            public void enter() {
                setAvailable(false);
                setLastError(ConnectivityManager.TETHER_ERROR_NO_ERROR);
                setTethered(false);
                mTetherController.sendTetherStateChangedBroadcast();
            }
            @Override
            public boolean processMessage(Message message) {
                boolean retValue = true;
                switch (message.what) {
                    case CMD_INTERFACE_UP:
                        transitionTo(mInitialState);
                        break;
                    default:
                        retValue = false;
                        break;
                }
                return retValue;
            }
        }

        void setLastErrorAndTransitionToInitialState(int error) {
            setLastError(error);
            transitionTo(mInitialState);
        }

    }

    /**
     * A NetworkCallback class that relays information of interest to the
     * tethering master state machine thread for subsequent processing.
+1 −3
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.server.connectivity.tethering;

import com.android.server.connectivity.Tethering;

/**
 * @hide
 *
@@ -25,5 +23,5 @@ import com.android.server.connectivity.Tethering;
 */
public interface IControlsTethering {
    void sendTetherStateChangedBroadcast();
    void notifyInterfaceTetheringReadiness(boolean isReady, Tethering.TetherInterfaceSM who);
    void notifyInterfaceTetheringReadiness(boolean isReady, TetherInterfaceSM who);
}
 No newline at end of file
+483 −0

File added.

Preview size limit exceeded, changes collapsed.