summaryrefslogtreecommitdiffstats
path: root/core/java/android/server/BluetoothPanProfileHandler.java
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2011-02-11 15:46:54 -0800
committerJaikumar Ganesh <jaikumar@google.com>2011-02-14 15:02:42 -0800
commita8571f15c39da2b7c6e672fef26106f5ac60dea0 (patch)
tree7d8b6b73c989043772a6a74c3df0a63f74f7d23e /core/java/android/server/BluetoothPanProfileHandler.java
parenta44a1e77ef435942b2ff60e9e60d3a64b43d775b (diff)
downloadframeworks_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.java402
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);
+ }
+}