diff options
author | Jaikumar Ganesh <jaikumar@google.com> | 2011-02-11 15:46:54 -0800 |
---|---|---|
committer | Jaikumar Ganesh <jaikumar@google.com> | 2011-02-14 15:02:42 -0800 |
commit | a8571f15c39da2b7c6e672fef26106f5ac60dea0 (patch) | |
tree | 7d8b6b73c989043772a6a74c3df0a63f74f7d23e /core/java/android/server/BluetoothPanProfileHandler.java | |
parent | a44a1e77ef435942b2ff60e9e60d3a64b43d775b (diff) | |
download | frameworks_base-a8571f15c39da2b7c6e672fef26106f5ac60dea0.zip frameworks_base-a8571f15c39da2b7c6e672fef26106f5ac60dea0.tar.gz frameworks_base-a8571f15c39da2b7c6e672fef26106f5ac60dea0.tar.bz2 |
Remove Bluetooth Pan operations from Bluetooth Service.
Change-Id: I4dfe14ce04ec357a952b577d452da2257165fc6f
Diffstat (limited to 'core/java/android/server/BluetoothPanProfileHandler.java')
-rw-r--r-- | core/java/android/server/BluetoothPanProfileHandler.java | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java new file mode 100644 index 0000000..e2d44e4 --- /dev/null +++ b/core/java/android/server/BluetoothPanProfileHandler.java @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.server; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothTetheringDataTracker; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources.NotFoundException; +import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; +import android.net.LinkAddress; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.ServiceManager; +import android.util.Log; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This handles the PAN profile. All calls into this are made + * from Bluetooth Service. + * @hide + */ + +public class BluetoothPanProfileHandler { + private static final String TAG = "BluetoothPanProfileHandler"; + private static final boolean DBG = true; + + private ArrayList<String> mBluetoothIfaceAddresses; + private int mMaxPanDevices; + + private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; + private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; + private static final String BLUETOOTH_NETMASK = "255.255.255.0"; + public static BluetoothPanProfileHandler sInstance; + private final HashMap<BluetoothDevice, BluetoothPanDevice> mPanDevices; + private boolean mTetheringOn; + private Context mContext; + private BluetoothService mBluetoothService; + + private BluetoothPanProfileHandler(Context context, BluetoothService service) { + mContext = context; + mPanDevices = new HashMap<BluetoothDevice, BluetoothPanDevice>(); + mBluetoothService = service; + mTetheringOn = false; + mBluetoothIfaceAddresses = new ArrayList<String>(); + try { + mMaxPanDevices = context.getResources().getInteger( + com.android.internal.R.integer.config_max_pan_devices); + } catch (NotFoundException e) { + mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; + } + } + + public static synchronized BluetoothPanProfileHandler getInstance(Context context, + BluetoothService service) { + if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service); + return sInstance; + } + + public Object Clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + public synchronized boolean isTetheringOn() { + return mTetheringOn; + } + + /*package*/ synchronized boolean allowIncomingTethering() { + if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) + return true; + return false; + } + + private BroadcastReceiver mTetheringReceiver = null; + + synchronized void setBluetoothTethering(boolean value) { + if (!value) { + disconnectPanServerDevices(); + } + + if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) { + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mTetheringReceiver = new BroadcastReceiver() { + @Override + public synchronized void onReceive(Context context, Intent intent) { + if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) + == BluetoothAdapter.STATE_ON) { + mTetheringOn = true; + mContext.unregisterReceiver(mTetheringReceiver); + } + } + }; + mContext.registerReceiver(mTetheringReceiver, filter); + } else { + mTetheringOn = value; + } + } + + synchronized int getPanDeviceState(BluetoothDevice device) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + if (panDevice == null) { + return BluetoothPan.STATE_DISCONNECTED; + } + return panDevice.mState; + } + + synchronized boolean connectPanDevice(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")"); + if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) { + errorLog(device + " already connected to PAN"); + } + + int connectedCount = 0; + for (BluetoothDevice panDevice: mPanDevices.keySet()) { + if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) { + connectedCount ++; + } + } + if (connectedCount > 8) { + debugLog(device + " could not connect to PAN because 8 other devices are" + + "already connected"); + return false; + } + + handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING, + BluetoothPan.LOCAL_PANU_ROLE); + if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) { + debugLog("connecting to PAN"); + return true; + } else { + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED, + BluetoothPan.LOCAL_PANU_ROLE); + errorLog("could not connect to PAN"); + return false; + } + } + + private synchronized boolean disconnectPanServerDevices() { + debugLog("disconnect all PAN devices"); + + for (BluetoothDevice device: mPanDevices.keySet()) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + int state = panDevice.mState; + if (state == BluetoothPan.STATE_CONNECTED && + panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, + panDevice.mLocalRole); + + if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, + device.getAddress(), + panDevice.mIfaceAddr)) { + errorLog("could not disconnect Pan Server Device "+device.getAddress()); + + // Restore prev state + handlePanDeviceStateChange(device, state, + panDevice.mLocalRole); + + return false; + } + } + } + return true; + } + + synchronized List<BluetoothDevice> getConnectedPanDevices() { + List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); + + for (BluetoothDevice device: mPanDevices.keySet()) { + if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { + devices.add(device); + } + } + return devices; + } + + synchronized boolean disconnectPanDevice(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + debugLog("disconnect PAN(" + objectPath + ")"); + + int state = getPanDeviceState(device); + if (state != BluetoothPan.STATE_CONNECTED) { + debugLog(device + " already disconnected from PAN"); + return false; + } + + BluetoothPanDevice panDevice = mPanDevices.get(device); + + if (panDevice == null) { + errorLog("No record for this Pan device:" + device); + return false; + } + + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, + panDevice.mLocalRole); + if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { + if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(), + panDevice.mIface)) { + // Restore prev state, this shouldn't happen + handlePanDeviceStateChange(device, state, panDevice.mLocalRole); + return false; + } + } else { + if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) { + // Restore prev state, this shouldn't happen + handlePanDeviceStateChange(device, state, panDevice.mLocalRole); + return false; + } + } + return true; + } + + synchronized void handlePanDeviceStateChange(BluetoothDevice device, + String iface, int state, int role) { + int prevState; + String ifaceAddr = null; + BluetoothPanDevice panDevice = mPanDevices.get(device); + + if (panDevice == null) { + prevState = BluetoothPan.STATE_DISCONNECTED; + } else { + prevState = panDevice.mState; + ifaceAddr = panDevice.mIfaceAddr; + } + if (prevState == state) return; + + if (role == BluetoothPan.LOCAL_NAP_ROLE) { + if (state == BluetoothPan.STATE_CONNECTED) { + ifaceAddr = enableTethering(iface); + if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); + } else if (state == BluetoothPan.STATE_DISCONNECTED) { + if (ifaceAddr != null) { + mBluetoothIfaceAddresses.remove(ifaceAddr); + ifaceAddr = null; + } + } + } else { + // PANU Role = reverse Tether + if (state == BluetoothPan.STATE_CONNECTED) { + BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device); + } else if (state == BluetoothPan.STATE_DISCONNECTED && + (prevState == BluetoothPan.STATE_CONNECTED || + prevState == BluetoothPan.STATE_DISCONNECTING)) { + BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface); + } + } + + if (panDevice == null) { + panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role); + mPanDevices.put(device, panDevice); + } else { + panDevice.mState = state; + panDevice.mIfaceAddr = ifaceAddr; + panDevice.mLocalRole = role; + } + + if (state == BluetoothPan.STATE_DISCONNECTED) { + mPanDevices.remove(device); + } + + Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState); + intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state); + intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); + mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); + + debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state); + mBluetoothService.sendConnectionStateChange(device, state, prevState); + } + + /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + int state, int role) { + handlePanDeviceStateChange(device, null, state, role); + } + + private class BluetoothPanDevice { + private int mState; + private String mIfaceAddr; + private String mIface; + private int mLocalRole; // Which local role is this PAN device bound too + + BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) { + mState = state; + mIfaceAddr = ifaceAddr; + mIface = iface; + mLocalRole = localRole; + } + } + + private String createNewTetheringAddressLocked() { + if (getConnectedPanDevices().size() == mMaxPanDevices) { + debugLog ("Max PAN device connections reached"); + return null; + } + String address = BLUETOOTH_IFACE_ADDR_START; + while (true) { + if (mBluetoothIfaceAddresses.contains(address)) { + String[] addr = address.split("\\."); + Integer newIp = Integer.parseInt(addr[2]) + 1; + address = address.replace(addr[2], newIp.toString()); + } else { + break; + } + } + mBluetoothIfaceAddresses.add(address); + return address; + } + + // configured when we start tethering + private synchronized String enableTethering(String iface) { + debugLog("updateTetherState:" + iface); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + ConnectivityManager cm = + (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); + + // bring toggle the interfaces + String[] currentIfaces = new String[0]; + try { + currentIfaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return null; + } + + boolean found = false; + for (String currIface: currentIfaces) { + if (currIface.equals(iface)) { + found = true; + break; + } + } + + if (!found) return null; + + String address = createNewTetheringAddressLocked(); + if (address == null) return null; + + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + if (ifcg != null) { + InetAddress mask = InetAddress.getByName(BLUETOOTH_NETMASK); + InetAddress addr = null; + if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null || + addr.equals(InetAddress.getByName("0.0.0.0")) || + addr.equals(InetAddress.getByName("::0"))) { + addr = InetAddress.getByName(address); + } + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + ifcg.addr = new LinkAddress(addr, mask); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); + service.setInterfaceConfig(iface, ifcg); + if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Log.e(TAG, "Error tethering "+iface); + } + } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface + ", :" + e); + return null; + } + return address; + } + + private static void debugLog(String msg) { + if (DBG) Log.d(TAG, msg); + } + + private static void errorLog(String msg) { + Log.e(TAG, msg); + } +} |