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