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