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