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