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