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