Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.bluetooth;
     18 
     19 import android.net.BaseNetworkStateTracker;
     20 import android.os.IBinder;
     21 import android.os.ServiceManager;
     22 import android.os.INetworkManagementService;
     23 import android.content.Context;
     24 import android.net.ConnectivityManager;
     25 import android.net.DhcpResults;
     26 import android.net.LinkCapabilities;
     27 import android.net.LinkProperties;
     28 import android.net.NetworkInfo;
     29 import android.net.NetworkInfo.DetailedState;
     30 import android.net.NetworkStateTracker;
     31 import android.net.NetworkUtils;
     32 import android.os.Handler;
     33 import android.os.Looper;
     34 import android.os.Message;
     35 import android.os.Messenger;
     36 import android.text.TextUtils;
     37 import android.util.Log;
     38 import java.net.InterfaceAddress;
     39 import android.net.LinkAddress;
     40 import android.net.RouteInfo;
     41 import java.net.Inet4Address;
     42 import android.os.SystemProperties;
     43 
     44 import com.android.internal.util.AsyncChannel;
     45 
     46 import java.util.concurrent.atomic.AtomicBoolean;
     47 import java.util.concurrent.atomic.AtomicInteger;
     48 import java.util.concurrent.atomic.AtomicReference;
     49 
     50 /**
     51  * This class tracks the data connection associated with Bluetooth
     52  * reverse tethering. This is a singleton class and an instance will be
     53  * created by ConnectivityService. BluetoothService will call into this
     54  * when a reverse tethered connection needs to be activated.
     55  *
     56  * @hide
     57  */
     58 public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker {
     59     private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
     60     private static final String TAG = "BluetoothTethering";
     61     private static final boolean DBG = true;
     62     private static final boolean VDBG = true;
     63 
     64     private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
     65     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
     66     private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
     67     private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
     68 
     69     private final Object mLinkPropertiesLock = new Object();
     70     private final Object mNetworkInfoLock = new Object();
     71 
     72     private BluetoothPan mBluetoothPan;
     73     private static String mRevTetheredIface;
     74     /* For sending events to connectivity service handler */
     75     private Handler mCsHandler;
     76     private static BluetoothTetheringDataTracker sInstance;
     77     private BtdtHandler mBtdtHandler;
     78     private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null);
     79 
     80     private BluetoothTetheringDataTracker() {
     81         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
     82         mLinkProperties = new LinkProperties();
     83         mLinkCapabilities = new LinkCapabilities();
     84 
     85         mNetworkInfo.setIsAvailable(false);
     86         setTeardownRequested(false);
     87     }
     88 
     89     public static synchronized BluetoothTetheringDataTracker getInstance() {
     90         if (sInstance == null) sInstance = new BluetoothTetheringDataTracker();
     91         return sInstance;
     92     }
     93 
     94     public Object Clone() throws CloneNotSupportedException {
     95         throw new CloneNotSupportedException();
     96     }
     97 
     98     public void setTeardownRequested(boolean isRequested) {
     99         mTeardownRequested.set(isRequested);
    100     }
    101 
    102     public boolean isTeardownRequested() {
    103         return mTeardownRequested.get();
    104     }
    105 
    106     /**
    107      * Begin monitoring connectivity
    108      */
    109     public void startMonitoring(Context context, Handler target) {
    110         if (DBG) Log.d(TAG, "startMonitoring: target: " + target);
    111         mContext = context;
    112         mCsHandler = target;
    113         if (VDBG) Log.d(TAG, "startMonitoring: mCsHandler: " + mCsHandler);
    114         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    115         if (adapter != null) {
    116             adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
    117         }
    118         mBtdtHandler = new BtdtHandler(target.getLooper(), this);
    119     }
    120 
    121     private BluetoothProfile.ServiceListener mProfileServiceListener =
    122         new BluetoothProfile.ServiceListener() {
    123         public void onServiceConnected(int profile, BluetoothProfile proxy) {
    124             mBluetoothPan = (BluetoothPan) proxy;
    125         }
    126         public void onServiceDisconnected(int profile) {
    127             mBluetoothPan = null;
    128         }
    129     };
    130 
    131     /**
    132      * Disable connectivity to a network
    133      * TODO: do away with return value after making MobileDataStateTracker async
    134      */
    135     public boolean teardown() {
    136         mTeardownRequested.set(true);
    137         if (mBluetoothPan != null) {
    138             for (BluetoothDevice device: mBluetoothPan.getConnectedDevices()) {
    139                 mBluetoothPan.disconnect(device);
    140             }
    141         }
    142         return true;
    143     }
    144 
    145     @Override
    146     public void captivePortalCheckComplete() {
    147         // not implemented
    148     }
    149 
    150     @Override
    151     public void captivePortalCheckCompleted(boolean isCaptivePortal) {
    152         // not implemented
    153     }
    154 
    155     /**
    156      * Re-enable connectivity to a network after a {@link #teardown()}.
    157      */
    158     public boolean reconnect() {
    159         mTeardownRequested.set(false);
    160         //Ignore
    161         return true;
    162     }
    163 
    164     /**
    165      * Turn the wireless radio off for a network.
    166      * @param turnOn {@code true} to turn the radio on, {@code false}
    167      */
    168     public boolean setRadio(boolean turnOn) {
    169         return true;
    170     }
    171 
    172     /**
    173      * @return true - If are we currently tethered with another device.
    174      */
    175     public synchronized boolean isAvailable() {
    176         return mNetworkInfo.isAvailable();
    177     }
    178 
    179     /**
    180      * Tells the underlying networking system that the caller wants to
    181      * begin using the named feature. The interpretation of {@code feature}
    182      * is completely up to each networking implementation.
    183      * @param feature the name of the feature to be used
    184      * @param callingPid the process ID of the process that is issuing this request
    185      * @param callingUid the user ID of the process that is issuing this request
    186      * @return an integer value representing the outcome of the request.
    187      * The interpretation of this value is specific to each networking
    188      * implementation+feature combination, except that the value {@code -1}
    189      * always indicates failure.
    190      * TODO: needs to go away
    191      */
    192     public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
    193         return -1;
    194     }
    195 
    196     /**
    197      * Tells the underlying networking system that the caller is finished
    198      * using the named feature. The interpretation of {@code feature}
    199      * is completely up to each networking implementation.
    200      * @param feature the name of the feature that is no longer needed.
    201      * @param callingPid the process ID of the process that is issuing this request
    202      * @param callingUid the user ID of the process that is issuing this request
    203      * @return an integer value representing the outcome of the request.
    204      * The interpretation of this value is specific to each networking
    205      * implementation+feature combination, except that the value {@code -1}
    206      * always indicates failure.
    207      * TODO: needs to go away
    208      */
    209     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
    210         return -1;
    211     }
    212 
    213     @Override
    214     public void setUserDataEnable(boolean enabled) {
    215         Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
    216     }
    217 
    218     @Override
    219     public void setPolicyDataEnable(boolean enabled) {
    220         Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
    221     }
    222 
    223     /**
    224      * Check if private DNS route is set for the network
    225      */
    226     public boolean isPrivateDnsRouteSet() {
    227         return mPrivateDnsRouteSet.get();
    228     }
    229 
    230     /**
    231      * Set a flag indicating private DNS route is set
    232      */
    233     public void privateDnsRouteSet(boolean enabled) {
    234         mPrivateDnsRouteSet.set(enabled);
    235     }
    236 
    237     /**
    238      * Fetch NetworkInfo for the network
    239      */
    240     public NetworkInfo getNetworkInfo() {
    241         synchronized (mNetworkInfoLock) {
    242             return new NetworkInfo(mNetworkInfo);
    243         }
    244     }
    245 
    246     /**
    247      * Fetch LinkProperties for the network
    248      */
    249     public LinkProperties getLinkProperties() {
    250         synchronized (mLinkPropertiesLock) {
    251             return new LinkProperties(mLinkProperties);
    252         }
    253     }
    254 
    255    /**
    256      * A capability is an Integer/String pair, the capabilities
    257      * are defined in the class LinkSocket#Key.
    258      *
    259      * @return a copy of this connections capabilities, may be empty but never null.
    260      */
    261     public LinkCapabilities getLinkCapabilities() {
    262         return new LinkCapabilities(mLinkCapabilities);
    263     }
    264 
    265     /**
    266      * Fetch default gateway address for the network
    267      */
    268     public int getDefaultGatewayAddr() {
    269         return mDefaultGatewayAddr.get();
    270     }
    271 
    272     /**
    273      * Check if default route is set
    274      */
    275     public boolean isDefaultRouteSet() {
    276         return mDefaultRouteSet.get();
    277     }
    278 
    279     /**
    280      * Set a flag indicating default route is set for the network
    281      */
    282     public void defaultRouteSet(boolean enabled) {
    283         mDefaultRouteSet.set(enabled);
    284     }
    285 
    286     /**
    287      * Return the system properties name associated with the tcp buffer sizes
    288      * for this network.
    289      */
    290     public String getTcpBufferSizesPropName() {
    291         return "net.tcp.buffersize.wifi";
    292     }
    293 
    294     private static short countPrefixLength(byte [] mask) {
    295         short count = 0;
    296         for (byte b : mask) {
    297             for (int i = 0; i < 8; ++i) {
    298                 if ((b & (1 << i)) != 0) {
    299                     ++count;
    300                 }
    301             }
    302         }
    303         return count;
    304     }
    305 
    306     void startReverseTether(final LinkProperties linkProperties) {
    307         if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) {
    308             Log.e(TAG, "attempted to reverse tether with empty interface");
    309             return;
    310         }
    311         synchronized (mLinkPropertiesLock) {
    312             if (mLinkProperties.getInterfaceName() != null) {
    313                 Log.e(TAG, "attempted to reverse tether while already in process");
    314                 return;
    315             }
    316             mLinkProperties = linkProperties;
    317         }
    318         Thread dhcpThread = new Thread(new Runnable() {
    319             public void run() {
    320                 //Currently this thread runs independently.
    321                 DhcpResults dhcpResults = new DhcpResults();
    322                 boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(),
    323                         dhcpResults);
    324                 synchronized (mLinkPropertiesLock) {
    325                     if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) {
    326                         Log.e(TAG, "obsolete DHCP run aborted");
    327                         return;
    328                     }
    329                     if (!success) {
    330                         Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
    331                         return;
    332                     }
    333                     mLinkProperties = dhcpResults.linkProperties;
    334                     synchronized (mNetworkInfoLock) {
    335                         mNetworkInfo.setIsAvailable(true);
    336                         mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
    337                         if (mCsHandler != null) {
    338                             Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
    339                                     new NetworkInfo(mNetworkInfo));
    340                             msg.sendToTarget();
    341                        }
    342                     }
    343                     return;
    344                 }
    345             }
    346         });
    347         dhcpThread.start();
    348     }
    349 
    350     void stopReverseTether() {
    351         synchronized (mLinkPropertiesLock) {
    352             if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
    353                 Log.e(TAG, "attempted to stop reverse tether with nothing tethered");
    354                 return;
    355             }
    356             NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName());
    357             mLinkProperties.clear();
    358             synchronized (mNetworkInfoLock) {
    359                 mNetworkInfo.setIsAvailable(false);
    360                 mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
    361 
    362                 if (mCsHandler != null) {
    363                     mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)).
    364                             sendToTarget();
    365                 }
    366             }
    367         }
    368     }
    369 
    370     public void setDependencyMet(boolean met) {
    371         // not supported on this network
    372     }
    373 
    374     @Override
    375     public void addStackedLink(LinkProperties link) {
    376         mLinkProperties.addStackedLink(link);
    377     }
    378 
    379     @Override
    380     public void removeStackedLink(LinkProperties link) {
    381         mLinkProperties.removeStackedLink(link);
    382     }
    383 
    384     static class BtdtHandler extends Handler {
    385         private AsyncChannel mStackChannel;
    386         private final BluetoothTetheringDataTracker mBtdt;
    387 
    388         BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) {
    389             super(looper);
    390             mBtdt = parent;
    391         }
    392 
    393         @Override
    394         public void handleMessage(Message msg) {
    395             switch (msg.what) {
    396                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
    397                     if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED");
    398                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    399                         AsyncChannel ac = (AsyncChannel)msg.obj;
    400                         if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) {
    401                             Log.e(TAG, "Trying to set mAsyncChannel twice!");
    402                         } else {
    403                             ac.sendMessage(
    404                                     AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
    405                         }
    406                     }
    407                     break;
    408                 case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
    409                     if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED");
    410                     mBtdt.stopReverseTether();
    411                     mBtdt.mAsyncChannel.set(null);
    412                     break;
    413                 case NetworkStateTracker.EVENT_NETWORK_CONNECTED:
    414                     LinkProperties linkProperties = (LinkProperties)(msg.obj);
    415                     if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties);
    416                     mBtdt.startReverseTether(linkProperties);
    417                     break;
    418                 case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED:
    419                     linkProperties = (LinkProperties)(msg.obj);
    420                     if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties);
    421                     mBtdt.stopReverseTether();
    422                     break;
    423             }
    424         }
    425     }
    426 
    427     @Override
    428     public void supplyMessenger(Messenger messenger) {
    429         if (messenger != null) {
    430             new AsyncChannel().connect(mContext, mBtdtHandler, messenger);
    431         }
    432     }
    433 }
    434