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
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.connectivity.tethering;
|
||||
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.INetworkStatsService;
|
||||
import android.net.InterfaceConfiguration;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.NetworkUtils;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.util.IState;
|
||||
import com.android.internal.util.MessageUtils;
|
||||
import com.android.internal.util.Protocol;
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.StateMachine;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*
|
||||
* Tracks the eligibility of a given network interface for tethering.
|
||||
*/
|
||||
public class TetherInterfaceSM extends StateMachine {
|
||||
private static final String USB_NEAR_IFACE_ADDR = "192.168.42.129";
|
||||
private static final int USB_PREFIX_LENGTH = 24;
|
||||
|
||||
private final static String TAG = "TetherInterfaceSM";
|
||||
private final static boolean DBG = false;
|
||||
private final static boolean VDBG = false;
|
||||
private static final Class[] messageClasses = {
|
||||
TetherInterfaceSM.class
|
||||
};
|
||||
private static final SparseArray<String> sMagicDecoderRing =
|
||||
MessageUtils.findMessageNames(messageClasses);
|
||||
|
||||
private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100;
|
||||
// notification from the master SM that it's not in tether mode
|
||||
public static final int CMD_TETHER_MODE_DEAD = BASE_IFACE + 1;
|
||||
// request from the user that it wants to tether
|
||||
public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2;
|
||||
// request from the user that it wants to untether
|
||||
public static final int CMD_TETHER_UNREQUESTED = BASE_IFACE + 3;
|
||||
// notification that this interface is down
|
||||
public static final int CMD_INTERFACE_DOWN = BASE_IFACE + 4;
|
||||
// notification that this interface is up
|
||||
public static final int CMD_INTERFACE_UP = BASE_IFACE + 5;
|
||||
// notification from the master SM that it had an error turning on cellular dun
|
||||
public static final int CMD_CELL_DUN_ERROR = BASE_IFACE + 6;
|
||||
// notification from the master SM that it had trouble enabling IP Forwarding
|
||||
public static final int CMD_IP_FORWARDING_ENABLE_ERROR = BASE_IFACE + 7;
|
||||
// notification from the master SM that it had trouble disabling IP Forwarding
|
||||
public static final int CMD_IP_FORWARDING_DISABLE_ERROR = BASE_IFACE + 8;
|
||||
// notification from the master SM that it had trouble starting tethering
|
||||
public static final int CMD_START_TETHERING_ERROR = BASE_IFACE + 9;
|
||||
// notification from the master SM that it had trouble stopping tethering
|
||||
public static final int CMD_STOP_TETHERING_ERROR = BASE_IFACE + 10;
|
||||
// notification from the master SM that it had trouble setting the DNS forwarders
|
||||
public static final int CMD_SET_DNS_FORWARDERS_ERROR = BASE_IFACE + 11;
|
||||
// the upstream connection has changed
|
||||
public 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
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user