diff options
author | Danica Chang <danicachang@google.com> | 2010-08-18 16:58:37 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-08-18 16:58:37 -0700 |
commit | 7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7 (patch) | |
tree | c58a824ee8021c21f5152f0b664fe53310f36811 | |
parent | b8ea4aa79d395eb392d112290cd7d292861c6b6a (diff) | |
parent | 6fdd0c6274c81b337ad35b70480f881daf7354c3 (diff) | |
download | frameworks_base-7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7.zip frameworks_base-7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7.tar.gz frameworks_base-7b9efd00ecbde416b75f70cc9fe2b0d128ddb9c7.tar.bz2 |
Merge "bluetooth tethering"
-rw-r--r-- | core/java/android/bluetooth/BluetoothDevicePicker.java | 5 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothPan.java | 192 | ||||
-rw-r--r-- | core/java/android/bluetooth/BluetoothUuid.java | 13 | ||||
-rw-r--r-- | core/java/android/bluetooth/IBluetooth.aidl | 7 | ||||
-rw-r--r-- | core/java/android/net/ConnectivityManager.java | 15 | ||||
-rw-r--r-- | core/java/android/net/IConnectivityManager.aidl | 18 | ||||
-rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 49 | ||||
-rw-r--r-- | core/java/android/server/BluetoothService.java | 215 | ||||
-rw-r--r-- | core/jni/android_bluetooth_common.cpp | 12 | ||||
-rw-r--r-- | core/jni/android_bluetooth_common.h | 1 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothEventLoop.cpp | 53 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothService.cpp | 111 | ||||
-rw-r--r-- | core/res/res/values/config.xml | 6 | ||||
-rw-r--r-- | services/java/com/android/server/ConnectivityService.java | 12 | ||||
-rw-r--r-- | services/java/com/android/server/NativeDaemonConnector.java | 12 | ||||
-rw-r--r-- | services/java/com/android/server/connectivity/Tethering.java | 47 |
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; } |