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