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