Merge "bluetooth tethering"

This commit is contained in:
Danica Chang
2010-08-18 16:58:37 -07:00
committed by Android (Google) Code Review
16 changed files with 736 additions and 32 deletions

View File

@@ -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;
}

View File

@@ -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;
}
}
}

View File

@@ -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
*

View File

@@ -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);
}

View File

@@ -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} */

View File

@@ -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);
}

View File

@@ -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?) - " +

View File

@@ -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);
}

View File

@@ -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));

View File

@@ -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);

View File

@@ -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[] = {

View File

@@ -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));

View File

@@ -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>

View File

@@ -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() {

View File

@@ -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>();

View File

@@ -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;
}