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