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             if (service == null) return false;
230             return service.isPanUOn();
231         }
isTetheringOn()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         }
setBluetoothTethering(boolean value)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 
getConnectedDevices()245         public List<BluetoothDevice> getConnectedDevices() {
246             PanService service = getService();
247             if (service == null) return new ArrayList<BluetoothDevice>(0);
248             return service.getConnectedDevices();
249         }
250 
getDevicesMatchingConnectionStates(int[] states)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 
connect(BluetoothDevice device)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 
disconnect(BluetoothDevice device)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 
getConnectionState(BluetoothDevice device)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 
isPanNapOn()284     boolean isPanNapOn() {
285         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
286         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
287     }
isPanUOn()288      boolean isPanUOn() {
289         if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
290         return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
291     }
isTetheringOn()292      boolean isTetheringOn() {
293         // TODO(BT) have a variable marking the on/off state
294         return mTetherOn;
295     }
296 
setBluetoothTethering(boolean value)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 
getConnectedDevices()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 
getDevicesMatchingConnectionStates(int[] states)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 {
ConnectState(byte[] address, int state, int error, int local_role, int remote_role)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     };
onConnectStateChanged(byte[] address, int state, int error, int local_role, int remote_role)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     }
onControlStateChanged(int local_role, int state, int error, String ifname)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 
convertHalState(int halState)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 
handlePanDeviceStateChange(BluetoothDevice device, String iface, int state, int local_role, int remote_role)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
enableTethering(String iface)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 
createNewTetheringAddressLocked()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 
getConnectedPanDevices()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 
getPanDeviceConnectionState(BluetoothDevice device)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
dump(StringBuilder sb)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 
BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole)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 
classInitNative()602     private native static void classInitNative();
initializeNative()603     private native void initializeNative();
cleanupNative()604     private native void cleanupNative();
connectPanNative(byte[] btAddress, int local_role, int remote_role)605     private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
disconnectPanNative(byte[] btAddress)606     private native boolean disconnectPanNative(byte[] btAddress);
enablePanNative(int local_role)607     private native boolean enablePanNative(int local_role);
getPanLocalRoleNative()608     private native int getPanLocalRoleNative();
609 
610 }
611