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