Files
frameworks_base/services/java/com/android/server/connectivity/Tethering.java
Mike Lockwood 1cb3cb1a94 Tethering: Use battery changed broadcast instead of UMS broadcast to determine
when USB is connected and disconnected.

This is needed because the UMS broadcasts will not be sent when UMSis disabled.

Change-Id: I4488fee2c19ac9c901596d1430ea371df97e4325
Signed-off-by: Mike Lockwood <lockwood@android.com>
2010-02-26 18:00:58 -05:00

1593 lines
64 KiB
Java

/*
* Copyright (C) 2010 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;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
import android.os.BatteryManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import com.android.internal.telephony.Phone;
import com.android.internal.util.HierarchicalState;
import com.android.internal.util.HierarchicalStateMachine;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/**
* @hide
*
* Timeout
* TODO - review error states - they currently are dead-ends with no recovery possible
*
* TODO - look for parent classes and code sharing
*/
public class Tethering extends INetworkManagementEventObserver.Stub {
private Notification mTetheringNotification;
private Context mContext;
private final String TAG = "Tethering";
private boolean mPlaySounds = false;
// TODO - remove both of these - should be part of interface inspection/selection stuff
private String[] mTetherableUsbRegexs;
private String[] mTetherableWifiRegexs;
private HashMap<String, TetherInterfaceSM> mIfaces;
private ArrayList<String> mActiveTtys;
private BroadcastReceiver mStateReceiver;
private static final String USB_NEAR_IFACE_ADDR = "169.254.2.1";
private String[] mDhcpRange;
private static final String DHCP_DEFAULT_RANGE_START = "169.254.2.10";
private static final String DHCP_DEFAULT_RANGE_STOP = "169.254.2.64";
private String[] mDnsServers;
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
private String mUpstreamIfaceName;
HierarchicalStateMachine mTetherMasterSM;
public Tethering(Context context) {
Log.d(TAG, "Tethering starting");
mContext = context;
// register for notifications from NetworkManagement Service
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
service.registerObserver(this);
} catch (RemoteException e) {
Log.e(TAG, "Error registering observer :" + e);
}
mIfaces = new HashMap<String, TetherInterfaceSM>();
mActiveTtys = new ArrayList<String>();
mTetherMasterSM = new TetherMasterSM("TetherMaster");
mTetherMasterSM.start();
// TODO - remove this hack after real USB connections are detected.
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
mStateReceiver = new StateReceiver();
mContext.registerReceiver(mStateReceiver, filter);
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
if (mDhcpRange.length == 0) {
mDhcpRange = new String[2];
mDhcpRange[0] = DHCP_DEFAULT_RANGE_START;
mDhcpRange[1] = DHCP_DEFAULT_RANGE_STOP;
} else if(mDhcpRange.length == 1) {
String[] tmp = new String[2];
tmp[0] = mDhcpRange[0];
tmp[1] = new String("");
mDhcpRange = tmp;
}
mTetherableUsbRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_usb_regexs);
mTetherableWifiRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_wifi_regexs);
// TODO - remove and rely on real notifications of the current iface
mDnsServers = new String[2];
mDnsServers[0] = DNS_DEFAULT_SERVER1;
mDnsServers[1] = DNS_DEFAULT_SERVER2;
mUpstreamIfaceName = "rmnet0";
}
public void interfaceLinkStatusChanged(String iface, boolean link) {
Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
boolean found = false;
boolean usb = false;
for (String regex : mTetherableWifiRegexs) {
if (iface.matches(regex)) {
found = true;
break;
}
}
for (String regex: mTetherableUsbRegexs) {
if (iface.matches(regex)) {
found = true;
usb = true;
break;
}
}
if (found == false) return;
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (link) {
if (sm == null) {
sm = new TetherInterfaceSM(iface, usb);
mIfaces.put(iface, sm);
sm.start();
}
} else {
if (sm != null) {
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN));
mIfaces.remove(iface);
}
}
}
}
public void interfaceAdded(String iface) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
boolean found = false;
boolean usb = false;
for (String regex : mTetherableWifiRegexs) {
if (iface.matches(regex)) {
found = true;
break;
}
}
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) {
found = true;
usb = true;
break;
}
}
if (found == false) {
Log.d(TAG, iface + " is not a tetherable iface, ignoring");
return;
}
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
return;
}
sm = new TetherInterfaceSM(iface, usb);
mIfaces.put(iface, sm);
sm.start();
}
Log.d(TAG, "interfaceAdded :" + iface);
}
public void interfaceRemoved(String iface) {
synchronized (mIfaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm == null) {
Log.e(TAG, "attempting to remove unknown iface (" + iface + "), ignoring");
return;
}
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN));
mIfaces.remove(iface);
}
}
public boolean tether(String iface) {
Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
synchronized (mIfaces) {
sm = mIfaces.get(iface);
}
if (sm == null) {
Log.e(TAG, "Tried to Tether an unknown iface :" + iface + ", ignoring");
return false;
}
if (sm.isErrored()) {
Log.e(TAG, "Tried to Tether to an errored iface :" + iface + ", ignoring");
return false;
}
if (!sm.isAvailable()) {
Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
return false;
}
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_REQUESTED));
return true;
}
public boolean untether(String iface) {
Log.d(TAG, "Untethering " + iface);
TetherInterfaceSM sm = null;
synchronized (mIfaces) {
sm = mIfaces.get(iface);
}
if (sm == null) {
Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
return false;
}
if (sm.isErrored()) {
Log.e(TAG, "Tried to Untethered an errored iface :" + iface + ", ignoring");
return false;
}
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_UNREQUESTED));
return true;
}
private void sendTetherStateChangedBroadcast() {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
if (!service.isTetheringSupported()) return;
} catch (RemoteException e) {
return;
}
ArrayList<String> availableList = new ArrayList<String>();
ArrayList<String> activeList = new ArrayList<String>();
ArrayList<String> erroredList = new ArrayList<String>();
synchronized (mIfaces) {
Set ifaces = mIfaces.keySet();
for (Object iface : ifaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
if(sm.isErrored()) {
erroredList.add((String)iface);
} else if (sm.isAvailable()) {
availableList.add((String)iface);
} else if (sm.isTethered()) {
activeList.add((String)iface);
}
}
}
}
Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER,
availableList);
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER, activeList);
broadcast.putStringArrayListExtra(ConnectivityManager.EXTRA_ERRORED_TETHER,
erroredList);
mContext.sendStickyBroadcast(broadcast);
Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " +
activeList.size() + ", " + erroredList.size());
// check if we need to send a USB notification
// Check if the user wants to be bothered
boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.TETHER_NOTIFY, 0) == 1);
for (Object o : activeList) {
String s = (String)o;
for (Object regexObject : mTetherableUsbRegexs) {
if (s.matches((String)regexObject)) {
showTetheredNotification();
return;
}
}
}
if (tellUser) {
for (Object o : availableList) {
String s = (String)o;
for (String match : mTetherableUsbRegexs) {
if (s.matches(match)) {
showTetherAvailableNotification();
return;
}
}
}
}
clearNotification();
}
private void showTetherAvailableNotification() {
NotificationManager notificationManager = (NotificationManager)mContext.
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
Intent intent = new Intent();
intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
Resources r = Resources.getSystem();
CharSequence title = r.getText(com.android.internal.R.string.
tether_available_notification_title);
CharSequence message = r.getText(com.android.internal.R.string.
tether_available_notification_message);
if(mTetheringNotification == null) {
mTetheringNotification = new Notification();
mTetheringNotification.when = 0;
}
mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
boolean playSounds = false;
//playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
if (playSounds) {
mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
} else {
mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
}
mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
mTetheringNotification.tickerText = title;
mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
}
private void showTetheredNotification() {
NotificationManager notificationManager = (NotificationManager)mContext.
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
Intent intent = new Intent();
intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
Resources r = Resources.getSystem();
CharSequence title = r.getText(com.android.internal.R.string.
tether_stop_notification_title);
CharSequence message = r.getText(com.android.internal.R.string.
tether_stop_notification_message);
if(mTetheringNotification == null) {
mTetheringNotification = new Notification();
mTetheringNotification.when = 0;
}
mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
boolean playSounds = false;
//playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
if (playSounds) {
mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
} else {
mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
}
mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
mTetheringNotification.tickerText = title;
mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
}
private void clearNotification() {
NotificationManager notificationManager = (NotificationManager)mContext.
getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && mTetheringNotification != null) {
notificationManager.cancel(mTetheringNotification.icon);
mTetheringNotification = null;
}
}
private class StateReceiver extends BroadcastReceiver {
public void onReceive(Context content, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
boolean usbConnected = (intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1)
== BatteryManager.BATTERY_PLUGGED_USB);
Tethering.this.enableUsbIfaces(usbConnected); // add or remove them
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
NetworkInfo info = service.getNetworkInfo(ConnectivityManager.TYPE_MOBILE_DUN);
int msg;
if (info != null && info.isConnected() == true) {
msg = TetherMasterSM.CMD_CELL_DUN_ENABLED;
} else {
msg = TetherMasterSM.CMD_CELL_DUN_DISABLED;
}
mTetherMasterSM.sendMessage(mTetherMasterSM.obtainMessage(msg));
} catch (RemoteException e) {}
}
}
}
// used on cable insert/remove
private void enableUsbIfaces(boolean enable) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
String[] ifaces = new String[0];
try {
ifaces = service.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return;
}
for (String iface : ifaces) {
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) {
if (enable) {
interfaceAdded(iface);
} else {
interfaceRemoved(iface);
}
}
}
}
}
// toggled when we enter/leave the fully teathered state
private boolean enableUsbRndis(boolean enabled) {
Log.d(TAG, "enableUsbRndis(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
if (enabled) {
synchronized (this) {
if (!service.isUsbRNDISStarted()) {
service.startUsbRNDIS();
}
}
} else {
if (service.isUsbRNDISStarted()) {
service.stopUsbRNDIS();
}
}
} catch (Exception e) {
Log.e(TAG, "Error toggling usb RNDIS :" + e);
return false;
}
return true;
}
// configured when we start tethering and unconfig'd on error or conclusion
private boolean configureUsbIface(boolean enabled) {
Log.d(TAG, "configureUsbIface(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
// bring toggle the interfaces
String[] ifaces = new String[0];
try {
ifaces = service.listInterfaces();
} catch (Exception e) {
Log.e(TAG, "Error listing Interfaces :" + e);
return false;
}
for (String iface : ifaces) {
for (String regex : mTetherableUsbRegexs) {
if (iface.matches(regex)) {
InterfaceConfiguration ifcg = null;
try {
ifcg = service.getInterfaceConfig(iface);
if (ifcg != null) {
ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1;
ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0;
if (enabled) {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
} else {
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
// TODO - clean this up - maybe a better regex?
ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" running", "");
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running ","");
ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running","");
}
service.setInterfaceConfig(iface, ifcg);
}
} catch (Exception e) {
Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
return false;
}
}
}
}
return true;
}
private void handleTtyConnect() {
Log.d(TAG, "handleTtyConnect");
// for each of the available Tty not already supported by a ppp session,
// create a ppp session
// TODO - this should be data-driven rather than hard coded.
String[] allowedTtys = new String[1];
allowedTtys[0] = new String("ttyGS0");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
String[] availableTtys;
try {
availableTtys = service.listTtys();
} catch (RemoteException e) {
Log.e(TAG, "error listing Ttys :" + e);
return;
}
for (String tty : availableTtys) {
for (String pattern : allowedTtys) {
if (tty.matches(pattern)) {
synchronized (this) {
if (!mActiveTtys.contains(tty)) {
// TODO - don't hardcode this
try {
// local, remote, dns
service.attachPppd(tty, "169.254.1.128", "169.254.1.1",
"169.254.1.128", "0.0.0.0");
} catch (Exception e) {
Log.e(TAG, "error calling attachPppd: " + e);
return;
}
Log.d(TAG, "started Pppd on tty " + tty);
mActiveTtys.add(tty);
// TODO - remove this after we detect the new iface
interfaceAdded("ppp0");
}
}
}
}
}
}
private synchronized void handleTtyDisconnect() {
Log.d(TAG, "handleTtyDisconnect");
// TODO - this should be data-driven rather than hard coded.
String[] allowedTtys = new String[1];
allowedTtys[0] = new String("ttyGS0");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
String[] availableTtys;
try {
availableTtys = service.listTtys();
} catch (RemoteException e) {
Log.e(TAG, "error listing Ttys :" + e);
return;
}
for (String tty : availableTtys) {
for (String pattern : allowedTtys) {
if (tty.matches(pattern)) {
synchronized (this) {
if (mActiveTtys.contains(tty)) {
try {
service.detachPppd(tty);
} catch (Exception e) {
Log.e(TAG, "error calling detachPppd on " + tty + " :" + e);
}
mActiveTtys.remove(tty);
// TODO - remove this after we detect the new iface
interfaceRemoved("ppp0");
return;
}
}
}
}
}
}
public String[] getTetherableUsbRegexs() {
return mTetherableUsbRegexs;
}
public String[] getTetherableWifiRegexs() {
return mTetherableWifiRegexs;
}
public String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mIfaces) {
Set keys = mIfaces.keySet();
for (Object key : keys) {
TetherInterfaceSM sm = mIfaces.get(key);
if (sm.isTethered()) {
list.add((String)key);
}
}
}
String[] retVal = new String[list.size()];
for (int i=0; i < list.size(); i++) {
retVal[i] = list.get(i);
}
return retVal;
}
public String[] getTetherableIfaces() {
ArrayList<String> list = new ArrayList<String>();
synchronized (mIfaces) {
Set keys = mIfaces.keySet();
for (Object key : keys) {
TetherInterfaceSM sm = mIfaces.get(key);
if (sm.isAvailable()) {
list.add((String)key);
}
}
}
String[] retVal = new String[list.size()];
for (int i=0; i < list.size(); i++) {
retVal[i] = list.get(i);
}
return retVal;
}
class TetherInterfaceSM extends HierarchicalStateMachine {
// notification from the master SM that it's in tether mode
static final int CMD_TETHER_MODE_ALIVE = 1;
// notification from the master SM that it's not in tether mode
static final int CMD_TETHER_MODE_DEAD = 2;
// request from the user that it wants to tether
static final int CMD_TETHER_REQUESTED = 3;
// request from the user that it wants to untether
static final int CMD_TETHER_UNREQUESTED = 4;
// notification that this interface is down
static final int CMD_INTERFACE_DOWN = 5;
// notification that this interface is up
static final int CMD_INTERFACE_UP = 6;
// notification from the master SM that it had an error turning on cellular dun
static final int CMD_CELL_DUN_ERROR = 10;
// notification from the master SM that it had trouble enabling IP Forwarding
static final int CMD_IP_FORWARDING_ENABLE_ERROR = 11;
// notification from the master SM that it had trouble disabling IP Forwarding
static final int CMD_IP_FORWARDING_DISABLE_ERROR = 12;
// notification from the master SM that it had trouble staring tethering
static final int CMD_START_TETHERING_ERROR = 13;
// notification from the master SM that it had trouble stopping tethering
static final int CMD_STOP_TETHERING_ERROR = 14;
// notification from the master SM that it had trouble setting the DNS forwarders
static final int CMD_SET_DNS_FORWARDERS_ERROR = 15;
// a mechanism to transition self to error state from an enter function
static final int CMD_TRANSITION_TO_ERROR = 16;
private HierarchicalState mDefaultState;
private HierarchicalState mInitialState;
private HierarchicalState mStartingState;
private HierarchicalState mTetheredState;
private HierarchicalState mMasterTetherErrorState;
private HierarchicalState mTetherInterfaceErrorState;
private HierarchicalState mUntetherInterfaceErrorState;
private HierarchicalState mEnableNatErrorState;
private HierarchicalState mDisableNatErrorState;
private HierarchicalState mUsbConfigurationErrorState;
private HierarchicalState mUnavailableState;
private boolean mAvailable;
private boolean mErrored;
private boolean mTethered;
String mIfaceName;
boolean mUsb;
TetherInterfaceSM(String name, boolean usb) {
super(name);
mIfaceName = name;
mUsb = usb;
mInitialState = new InitialState();
addState(mInitialState);
mStartingState = new StartingState();
addState(mStartingState);
mTetheredState = new TetheredState();
addState(mTetheredState);
mMasterTetherErrorState = new MasterTetherErrorState();
addState(mMasterTetherErrorState);
mTetherInterfaceErrorState = new TetherInterfaceErrorState();
addState(mTetherInterfaceErrorState);
mUntetherInterfaceErrorState = new UntetherInterfaceErrorState();
addState(mUntetherInterfaceErrorState);
mEnableNatErrorState = new EnableNatErrorState();
addState(mEnableNatErrorState);
mDisableNatErrorState = new DisableNatErrorState();
addState(mDisableNatErrorState);
mUsbConfigurationErrorState = new UsbConfigurationErrorState();
addState(mUsbConfigurationErrorState);
mUnavailableState = new UnavailableState();
addState(mUnavailableState);
setInitialState(mInitialState);
}
public String toString() {
String res = new String();
res += mIfaceName + " - ";
HierarchicalState current = getCurrentState();
if (current == mInitialState) res += "InitialState";
if (current == mStartingState) res += "StartingState";
if (current == mTetheredState) res += "TetheredState";
if (current == mMasterTetherErrorState) res += "MasterTetherErrorState";
if (current == mTetherInterfaceErrorState) res += "TetherInterfaceErrorState";
if (current == mUntetherInterfaceErrorState) res += "UntetherInterfaceErrorState";
if (current == mEnableNatErrorState) res += "EnableNatErrorState";
if (current == mDisableNatErrorState) res += "DisableNatErrorState";
if (current == mUsbConfigurationErrorState) res += "UsbConfigurationErrorState";
if (current == mUnavailableState) res += "UnavailableState";
if (mAvailable) res += " - Available";
if (mTethered) res += " - Tethered";
if (mErrored) res += " - ERRORED";
return res;
}
// synchronized between this getter and the following setter
public synchronized boolean isAvailable() {
return mAvailable;
}
private synchronized void setAvailable(boolean available) {
mAvailable = available;
}
// synchronized between this getter and the following setter
public synchronized boolean isTethered() {
return mTethered;
}
private synchronized void setTethered(boolean tethered) {
mTethered = tethered;
}
// synchronized between this getter and the following setter
public synchronized boolean isErrored() {
return mErrored;
}
private void setErrored(boolean errored) {
synchronized (this) {
mErrored = errored;
}
if (errored && mUsb) {
// note everything's been unwound by this point so nothing to do on
// further error..
Tethering.this.configureUsbIface(false);
}
}
class InitialState extends HierarchicalState {
@Override
public void enter() {
setAvailable(true);
setTethered(false);
setErrored(false);
sendTetherStateChangedBroadcast();
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "InitialState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
Message m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_REQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
transitionTo(mStartingState);
break;
case CMD_INTERFACE_DOWN:
transitionTo(mUnavailableState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class StartingState extends HierarchicalState {
@Override
public void enter() {
setAvailable(false);
if (mUsb) {
if (!Tethering.this.configureUsbIface(true)) {
Message m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
m = obtainMessage(CMD_TRANSITION_TO_ERROR);
m.obj = mUsbConfigurationErrorState;
sendMessageAtFrontOfQueue(m);
return;
}
}
sendTetherStateChangedBroadcast();
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "StartingState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
// maybe a parent class?
case CMD_TETHER_UNREQUESTED:
Message m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
transitionTo(mUsbConfigurationErrorState);
break;
}
}
transitionTo(mInitialState);
break;
case CMD_TETHER_MODE_ALIVE:
transitionTo(mTetheredState);
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:
transitionTo(mMasterTetherErrorState);
break;
case CMD_INTERFACE_DOWN:
m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
transitionTo(mUnavailableState);
break;
case CMD_TRANSITION_TO_ERROR:
HierarchicalState s = (HierarchicalState)(message.obj);
transitionTo(s);
break;
default:
retValue = false;
}
return retValue;
}
}
class TetheredState extends HierarchicalState {
@Override
public void enter() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.tetherInterface(mIfaceName);
} catch (Exception e) {
Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
m.obj = mTetherInterfaceErrorState;
sendMessageAtFrontOfQueue(m);
return;
}
try {
service.enableNat(mIfaceName, mUpstreamIfaceName);
} catch (Exception e) {
Message m = obtainMessage(CMD_TRANSITION_TO_ERROR);
m.obj = mEnableNatErrorState;
sendMessageAtFrontOfQueue(m);
return;
}
if (mUsb) Tethering.this.enableUsbRndis(true);
Log.d(TAG, "Tethered " + mIfaceName);
setAvailable(false);
setTethered(true);
sendTetherStateChangedBroadcast();
}
@Override
public void exit() {
if (mUsb) Tethering.this.enableUsbRndis(false);
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "TetheredState.processMessage what=" + message.what);
boolean retValue = true;
boolean error = false;
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
case CMD_INTERFACE_DOWN:
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.disableNat(mIfaceName, mUpstreamIfaceName);
} catch (Exception e) {
transitionTo(mDisableNatErrorState);
break;
}
try {
service.untetherInterface(mIfaceName);
} catch (Exception e) {
transitionTo(mUntetherInterfaceErrorState);
break;
}
Message m = mTetherMasterSM.obtainMessage(
TetherMasterSM.CMD_TETHER_MODE_UNREQUESTED);
m.obj = TetherInterfaceSM.this;
mTetherMasterSM.sendMessage(m);
if (message.what == CMD_TETHER_UNREQUESTED) {
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
transitionTo(mUsbConfigurationErrorState);
} else {
transitionTo(mInitialState);
}
} else {
transitionTo(mInitialState);
}
} else if (message.what == CMD_INTERFACE_DOWN) {
transitionTo(mUnavailableState);
}
Log.d(TAG, "Untethered " + mIfaceName);
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:
b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
service = INetworkManagementService.Stub.asInterface(b);
try {
service.disableNat(mIfaceName, mUpstreamIfaceName);
} catch (Exception e) {
transitionTo(mDisableNatErrorState);
break;
}
try {
service.untetherInterface(mIfaceName);
} catch (Exception e) {
transitionTo(mUntetherInterfaceErrorState);
break;
}
if (error) {
transitionTo(mMasterTetherErrorState);
break;
}
Log.d(TAG, "Tether lost upstream connection " + mIfaceName);
sendTetherStateChangedBroadcast();
if (mUsb) {
if (!Tethering.this.configureUsbIface(false)) {
transitionTo(mUsbConfigurationErrorState);
break;
}
}
transitionTo(mInitialState);
break;
case CMD_TRANSITION_TO_ERROR:
HierarchicalState s = (HierarchicalState)(message.obj);
transitionTo(s);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class UnavailableState extends HierarchicalState {
@Override
public void enter() {
setAvailable(false);
setErrored(false);
setTethered(false);
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;
}
}
class ErrorState extends HierarchicalState {
int mErrorNotification;
@Override
public boolean processMessage(Message message) {
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_REQUESTED:
sendTetherStateChangedBroadcast();
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class MasterTetherErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error in Master Tether state " + mIfaceName);
setAvailable(false);
setErrored(true);
sendTetherStateChangedBroadcast();
}
}
class TetherInterfaceErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to tether " + mIfaceName);
setAvailable(false);
setErrored(true);
sendTetherStateChangedBroadcast();
}
}
class UntetherInterfaceErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to untether " + mIfaceName);
setAvailable(false);
setErrored(true);
sendTetherStateChangedBroadcast();
}
}
class EnableNatErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to enable NAT " + mIfaceName);
setAvailable(false);
setErrored(true);
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
service.untetherInterface(mIfaceName);
} catch (Exception e) {}
sendTetherStateChangedBroadcast();
}
}
class DisableNatErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to disable NAT " + mIfaceName);
setAvailable(false);
setErrored(true);
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
try {
service.untetherInterface(mIfaceName);
} catch (Exception e) {}
sendTetherStateChangedBroadcast();
}
}
class UsbConfigurationErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to configure USB " + mIfaceName);
setAvailable(false);
setErrored(true);
}
}
}
class TetherMasterSM extends HierarchicalStateMachine {
// an interface SM has requested Tethering
static final int CMD_TETHER_MODE_REQUESTED = 1;
// an interface SM has unrequested Tethering
static final int CMD_TETHER_MODE_UNREQUESTED = 2;
// we received notice that the cellular DUN connection is up
static final int CMD_CELL_DUN_ENABLED = 3;
// we received notice that the cellular DUN connection is down
static final int CMD_CELL_DUN_DISABLED = 4;
// we timed out on a cellular DUN toggle
static final int CMD_CELL_DUN_TIMEOUT = 5;
// it's time to renew our cellular DUN reservation
static final int CMD_CELL_DUN_RENEW = 6;
// This indicates what a timeout event relates to. A state that
// sends itself a delayed timeout event and handles incoming timeout events
// should inc this when it is entered and whenever it sends a new timeout event.
// We do not flush the old ones.
private int mSequenceNumber;
private HierarchicalState mInitialState;
private HierarchicalState mCellDunRequestedState;
private HierarchicalState mCellDunAliveState;
private HierarchicalState mTetherModeAliveState;
private HierarchicalState mCellDunErrorState;
private HierarchicalState mSetIpForwardingEnabledErrorState;
private HierarchicalState mSetIpForwardingDisabledErrorState;
private HierarchicalState mStartTetheringErrorState;
private HierarchicalState mStopTetheringErrorState;
private HierarchicalState mSetDnsForwardersErrorState;
private ArrayList mNotifyList;
private static final int CELL_DUN_TIMEOUT_MS = 45000;
private static final int CELL_DISABLE_DUN_TIMEOUT_MS = 3000;
private static final int CELL_DUN_RENEW_MS = 40000;
TetherMasterSM(String name) {
super(name);
//Add states
mInitialState = new InitialState();
addState(mInitialState);
mCellDunRequestedState = new CellDunRequestedState();
addState(mCellDunRequestedState);
mCellDunAliveState = new CellDunAliveState();
addState(mCellDunAliveState);
mTetherModeAliveState = new TetherModeAliveState();
addState(mTetherModeAliveState);
mCellDunErrorState = new CellDunErrorState();
addState(mCellDunErrorState);
mSetIpForwardingEnabledErrorState = new SetIpForwardingEnabledErrorState();
addState(mSetIpForwardingEnabledErrorState);
mSetIpForwardingDisabledErrorState = new SetIpForwardingDisabledErrorState();
addState(mSetIpForwardingDisabledErrorState);
mStartTetheringErrorState = new StartTetheringErrorState();
addState(mStartTetheringErrorState);
mStopTetheringErrorState = new StopTetheringErrorState();
addState(mStopTetheringErrorState);
mSetDnsForwardersErrorState = new SetDnsForwardersErrorState();
addState(mSetDnsForwardersErrorState);
mNotifyList = new ArrayList();
setInitialState(mInitialState);
}
class TetherMasterUtilState extends HierarchicalState {
@Override
public boolean processMessage(Message m) {
return false;
}
public int turnOnMobileDun() {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
int retValue = Phone.APN_REQUEST_FAILED;
try {
retValue = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
Phone.FEATURE_ENABLE_DUN, new Binder());
} catch (Exception e) {
}
return retValue;
}
public boolean turnOffMobileDun() {
IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service =
IConnectivityManager.Stub.asInterface(b);
try {
service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
Phone.FEATURE_ENABLE_DUN);
} catch (Exception e) {
return false;
}
return true;
}
public boolean turnOnMasterTetherSettings() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.setIpForwardingEnabled(true);
} catch (Exception e) {
transitionTo(mSetIpForwardingEnabledErrorState);
return false;
}
try {
service.startTethering(mDhcpRange[0], mDhcpRange[1]);
} catch (Exception e) {
transitionTo(mStartTetheringErrorState);
return false;
}
try {
service.setDnsForwarders(mDnsServers);
} catch (Exception e) {
transitionTo(mSetDnsForwardersErrorState);
return false;
}
transitionTo(mTetherModeAliveState);
return true;
}
public boolean turnOffMasterTetherSettings() {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.stopTethering();
} catch (Exception e) {
transitionTo(mStopTetheringErrorState);
return false;
}
try {
service.setIpForwardingEnabled(false);
} catch (Exception e) {
transitionTo(mSetIpForwardingDisabledErrorState);
return false;
}
transitionTo(mInitialState);
return true;
}
}
class InitialState extends TetherMasterUtilState {
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "MasterInitialState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
Log.d(TAG, "Tether Mode requested by " + who.toString());
mNotifyList.add(who);
transitionTo(mCellDunRequestedState);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
Log.d(TAG, "Tether Mode unrequested by " + who.toString());
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(who);
}
break;
case CMD_CELL_DUN_ENABLED:
transitionTo(mCellDunAliveState);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class CellDunRequestedState extends TetherMasterUtilState {
@Override
public void enter() {
++mSequenceNumber;
int result = turnOnMobileDun();
switch (result) {
case Phone.APN_ALREADY_ACTIVE:
Log.d(TAG, "Dun already active");
sendMessage(obtainMessage(CMD_CELL_DUN_ENABLED));
break;
case Phone.APN_REQUEST_FAILED:
case Phone.APN_TYPE_NOT_AVAILABLE:
Log.d(TAG, "Error bringing up Dun connection");
Message m = obtainMessage(CMD_CELL_DUN_TIMEOUT);
m.arg1 = mSequenceNumber;
sendMessage(m);
break;
case Phone.APN_REQUEST_STARTED:
Log.d(TAG, "Started bringing up Dun connection");
m = obtainMessage(CMD_CELL_DUN_TIMEOUT);
m.arg1 = mSequenceNumber;
sendMessageDelayed(m, CELL_DUN_TIMEOUT_MS);
break;
default:
Log.e(TAG, "Unknown return value from startUsingNetworkFeature " + result);
}
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "CellDunRequestedState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
mNotifyList.add(who);
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
turnOffMobileDun();
transitionTo(mInitialState);
}
}
break;
case CMD_CELL_DUN_ENABLED:
turnOnMasterTetherSettings();
break;
case CMD_CELL_DUN_DISABLED:
break;
case CMD_CELL_DUN_TIMEOUT:
if (message.arg1 == mSequenceNumber) {
transitionTo(mCellDunErrorState);
}
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class CellDunAliveState extends TetherMasterUtilState {
@Override
public void enter() {
Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "CellDunAliveState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
mNotifyList.add(who);
turnOnMasterTetherSettings();
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
turnOffMobileDun();
transitionTo(mInitialState);
}
}
break;
case CMD_CELL_DUN_DISABLED:
transitionTo(mInitialState);
break;
case CMD_CELL_DUN_RENEW:
Log.d(TAG, "renewing dun connection - requeuing for another " +
CELL_DUN_RENEW_MS + "ms");
turnOnMobileDun();
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class TetherModeAliveState extends TetherMasterUtilState {
@Override
public void enter() {
Log.d(TAG, "renewing Dun in " + CELL_DUN_RENEW_MS + "ms");
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
for (Object o : mNotifyList) {
TetherInterfaceSM sm = (TetherInterfaceSM)o;
sm.sendMessage(sm.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
}
}
@Override
public boolean processMessage(Message message) {
Log.d(TAG, "TetherModeAliveState.processMessage what=" + message.what);
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
mNotifyList.add(who);
who.sendMessage(who.obtainMessage(TetherInterfaceSM.CMD_TETHER_MODE_ALIVE));
break;
case CMD_TETHER_MODE_UNREQUESTED:
who = (TetherInterfaceSM)message.obj;
int index = mNotifyList.indexOf(who);
if (index != -1) {
mNotifyList.remove(index);
if (mNotifyList.isEmpty()) {
turnOffMobileDun();
turnOffMasterTetherSettings(); // transitions appropriately
}
}
break;
case CMD_CELL_DUN_DISABLED:
int size = mNotifyList.size();
for (int i = 0; i < size; i++) {
TetherInterfaceSM sm = (TetherInterfaceSM)mNotifyList.get(i);
mNotifyList.remove(i);
sm.sendMessage(sm.obtainMessage(
TetherInterfaceSM.CMD_TETHER_MODE_DEAD));
}
turnOffMasterTetherSettings(); // transitions appropriately
break;
case CMD_CELL_DUN_RENEW:
Log.d(TAG, "renewing dun connection - requeuing for another " +
CELL_DUN_RENEW_MS + "ms");
turnOnMobileDun();
sendMessageDelayed(obtainMessage(CMD_CELL_DUN_RENEW), CELL_DUN_RENEW_MS);
break;
default:
retValue = false;
break;
}
return retValue;
}
}
class ErrorState extends HierarchicalState {
int mErrorNotification;
@Override
public boolean processMessage(Message message) {
boolean retValue = true;
switch (message.what) {
case CMD_TETHER_MODE_REQUESTED:
TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
who.sendMessage(who.obtainMessage(mErrorNotification));
break;
default:
retValue = false;
}
return retValue;
}
void notify(int msgType) {
mErrorNotification = msgType;
for (Object o : mNotifyList) {
TetherInterfaceSM sm = (TetherInterfaceSM)o;
sm.sendMessage(sm.obtainMessage(msgType));
}
}
}
class CellDunErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error trying to enable Cell DUN");
notify(TetherInterfaceSM.CMD_CELL_DUN_ERROR);
}
}
class SetIpForwardingEnabledErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingEnabled");
notify(TetherInterfaceSM.CMD_IP_FORWARDING_ENABLE_ERROR);
}
}
class SetIpForwardingDisabledErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error in setIpForwardingDisabled");
notify(TetherInterfaceSM.CMD_IP_FORWARDING_DISABLE_ERROR);
}
}
class StartTetheringErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error in startTethering");
notify(TetherInterfaceSM.CMD_START_TETHERING_ERROR);
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
class StopTetheringErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error in stopTethering");
notify(TetherInterfaceSM.CMD_STOP_TETHERING_ERROR);
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
class SetDnsForwardersErrorState extends ErrorState {
@Override
public void enter() {
Log.e(TAG, "Error in setDnsForwarders");
notify(TetherInterfaceSM.CMD_SET_DNS_FORWARDERS_ERROR);
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service =
INetworkManagementService.Stub.asInterface(b);
try {
service.stopTethering();
} catch (Exception e) {}
try {
service.setIpForwardingEnabled(false);
} catch (Exception e) {}
}
}
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump ConnectivityService.Tether " +
"from from pid=" + Binder.getCallingPid() + ", uid=" +
Binder.getCallingUid());
return;
}
pw.println();
pw.println("Tether state:");
synchronized (mIfaces) {
for (Object o : mIfaces.values()) {
pw.println(" "+o.toString());
}
}
pw.println();
return;
}
}