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 {
classInitNative()81         classInitNative();
82     }
83 
getName()84     protected String getName() {
85         return TAG;
86     }
87 
initBinder()88     public IProfileServiceBinder initBinder() {
89         return new BluetoothPanBinder(this);
90     }
91 
start()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 
stop()110     protected boolean stop() {
111         mHandler.removeCallbacksAndMessages(null);
112         return true;
113     }
114 
cleanup()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;
BluetoothPanBinder(PanService svc)188         public BluetoothPanBinder(PanService svc) {
189             mService = svc;
190         }
cleanup()191         public boolean cleanup() {
192             mService = null;
193             return true;
194         }
getService()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         }
connect(BluetoothDevice device)206         public boolean connect(BluetoothDevice device) {
207             PanService service = getService();
208             if (service == null) return false;
209             return service.connect(device);
210         }
disconnect(BluetoothDevice device)211         public boolean disconnect(BluetoothDevice device) {
212             PanService service = getService();
213             if (service == null) return false;
214             return service.disconnect(device);
215         }
getConnectionState(BluetoothDevice device)216         public int getConnectionState(BluetoothDevice device) {
217             PanService service = getService();
218             if (service == null) return BluetoothPan.STATE_DISCONNECTED;
219             return service.getConnectionState(device);
220         }
isPanNapOn()221         private boolean isPanNapOn() {
222             PanService service = getService();
223             if (service == null) return false;
224             return service.isPanNapOn();
225         }
isPanUOn()226         private boolean isPanUOn() {
227             if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
228             PanService service = getService();
229             return service.isPanUOn();
230         }
isTetheringOn()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         }
setBluetoothTethering(boolean value)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 
getConnectedDevices()244         public List<BluetoothDevice> getConnectedDevices() {
245             PanService service = getService();
246             if (service == null) return new ArrayList<BluetoothDevice>(0);
247             return service.getConnectedDevices();
248         }
249 
getDevicesMatchingConnectionStates(int[] states)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 
connect(BluetoothDevice device)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 
disconnect(BluetoothDevice device)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 
getConnectionState(BluetoothDevice device)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 
isPanNapOn()283     boolean isPanNapOn() {
284         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
285         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
286     }
isPanUOn()287      boolean isPanUOn() {
288         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
289         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
290     }
isTetheringOn()291      boolean isTetheringOn() {
292         // TODO(BT) have a variable marking the on/off state
293         return mTetherOn;
294     }
295 
setBluetoothTethering(boolean value)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 
getConnectedDevices()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 
getDevicesMatchingConnectionStates(int[] states)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 {
ConnectState(byte[] address, int state, int error, int local_role, int remote_role)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     };
onConnectStateChanged(byte[] address, int state, int error, int local_role, int remote_role)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     }
onControlStateChanged(int local_role, int state, int error, String ifname)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 
convertHalState(int halState)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 
handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int local_role, int remote_role)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
enableTethering(String iface)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 
createNewTetheringAddressLocked()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 
getConnectedPanDevices()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 
getPanDeviceConnectionState(BluetoothDevice device)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
dump(StringBuilder sb)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 
BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole)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 
classInitNative()601     private native static void classInitNative();
initializeNative()602     private native void initializeNative();
cleanupNative()603     private native void cleanupNative();
connectPanNative(byte[] btAddress, int local_role, int remote_role)604     private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
disconnectPanNative(byte[] btAddress)605     private native boolean disconnectPanNative(byte[] btAddress);
enablePanNative(int local_role)606     private native boolean enablePanNative(int local_role);
getPanLocalRoleNative()607     private native int getPanLocalRoleNative();
608 
609 }
610