Home | History | Annotate | Download | only in pan
      1 /*
      2  * Copyright (C) 2012 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 com.android.bluetooth.pan;
     18 
     19 import android.app.Service;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.bluetooth.BluetoothPan;
     22 import android.bluetooth.BluetoothProfile;
     23 import android.bluetooth.IBluetooth;
     24 import android.bluetooth.IBluetoothPan;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.pm.PackageManager;
     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.Handler;
     34 import android.os.IBinder;
     35 import android.os.INetworkManagementService;
     36 import android.os.Message;
     37 import android.os.RemoteException;
     38 import android.os.ServiceManager;
     39 import android.os.UserManager;
     40 import android.provider.Settings;
     41 import android.util.Log;
     42 
     43 import com.android.bluetooth.btservice.ProfileService;
     44 import com.android.bluetooth.Utils;
     45 
     46 import java.net.InetAddress;
     47 import java.util.ArrayList;
     48 import java.util.Collections;
     49 import java.util.HashMap;
     50 import java.util.List;
     51 import java.util.Map;
     52 
     53 /**
     54  * Provides Bluetooth Pan Device profile, as a service in
     55  * the Bluetooth application.
     56  * @hide
     57  */
     58 public class PanService extends ProfileService {
     59     private static final String TAG = "PanService";
     60     private static final boolean DBG = false;
     61 
     62     private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
     63     private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
     64     private static final int BLUETOOTH_PREFIX_LENGTH        = 24;
     65 
     66     private HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices;
     67     private ArrayList<String> mBluetoothIfaceAddresses;
     68     private int mMaxPanDevices;
     69     private String mPanIfName;
     70     private boolean mNativeAvailable;
     71 
     72     private static final int MESSAGE_CONNECT = 1;
     73     private static final int MESSAGE_DISCONNECT = 2;
     74     private static final int MESSAGE_CONNECT_STATE_CHANGED = 11;
     75     private boolean mTetherOn = false;
     76 
     77     private BluetoothTetheringNetworkFactory mNetworkFactory;
     78 
     79 
     80     static {
     81         classInitNative();
     82     }
     83 
     84     protected String getName() {
     85         return TAG;
     86     }
     87 
     88     public IProfileServiceBinder initBinder() {
     89         return new BluetoothPanBinder(this);
     90     }
     91 
     92     protected boolean start() {
     93         mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>();
     94         mBluetoothIfaceAddresses = new ArrayList<String>();
     95         try {
     96             mMaxPanDevices = getResources().getInteger(
     97                                  com.android.internal.R.integer.config_max_pan_devices);
     98         } catch (NotFoundException e) {
     99             mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
    100         }
    101         initializeNative();
    102         mNativeAvailable=true;
    103 
    104         mNetworkFactory = new BluetoothTetheringNetworkFactory(getBaseContext(), getMainLooper(),
    105                 this);
    106 
    107         return true;
    108     }
    109 
    110     protected boolean stop() {
    111         mHandler.removeCallbacksAndMessages(null);
    112         return true;
    113     }
    114 
    115     protected boolean cleanup() {
    116         if (mNativeAvailable) {
    117             cleanupNative();
    118             mNativeAvailable=false;
    119         }
    120         if(mPanDevices != null) {
    121             List<BluetoothDevice> DevList = getConnectedDevices();
    122             for(BluetoothDevice dev : DevList) {
    123                 handlePanDeviceStateChange(dev, mPanIfName, BluetoothProfile.STATE_DISCONNECTED,
    124                         BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
    125             }
    126             mPanDevices.clear();
    127         }
    128         if(mBluetoothIfaceAddresses != null) {
    129             mBluetoothIfaceAddresses.clear();
    130         }
    131         return true;
    132     }
    133 
    134     private final Handler mHandler = new Handler() {
    135         @Override
    136         public void handleMessage(Message msg) {
    137             switch (msg.what) {
    138                 case MESSAGE_CONNECT:
    139                 {
    140                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    141                     if (!connectPanNative(Utils.getByteAddress(device),
    142                             BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE)) {
    143                         handlePanDeviceStateChange(device, null, BluetoothProfile.STATE_CONNECTING,
    144                                 BluetoothPan.LOCAL_PANU_ROLE, BluetoothPan.REMOTE_NAP_ROLE);
    145                         handlePanDeviceStateChange(device, null,
    146                                 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
    147                                 BluetoothPan.REMOTE_NAP_ROLE);
    148                         break;
    149                     }
    150                 }
    151                     break;
    152                 case MESSAGE_DISCONNECT:
    153                 {
    154                     BluetoothDevice device = (BluetoothDevice) msg.obj;
    155                     if (!disconnectPanNative(Utils.getByteAddress(device)) ) {
    156                         handlePanDeviceStateChange(device, mPanIfName,
    157                                 BluetoothProfile.STATE_DISCONNECTING, BluetoothPan.LOCAL_PANU_ROLE,
    158                                 BluetoothPan.REMOTE_NAP_ROLE);
    159                         handlePanDeviceStateChange(device, mPanIfName,
    160                                 BluetoothProfile.STATE_DISCONNECTED, BluetoothPan.LOCAL_PANU_ROLE,
    161                                 BluetoothPan.REMOTE_NAP_ROLE);
    162                         break;
    163                     }
    164                 }
    165                     break;
    166                 case MESSAGE_CONNECT_STATE_CHANGED:
    167                 {
    168                     ConnectState cs = (ConnectState)msg.obj;
    169                     BluetoothDevice device = getDevice(cs.addr);
    170                     // TBD get iface from the msg
    171                     if (DBG) {
    172                         log("MESSAGE_CONNECT_STATE_CHANGED: " + device + " state: " + cs.state);
    173                     }
    174                     handlePanDeviceStateChange(device, mPanIfName /* iface */,
    175                             convertHalState(cs.state), cs.local_role,  cs.remote_role);
    176                 }
    177                 break;
    178             }
    179         }
    180     };
    181 
    182     /**
    183      * Handlers for incoming service calls
    184      */
    185     private static class BluetoothPanBinder extends IBluetoothPan.Stub
    186             implements IProfileServiceBinder {
    187         private PanService mService;
    188         public BluetoothPanBinder(PanService svc) {
    189             mService = svc;
    190         }
    191         public boolean cleanup() {
    192             mService = null;
    193             return true;
    194         }
    195         private PanService getService() {
    196             if (!Utils.checkCaller()) {
    197                 Log.w(TAG,"Pan call not allowed for non-active user");
    198                 return null;
    199             }
    200 
    201             if (mService  != null && mService.isAvailable()) {
    202                 return mService;
    203             }
    204             return null;
    205         }
    206         public boolean connect(BluetoothDevice device) {
    207             PanService service = getService();
    208             if (service == null) return false;
    209             return service.connect(device);
    210         }
    211         public boolean disconnect(BluetoothDevice device) {
    212             PanService service = getService();
    213             if (service == null) return false;
    214             return service.disconnect(device);
    215         }
    216         public int getConnectionState(BluetoothDevice device) {
    217             PanService service = getService();
    218             if (service == null) return BluetoothPan.STATE_DISCONNECTED;
    219             return service.getConnectionState(device);
    220         }
    221         private boolean isPanNapOn() {
    222             PanService service = getService();
    223             if (service == null) return false;
    224             return service.isPanNapOn();
    225         }
    226         private boolean isPanUOn() {
    227             if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
    228             PanService service = getService();
    229             if (service == null) return false;
    230             return service.isPanUOn();
    231         }
    232         public boolean isTetheringOn() {
    233             // TODO(BT) have a variable marking the on/off state
    234             PanService service = getService();
    235             if (service == null) return false;
    236             return service.isTetheringOn();
    237         }
    238         public void setBluetoothTethering(boolean value) {
    239             PanService service = getService();
    240             if (service == null) return;
    241             Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn);
    242             service.setBluetoothTethering(value);
    243         }
    244 
    245         public List<BluetoothDevice> getConnectedDevices() {
    246             PanService service = getService();
    247             if (service == null) return new ArrayList<BluetoothDevice>(0);
    248             return service.getConnectedDevices();
    249         }
    250 
    251         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    252             PanService service = getService();
    253             if (service == null) return new ArrayList<BluetoothDevice>(0);
    254             return service.getDevicesMatchingConnectionStates(states);
    255         }
    256     };
    257 
    258     boolean connect(BluetoothDevice device) {
    259         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    260         if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
    261             Log.e(TAG, "Pan Device not disconnected: " + device);
    262             return false;
    263         }
    264         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device);
    265         mHandler.sendMessage(msg);
    266         return true;
    267     }
    268 
    269     boolean disconnect(BluetoothDevice device) {
    270         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    271         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device);
    272         mHandler.sendMessage(msg);
    273         return true;
    274     }
    275 
    276     int getConnectionState(BluetoothDevice device) {
    277         BluetoothPanDevice panDevice = mPanDevices.get(device);
    278         if (panDevice == null) {
    279             return BluetoothPan.STATE_DISCONNECTED;
    280         }
    281         return panDevice.mState;
    282     }
    283 
    284     boolean isPanNapOn() {
    285         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
    286         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
    287     }
    288      boolean isPanUOn() {
    289         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
    290         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
    291     }
    292      boolean isTetheringOn() {
    293         // TODO(BT) have a variable marking the on/off state
    294         return mTetherOn;
    295     }
    296 
    297     void setBluetoothTethering(boolean value) {
    298         if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn);
    299         ConnectivityManager.enforceTetherChangePermission(getBaseContext());
    300         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    301         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
    302         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
    303             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
    304         }
    305         if(mTetherOn != value) {
    306             //drop any existing panu or pan-nap connection when changing the tethering state
    307             mTetherOn = value;
    308             List<BluetoothDevice> DevList = getConnectedDevices();
    309             for(BluetoothDevice dev : DevList)
    310                 disconnect(dev);
    311         }
    312     }
    313 
    314     List<BluetoothDevice> getConnectedDevices() {
    315         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    316         List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
    317                 new int[] {BluetoothProfile.STATE_CONNECTED});
    318         return devices;
    319     }
    320 
    321     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    322          enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    323         List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>();
    324 
    325         for (BluetoothDevice device: mPanDevices.keySet()) {
    326             int panDeviceState = getConnectionState(device);
    327             for (int state : states) {
    328                 if (state == panDeviceState) {
    329                     panDevices.add(device);
    330                     break;
    331                 }
    332             }
    333         }
    334         return panDevices;
    335     }
    336 
    337     static protected class ConnectState {
    338         public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) {
    339             this.addr = address;
    340             this.state = state;
    341             this.error = error;
    342             this.local_role = local_role;
    343             this.remote_role = remote_role;
    344         }
    345         byte[] addr;
    346         int state;
    347         int error;
    348         int local_role;
    349         int remote_role;
    350     };
    351     private void onConnectStateChanged(byte[] address, int state, int error, int local_role,
    352             int remote_role) {
    353         if (DBG) {
    354             log("onConnectStateChanged: " + state + ", local role:" + local_role +
    355                     ", remote_role: " + remote_role);
    356         }
    357         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
    358         msg.obj = new ConnectState(address, state, error, local_role, remote_role);
    359         mHandler.sendMessage(msg);
    360     }
    361     private void onControlStateChanged(int local_role, int state, int error, String ifname) {
    362         if (DBG)
    363             log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname);
    364         if(error == 0)
    365             mPanIfName = ifname;
    366     }
    367 
    368     private static int convertHalState(int halState) {
    369         switch (halState) {
    370             case CONN_STATE_CONNECTED:
    371                 return BluetoothProfile.STATE_CONNECTED;
    372             case CONN_STATE_CONNECTING:
    373                 return BluetoothProfile.STATE_CONNECTING;
    374             case CONN_STATE_DISCONNECTED:
    375                 return BluetoothProfile.STATE_DISCONNECTED;
    376             case CONN_STATE_DISCONNECTING:
    377                 return BluetoothProfile.STATE_DISCONNECTING;
    378             default:
    379                 Log.e(TAG, "bad pan connection state: " + halState);
    380                 return BluetoothProfile.STATE_DISCONNECTED;
    381         }
    382     }
    383 
    384     void handlePanDeviceStateChange(BluetoothDevice device,
    385                                     String iface, int state, int local_role, int remote_role) {
    386         if(DBG) {
    387             Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface +
    388                     ", state: " + state + ", local_role:" + local_role + ", remote_role:" +
    389                     remote_role);
    390         }
    391         int prevState;
    392         String ifaceAddr = null;
    393         BluetoothPanDevice panDevice = mPanDevices.get(device);
    394         if (panDevice == null) {
    395             prevState = BluetoothProfile.STATE_DISCONNECTED;
    396         } else {
    397             prevState = panDevice.mState;
    398             ifaceAddr = panDevice.mIfaceAddr;
    399         }
    400 
    401         // Avoid race condition that gets this class stuck in STATE_DISCONNECTING. While we
    402         // are in STATE_CONNECTING, if a BluetoothPan#disconnect call comes in, the original
    403         // connect call will put us in STATE_DISCONNECTED. Then, the disconnect completes and
    404         // changes the state to STATE_DISCONNECTING. All future calls to BluetoothPan#connect
    405         // will fail until the caller explicitly calls BluetoothPan#disconnect.
    406         if (prevState == BluetoothProfile.STATE_DISCONNECTED && state == BluetoothProfile.STATE_DISCONNECTING) {
    407             Log.d(TAG, "Ignoring state change from " + prevState + " to " + state);
    408             return;
    409         }
    410 
    411         Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state);
    412         if (prevState == state) return;
    413         if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) {
    414             if (state == BluetoothProfile.STATE_CONNECTED) {
    415                 if((!mTetherOn)||(local_role == BluetoothPan.LOCAL_PANU_ROLE)){
    416                     Log.d(TAG,"handlePanDeviceStateChange BT tethering is off/Local role is PANU "+
    417                               "drop the connection");
    418                     disconnectPanNative(Utils.getByteAddress(device));
    419                     return;
    420                 }
    421                 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
    422                 ifaceAddr = enableTethering(iface);
    423                 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
    424 
    425             } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
    426                 if (ifaceAddr != null) {
    427                     mBluetoothIfaceAddresses.remove(ifaceAddr);
    428                     ifaceAddr = null;
    429                 }
    430             }
    431         } else if (mNetworkFactory != null) {
    432             // PANU Role = reverse Tether
    433             Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " +
    434                     state + ", prevState = " + prevState);
    435             if (state == BluetoothProfile.STATE_CONNECTED) {
    436                 mNetworkFactory.startReverseTether(iface);
    437            } else if (state == BluetoothProfile.STATE_DISCONNECTED &&
    438                    (prevState == BluetoothProfile.STATE_CONNECTED ||
    439                    prevState == BluetoothProfile.STATE_DISCONNECTING)) {
    440                 mNetworkFactory.stopReverseTether();
    441             }
    442         }
    443 
    444         if (panDevice == null) {
    445             panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, local_role);
    446             mPanDevices.put(device, panDevice);
    447         } else {
    448             panDevice.mState = state;
    449             panDevice.mIfaceAddr = ifaceAddr;
    450             panDevice.mLocalRole = local_role;
    451             panDevice.mIface = iface;
    452         }
    453 
    454         /* Notifying the connection state change of the profile before sending the intent for
    455            connection state change, as it was causing a race condition, with the UI not being
    456            updated with the correct connection state. */
    457         Log.d(TAG, "Pan Device state : device: " + device + " State:" +
    458                        prevState + "->" + state);
    459         notifyProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState);
    460         Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
    461         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    462         intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
    463         intent.putExtra(BluetoothPan.EXTRA_STATE, state);
    464         intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role);
    465         sendBroadcast(intent, BLUETOOTH_PERM);
    466     }
    467 
    468     // configured when we start tethering
    469     private String enableTethering(String iface) {
    470         if (DBG) Log.d(TAG, "updateTetherState:" + iface);
    471 
    472         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    473         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
    474         ConnectivityManager cm =
    475             (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    476         String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
    477 
    478         // bring toggle the interfaces
    479         String[] currentIfaces = new String[0];
    480         try {
    481             currentIfaces = service.listInterfaces();
    482         } catch (Exception e) {
    483             Log.e(TAG, "Error listing Interfaces :" + e);
    484             return null;
    485         }
    486 
    487         boolean found = false;
    488         for (String currIface: currentIfaces) {
    489             if (currIface.equals(iface)) {
    490                 found = true;
    491                 break;
    492             }
    493         }
    494 
    495         if (!found) return null;
    496 
    497         String address = createNewTetheringAddressLocked();
    498         if (address == null) return null;
    499 
    500         InterfaceConfiguration ifcg = null;
    501         try {
    502             ifcg = service.getInterfaceConfig(iface);
    503             if (ifcg != null) {
    504                 InetAddress addr = null;
    505                 LinkAddress linkAddr = ifcg.getLinkAddress();
    506                 if (linkAddr == null || (addr = linkAddr.getAddress()) == null ||
    507                         addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
    508                         addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
    509                     addr = NetworkUtils.numericToInetAddress(address);
    510                 }
    511                 ifcg.setInterfaceUp();
    512                 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
    513                 ifcg.clearFlag("running");
    514                 // TODO(BT) ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
    515                 service.setInterfaceConfig(iface, ifcg);
    516                 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
    517                     Log.e(TAG, "Error tethering "+iface);
    518                 }
    519             }
    520         } catch (Exception e) {
    521             Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
    522             return null;
    523         }
    524         return address;
    525     }
    526 
    527     private String createNewTetheringAddressLocked() {
    528         if (getConnectedPanDevices().size() == mMaxPanDevices) {
    529             if (DBG) Log.d(TAG, "Max PAN device connections reached");
    530             return null;
    531         }
    532         String address = BLUETOOTH_IFACE_ADDR_START;
    533         while (true) {
    534             if (mBluetoothIfaceAddresses.contains(address)) {
    535                 String[] addr = address.split("\\.");
    536                 Integer newIp = Integer.parseInt(addr[2]) + 1;
    537                 address = address.replace(addr[2], newIp.toString());
    538             } else {
    539                 break;
    540             }
    541         }
    542         mBluetoothIfaceAddresses.add(address);
    543         return address;
    544     }
    545 
    546     private List<BluetoothDevice> getConnectedPanDevices() {
    547         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    548 
    549         for (BluetoothDevice device: mPanDevices.keySet()) {
    550             if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
    551                 devices.add(device);
    552             }
    553         }
    554         return devices;
    555     }
    556 
    557     private int getPanDeviceConnectionState(BluetoothDevice device) {
    558         BluetoothPanDevice panDevice = mPanDevices.get(device);
    559         if (panDevice == null) {
    560             return BluetoothProfile.STATE_DISCONNECTED;
    561         }
    562         return panDevice.mState;
    563     }
    564 
    565     @Override
    566     public void dump(StringBuilder sb) {
    567         super.dump(sb);
    568         println(sb, "mMaxPanDevices: " + mMaxPanDevices);
    569         println(sb, "mPanIfName: " + mPanIfName);
    570         println(sb, "mTetherOn: " + mTetherOn);
    571         println(sb, "mPanDevices:");
    572         for (BluetoothDevice device : mPanDevices.keySet()) {
    573             println(sb, "  " + device + " : " + mPanDevices.get(device));
    574         }
    575         println(sb, "mBluetoothIfaceAddresses:");
    576         for (String address : mBluetoothIfaceAddresses) {
    577             println(sb, "  " + address);
    578         }
    579     }
    580 
    581     private class BluetoothPanDevice {
    582         private int mState;
    583         private String mIfaceAddr;
    584         private String mIface;
    585         private int mLocalRole; // Which local role is this PAN device bound to
    586 
    587         BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
    588             mState = state;
    589             mIfaceAddr = ifaceAddr;
    590             mIface = iface;
    591             mLocalRole = localRole;
    592         }
    593     }
    594 
    595     // Constants matching Hal header file bt_hh.h
    596     // bthh_connection_state_t
    597     private final static int CONN_STATE_CONNECTED = 0;
    598     private final static int CONN_STATE_CONNECTING = 1;
    599     private final static int CONN_STATE_DISCONNECTED = 2;
    600     private final static int CONN_STATE_DISCONNECTING = 3;
    601 
    602     private native static void classInitNative();
    603     private native void initializeNative();
    604     private native void cleanupNative();
    605     private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
    606     private native boolean disconnectPanNative(byte[] btAddress);
    607     private native boolean enablePanNative(int local_role);
    608     private native int getPanLocalRoleNative();
    609 
    610 }
    611