/* * 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 android.bluetooth; import android.content.Context; import android.net.ConnectivityManager; import android.net.DhcpInfoInternal; import android.net.LinkCapabilities; import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkStateTracker; import android.net.NetworkUtils; import android.os.Handler; import android.os.Message; import android.util.Log; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * This class tracks the data connection associated with Bluetooth * reverse tethering. This is a singleton class and an instance will be * created by ConnectivityService. BluetoothService will call into this * when a reverse tethered connection needs to be activated. * * @hide */ public class BluetoothTetheringDataTracker implements NetworkStateTracker { private static final String NETWORKTYPE = "BLUETOOTH_TETHER"; private static final String TAG = "BluetoothTethering"; private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false); private LinkProperties mLinkProperties; private LinkCapabilities mLinkCapabilities; private NetworkInfo mNetworkInfo; private BluetoothPan mBluetoothPan; private BluetoothDevice mDevice; private static String mIface; /* For sending events to connectivity service handler */ private Handler mCsHandler; private Context mContext; public static BluetoothTetheringDataTracker sInstance; private BluetoothTetheringDataTracker() { mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, ""); mLinkProperties = new LinkProperties(); mLinkCapabilities = new LinkCapabilities(); mNetworkInfo.setIsAvailable(false); setTeardownRequested(false); } public static synchronized BluetoothTetheringDataTracker getInstance() { if (sInstance == null) sInstance = new BluetoothTetheringDataTracker(); return sInstance; } public Object Clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } public void setTeardownRequested(boolean isRequested) { mTeardownRequested.set(isRequested); } public boolean isTeardownRequested() { return mTeardownRequested.get(); } /** * Begin monitoring connectivity */ public void startMonitoring(Context context, Handler target) { mContext = context; mCsHandler = target; BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN); } } private BluetoothProfile.ServiceListener mProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { mBluetoothPan = (BluetoothPan) proxy; } public void onServiceDisconnected(int profile) { mBluetoothPan = null; } }; /** * Disable connectivity to a network * TODO: do away with return value after making MobileDataStateTracker async */ public boolean teardown() { mTeardownRequested.set(true); if (mBluetoothPan != null) { for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) { mBluetoothPan.disconnect(device); } } return true; } /** * Re-enable connectivity to a network after a {@link #teardown()}. */ public boolean reconnect() { mTeardownRequested.set(false); //Ignore return true; } /** * Turn the wireless radio off for a network. * @param turnOn {@code true} to turn the radio on, {@code false} */ public boolean setRadio(boolean turnOn) { return true; } /** * @return true - If are we currently tethered with another device. */ public synchronized boolean isAvailable() { return mNetworkInfo.isAvailable(); } /** * Tells the underlying networking system that the caller wants to * begin using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. * @param feature the name of the feature to be used * @param callingPid the process ID of the process that is issuing this request * @param callingUid the user ID of the process that is issuing this request * @return an integer value representing the outcome of the request. * The interpretation of this value is specific to each networking * implementation+feature combination, except that the value {@code -1} * always indicates failure. * TODO: needs to go away */ public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { return -1; } /** * Tells the underlying networking system that the caller is finished * using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. * @param feature the name of the feature that is no longer needed. * @param callingPid the process ID of the process that is issuing this request * @param callingUid the user ID of the process that is issuing this request * @return an integer value representing the outcome of the request. * The interpretation of this value is specific to each networking * implementation+feature combination, except that the value {@code -1} * always indicates failure. * TODO: needs to go away */ public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { return -1; } @Override public void setUserDataEnable(boolean enabled) { Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")"); } @Override public void setPolicyDataEnable(boolean enabled) { Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")"); } /** * Check if private DNS route is set for the network */ public boolean isPrivateDnsRouteSet() { return mPrivateDnsRouteSet.get(); } /** * Set a flag indicating private DNS route is set */ public void privateDnsRouteSet(boolean enabled) { mPrivateDnsRouteSet.set(enabled); } /** * Fetch NetworkInfo for the network */ public synchronized NetworkInfo getNetworkInfo() { return mNetworkInfo; } /** * Fetch LinkProperties for the network */ public synchronized LinkProperties getLinkProperties() { return new LinkProperties(mLinkProperties); } /** * A capability is an Integer/String pair, the capabilities * are defined in the class LinkSocket#Key. * * @return a copy of this connections capabilities, may be empty but never null. */ public LinkCapabilities getLinkCapabilities() { return new LinkCapabilities(mLinkCapabilities); } /** * Fetch default gateway address for the network */ public int getDefaultGatewayAddr() { return mDefaultGatewayAddr.get(); } /** * Check if default route is set */ public boolean isDefaultRouteSet() { return mDefaultRouteSet.get(); } /** * Set a flag indicating default route is set for the network */ public void defaultRouteSet(boolean enabled) { mDefaultRouteSet.set(enabled); } /** * Return the system properties name associated with the tcp buffer sizes * for this network. */ public String getTcpBufferSizesPropName() { return "net.tcp.buffersize.wifi"; } public synchronized void startReverseTether(String iface, BluetoothDevice device) { mIface = iface; mDevice = device; Thread dhcpThread = new Thread(new Runnable() { public void run() { //TODO(): Add callbacks for failure and success case. //Currently this thread runs independently. DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal(); if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); return; } mLinkProperties = dhcpInfoInternal.makeLinkProperties(); mLinkProperties.setInterfaceName(mIface); mNetworkInfo.setIsAvailable(true); mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); } }); dhcpThread.start(); } public synchronized void stopReverseTether(String iface) { NetworkUtils.stopDhcp(iface); mLinkProperties.clear(); mNetworkInfo.setIsAvailable(false); mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo); msg.sendToTarget(); msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo); msg.sendToTarget(); } public void setDependencyMet(boolean met) { // not supported on this network } }