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             return service.isPanUOn();
    230         }
    231         public boolean isTetheringOn() {
    232             // TODO(BT) have a variable marking the on/off state
    233             PanService service = getService();
    234             if (service == null) return false;
    235             return service.isTetheringOn();
    236         }
    237         public void setBluetoothTethering(boolean value) {
    238             PanService service = getService();
    239             if (service == null) return;
    240             Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn);
    241             service.setBluetoothTethering(value);
    242         }
    243 
    244         public List<BluetoothDevice> getConnectedDevices() {
    245             PanService service = getService();
    246             if (service == null) return new ArrayList<BluetoothDevice>(0);
    247             return service.getConnectedDevices();
    248         }
    249 
    250         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    251             PanService service = getService();
    252             if (service == null) return new ArrayList<BluetoothDevice>(0);
    253             return service.getDevicesMatchingConnectionStates(states);
    254         }
    255     };
    256 
    257     boolean connect(BluetoothDevice device) {
    258         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    259         if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
    260             Log.e(TAG, "Pan Device not disconnected: " + device);
    261             return false;
    262         }
    263         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device);
    264         mHandler.sendMessage(msg);
    265         return true;
    266     }
    267 
    268     boolean disconnect(BluetoothDevice device) {
    269         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    270         Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device);
    271         mHandler.sendMessage(msg);
    272         return true;
    273     }
    274 
    275     int getConnectionState(BluetoothDevice device) {
    276         BluetoothPanDevice panDevice = mPanDevices.get(device);
    277         if (panDevice == null) {
    278             return BluetoothPan.STATE_DISCONNECTED;
    279         }
    280         return panDevice.mState;
    281     }
    282 
    283     boolean isPanNapOn() {
    284         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
    285         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
    286     }
    287      boolean isPanUOn() {
    288         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
    289         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
    290     }
    291      boolean isTetheringOn() {
    292         // TODO(BT) have a variable marking the on/off state
    293         return mTetherOn;
    294     }
    295 
    296     void setBluetoothTethering(boolean value) {
    297         if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn);
    298         ConnectivityManager.enforceTetherChangePermission(getBaseContext());
    299         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
    300         UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
    301         if (um.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING)) {
    302             throw new SecurityException("DISALLOW_CONFIG_TETHERING is enabled for this user.");
    303         }
    304         if(mTetherOn != value) {
    305             //drop any existing panu or pan-nap connection when changing the tethering state
    306             mTetherOn = value;
    307             List<BluetoothDevice> DevList = getConnectedDevices();
    308             for(BluetoothDevice dev : DevList)
    309                 disconnect(dev);
    310         }
    311     }
    312 
    313     List<BluetoothDevice> getConnectedDevices() {
    314         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    315         List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
    316                 new int[] {BluetoothProfile.STATE_CONNECTED});
    317         return devices;
    318     }
    319 
    320     List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    321          enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
    322         List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>();
    323 
    324         for (BluetoothDevice device: mPanDevices.keySet()) {
    325             int panDeviceState = getConnectionState(device);
    326             for (int state : states) {
    327                 if (state == panDeviceState) {
    328                     panDevices.add(device);
    329                     break;
    330                 }
    331             }
    332         }
    333         return panDevices;
    334     }
    335 
    336     static protected class ConnectState {
    337         public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) {
    338             this.addr = address;
    339             this.state = state;
    340             this.error = error;
    341             this.local_role = local_role;
    342             this.remote_role = remote_role;
    343         }
    344         byte[] addr;
    345         int state;
    346         int error;
    347         int local_role;
    348         int remote_role;
    349     };
    350     private void onConnectStateChanged(byte[] address, int state, int error, int local_role,
    351             int remote_role) {
    352         if (DBG) {
    353             log("onConnectStateChanged: " + state + ", local role:" + local_role +
    354                     ", remote_role: " + remote_role);
    355         }
    356         Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
    357         msg.obj = new ConnectState(address, state, error, local_role, remote_role);
    358         mHandler.sendMessage(msg);
    359     }
    360     private void onControlStateChanged(int local_role, int state, int error, String ifname) {
    361         if (DBG)
    362             log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname);
    363         if(error == 0)
    364             mPanIfName = ifname;
    365     }
    366 
    367     private static int convertHalState(int halState) {
    368         switch (halState) {
    369             case CONN_STATE_CONNECTED:
    370                 return BluetoothProfile.STATE_CONNECTED;
    371             case CONN_STATE_CONNECTING:
    372                 return BluetoothProfile.STATE_CONNECTING;
    373             case CONN_STATE_DISCONNECTED:
    374                 return BluetoothProfile.STATE_DISCONNECTED;
    375             case CONN_STATE_DISCONNECTING:
    376                 return BluetoothProfile.STATE_DISCONNECTING;
    377             default:
    378                 Log.e(TAG, "bad pan connection state: " + halState);
    379                 return BluetoothProfile.STATE_DISCONNECTED;
    380         }
    381     }
    382 
    383     void handlePanDeviceStateChange(BluetoothDevice device,
    384                                     String iface, int state, int local_role, int remote_role) {
    385         if(DBG) {
    386             Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface +
    387                     ", state: " + state + ", local_role:" + local_role + ", remote_role:" +
    388                     remote_role);
    389         }
    390         int prevState;
    391         String ifaceAddr = null;
    392         BluetoothPanDevice panDevice = mPanDevices.get(device);
    393         if (panDevice == null) {
    394             prevState = BluetoothProfile.STATE_DISCONNECTED;
    395         } else {
    396             prevState = panDevice.mState;
    397             ifaceAddr = panDevice.mIfaceAddr;
    398         }
    399 
    400         // Avoid race condition that gets this class stuck in STATE_DISCONNECTING. While we
    401         // are in STATE_CONNECTING, if a BluetoothPan#disconnect call comes in, the original
    402         // connect call will put us in STATE_DISCONNECTED. Then, the disconnect completes and
    403         // changes the state to STATE_DISCONNECTING. All future calls to BluetoothPan#connect
    404         // will fail until the caller explicitly calls BluetoothPan#disconnect.
    405         if (prevState == BluetoothProfile.STATE_DISCONNECTED && state == BluetoothProfile.STATE_DISCONNECTING) {
    406             Log.d(TAG, "Ignoring state change from " + prevState + " to " + state);
    407             return;
    408         }
    409 
    410         Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state);
    411         if (prevState == state) return;
    412         if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) {
    413             if (state == BluetoothProfile.STATE_CONNECTED) {
    414                 if((!mTetherOn)||(local_role == BluetoothPan.LOCAL_PANU_ROLE)){
    415                     Log.d(TAG,"handlePanDeviceStateChange BT tethering is off/Local role is PANU "+
    416                               "drop the connection");
    417                     disconnectPanNative(Utils.getByteAddress(device));
    418                     return;
    419                 }
    420                 Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
    421                 ifaceAddr = enableTethering(iface);
    422                 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
    423 
    424             } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
    425                 if (ifaceAddr != null) {
    426                     mBluetoothIfaceAddresses.remove(ifaceAddr);
    427                     ifaceAddr = null;
    428                 }
    429             }
    430         } else if (mNetworkFactory != null) {
    431             // PANU Role = reverse Tether
    432             Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE state = " +
    433                     state + ", prevState = " + prevState);
    434             if (state == BluetoothProfile.STATE_CONNECTED) {
    435                 mNetworkFactory.startReverseTether(iface);
    436            } else if (state == BluetoothProfile.STATE_DISCONNECTED &&
    437                    (prevState == BluetoothProfile.STATE_CONNECTED ||
    438                    prevState == BluetoothProfile.STATE_DISCONNECTING)) {
    439                 mNetworkFactory.stopReverseTether();
    440             }
    441         }
    442 
    443         if (panDevice == null) {
    444             panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, local_role);
    445             mPanDevices.put(device, panDevice);
    446         } else {
    447             panDevice.mState = state;
    448             panDevice.mIfaceAddr = ifaceAddr;
    449             panDevice.mLocalRole = local_role;
    450             panDevice.mIface = iface;
    451         }
    452 
    453         /* Notifying the connection state change of the profile before sending the intent for
    454            connection state change, as it was causing a race condition, with the UI not being
    455            updated with the correct connection state. */
    456         Log.d(TAG, "Pan Device state : device: " + device + " State:" +
    457                        prevState + "->" + state);
    458         notifyProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState);
    459         Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
    460         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    461         intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
    462         intent.putExtra(BluetoothPan.EXTRA_STATE, state);
    463         intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role);
    464         sendBroadcast(intent, BLUETOOTH_PERM);
    465     }
    466 
    467     // configured when we start tethering
    468     private String enableTethering(String iface) {
    469         if (DBG) Log.d(TAG, "updateTetherState:" + iface);
    470 
    471         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    472         INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
    473         ConnectivityManager cm =
    474             (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    475         String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
    476 
    477         // bring toggle the interfaces
    478         String[] currentIfaces = new String[0];
    479         try {
    480             currentIfaces = service.listInterfaces();
    481         } catch (Exception e) {
    482             Log.e(TAG, "Error listing Interfaces :" + e);
    483             return null;
    484         }
    485 
    486         boolean found = false;
    487         for (String currIface: currentIfaces) {
    488             if (currIface.equals(iface)) {
    489                 found = true;
    490                 break;
    491             }
    492         }
    493 
    494         if (!found) return null;
    495 
    496         String address = createNewTetheringAddressLocked();
    497         if (address == null) return null;
    498 
    499         InterfaceConfiguration ifcg = null;
    500         try {
    501             ifcg = service.getInterfaceConfig(iface);
    502             if (ifcg != null) {
    503                 InetAddress addr = null;
    504                 LinkAddress linkAddr = ifcg.getLinkAddress();
    505                 if (linkAddr == null || (addr = linkAddr.getAddress()) == null ||
    506                         addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
    507                         addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
    508                     addr = NetworkUtils.numericToInetAddress(address);
    509                 }
    510                 ifcg.setInterfaceUp();
    511                 ifcg.setLinkAddress(new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH));
    512                 ifcg.clearFlag("running");
    513                 // TODO(BT) ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
    514                 service.setInterfaceConfig(iface, ifcg);
    515                 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
    516                     Log.e(TAG, "Error tethering "+iface);
    517                 }
    518             }
    519         } catch (Exception e) {
    520             Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
    521             return null;
    522         }
    523         return address;
    524     }
    525 
    526     private String createNewTetheringAddressLocked() {
    527         if (getConnectedPanDevices().size() == mMaxPanDevices) {
    528             if (DBG) Log.d(TAG, "Max PAN device connections reached");
    529             return null;
    530         }
    531         String address = BLUETOOTH_IFACE_ADDR_START;
    532         while (true) {
    533             if (mBluetoothIfaceAddresses.contains(address)) {
    534                 String[] addr = address.split("\\.");
    535                 Integer newIp = Integer.parseInt(addr[2]) + 1;
    536                 address = address.replace(addr[2], newIp.toString());
    537             } else {
    538                 break;
    539             }
    540         }
    541         mBluetoothIfaceAddresses.add(address);
    542         return address;
    543     }
    544 
    545     private List<BluetoothDevice> getConnectedPanDevices() {
    546         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    547 
    548         for (BluetoothDevice device: mPanDevices.keySet()) {
    549             if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
    550                 devices.add(device);
    551             }
    552         }
    553         return devices;
    554     }
    555 
    556     private int getPanDeviceConnectionState(BluetoothDevice device) {
    557         BluetoothPanDevice panDevice = mPanDevices.get(device);
    558         if (panDevice == null) {
    559             return BluetoothProfile.STATE_DISCONNECTED;
    560         }
    561         return panDevice.mState;
    562     }
    563 
    564     @Override
    565     public void dump(StringBuilder sb) {
    566         super.dump(sb);
    567         println(sb, "mMaxPanDevices: " + mMaxPanDevices);
    568         println(sb, "mPanIfName: " + mPanIfName);
    569         println(sb, "mTetherOn: " + mTetherOn);
    570         println(sb, "mPanDevices:");
    571         for (BluetoothDevice device : mPanDevices.keySet()) {
    572             println(sb, "  " + device + " : " + mPanDevices.get(device));
    573         }
    574         println(sb, "mBluetoothIfaceAddresses:");
    575         for (String address : mBluetoothIfaceAddresses) {
    576             println(sb, "  " + address);
    577         }
    578     }
    579 
    580     private class BluetoothPanDevice {
    581         private int mState;
    582         private String mIfaceAddr;
    583         private String mIface;
    584         private int mLocalRole; // Which local role is this PAN device bound to
    585 
    586         BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
    587             mState = state;
    588             mIfaceAddr = ifaceAddr;
    589             mIface = iface;
    590             mLocalRole = localRole;
    591         }
    592     }
    593 
    594     // Constants matching Hal header file bt_hh.h
    595     // bthh_connection_state_t
    596     private final static int CONN_STATE_CONNECTED = 0;
    597     private final static int CONN_STATE_CONNECTING = 1;
    598     private final static int CONN_STATE_DISCONNECTED = 2;
    599     private final static int CONN_STATE_DISCONNECTING = 3;
    600 
    601     private native static void classInitNative();
    602     private native void initializeNative();
    603     private native void cleanupNative();
    604     private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
    605     private native boolean disconnectPanNative(byte[] btAddress);
    606     private native boolean enablePanNative(int local_role);
    607     private native int getPanLocalRoleNative();
    608 
    609 }
    610