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