Home | History | Annotate | Download | only in server
      1 /*
      2  * Copyright (C) 2011 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.server;
     18 
     19 import android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.bluetooth.BluetoothPan;
     22 import android.bluetooth.BluetoothProfile;
     23 import android.bluetooth.BluetoothTetheringDataTracker;
     24 import android.content.BroadcastReceiver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.res.Resources.NotFoundException;
     29 import android.net.ConnectivityManager;
     30 import android.net.InterfaceConfiguration;
     31 import android.net.LinkAddress;
     32 import android.net.NetworkUtils;
     33 import android.os.IBinder;
     34 import android.os.INetworkManagementService;
     35 import android.os.ServiceManager;
     36 import android.util.Log;
     37 
     38 import java.net.InetAddress;
     39 import java.util.ArrayList;
     40 import java.util.HashMap;
     41 import java.util.List;
     42 
     43 /**
     44  * This handles the PAN profile. All calls into this are made
     45  * from Bluetooth Service.
     46  */
     47 final class BluetoothPanProfileHandler {
     48     private static final String TAG = "BluetoothPanProfileHandler";
     49     private static final boolean DBG = true;
     50 
     51     private ArrayList<String> mBluetoothIfaceAddresses;
     52     private int mMaxPanDevices;
     53 
     54     private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
     55     private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
     56     private static final int BLUETOOTH_PREFIX_LENGTH        = 24;
     57     public static BluetoothPanProfileHandler sInstance;
     58     private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
     59     private boolean mTetheringOn;
     60     private Context mContext;
     61     private BluetoothService mBluetoothService;
     62 
     63     static final String NAP_ROLE = "nap";
     64     static final String NAP_BRIDGE = "pan1";
     65 
     66     private BluetoothPanProfileHandler(Context context, BluetoothService service) {
     67         mContext = context;
     68         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
     69         mBluetoothService = service;
     70         mTetheringOn = false;
     71         mBluetoothIfaceAddresses = new ArrayList<String>();
     72         try {
     73             mMaxPanDevices = context.getResources().getInteger(
     74                             com.android.internal.R.integer.config_max_pan_devices);
     75         } catch (NotFoundException e) {
     76             mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
     77         }
     78     }
     79 
     80     static BluetoothPanProfileHandler getInstance(Context context,
     81             BluetoothService service) {
     82         if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service);
     83         return sInstance;
     84     }
     85 
     86     boolean isTetheringOn() {
     87         return mTetheringOn;
     88     }
     89 
     90     boolean allowIncomingTethering() {
     91         if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
     92             return true;
     93         return false;
     94     }
     95 
     96     private BroadcastReceiver mTetheringReceiver = null;
     97 
     98     void setBluetoothTethering(boolean value) {
     99         if (!value) {
    100             disconnectPanServerDevices();
    101         }
    102 
    103         if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
    104             IntentFilter filter = new IntentFilter();
    105             filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    106             mTetheringReceiver = new BroadcastReceiver() {
    107                 @Override
    108                 public void onReceive(Context context, Intent intent) {
    109                     if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
    110                             == BluetoothAdapter.STATE_ON) {
    111                         mTetheringOn = true;
    112                         mContext.unregisterReceiver(mTetheringReceiver);
    113                     }
    114                 }
    115             };
    116             mContext.registerReceiver(mTetheringReceiver, filter);
    117         } else {
    118             mTetheringOn = value;
    119         }
    120     }
    121 
    122     int getPanDeviceConnectionState(BluetoothDevice device) {
    123         BluetoothPanDevice panDevice = mPanDevices.get(device);
    124         if (panDevice == null) {
    125             return BluetoothPan.STATE_DISCONNECTED;
    126         }
    127         return panDevice.mState;
    128     }
    129 
    130     boolean connectPanDevice(BluetoothDevice device) {
    131         String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
    132         if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")");
    133         if (getPanDeviceConnectionState(device) != BluetoothPan.STATE_DISCONNECTED) {
    134             errorLog(device + " already connected to PAN");
    135         }
    136 
    137         int connectedCount = 0;
    138         for (BluetoothDevice panDevice: mPanDevices.keySet()) {
    139             if (getPanDeviceConnectionState(panDevice) == BluetoothPan.STATE_CONNECTED) {
    140                 connectedCount ++;
    141             }
    142         }
    143         if (connectedCount > 8) {
    144             debugLog(device + " could not connect to PAN because 8 other devices are"
    145                     + "already connected");
    146             return false;
    147         }
    148 
    149         // Send interface as null as it is not known
    150         handlePanDeviceStateChange(device, null, BluetoothPan.STATE_CONNECTING,
    151                                            BluetoothPan.LOCAL_PANU_ROLE);
    152         if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) {
    153             debugLog("connecting to PAN");
    154             return true;
    155         } else {
    156             handlePanDeviceStateChange(device, null, BluetoothPan.STATE_DISCONNECTED,
    157                                                 BluetoothPan.LOCAL_PANU_ROLE);
    158             errorLog("could not connect to PAN");
    159             return false;
    160         }
    161     }
    162 
    163     private boolean disconnectPanServerDevices() {
    164         debugLog("disconnect all PAN devices");
    165 
    166         for (BluetoothDevice device: mPanDevices.keySet()) {
    167             BluetoothPanDevice panDevice = mPanDevices.get(device);
    168             int state = panDevice.mState;
    169             if (state == BluetoothPan.STATE_CONNECTED &&
    170                     panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
    171                 String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
    172 
    173                 handlePanDeviceStateChange(device, panDevice.mIface,
    174                         BluetoothPan.STATE_DISCONNECTING, panDevice.mLocalRole);
    175 
    176                 if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath,
    177                         device.getAddress(),
    178                         panDevice.mIface)) {
    179                     errorLog("could not disconnect Pan Server Device "+device.getAddress());
    180 
    181                     // Restore prev state
    182                     handlePanDeviceStateChange(device, panDevice.mIface, state,
    183                             panDevice.mLocalRole);
    184 
    185                     return false;
    186                 }
    187             }
    188         }
    189         return true;
    190     }
    191 
    192     List<BluetoothDevice> getConnectedPanDevices() {
    193         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    194 
    195         for (BluetoothDevice device: mPanDevices.keySet()) {
    196             if (getPanDeviceConnectionState(device) == BluetoothPan.STATE_CONNECTED) {
    197                 devices.add(device);
    198             }
    199         }
    200         return devices;
    201     }
    202 
    203     List<BluetoothDevice> getPanDevicesMatchingConnectionStates(int[] states) {
    204         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    205 
    206         for (BluetoothDevice device: mPanDevices.keySet()) {
    207             int panDeviceState = getPanDeviceConnectionState(device);
    208             for (int state : states) {
    209                 if (state == panDeviceState) {
    210                     devices.add(device);
    211                     break;
    212                 }
    213             }
    214         }
    215         return devices;
    216     }
    217 
    218     boolean disconnectPanDevice(BluetoothDevice device) {
    219         String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
    220         debugLog("disconnect PAN(" + objectPath + ")");
    221 
    222         int state = getPanDeviceConnectionState(device);
    223         if (state != BluetoothPan.STATE_CONNECTED) {
    224             debugLog(device + " already disconnected from PAN");
    225             return false;
    226         }
    227 
    228         BluetoothPanDevice panDevice = mPanDevices.get(device);
    229 
    230         if (panDevice == null) {
    231             errorLog("No record for this Pan device:" + device);
    232             return false;
    233         }
    234 
    235         handlePanDeviceStateChange(device, panDevice.mIface, BluetoothPan.STATE_DISCONNECTING,
    236                                     panDevice.mLocalRole);
    237         if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) {
    238             if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(),
    239                     panDevice.mIface)) {
    240                 // Restore prev state, this shouldn't happen
    241                 handlePanDeviceStateChange(device, panDevice.mIface, state, panDevice.mLocalRole);
    242                 return false;
    243             }
    244         } else {
    245             if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) {
    246                 // Restore prev state, this shouldn't happen
    247                 handlePanDeviceStateChange(device, panDevice.mIface, state, panDevice.mLocalRole);
    248                 return false;
    249             }
    250         }
    251         return true;
    252     }
    253 
    254     void handlePanDeviceStateChange(BluetoothDevice device,
    255                                                  String iface, int state, int role) {
    256         int prevState;
    257         String ifaceAddr = null;
    258         BluetoothPanDevice panDevice = mPanDevices.get(device);
    259 
    260         if (panDevice == null) {
    261             prevState = BluetoothPan.STATE_DISCONNECTED;
    262         } else {
    263             prevState = panDevice.mState;
    264             ifaceAddr = panDevice.mIfaceAddr;
    265         }
    266         if (prevState == state) return;
    267 
    268         if (role == BluetoothPan.LOCAL_NAP_ROLE) {
    269             if (state == BluetoothPan.STATE_CONNECTED) {
    270                 ifaceAddr = enableTethering(iface);
    271                 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
    272             } else if (state == BluetoothPan.STATE_DISCONNECTED) {
    273                 if (ifaceAddr != null) {
    274                     mBluetoothIfaceAddresses.remove(ifaceAddr);
    275                     ifaceAddr = null;
    276                 }
    277             }
    278         } else {
    279             // PANU Role = reverse Tether
    280             if (state == BluetoothPan.STATE_CONNECTED) {
    281                 BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device);
    282             } else if (state == BluetoothPan.STATE_DISCONNECTED &&
    283                   (prevState == BluetoothPan.STATE_CONNECTED ||
    284                   prevState == BluetoothPan.STATE_DISCONNECTING)) {
    285                 BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface);
    286             }
    287         }
    288 
    289         if (panDevice == null) {
    290             panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role);
    291             mPanDevices.put(device, panDevice);
    292         } else {
    293             panDevice.mState = state;
    294             panDevice.mIfaceAddr = ifaceAddr;
    295             panDevice.mLocalRole = role;
    296             panDevice.mIface = iface;
    297         }
    298 
    299         Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
    300         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    301         intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
    302         intent.putExtra(BluetoothPan.EXTRA_STATE, state);
    303         intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role);
    304         mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
    305 
    306         debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
    307         mBluetoothService.sendConnectionStateChange(device, BluetoothProfile.PAN, state,
    308                                                     prevState);
    309     }
    310 
    311     private class BluetoothPanDevice {
    312         private int mState;
    313         private String mIfaceAddr;
    314         private String mIface;
    315         private int mLocalRole; // Which local role is this PAN device bound to
    316 
    317         BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
    318             mState = state;
    319             mIfaceAddr = ifaceAddr;
    320             mIface = iface;
    321             mLocalRole = localRole;
    322         }
    323     }
    324 
    325     private String createNewTetheringAddressLocked() {
    326         if (getConnectedPanDevices().size() == mMaxPanDevices) {
    327             debugLog ("Max PAN device connections reached");
    328             return null;
    329         }
    330         String address = BLUETOOTH_IFACE_ADDR_START;
    331         while (true) {
    332             if (mBluetoothIfaceAddresses.contains(address)) {
    333                 String[] addr = address.split("\\.");
    334                 Integer newIp = Integer.parseInt(addr[2]) + 1;
    335                 address = address.replace(addr[2], newIp.toString());
    336             } else {
    337                 break;
    338             }
    339         }
    340         mBluetoothIfaceAddresses.add(address);
    341         return address;
    342     }
    343 
    344     // configured when we start tethering
    345     private String enableTethering(String iface) {
    346         debugLog("updateTetherState:" + iface);
    347 
    348         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    349         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
    350         ConnectivityManager cm =
    351             (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    352         String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
    353 
    354         // bring toggle the interfaces
    355         String[] currentIfaces = new String[0];
    356         try {
    357             currentIfaces = service.listInterfaces();
    358         } catch (Exception e) {
    359             Log.e(TAG, "Error listing Interfaces :" + e);
    360             return null;
    361         }
    362 
    363         boolean found = false;
    364         for (String currIface: currentIfaces) {
    365             if (currIface.equals(iface)) {
    366                 found = true;
    367                 break;
    368             }
    369         }
    370 
    371         if (!found) return null;
    372 
    373         String address = createNewTetheringAddressLocked();
    374         if (address == null) return null;
    375 
    376         InterfaceConfiguration ifcg = null;
    377         try {
    378             ifcg = service.getInterfaceConfig(iface);
    379             if (ifcg != null) {
    380                 final LinkAddress linkAddr = ifcg.getLinkAddress();
    381                 InetAddress addr = null;
    382                 if (linkAddr == null || (addr = linkAddr.getAddress()) == null ||
    383                         addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
    384                         addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
    385                     addr = NetworkUtils.numericToInetAddress(address);
    386                 }
    387                 ifcg.setInterfaceUp();
    388                 ifcg.clearFlag("running");
    389                 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
    390                 service.setInterfaceConfig(iface, ifcg);
    391                 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
    392                     Log.e(TAG, "Error tethering "+iface);
    393                 }
    394             }
    395         } catch (Exception e) {
    396             Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
    397             return null;
    398         }
    399         return address;
    400     }
    401 
    402     private static void debugLog(String msg) {
    403         if (DBG) Log.d(TAG, msg);
    404     }
    405 
    406     private static void errorLog(String msg) {
    407         Log.e(TAG, msg);
    408     }
    409 }
    410