PanService.java revision 74ae04c73312403e89db0f8e9bd9601d403b4783
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.IBinder;
23import android.os.INetworkManagementService;
24import android.os.Handler;
25import android.os.Message;
26import android.os.RemoteException;
27import android.os.ServiceManager;
28import android.provider.Settings;
29import android.util.Log;
30
31import java.net.InetAddress;
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.HashMap;
35import java.util.List;
36import java.util.Map;
37import com.android.bluetooth.Utils;
38import com.android.bluetooth.btservice.ProfileService;
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        if (DBG) log("stop");
93        mHandler.removeCallbacksAndMessages(null);
94        return true;
95    }
96
97    protected boolean cleanup() {
98        if (mNativeAvailable) {
99            cleanupNative();
100            mNativeAvailable=false;
101        }
102        if(mPanDevices != null) {
103            mPanDevices.clear();
104            mPanDevices = null;
105        }
106        if(mBluetoothIfaceAddresses != null) {
107            mBluetoothIfaceAddresses.clear();
108            mBluetoothIfaceAddresses = null;
109        }
110        if(mPanIfName != null)
111            mPanIfName = null;
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 (mService  != null && mService.isAvailable()) {
171                return mService;
172            }
173            return null;
174        }
175        public boolean connect(BluetoothDevice device) {
176            PanService service = getService();
177            if (service == null) return false;
178            return service.connect(device);
179        }
180        public boolean disconnect(BluetoothDevice device) {
181            PanService service = getService();
182            if (service == null) return false;
183            return service.disconnect(device);
184        }
185        public int getConnectionState(BluetoothDevice device) {
186            PanService service = getService();
187            if (service == null) return BluetoothPan.STATE_DISCONNECTED;
188            return service.getConnectionState(device);
189        }
190        private boolean isPanNapOn() {
191            PanService service = getService();
192            if (service == null) return false;
193            return service.isPanNapOn();
194        }
195        private boolean isPanUOn() {
196            if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
197            PanService service = getService();
198            return service.isPanUOn();
199        }
200        public boolean isTetheringOn() {
201            // TODO(BT) have a variable marking the on/off state
202            PanService service = getService();
203            if (service == null) return false;
204            return service.isTetheringOn();
205        }
206        public void setBluetoothTethering(boolean value) {
207            PanService service = getService();
208            if (service == null) return;
209            if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + service.mTetherOn);
210            service.setBluetoothTethering(value);
211        }
212
213        public List<BluetoothDevice> getConnectedDevices() {
214            PanService service = getService();
215            if (service == null) return new ArrayList<BluetoothDevice>(0);
216            return service.getConnectedDevices();
217        }
218
219        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
220            PanService service = getService();
221            if (service == null) return new ArrayList<BluetoothDevice>(0);
222            return service.getDevicesMatchingConnectionStates(states);
223        }
224    };
225
226    boolean connect(BluetoothDevice device) {
227        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
228        if (getConnectionState(device) != BluetoothProfile.STATE_DISCONNECTED) {
229            Log.e(TAG, "Pan Device not disconnected: " + device);
230            return false;
231        }
232        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT,device);
233        mHandler.sendMessage(msg);
234        return true;
235    }
236
237    boolean disconnect(BluetoothDevice device) {
238        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
239        Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device);
240        mHandler.sendMessage(msg);
241        return true;
242    }
243
244    int getConnectionState(BluetoothDevice device) {
245        BluetoothPanDevice panDevice = mPanDevices.get(device);
246        if (panDevice == null) {
247            return BluetoothPan.STATE_DISCONNECTED;
248        }
249        return panDevice.mState;
250    }
251
252    boolean isPanNapOn() {
253        if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
254        return (getPanLocalRoleNative() & BluetoothPan.LOCAL_NAP_ROLE) != 0;
255    }
256     boolean isPanUOn() {
257        if(DBG) Log.d(TAG, "isTetheringOn call getPanLocalRoleNative");
258        return (getPanLocalRoleNative() & BluetoothPan.LOCAL_PANU_ROLE) != 0;
259    }
260     boolean isTetheringOn() {
261        // TODO(BT) have a variable marking the on/off state
262        return mTetherOn;
263    }
264
265    void setBluetoothTethering(boolean value) {
266        if(DBG) Log.d(TAG, "setBluetoothTethering: " + value +", mTetherOn: " + mTetherOn);
267        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
268        if(mTetherOn != value) {
269            //drop any existing panu or pan-nap connection when changing the tethering state
270            mTetherOn = value;
271            List<BluetoothDevice> DevList = getConnectedDevices();
272            for(BluetoothDevice dev : DevList)
273                disconnect(dev);
274        }
275    }
276
277    List<BluetoothDevice> getConnectedDevices() {
278        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
279        List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
280                new int[] {BluetoothProfile.STATE_CONNECTED});
281        return devices;
282    }
283
284    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
285         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
286        List<BluetoothDevice> panDevices = new ArrayList<BluetoothDevice>();
287
288        for (BluetoothDevice device: mPanDevices.keySet()) {
289            int panDeviceState = getConnectionState(device);
290            for (int state : states) {
291                if (state == panDeviceState) {
292                    panDevices.add(device);
293                    break;
294                }
295            }
296        }
297        return panDevices;
298    }
299
300    static protected class ConnectState {
301        public ConnectState(byte[] address, int state, int error, int local_role, int remote_role) {
302            this.addr = address;
303            this.state = state;
304            this.error = error;
305            this.local_role = local_role;
306            this.remote_role = remote_role;
307        }
308        byte[] addr;
309        int state;
310        int error;
311        int local_role;
312        int remote_role;
313    };
314    private void onConnectStateChanged(byte[] address, int state, int error, int local_role, int remote_role) {
315        if (DBG) log("onConnectStateChanged: " + state + ", local role:" + local_role + ", remote_role: " + remote_role);
316        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
317        msg.obj = new ConnectState(address, state, error, local_role, remote_role);
318        mHandler.sendMessage(msg);
319    }
320    private void onControlStateChanged(int local_role, int state, int error, String ifname) {
321        if (DBG)
322            log("onControlStateChanged: " + state + ", error: " + error + ", ifname: " + ifname);
323        if(error == 0)
324            mPanIfName = ifname;
325    }
326
327    private static int convertHalState(int halState) {
328        switch (halState) {
329            case CONN_STATE_CONNECTED:
330                return BluetoothProfile.STATE_CONNECTED;
331            case CONN_STATE_CONNECTING:
332                return BluetoothProfile.STATE_CONNECTING;
333            case CONN_STATE_DISCONNECTED:
334                return BluetoothProfile.STATE_DISCONNECTED;
335            case CONN_STATE_DISCONNECTING:
336                return BluetoothProfile.STATE_DISCONNECTING;
337            default:
338                Log.e(TAG, "bad pan connection state: " + halState);
339                return BluetoothProfile.STATE_DISCONNECTED;
340        }
341    }
342
343    void handlePanDeviceStateChange(BluetoothDevice device,
344                                    String iface, int state, int local_role, int remote_role) {
345        if(DBG) Log.d(TAG, "handlePanDeviceStateChange: device: " + device + ", iface: " + iface +
346                    ", state: " + state + ", local_role:" + local_role + ", remote_role:" + remote_role);
347        int prevState;
348        String ifaceAddr = null;
349        BluetoothPanDevice panDevice = mPanDevices.get(device);
350        if (panDevice == null) {
351            prevState = BluetoothProfile.STATE_DISCONNECTED;
352        } else {
353            prevState = panDevice.mState;
354            ifaceAddr = panDevice.mIfaceAddr;
355        }
356        Log.d(TAG, "handlePanDeviceStateChange preState: " + prevState + " state: " + state);
357        if (prevState == state) return;
358        if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) {
359            Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE");
360            if (state == BluetoothProfile.STATE_CONNECTED) {
361                if(!mTetherOn) {
362                    Log.d(TAG, "handlePanDeviceStateChange bluetooth tethering is off, drop the connection");
363                    disconnectPanNative(Utils.getByteAddress(device));
364                    return;
365                }
366                ifaceAddr = enableTethering(iface);
367                if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
368
369            } else if (state == BluetoothProfile.STATE_DISCONNECTED) {
370                if (ifaceAddr != null) {
371                    mBluetoothIfaceAddresses.remove(ifaceAddr);
372                    ifaceAddr = null;
373                }
374            }
375        } else {
376            // PANU Role = reverse Tether
377            Log.d(TAG, "handlePanDeviceStateChange LOCAL_PANU_ROLE:REMOTE_NAP_ROLE");
378            if (state == BluetoothProfile.STATE_CONNECTED) {
379                if(DBG) Log.d(TAG, "handlePanDeviceStateChange: panu STATE_CONNECTED, startReverseTether");
380                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
381                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
382                Log.d(TAG, "call INetworkManagementService.startReverseTethering()");
383                try {
384                    service.startReverseTethering(iface);
385                } catch (Exception e) {
386                    Log.e(TAG, "Cannot start reverse tethering: " + e);
387                    return;
388                }
389            } else if (state == BluetoothProfile.STATE_DISCONNECTED &&
390                  (prevState == BluetoothProfile.STATE_CONNECTED ||
391                  prevState == BluetoothProfile.STATE_DISCONNECTING)) {
392                if(DBG) Log.d(TAG, "handlePanDeviceStateChange: stopReverseTether, panDevice.mIface: "
393                                    + panDevice.mIface);
394                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
395                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
396                try {
397                    service.stopReverseTethering();
398                } catch(Exception e) {
399                    Log.e(TAG, "Cannot stop reverse tethering: " + e);
400                    return;
401                }
402            }
403        }
404
405        if (panDevice == null) {
406            panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, local_role);
407            mPanDevices.put(device, panDevice);
408        } else {
409            panDevice.mState = state;
410            panDevice.mIfaceAddr = ifaceAddr;
411            panDevice.mLocalRole = local_role;
412            panDevice.mIface = iface;
413        }
414
415        Intent intent = new Intent(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
416        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
417        intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_STATE, prevState);
418        intent.putExtra(BluetoothPan.EXTRA_STATE, state);
419        intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, local_role);
420        sendBroadcast(intent, BLUETOOTH_PERM);
421
422        if (DBG) Log.d(TAG, "Pan Device state : device: " + device + " State:" +
423                       prevState + "->" + state);
424        notifyProfileConnectionStateChanged(device, BluetoothProfile.PAN, state, prevState);
425    }
426
427    // configured when we start tethering
428    private String enableTethering(String iface) {
429        if (DBG) Log.d(TAG, "updateTetherState:" + iface);
430
431        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
432        INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
433        ConnectivityManager cm =
434            (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
435        String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
436
437        // bring toggle the interfaces
438        String[] currentIfaces = new String[0];
439        try {
440            currentIfaces = service.listInterfaces();
441        } catch (Exception e) {
442            Log.e(TAG, "Error listing Interfaces :" + e);
443            return null;
444        }
445
446        boolean found = false;
447        for (String currIface: currentIfaces) {
448            if (currIface.equals(iface)) {
449                found = true;
450                break;
451            }
452        }
453
454        if (!found) return null;
455
456        String address = createNewTetheringAddressLocked();
457        if (address == null) return null;
458
459        InterfaceConfiguration ifcg = null;
460        try {
461            ifcg = service.getInterfaceConfig(iface);
462            if (ifcg != null) {
463                InetAddress addr = null;
464                if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
465                        addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
466                        addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
467                    addr = NetworkUtils.numericToInetAddress(address);
468                }
469                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
470                ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH);
471                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
472                ifcg.interfaceFlags = ifcg.interfaceFlags.replace("  "," ");
473                service.setInterfaceConfig(iface, ifcg);
474                if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
475                    Log.e(TAG, "Error tethering "+iface);
476                }
477            }
478        } catch (Exception e) {
479            Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
480            return null;
481        }
482        return address;
483    }
484
485    private String createNewTetheringAddressLocked() {
486        if (getConnectedPanDevices().size() == mMaxPanDevices) {
487            if (DBG) Log.d(TAG, "Max PAN device connections reached");
488            return null;
489        }
490        String address = BLUETOOTH_IFACE_ADDR_START;
491        while (true) {
492            if (mBluetoothIfaceAddresses.contains(address)) {
493                String[] addr = address.split("\\.");
494                Integer newIp = Integer.parseInt(addr[2]) + 1;
495                address = address.replace(addr[2], newIp.toString());
496            } else {
497                break;
498            }
499        }
500        mBluetoothIfaceAddresses.add(address);
501        return address;
502    }
503
504    private List<BluetoothDevice> getConnectedPanDevices() {
505        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
506
507        for (BluetoothDevice device: mPanDevices.keySet()) {
508            if (getPanDeviceConnectionState(device) == BluetoothProfile.STATE_CONNECTED) {
509                devices.add(device);
510            }
511        }
512        return devices;
513    }
514
515    private int getPanDeviceConnectionState(BluetoothDevice device) {
516        BluetoothPanDevice panDevice = mPanDevices.get(device);
517        if (panDevice == null) {
518            return BluetoothProfile.STATE_DISCONNECTED;
519        }
520        return panDevice.mState;
521    }
522
523    private class BluetoothPanDevice {
524        private int mState;
525        private String mIfaceAddr;
526        private String mIface;
527        private int mLocalRole; // Which local role is this PAN device bound to
528
529        BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) {
530            mState = state;
531            mIfaceAddr = ifaceAddr;
532            mIface = iface;
533            mLocalRole = localRole;
534        }
535    }
536
537    // Constants matching Hal header file bt_hh.h
538    // bthh_connection_state_t
539    private final static int CONN_STATE_CONNECTED = 0;
540    private final static int CONN_STATE_CONNECTING = 1;
541    private final static int CONN_STATE_DISCONNECTED = 2;
542    private final static int CONN_STATE_DISCONNECTING = 3;
543
544    private native static void classInitNative();
545    private native void initializeNative();
546    private native void cleanupNative();
547    private native boolean connectPanNative(byte[] btAddress, int local_role, int remote_role);
548    private native boolean disconnectPanNative(byte[] btAddress);
549    private native boolean enablePanNative(int local_role);
550    private native int getPanLocalRoleNative();
551
552}
553