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