summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanica Chang <danicachang@google.com>2010-08-18 16:58:37 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2010-08-18 16:58:37 -0700
commit7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7 (patch)
treec58a824ee8021c21f5152f0b664fe53310f36811
parentb8ea4aa79d395eb392d112290cd7d292861c6b6a (diff)
parent6fdd0c6274c81b337ad35b70480f881daf7354c3 (diff)
downloadframeworks_base-7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7.zip
frameworks_base-7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7.tar.gz
frameworks_base-7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7.tar.bz2
Merge "bluetooth tethering"
-rw-r--r--core/java/android/bluetooth/BluetoothDevicePicker.java5
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java192
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java13
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl7
-rw-r--r--core/java/android/net/ConnectivityManager.java15
-rw-r--r--core/java/android/net/IConnectivityManager.aidl18
-rw-r--r--core/java/android/server/BluetoothEventLoop.java49
-rw-r--r--core/java/android/server/BluetoothService.java215
-rw-r--r--core/jni/android_bluetooth_common.cpp12
-rw-r--r--core/jni/android_bluetooth_common.h1
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp53
-rw-r--r--core/jni/android_server_BluetoothService.cpp111
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--services/java/com/android/server/ConnectivityService.java12
-rw-r--r--services/java/com/android/server/NativeDaemonConnector.java12
-rw-r--r--services/java/com/android/server/connectivity/Tethering.java47
16 files changed, 736 insertions, 32 deletions
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
index 05eed0e..7415721 100644
--- a/core/java/android/bluetooth/BluetoothDevicePicker.java
+++ b/core/java/android/bluetooth/BluetoothDevicePicker.java
@@ -63,4 +63,9 @@ public interface BluetoothDevicePicker {
public static final int FILTER_TYPE_AUDIO = 1;
/** Ask device picker to show BT devices that support Object Transfer */
public static final int FILTER_TYPE_TRANSFER = 2;
+ /** Ask device picker to show BT devices that support
+ * Personal Area Networking User (PANU) profile*/
+ public static final int FILTER_TYPE_PANU = 3;
+ /** Ask device picker to show BT devices that support Network Access Point (NAP) profile */
+ public static final int FILTER_TYPE_NAP = 4;
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
new file mode 100644
index 0000000..952765d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2008 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.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @hide
+ */
+public final class BluetoothPan {
+ private static final String TAG = "BluetoothPan";
+ private static final boolean DBG = false;
+
+ /** int extra for ACTION_PAN_STATE_CHANGED */
+ public static final String EXTRA_PAN_STATE =
+ "android.bluetooth.pan.extra.STATE";
+ /** int extra for ACTION_PAN_STATE_CHANGED */
+ public static final String EXTRA_PREVIOUS_PAN_STATE =
+ "android.bluetooth.pan.extra.PREVIOUS_STATE";
+
+ /** Indicates the state of an PAN device has changed.
+ * This intent will always contain EXTRA_DEVICE_STATE,
+ * EXTRA_PREVIOUS_DEVICE_STATE and BluetoothDevice.EXTRA_DEVICE
+ * extras.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PAN_STATE_CHANGED =
+ "android.bluetooth.pan.action.STATE_CHANGED";
+
+ public static final String NAP_ROLE = "nap";
+ public static final String NAP_BRIDGE = "pan1";
+
+ public static final int MAX_CONNECTIONS = 7;
+
+ public static final int STATE_DISCONNECTED = 0;
+ public static final int STATE_CONNECTING = 1;
+ public static final int STATE_CONNECTED = 2;
+ public static final int STATE_DISCONNECTING = 3;
+
+ private final IBluetooth mService;
+ private final Context mContext;
+
+ /**
+ * Create a BluetoothPan proxy object for interacting with the local
+ * Bluetooth Pan service.
+ * @param c Context
+ */
+ public BluetoothPan(Context c) {
+ mContext = c;
+
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+ if (b != null) {
+ mService = IBluetooth.Stub.asInterface(b);
+ } else {
+ Log.w(TAG, "Bluetooth Service not available!");
+
+ // Instead of throwing an exception which prevents people from going
+ // into Wireless settings in the emulator. Let it crash later
+ // when it is actually used.
+ mService = null;
+ }
+ }
+
+ /**
+ * Initiate a PAN connection.
+ *
+ * This function returns false on error and true if the connection
+ * attempt is being made.
+ *
+ * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when the
+ * connection is completed.
+ *
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean connect(BluetoothDevice device) {
+ if (DBG) log("connect(" + device + ")");
+ try {
+ return mService.connectPanDevice(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /**
+ * Initiate disconnect from PAN.
+ *
+ * This function return false on error and true if the disconnection
+ * attempt is being made.
+ *
+ * Listen for {@link #ACTION_PAN_STATE_CHANGED} to find out when
+ * disconnect is completed.
+ *
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean disconnect(BluetoothDevice device) {
+ if (DBG) log("disconnect(" + device + ")");
+ try {
+ return mService.disconnectPanDevice(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /** Get the state of a PAN Device.
+ *
+ * This function returns an int representing the state of the PAN connection
+ *
+ * @param device Remote BT device.
+ * @return The current state of the PAN Device
+ * @hide
+ */
+ public int getPanDeviceState(BluetoothDevice device) {
+ if (DBG) log("getPanDeviceState(" + device + ")");
+ try {
+ return mService.getPanDeviceState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return STATE_DISCONNECTED;
+ }
+ }
+
+ /** Returns a set of all the connected PAN Devices
+ *
+ * Does not include devices that are currently connecting or disconnecting
+ *
+ * @return a unmodifiable set of connected PAN Devices, or null on error.
+ * @hide
+ */
+ public Set<BluetoothDevice> getConnectedDevices() {
+ if (DBG) log("getConnectedDevices");
+ try {
+ return Collections.unmodifiableSet(
+ new HashSet<BluetoothDevice>(
+ Arrays.asList(mService.getConnectedPanDevices())));
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return null;
+ }
+ }
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+
+ public void setBluetoothTethering(boolean value, String uuid, String bridge) {
+ try {
+ mService.setBluetoothTethering(value, uuid, bridge);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ public boolean isTetheringOn() {
+ try {
+ return mService.isTetheringOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index f1ee907..fb3dfe4 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -51,10 +51,14 @@ public final class BluetoothUuid {
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
public static final ParcelUuid Hid =
ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
+ public static final ParcelUuid PANU =
+ ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid NAP =
+ ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid[] RESERVED_UUIDS = {
AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
- ObexObjectPush};
+ ObexObjectPush, PANU, NAP};
public static boolean isAudioSource(ParcelUuid uuid) {
return uuid.equals(AudioSource);
@@ -88,6 +92,13 @@ public final class BluetoothUuid {
return uuid.equals(Hid);
}
+ public static boolean isPANU(ParcelUuid uuid) {
+ return uuid.equals(PANU);
+ }
+
+ public static boolean isNAP(ParcelUuid uuid) {
+ return uuid.equals(NAP);
+ }
/**
* Returns true if ParcelUuid is present in uuidArray
*
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 75f093c..f8f678b 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -81,4 +81,11 @@ interface IBluetooth
int getInputDeviceState(in BluetoothDevice device);
boolean setInputDevicePriority(in BluetoothDevice device, int priority);
int getInputDevicePriority(in BluetoothDevice device);
+
+ boolean isTetheringOn();
+ void setBluetoothTethering(boolean value, String uuid, String bridge);
+ int getPanDeviceState(in BluetoothDevice device);
+ BluetoothDevice[] getConnectedPanDevices();
+ boolean connectPanDevice(in BluetoothDevice device);
+ boolean disconnectPanDevice(in BluetoothDevice device);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6335296..8d1a04c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -324,14 +324,14 @@ public class ConnectivityManager
* <p>
* All applications that have background services that use the network
* should listen to {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
- *
+ *
* @return Whether background data usage is allowed.
*/
public boolean getBackgroundDataSetting() {
try {
return mService.getBackgroundDataSetting();
} catch (RemoteException e) {
- // Err on the side of safety
+ // Err on the side of safety
return false;
}
}
@@ -489,6 +489,17 @@ public class ConnectivityManager
}
}
+ /**
+ * {@hide}
+ */
+ public String[] getTetherableBluetoothRegexs() {
+ try {
+ return mService.getTetherableBluetoothRegexs();
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
/** {@hide} */
public static final int TETHER_ERROR_NO_ERROR = 0;
/** {@hide} */
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 5a14cc9..1d57019 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -1,16 +1,16 @@
/**
* Copyright (c) 2008, 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
+ * 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
+ * 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
+ * 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.
*/
@@ -73,5 +73,7 @@ interface IConnectivityManager
String[] getTetherableWifiRegexs();
+ String[] getTetherableBluetoothRegexs();
+
void requestNetworkTransitionWakelock(in String forWhom);
}
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index eb9b62b..9948060 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
import android.content.Intent;
@@ -365,7 +366,8 @@ class BluetoothEventLoop {
return;
}
if (DBG) {
- log("Device property changed:" + address + "property:" + name);
+ log("Device property changed: " + address + " property: "
+ + name + " value: " + propValues[1]);
}
BluetoothDevice device = mAdapter.getRemoteDevice(address);
if (name.equals("Name")) {
@@ -442,6 +444,25 @@ class BluetoothEventLoop {
mBluetoothService.handleInputDevicePropertyChange(address, state);
}
+ private void onPanDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
+ String name = propValues[0];
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address == null) {
+ Log.e(TAG, "onPanDevicePropertyChanged: Address of the remote device in null");
+ return;
+ }
+ if (DBG) {
+ log("Pan Device property changed: " + address + " property: "
+ + name + " value: "+ propValues[1]);
+ }
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (name.equals("Connected")) {
+ int state = propValues[1].equals("true") ? BluetoothInputDevice.STATE_CONNECTED :
+ BluetoothInputDevice.STATE_DISCONNECTED;
+ mBluetoothService.handlePanDeviceStateChange(device, state);
+ }
+ }
+
private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
if (address == null) {
@@ -623,6 +644,8 @@ class BluetoothEventLoop {
} else {
Log.i(TAG, "Rejecting incoming HID connection from " + address);
}
+ } else if (BluetoothUuid.isNAP(uuid)){
+ authorized = true;
} else {
Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
}
@@ -713,6 +736,30 @@ class BluetoothEventLoop {
}
}
+ private void onPanDeviceConnectionResult(String path, boolean result) {
+ log ("onPanDeviceConnectionResult " + path + " " + result);
+ // Success case gets handled by Property Change signal
+ if (!result) {
+ String address = mBluetoothService.getAddressFromObjectPath(path);
+ if (address == null) return;
+
+ boolean connected = false;
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ int state = mBluetoothService.getPanDeviceState(device);
+ if (state == BluetoothPan.STATE_CONNECTING) {
+ connected = false;
+ } else if (state == BluetoothPan.STATE_DISCONNECTING) {
+ connected = true;
+ } else {
+ Log.e(TAG, "Error onPanDeviceConnectionResult. State is: "
+ + state + " result: "+ result);
+ }
+ int newState = connected? BluetoothPan.STATE_CONNECTED :
+ BluetoothPan.STATE_DISCONNECTED;
+ mBluetoothService.handlePanDeviceStateChange(device, newState);
+ }
+ }
+
private void onRestartRequired() {
if (mBluetoothService.isEnabled()) {
Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index acfc0d7..ab78aeb 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -24,28 +24,30 @@
package android.server;
-import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothDeviceProfileState;
+import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfileState;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothSocket;
import android.bluetooth.BluetoothUuid;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
-import android.bluetooth.IBluetoothHeadset;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.INetworkManagementService;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.RemoteException;
@@ -89,6 +91,7 @@ public class BluetoothService extends IBluetooth.Stub {
private int mBluetoothState;
private boolean mRestart = false; // need to call enable() after disable()
private boolean mIsDiscovering;
+ private boolean mTetheringOn;
private BluetoothAdapter mAdapter; // constant after init()
private final BondState mBondState = new BondState(); // local cache of bondings
@@ -109,6 +112,10 @@ public class BluetoothService extends IBluetooth.Stub {
private static final int MESSAGE_UUID_INTENT = 3;
private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
+ private ArrayList<String> mBluetoothIfaceAddresses;
+ private static final String BLUETOOTH_NEAR_IFACE_ADDR_PREFIX= "192.168.44.";
+ private static final String BLUETOOTH_NETMASK = "255.255.255.0";
+
// The timeout used to sent the UUIDs Intent
// This timeout should be greater than the page timeout
private static final int UUID_INTENT_DELAY = 6000;
@@ -136,6 +143,7 @@ public class BluetoothService extends IBluetooth.Stub {
private BluetoothA2dpService mA2dpService;
private final HashMap<BluetoothDevice, Integer> mInputDevices;
+ private final HashMap<BluetoothDevice, Integer> mPanDevices;
private static String mDockAddress;
private String mDockPin;
@@ -187,6 +195,7 @@ public class BluetoothService extends IBluetooth.Stub {
mBluetoothState = BluetoothAdapter.STATE_OFF;
mIsDiscovering = false;
+ mTetheringOn = false;
mAdapterProperties = new HashMap<String, String>();
mDeviceProperties = new HashMap<String, Map<String,String>>();
@@ -199,6 +208,12 @@ public class BluetoothService extends IBluetooth.Stub {
mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
+ // Can tether to up to 7 devices
+ mBluetoothIfaceAddresses = new ArrayList<String>(BluetoothPan.MAX_CONNECTIONS);
+ for (int i=1; i <= BluetoothPan.MAX_CONNECTIONS; i++) {
+ mBluetoothIfaceAddresses.add(BLUETOOTH_NEAR_IFACE_ADDR_PREFIX + i);
+ }
+
mHfpProfileState.start();
mA2dpProfileState.start();
mHidProfileState.start();
@@ -209,6 +224,7 @@ public class BluetoothService extends IBluetooth.Stub {
filter.addAction(Intent.ACTION_DOCK_EVENT);
mContext.registerReceiver(mReceiver, filter);
mInputDevices = new HashMap<BluetoothDevice, Integer>();
+ mPanDevices = new HashMap<BluetoothDevice, Integer>();
}
public static synchronized String readDockBluetoothAddress() {
@@ -336,6 +352,7 @@ public class BluetoothService extends IBluetooth.Stub {
}
setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
+ setBluetoothTethering(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
// Allow 3 seconds for profiles to gracefully disconnect
// TODO: Introduce a callback mechanism so that each profile can notify
@@ -1235,6 +1252,194 @@ public class BluetoothService extends IBluetooth.Stub {
return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
}
+ public synchronized boolean isTetheringOn() {
+ return mTetheringOn;
+ }
+
+ public synchronized void setBluetoothTethering(boolean value, String uuid, String bridge) {
+ mTetheringOn = value;
+ if (!value) {
+ disconnectPan();
+ }
+ setBluetoothTetheringNative(value, uuid, bridge);
+ }
+
+ public synchronized int getPanDeviceState(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+ if (mPanDevices.get(device) == null) {
+ return BluetoothPan.STATE_DISCONNECTED;
+ }
+ return mPanDevices.get(device);
+ }
+
+ public synchronized boolean connectPanDevice(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+
+ String objectPath = getObjectPathFromAddress(device.getAddress());
+ if (DBG) log("connect PAN(" + objectPath + ")");
+ if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
+ log (device + " already connected to PAN");
+ }
+
+ int connectedCount = 0;
+ for (BluetoothDevice BTdevice: mPanDevices.keySet()) {
+ if (getPanDeviceState(BTdevice) == BluetoothPan.STATE_CONNECTED) {
+ connectedCount ++;
+ }
+ }
+ if (connectedCount > 8) {
+ log (device + " could not connect to PAN because 8 other devices are already connected");
+ return false;
+ }
+
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
+ if (connectPanDeviceNative(objectPath, "nap", "panu")) {
+ log ("connecting to PAN");
+ return true;
+ } else {
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
+ log ("could not connect to PAN");
+ return false;
+ }
+ }
+
+ private synchronized boolean disconnectPan() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+ if (DBG) log("disconnect all PAN devices");
+
+ for (BluetoothDevice device: mPanDevices.keySet()) {
+ if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
+ if (!disconnectPanDevice(device)) {
+ log ("could not disconnect Pan Device "+device.getAddress());
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public synchronized BluetoothDevice[] getConnectedPanDevices() {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+
+ Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>();
+ for (BluetoothDevice device: mPanDevices.keySet()) {
+ if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
+ devices.add(device);
+ }
+ }
+ return devices.toArray(new BluetoothDevice[devices.size()]);
+ }
+
+ public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
+ mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+ "Need BLUETOOTH_ADMIN permission");
+ String objectPath = getObjectPathFromAddress(device.getAddress());
+ if (DBG) log("disconnect PAN(" + objectPath + ")");
+ if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
+ log (device + " already disconnected from PAN");
+ }
+ handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING);
+ return disconnectPanDeviceNative(objectPath);
+ }
+
+ /*package*/ void handlePanDeviceStateChange(BluetoothDevice device, int state) {
+ int prevState;
+ if (mPanDevices.get(device) == null) {
+ prevState = BluetoothPan.STATE_DISCONNECTED;
+ } else {
+ prevState = mPanDevices.get(device);
+ }
+ if (prevState == state) return;
+
+ mPanDevices.put(device, state);
+
+ if (state == BluetoothPan.STATE_CONNECTED) {
+ updateTetherState(true);
+ }
+
+ 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);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+
+ if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
+
+ }
+
+ // configured when we start tethering and unconfig'd on error or conclusion
+ private boolean updateTetherState(boolean enabled) {
+ Log.d(TAG, "configureBluetoothIface(" + enabled + ")");
+
+ 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[] ifaces = new String[0];
+ try {
+ ifaces = service.listInterfaces();
+ } catch (Exception e) {
+ Log.e(TAG, "Error listing Interfaces :" + e);
+ return false;
+ }
+
+ ArrayList<String> ifaceAddresses = (ArrayList<String>) mBluetoothIfaceAddresses.clone();
+ for (String iface : ifaces) {
+ for (String regex : bluetoothRegexs) {
+ if (iface.matches(regex)) {
+ InterfaceConfiguration ifcg = null;
+ try {
+ ifcg = service.getInterfaceConfig(iface);
+ if (ifcg != null) {
+ if (enabled) {
+ String[] addr = BLUETOOTH_NETMASK.split("\\.");
+ ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
+ (Integer.parseInt(addr[1]) << 16) +
+ (Integer.parseInt(addr[2]) << 8) +
+ (Integer.parseInt(addr[3]));
+ if (ifcg.ipAddr == 0 && !ifaceAddresses.isEmpty()) {
+ addr = ifaceAddresses.remove(0).split("\\.");
+ ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
+ (Integer.parseInt(addr[1]) << 16) +
+ (Integer.parseInt(addr[2]) << 8) +
+ (Integer.parseInt(addr[3]));
+ } else {
+ String IfaceAddress =
+ String.valueOf(ifcg.ipAddr >>> 24) + "." +
+ String.valueOf((ifcg.ipAddr & 0x00FF0000) >>> 16) + "." +
+ String.valueOf((ifcg.ipAddr & 0x0000FF00) >>> 8) + "." +
+ String.valueOf(ifcg.ipAddr & 0x000000FF);
+ ifaceAddresses.remove(IfaceAddress);
+ }
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
+ } else {
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
+ }
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
+ ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
+ service.setInterfaceConfig(iface, ifcg);
+ if (enabled) {
+ if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ Log.e(TAG, "Error tethering "+ifaces);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
public synchronized boolean connectInputDevice(BluetoothDevice device) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
@@ -1418,7 +1623,7 @@ public class BluetoothService extends IBluetooth.Stub {
if (updateRemoteDevicePropertiesCache(address))
return getRemoteDeviceProperty(address, property);
}
- Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
+ Log.e(TAG, "getRemoteDeviceProperty: " + property + " not present: " + address);
return null;
}
@@ -2281,4 +2486,8 @@ public class BluetoothService extends IBluetooth.Stub {
private native boolean setLinkTimeoutNative(String path, int num_slots);
private native boolean connectInputDeviceNative(String path);
private native boolean disconnectInputDeviceNative(String path);
+
+ private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
+ private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
+ private native boolean disconnectPanDeviceNative(String path);
}
diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp
index a38d3b2..aae0f21 100644
--- a/core/jni/android_bluetooth_common.cpp
+++ b/core/jni/android_bluetooth_common.cpp
@@ -73,6 +73,12 @@ static Properties input_properties[] = {
{"Connected", DBUS_TYPE_BOOLEAN},
};
+static Properties pan_properties[] = {
+ {"Connected", DBUS_TYPE_BOOLEAN},
+ {"Interface", DBUS_TYPE_STRING},
+ {"UUID", DBUS_TYPE_STRING},
+};
+
typedef union {
char *str_val;
int int_val;
@@ -191,6 +197,7 @@ dbus_bool_t dbus_func_args_async(JNIEnv *env,
dbus_bool_t ret;
va_list lst;
va_start(lst, first_arg_type);
+
ret = dbus_func_args_async_valist(env, conn,
timeout_ms,
reply, user, nat,
@@ -708,6 +715,11 @@ jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg) {
sizeof(input_properties) / sizeof(Properties));
}
+jobjectArray parse_pan_property_change(JNIEnv *env, DBusMessage *msg) {
+ return parse_property_change(env, msg, (Properties *) &pan_properties,
+ sizeof(pan_properties) / sizeof(Properties));
+}
+
jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
return parse_properties(env, iter, (Properties *) &adapter_properties,
sizeof(adapter_properties) / sizeof(Properties));
diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h
index 27a00ae..9222e1a 100644
--- a/core/jni/android_bluetooth_common.h
+++ b/core/jni/android_bluetooth_common.h
@@ -164,6 +164,7 @@ jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg);
jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg);
jobjectArray parse_input_properties(JNIEnv *env, DBusMessageIter *iter);
jobjectArray parse_input_property_change(JNIEnv *env, DBusMessage *msg);
+jobjectArray parse_pan_property_change(JNIEnv *env, DBusMessage *msg);
void append_variant(DBusMessageIter *iter, int type, void *val);
int get_bdaddr(const char *str, bdaddr_t *ba);
void get_bdaddr_as_string(const bdaddr_t *ba, char *str);
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index 4dc012c..6ef154e 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -66,6 +66,8 @@ static jmethodID method_onAgentCancel;
static jmethodID method_onInputDevicePropertyChanged;
static jmethodID method_onInputDeviceConnectionResult;
+static jmethodID method_onPanDevicePropertyChanged;
+static jmethodID method_onPanDeviceConnectionResult;
typedef event_loop_native_data_t native_data_t;
@@ -120,9 +122,13 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
"(Ljava/lang/String;II)V");
method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged",
- "(Ljava/lang/String;[Ljava/lang/String;)V");
+ "(Ljava/lang/String;[Ljava/lang/String;)V");
method_onInputDeviceConnectionResult = env->GetMethodID(clazz, "onInputDeviceConnectionResult",
- "(Ljava/lang/String;Z)V");
+ "(Ljava/lang/String;Z)V");
+ method_onPanDevicePropertyChanged = env->GetMethodID(clazz, "onPanDevicePropertyChanged",
+ "(Ljava/lang/String;[Ljava/lang/String;)V");
+ method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult",
+ "(Ljava/lang/String;Z)V");
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
#endif
@@ -890,7 +896,23 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg,
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
}
goto success;
- }
+ } else if (dbus_message_is_signal(msg,
+ "org.bluez.Network",
+ "PropertyChanged")) {
+
+ jobjectArray str_array =
+ parse_pan_property_change(env, msg);
+ if (str_array != NULL) {
+ const char *c_path = dbus_message_get_path(msg);
+ env->CallVoidMethod(nat->me,
+ method_onPanDevicePropertyChanged,
+ env->NewStringUTF(c_path),
+ str_array);
+ } else {
+ LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
+ }
+ goto success;
+ }
ret = a2dp_event_filter(msg, env);
env->PopLocalFrame(NULL);
@@ -1273,6 +1295,31 @@ void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
free(user);
}
+void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
+ LOGV(__FUNCTION__);
+
+ native_data_t *nat = (native_data_t *)n;
+ const char *path = (const char *)user;
+ DBusError err;
+ dbus_error_init(&err);
+ JNIEnv *env;
+ nat->vm->GetEnv((void**)&env, nat->envVer);
+
+ bool result = JNI_TRUE;
+ if (dbus_set_error_from_message(&err, msg)) {
+ LOG_AND_FREE_DBUS_ERROR(&err);
+ result = JNI_FALSE;
+ }
+ LOGV("... Pan Device Path = %s, result = %d", path, result);
+ jstring jPath = env->NewStringUTF(path);
+ env->CallVoidMethod(nat->me,
+ method_onPanDeviceConnectionResult,
+ jPath,
+ result);
+ env->DeleteLocalRef(jPath);
+ free(user);
+}
+
#endif
static JNINativeMethod sMethods[] = {
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 69a8003..74127cf 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -17,6 +17,9 @@
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input"
+#define DBUS_NETWORK_IFACE BLUEZ_DBUS_BASE_IFC ".Network"
+#define DBUS_NETWORKSERVER_IFACE BLUEZ_DBUS_BASE_IFC ".NetworkServer"
+
#define LOG_TAG "BluetoothService.cpp"
@@ -71,6 +74,8 @@ void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
+void onConnectPanResult(DBusMessage *msg, void *user, void *n);
+void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
@@ -939,6 +944,105 @@ static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object,
return JNI_FALSE;
}
+static jboolean setBluetoothTetheringNative(JNIEnv *env, jobject object, jboolean value,
+ jstring src_role, jstring bridge) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ native_data_t *nat = get_native_data(env, object);
+ if (nat) {
+ DBusMessage *reply;
+ const char *c_role = env->GetStringUTFChars(src_role, NULL);
+ const char *c_bridge = env->GetStringUTFChars(bridge, NULL);
+ if (value) {
+ LOGE("setBluetoothTetheringNative true");
+ reply = dbus_func_args(env, nat->conn,
+ get_adapter_path(env, object),
+ DBUS_NETWORKSERVER_IFACE,
+ "Register",
+ DBUS_TYPE_STRING, &c_role,
+ DBUS_TYPE_STRING, &c_bridge,
+ DBUS_TYPE_INVALID);
+ } else {
+ LOGE("setBluetoothTetheringNative false");
+ reply = dbus_func_args(env, nat->conn,
+ get_adapter_path(env, object),
+ DBUS_NETWORKSERVER_IFACE,
+ "Unregister",
+ DBUS_TYPE_STRING, &c_role,
+ DBUS_TYPE_INVALID);
+ }
+ env->ReleaseStringUTFChars(src_role, c_role);
+ env->ReleaseStringUTFChars(bridge, c_bridge);
+ return reply ? JNI_TRUE : JNI_FALSE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean connectPanDeviceNative(JNIEnv *env, jobject object, jstring path,
+ jstring srcRole, jstring dstRole) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ LOGE("connectPanDeviceNative");
+ native_data_t *nat = get_native_data(env, object);
+ jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+ struct event_loop_native_data_t *eventLoopNat =
+ get_EventLoop_native_data(env, eventLoop);
+
+ if (nat && eventLoopNat) {
+ const char *c_path = env->GetStringUTFChars(path, NULL);
+ const char *src = env->GetStringUTFChars(srcRole, NULL);
+ const char *dst = env->GetStringUTFChars(dstRole, NULL);
+
+ int len = env->GetStringLength(path) + 1;
+ char *context_path = (char *)calloc(len, sizeof(char));
+ strlcpy(context_path, c_path, len); // for callback
+
+ bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult,
+ context_path, eventLoopNat, c_path,
+ DBUS_NETWORK_IFACE, "Connect",
+ DBUS_TYPE_STRING, &src,
+ DBUS_TYPE_STRING, &dst,
+ DBUS_TYPE_INVALID);
+
+ env->ReleaseStringUTFChars(path, c_path);
+ env->ReleaseStringUTFChars(srcRole, src);
+ env->ReleaseStringUTFChars(dstRole, dst);
+ return ret ? JNI_TRUE : JNI_FALSE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
+static jboolean disconnectPanDeviceNative(JNIEnv *env, jobject object,
+ jstring path) {
+ LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+ LOGE("disconnectPanDeviceNative");
+ native_data_t *nat = get_native_data(env, object);
+ jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+ struct event_loop_native_data_t *eventLoopNat =
+ get_EventLoop_native_data(env, eventLoop);
+
+ if (nat && eventLoopNat) {
+ const char *c_path = env->GetStringUTFChars(path, NULL);
+
+ int len = env->GetStringLength(path) + 1;
+ char *context_path = (char *)calloc(len, sizeof(char));
+ strlcpy(context_path, c_path, len); // for callback
+
+ bool ret = dbus_func_args_async(env, nat->conn, -1,onPanDeviceConnectionResult,
+ context_path, eventLoopNat, c_path,
+ DBUS_NETWORK_IFACE, "Disconnect",
+ DBUS_TYPE_INVALID);
+
+ env->ReleaseStringUTFChars(path, c_path);
+ return ret ? JNI_TRUE : JNI_FALSE;
+ }
+#endif
+ return JNI_FALSE;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"classInitNative", "()V", (void*)classInitNative},
@@ -987,8 +1091,15 @@ static JNINativeMethod sMethods[] = {
// HID functions
{"connectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)connectInputDeviceNative},
{"disconnectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectInputDeviceNative},
+
+ {"setBluetoothTetheringNative", "(ZLjava/lang/String;Ljava/lang/String;)Z",
+ (void *)setBluetoothTetheringNative},
+ {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ (void *)connectPanDeviceNative},
+ {"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative},
};
+
int register_android_server_BluetoothService(JNIEnv *env) {
return AndroidRuntime::registerNativeMethods(env,
"android/server/BluetoothService", sMethods, NELEM(sMethods));
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4430ea0..3e3f47c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -121,6 +121,12 @@
<string-array translatable="false" name="config_tether_wifi_regexs">
</string-array>
+ <!-- List of regexpressions describing the interface (if any) that represent tetherable
+ bluetooth interfaces. If the device doesn't want to support tethering over bluetooth this
+ should be empty. -->
+ <string-array translatable="false" name="config_tether_bluetooth_regexs">
+ </string-array>
+
<!-- Dhcp range (min, max) to use for tethering purposes -->
<string-array translatable="false" name="config_tether_dhcp_range">
</string-array>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 859353a..4da0bac 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -344,7 +344,8 @@ public class ConnectivityService extends IConnectivityManager.Stub {
mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
!mTethering.isDunRequired()) &&
(mTethering.getTetherableUsbRegexs().length != 0 ||
- mTethering.getTetherableWifiRegexs().length != 0) &&
+ mTethering.getTetherableWifiRegexs().length != 0 ||
+ mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceRegexs().length != 0);
}
@@ -1676,6 +1677,15 @@ public class ConnectivityService extends IConnectivityManager.Stub {
}
}
+ public String[] getTetherableBluetoothRegexs() {
+ enforceTetherAccessPermission();
+ if (isTetheringSupported()) {
+ return mTethering.getTetherableBluetoothRegexs();
+ } else {
+ return new String[0];
+ }
+ }
+
// TODO - move iface listing, queries, etc to new module
// javadoc from interface
public String[] getTetherableIfaces() {
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index c452590..249c32b 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -128,11 +128,12 @@ final class NativeDaemonConnector implements Runnable {
Slog.e(TAG, String.format(
"Error handling '%s'", event), ex);
}
- }
- try {
- mResponseQueue.put(event);
- } catch (InterruptedException ex) {
- Slog.e(TAG, "Failed to put response onto queue", ex);
+ } else {
+ try {
+ mResponseQueue.put(event);
+ } catch (InterruptedException ex) {
+ Slog.e(TAG, "Failed to put response onto queue", ex);
+ }
}
} catch (NumberFormatException nfe) {
Slog.w(TAG, String.format("Bad msg (%s)", event));
@@ -209,6 +210,7 @@ final class NativeDaemonConnector implements Runnable {
*/
public synchronized ArrayList<String> doCommand(String cmd)
throws NativeDaemonConnectorException {
+ mResponseQueue.clear();
sendCommand(cmd);
ArrayList<String> response = new ArrayList<String>();
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index acedd26..949b874 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -19,8 +19,8 @@ package com.android.server.connectivity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.bluetooth.BluetoothPan;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -32,7 +32,6 @@ import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.NetworkInfo;
-import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Environment;
import android.os.HandlerThread;
@@ -53,6 +52,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.Set;
/**
* @hide
@@ -75,6 +75,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
// TODO - remove both of these - should be part of interface inspection/selection stuff
private String[] mTetherableUsbRegexs;
private String[] mTetherableWifiRegexs;
+ private String[] mTetherableBluetoothRegexs;
private String[] mUpstreamIfaceRegexs;
private Looper mLooper;
@@ -94,6 +95,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
private static final String DHCP_DEFAULT_RANGE1_STOP = "192.168.42.254";
private static final String DHCP_DEFAULT_RANGE2_START = "192.168.43.2";
private static final String DHCP_DEFAULT_RANGE2_STOP = "192.168.43.254";
+ private static final String DHCP_DEFAULT_RANGE3_START = "192.168.44.9";
+ private static final String DHCP_DEFAULT_RANGE3_STOP = "192.168.44.254";
private String[] mDnsServers;
private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
@@ -153,11 +156,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
mDhcpRange = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_dhcp_range);
if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
- mDhcpRange = new String[4];
+ mDhcpRange = new String[6];
mDhcpRange[0] = DHCP_DEFAULT_RANGE1_START;
mDhcpRange[1] = DHCP_DEFAULT_RANGE1_STOP;
mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
+ mDhcpRange[4] = DHCP_DEFAULT_RANGE3_START;
+ mDhcpRange[5] = DHCP_DEFAULT_RANGE3_STOP;
}
mDunRequired = false; // resample when we turn on
@@ -165,6 +170,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
com.android.internal.R.array.config_tether_usb_regexs);
mTetherableWifiRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_wifi_regexs);
+ mTetherableBluetoothRegexs = context.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_bluetooth_regexs);
mUpstreamIfaceRegexs = context.getResources().getStringArray(
com.android.internal.R.array.config_tether_upstream_regexs);
@@ -183,6 +190,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
} else if (isUsb(iface)) {
found = true;
usb = true;
+ } else if (isBluetooth(iface)) {
+ found = true;
}
if (found == false) return;
@@ -217,6 +226,12 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return false;
}
+ public boolean isBluetooth(String iface) {
+ for (String regex : mTetherableBluetoothRegexs) {
+ if (iface.matches(regex)) return true;
+ }
+ return false;
+ }
public void interfaceAdded(String iface) {
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
@@ -229,6 +244,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
found = true;
usb = true;
}
+ if (isBluetooth(iface)) {
+ found = true;
+ }
if (found == false) {
if (DEBUG) Log.d(TAG, iface + " is not a tetherable iface, ignoring");
return;
@@ -324,13 +342,14 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
boolean wifiTethered = false;
boolean usbTethered = false;
+ boolean bluetoothTethered = false;
synchronized (mIfaces) {
Set ifaces = mIfaces.keySet();
for (Object iface : ifaces) {
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
- if(sm.isErrored()) {
+ if (sm.isErrored()) {
erroredList.add((String)iface);
} else if (sm.isAvailable()) {
availableList.add((String)iface);
@@ -339,6 +358,8 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
usbTethered = true;
} else if (isWifi((String)iface)) {
wifiTethered = true;
+ } else if (isBluetooth((String)iface)) {
+ bluetoothTethered = true;
}
activeList.add((String)iface);
}
@@ -359,13 +380,19 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
if (usbTethered) {
- if (wifiTethered) {
+ if (wifiTethered || bluetoothTethered) {
showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
} else {
showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_usb);
}
} else if (wifiTethered) {
- showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+ if (bluetoothTethered) {
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general);
+ } else {
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi);
+ }
+ } else if (bluetoothTethered) {
+ showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_bluetooth);
} else {
clearTetheredNotification();
}
@@ -396,7 +423,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
CharSequence message = r.getText(com.android.internal.R.string.
tethered_notification_message);
- if(mTetheredNotification == null) {
+ if (mTetheredNotification == null) {
mTetheredNotification = new Notification();
mTetheredNotification.when = 0;
}
@@ -471,7 +498,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
}
}
- // toggled when we enter/leave the fully teathered state
+ // toggled when we enter/leave the fully tethered state
private boolean enableUsbRndis(boolean enabled) {
if (DEBUG) Log.d(TAG, "enableUsbRndis(" + enabled + ")");
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
@@ -554,6 +581,10 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
return mTetherableWifiRegexs;
}
+ public String[] getTetherableBluetoothRegexs() {
+ return mTetherableBluetoothRegexs;
+ }
+
public String[] getUpstreamIfaceRegexs() {
return mUpstreamIfaceRegexs;
}