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