diff --git a/core/java/android/server/BluetoothInputProfileHandler.java b/core/java/android/server/BluetoothInputProfileHandler.java new file mode 100644 index 0000000000000..7ffa5ae2c7279 --- /dev/null +++ b/core/java/android/server/BluetoothInputProfileHandler.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.server; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothDeviceProfileState; +import android.bluetooth.BluetoothInputDevice; +import android.bluetooth.BluetoothProfileState; +import android.content.Context; +import android.content.Intent; +import android.os.Message; +import android.provider.Settings; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This handles all the operations on the HID profile. + * All functions are called by BluetoothService, as Bluetooth Service + * is the Service handler for the HID profile. + */ +final class BluetoothInputProfileHandler { + private static final String TAG = "BluetoothInputProfileHandler"; + private static final boolean DBG = true; + + public static BluetoothInputProfileHandler sInstance; + private Context mContext; + private BluetoothService mBluetoothService; + private final HashMap mInputDevices; + private final BluetoothProfileState mHidProfileState; + + private BluetoothInputProfileHandler(Context context, BluetoothService service) { + mContext = context; + mBluetoothService = service; + mInputDevices = new HashMap(); + mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID); + mHidProfileState.start(); + } + + static synchronized BluetoothInputProfileHandler getInstance(Context context, + BluetoothService service) { + if (sInstance == null) sInstance = new BluetoothInputProfileHandler(context, service); + return sInstance; + } + + synchronized boolean connectInputDevice(BluetoothDevice device, + BluetoothDeviceProfileState state) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (objectPath == null || + getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED || + getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { + return false; + } + if (state != null) { + Message msg = new Message(); + msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING; + msg.obj = state; + mHidProfileState.sendMessage(msg); + return true; + } + return false; + } + + synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); + if (!mBluetoothService.connectInputDeviceNative(objectPath)) { + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED); + return false; + } + return true; + } + + synchronized boolean disconnectInputDevice(BluetoothDevice device, + BluetoothDeviceProfileState state) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (objectPath == null || + getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { + return false; + } + if (state != null) { + Message msg = new Message(); + msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING; + msg.obj = state; + mHidProfileState.sendMessage(msg); + return true; + } + return false; + } + + synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); + if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) { + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED); + return false; + } + return true; + } + + synchronized int getInputDeviceState(BluetoothDevice device) { + if (mInputDevices.get(device) == null) { + return BluetoothInputDevice.STATE_DISCONNECTED; + } + return mInputDevices.get(device); + } + + synchronized List getConnectedInputDevices() { + List devices = lookupInputDevicesMatchingStates( + new int[] {BluetoothInputDevice.STATE_CONNECTED}); + return devices; + } + + synchronized int getInputDevicePriority(BluetoothDevice device) { + return Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), + BluetoothInputDevice.PRIORITY_UNDEFINED); + } + + synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { + if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { + return false; + } + return Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), + priority); + } + + synchronized List lookupInputDevicesMatchingStates(int[] states) { + List inputDevices = new ArrayList(); + + for (BluetoothDevice device: mInputDevices.keySet()) { + int inputDeviceState = getInputDeviceState(device); + for (int state : states) { + if (state == inputDeviceState) { + inputDevices.add(device); + break; + } + } + } + return inputDevices; + } + + private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) { + int prevState; + if (mInputDevices.get(device) == null) { + prevState = BluetoothInputDevice.STATE_DISCONNECTED; + } else { + prevState = mInputDevices.get(device); + } + if (prevState == state) return; + + mInputDevices.put(device, state); + + if (getInputDevicePriority(device) > + BluetoothInputDevice.PRIORITY_OFF && + state == BluetoothInputDevice.STATE_CONNECTING || + state == BluetoothInputDevice.STATE_CONNECTED) { + // We have connected or attempting to connect. + // Bump priority + setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT); + } + + Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState); + intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state); + mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); + + debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state); + mBluetoothService.sendConnectionStateChange(device, state, prevState); + } + + synchronized void handleInputDevicePropertyChange(String address, boolean connected) { + int state = connected ? BluetoothInputDevice.STATE_CONNECTED : + BluetoothInputDevice.STATE_DISCONNECTED; + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + BluetoothDevice device = adapter.getRemoteDevice(address); + handleInputDeviceStateChange(device, state); + } + + synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) { + switch (state) { + case BluetoothDevice.BOND_BONDED: + if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) { + setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); + } + break; + case BluetoothDevice.BOND_NONE: + setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED); + break; + } + } + + private static void debugLog(String msg) { + if (DBG) Log.d(TAG, msg); + } + + private static void errorLog(String msg) { + Log.e(TAG, msg); + } +} diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java new file mode 100644 index 0000000000000..fb964396e4fc3 --- /dev/null +++ b/core/java/android/server/BluetoothPanProfileHandler.java @@ -0,0 +1,395 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.server; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothPan; +import android.bluetooth.BluetoothTetheringDataTracker; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Resources.NotFoundException; +import android.net.ConnectivityManager; +import android.net.InterfaceConfiguration; +import android.net.LinkAddress; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.ServiceManager; +import android.util.Log; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * This handles the PAN profile. All calls into this are made + * from Bluetooth Service. + */ +final class BluetoothPanProfileHandler { + private static final String TAG = "BluetoothPanProfileHandler"; + private static final boolean DBG = true; + + private ArrayList mBluetoothIfaceAddresses; + private int mMaxPanDevices; + + private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; + private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; + private static final int BLUETOOTH_PREFIX_LENGTH = 24; + public static BluetoothPanProfileHandler sInstance; + private final HashMap mPanDevices; + private boolean mTetheringOn; + private Context mContext; + private BluetoothService mBluetoothService; + + private BluetoothPanProfileHandler(Context context, BluetoothService service) { + mContext = context; + mPanDevices = new HashMap(); + mBluetoothService = service; + mTetheringOn = false; + mBluetoothIfaceAddresses = new ArrayList(); + try { + mMaxPanDevices = context.getResources().getInteger( + com.android.internal.R.integer.config_max_pan_devices); + } catch (NotFoundException e) { + mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; + } + } + + static synchronized BluetoothPanProfileHandler getInstance(Context context, + BluetoothService service) { + if (sInstance == null) sInstance = new BluetoothPanProfileHandler(context, service); + return sInstance; + } + + synchronized boolean isTetheringOn() { + return mTetheringOn; + } + + synchronized boolean allowIncomingTethering() { + if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) + return true; + return false; + } + + private BroadcastReceiver mTetheringReceiver = null; + + synchronized void setBluetoothTethering(boolean value) { + if (!value) { + disconnectPanServerDevices(); + } + + if (mBluetoothService.getBluetoothState() != BluetoothAdapter.STATE_ON && value) { + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mTetheringReceiver = new BroadcastReceiver() { + @Override + public synchronized void onReceive(Context context, Intent intent) { + if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) + == BluetoothAdapter.STATE_ON) { + mTetheringOn = true; + mContext.unregisterReceiver(mTetheringReceiver); + } + } + }; + mContext.registerReceiver(mTetheringReceiver, filter); + } else { + mTetheringOn = value; + } + } + + synchronized int getPanDeviceState(BluetoothDevice device) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + if (panDevice == null) { + return BluetoothPan.STATE_DISCONNECTED; + } + return panDevice.mState; + } + + synchronized boolean connectPanDevice(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + if (DBG) Log.d(TAG, "connect PAN(" + objectPath + ")"); + if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) { + errorLog(device + " already connected to PAN"); + } + + int connectedCount = 0; + for (BluetoothDevice panDevice: mPanDevices.keySet()) { + if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) { + connectedCount ++; + } + } + if (connectedCount > 8) { + debugLog(device + " could not connect to PAN because 8 other devices are" + + "already connected"); + return false; + } + + handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING, + BluetoothPan.LOCAL_PANU_ROLE); + if (mBluetoothService.connectPanDeviceNative(objectPath, "nap")) { + debugLog("connecting to PAN"); + return true; + } else { + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED, + BluetoothPan.LOCAL_PANU_ROLE); + errorLog("could not connect to PAN"); + return false; + } + } + + private synchronized boolean disconnectPanServerDevices() { + debugLog("disconnect all PAN devices"); + + for (BluetoothDevice device: mPanDevices.keySet()) { + BluetoothPanDevice panDevice = mPanDevices.get(device); + int state = panDevice.mState; + if (state == BluetoothPan.STATE_CONNECTED && + panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, + panDevice.mLocalRole); + + if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, + device.getAddress(), + panDevice.mIfaceAddr)) { + errorLog("could not disconnect Pan Server Device "+device.getAddress()); + + // Restore prev state + handlePanDeviceStateChange(device, state, + panDevice.mLocalRole); + + return false; + } + } + } + return true; + } + + synchronized List getConnectedPanDevices() { + List devices = new ArrayList(); + + for (BluetoothDevice device: mPanDevices.keySet()) { + if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { + devices.add(device); + } + } + return devices; + } + + synchronized boolean disconnectPanDevice(BluetoothDevice device) { + String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress()); + debugLog("disconnect PAN(" + objectPath + ")"); + + int state = getPanDeviceState(device); + if (state != BluetoothPan.STATE_CONNECTED) { + debugLog(device + " already disconnected from PAN"); + return false; + } + + BluetoothPanDevice panDevice = mPanDevices.get(device); + + if (panDevice == null) { + errorLog("No record for this Pan device:" + device); + return false; + } + + handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING, + panDevice.mLocalRole); + if (panDevice.mLocalRole == BluetoothPan.LOCAL_NAP_ROLE) { + if (!mBluetoothService.disconnectPanServerDeviceNative(objectPath, device.getAddress(), + panDevice.mIface)) { + // Restore prev state, this shouldn't happen + handlePanDeviceStateChange(device, state, panDevice.mLocalRole); + return false; + } + } else { + if (!mBluetoothService.disconnectPanDeviceNative(objectPath)) { + // Restore prev state, this shouldn't happen + handlePanDeviceStateChange(device, state, panDevice.mLocalRole); + return false; + } + } + return true; + } + + synchronized void handlePanDeviceStateChange(BluetoothDevice device, + String iface, int state, int role) { + int prevState; + String ifaceAddr = null; + BluetoothPanDevice panDevice = mPanDevices.get(device); + + if (panDevice == null) { + prevState = BluetoothPan.STATE_DISCONNECTED; + } else { + prevState = panDevice.mState; + ifaceAddr = panDevice.mIfaceAddr; + } + if (prevState == state) return; + + if (role == BluetoothPan.LOCAL_NAP_ROLE) { + if (state == BluetoothPan.STATE_CONNECTED) { + ifaceAddr = enableTethering(iface); + if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); + } else if (state == BluetoothPan.STATE_DISCONNECTED) { + if (ifaceAddr != null) { + mBluetoothIfaceAddresses.remove(ifaceAddr); + ifaceAddr = null; + } + } + } else { + // PANU Role = reverse Tether + if (state == BluetoothPan.STATE_CONNECTED) { + BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device); + } else if (state == BluetoothPan.STATE_DISCONNECTED && + (prevState == BluetoothPan.STATE_CONNECTED || + prevState == BluetoothPan.STATE_DISCONNECTING)) { + BluetoothTetheringDataTracker.getInstance().stopReverseTether(panDevice.mIface); + } + } + + if (panDevice == null) { + panDevice = new BluetoothPanDevice(state, ifaceAddr, iface, role); + mPanDevices.put(device, panDevice); + } else { + panDevice.mState = state; + panDevice.mIfaceAddr = ifaceAddr; + panDevice.mLocalRole = role; + } + + if (state == BluetoothPan.STATE_DISCONNECTED) { + mPanDevices.remove(device); + } + + Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); + intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState); + intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state); + intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); + mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM); + + debugLog("Pan Device state : device: " + device + " State:" + prevState + "->" + state); + mBluetoothService.sendConnectionStateChange(device, state, prevState); + } + + synchronized void handlePanDeviceStateChange(BluetoothDevice device, + int state, int role) { + handlePanDeviceStateChange(device, null, state, role); + } + + private class BluetoothPanDevice { + private int mState; + private String mIfaceAddr; + private String mIface; + private int mLocalRole; // Which local role is this PAN device bound to + + BluetoothPanDevice(int state, String ifaceAddr, String iface, int localRole) { + mState = state; + mIfaceAddr = ifaceAddr; + mIface = iface; + mLocalRole = localRole; + } + } + + private String createNewTetheringAddressLocked() { + if (getConnectedPanDevices().size() == mMaxPanDevices) { + debugLog ("Max PAN device connections reached"); + return null; + } + String address = BLUETOOTH_IFACE_ADDR_START; + while (true) { + if (mBluetoothIfaceAddresses.contains(address)) { + String[] addr = address.split("\\."); + Integer newIp = Integer.parseInt(addr[2]) + 1; + address = address.replace(addr[2], newIp.toString()); + } else { + break; + } + } + mBluetoothIfaceAddresses.add(address); + return address; + } + + // configured when we start tethering + private synchronized String enableTethering(String iface) { + debugLog("updateTetherState:" + iface); + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); + ConnectivityManager cm = + (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); + String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); + + // bring toggle the interfaces + String[] currentIfaces = new String[0]; + try { + currentIfaces = service.listInterfaces(); + } catch (Exception e) { + Log.e(TAG, "Error listing Interfaces :" + e); + return null; + } + + boolean found = false; + for (String currIface: currentIfaces) { + if (currIface.equals(iface)) { + found = true; + break; + } + } + + if (!found) return null; + + String address = createNewTetheringAddressLocked(); + if (address == null) return null; + + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + if (ifcg != null) { + InetAddress addr = null; + if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null || + addr.equals(InetAddress.getByName("0.0.0.0")) || + addr.equals(InetAddress.getByName("::0"))) { + addr = InetAddress.getByName(address); + } + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); + service.setInterfaceConfig(iface, ifcg); + if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + Log.e(TAG, "Error tethering "+iface); + } + } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface + ", :" + e); + return null; + } + return address; + } + + private static void debugLog(String msg) { + if (DBG) Log.d(TAG, msg); + } + + private static void errorLog(String msg) { + Log.e(TAG, msg); + } +} diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index b6101b26bdea1..a295de5660f63 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -24,17 +24,17 @@ package android.server; +import com.android.internal.app.IBatteryStats; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDeviceProfileState; import android.bluetooth.BluetoothHeadset; -import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfileState; import android.bluetooth.BluetoothSocket; -import android.bluetooth.BluetoothTetheringDataTracker; import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; @@ -44,14 +44,9 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.res.Resources.NotFoundException; -import android.net.ConnectivityManager; -import android.net.InterfaceConfiguration; -import android.net.LinkAddress; 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; @@ -60,8 +55,6 @@ import android.provider.Settings; import android.util.Log; import android.util.Pair; -import com.android.internal.app.IBatteryStats; - import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.BufferedWriter; @@ -76,7 +69,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; -import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -96,7 +88,6 @@ 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 int[] mAdapterSdpHandles; private ParcelUuid[] mAdapterUuids; @@ -106,7 +97,7 @@ public class BluetoothService extends IBluetooth.Stub { private final Context mContext; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; - private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; + static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr"; private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin"; @@ -125,13 +116,6 @@ public class BluetoothService extends IBluetooth.Stub { private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000; private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000; - private ArrayList mBluetoothIfaceAddresses; - private int mMaxPanDevices; - - private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1"; - private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5; - private static final int BLUETOOTH_PREFIX_LENGTH = 24; - // 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; @@ -155,11 +139,8 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap mDeviceProfileState; private final BluetoothProfileState mA2dpProfileState; private final BluetoothProfileState mHfpProfileState; - private final BluetoothProfileState mHidProfileState; private BluetoothA2dpService mA2dpService; - private final HashMap mInputDevices; - private final HashMap> mPanDevices; private final HashMap> mDeviceOobData; private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0; @@ -167,9 +148,9 @@ public class BluetoothService extends IBluetooth.Stub { private static String mDockAddress; private String mDockPin; - private String mIface; - private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED; + private BluetoothPanProfileHandler mBluetoothPanProfileHandler; + private BluetoothInputProfileHandler mBluetoothInputProfileHandler; private static class RemoteService { public String address; @@ -218,7 +199,7 @@ public class BluetoothService extends IBluetooth.Stub { mBluetoothState = BluetoothAdapter.STATE_OFF; mIsDiscovering = false; - mTetheringOn = false; + mAdapterProperties = new HashMap(); mDeviceProperties = new HashMap>(); @@ -230,27 +211,17 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceProfileState = new HashMap(); mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP); mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP); - mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID); - - mBluetoothIfaceAddresses = new ArrayList(); - try { - mMaxPanDevices = context.getResources().getInteger( - com.android.internal.R.integer.config_max_pan_devices); - } catch (NotFoundException e) { - mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS; - } mHfpProfileState.start(); mA2dpProfileState.start(); - mHidProfileState.start(); IntentFilter filter = new IntentFilter(); registerForAirplaneMode(filter); filter.addAction(Intent.ACTION_DOCK_EVENT); mContext.registerReceiver(mReceiver, filter); - mInputDevices = new HashMap(); - mPanDevices = new HashMap>(); + mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this); + mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this); } public static synchronized String readDockBluetoothAddress() { @@ -590,21 +561,16 @@ public class BluetoothService extends IBluetooth.Stub { persistBluetoothOnSetting(true); } - updateSdpRecords(); - mIsDiscovering = false; mBondState.readAutoPairingData(); mBondState.loadBondState(); initProfileState(); - // Log bluetooth on to battery stats. - long ident = Binder.clearCallingIdentity(); - try { - mBatteryStats.noteBluetoothOn(); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } + // This should be the last step of the the enable thread. + // Because this adds SDP records which asynchronously + // broadcasts the Bluetooth On State in updateBluetoothState. + // So we want all internal state setup before this. + updateSdpRecords(); } else { setBluetoothState(BluetoothAdapter.STATE_OFF); } @@ -652,6 +618,11 @@ public class BluetoothService extends IBluetooth.Stub { } } + /** + * This function is called from Bluetooth Event Loop when onPropertyChanged + * for adapter comes in with UUID property. + * @param uuidsThe uuids of adapter as reported by Bluez. + */ synchronized void updateBluetoothState(String uuids) { if (mBluetoothState == BluetoothAdapter.STATE_TURNING_ON) { ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids); @@ -662,6 +633,15 @@ public class BluetoothService extends IBluetooth.Stub { String[] propVal = {"Pairable", getProperty("Pairable")}; mEventLoop.onPropertyChanged(propVal); + // Log bluetooth on to battery stats. + long ident = Binder.clearCallingIdentity(); + try { + mBatteryStats.noteBluetoothOn(); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { disable(false); } @@ -825,7 +805,8 @@ public class BluetoothService extends IBluetooth.Stub { // HID is handled by BluetoothService, other profiles // will be handled by their respective services. - setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state); + mBluetoothInputProfileHandler.setInitialInputDevicePriority( + mAdapter.getRemoteDevice(address), state); if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" + reason + ")"); @@ -1472,424 +1453,6 @@ public class BluetoothService extends IBluetooth.Stub { return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address); } - public synchronized boolean isTetheringOn() { - return mTetheringOn; - } - - /*package*/ synchronized boolean allowIncomingTethering() { - if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices) - return true; - return false; - } - - private BroadcastReceiver mTetheringReceiver = null; - - public synchronized void setBluetoothTethering(boolean value) { - if (!value) { - disconnectPan(); - } - - if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) { - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); - mTetheringReceiver = new BroadcastReceiver() { - @Override - public synchronized void onReceive(Context context, Intent intent) { - if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF) - == BluetoothAdapter.STATE_ON) { - mTetheringOn = true; - mContext.unregisterReceiver(mTetheringReceiver); - } - } - }; - mContext.registerReceiver(mTetheringReceiver, filter); - } else { - mTetheringOn = value; - } - } - - public synchronized int getPanDeviceState(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - - Pair panDevice = mPanDevices.get(device); - if (panDevice == null) { - return BluetoothPan.STATE_DISCONNECTED; - } - return panDevice.first; - } - - 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 panDevice: mPanDevices.keySet()) { - if (getPanDeviceState(panDevice) == 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, - BluetoothPan.LOCAL_PANU_ROLE); - if (connectPanDeviceNative(objectPath, "nap")) { - log ("connecting to PAN"); - return true; - } else { - handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED, - BluetoothPan.LOCAL_PANU_ROLE); - 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 List getConnectedPanDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - - List devices = new ArrayList(); - - for (BluetoothDevice device: mPanDevices.keySet()) { - if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) { - devices.add(device); - } - } - return devices; - } - - 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, - BluetoothPan.LOCAL_PANU_ROLE); - return disconnectPanDeviceNative(objectPath); - } - - /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, - String iface, - int state, - int role) { - int prevState; - String ifaceAddr = null; - - if (mPanDevices.get(device) == null) { - prevState = BluetoothPan.STATE_DISCONNECTED; - } else { - prevState = mPanDevices.get(device).first; - ifaceAddr = mPanDevices.get(device).second; - } - if (prevState == state) return; - - if (role == BluetoothPan.LOCAL_NAP_ROLE) { - if (state == BluetoothPan.STATE_CONNECTED) { - ifaceAddr = enableTethering(iface); - if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface"); - } else if (state == BluetoothPan.STATE_DISCONNECTED) { - if (ifaceAddr != null) { - mBluetoothIfaceAddresses.remove(ifaceAddr); - ifaceAddr = null; - } - } - } else { - // PANU Role = reverse Tether - if (state == BluetoothPan.STATE_CONNECTED) { - mIface = iface; - BluetoothTetheringDataTracker.getInstance().startReverseTether(iface, device); - } else if (state == BluetoothPan.STATE_DISCONNECTED && - (prevState == BluetoothPan.STATE_CONNECTED || - prevState == BluetoothPan.STATE_DISCONNECTING)) { - BluetoothTetheringDataTracker.getInstance().stopReverseTether(mIface); - } - } - - Pair value = new Pair(state, ifaceAddr); - mPanDevices.put(device, value); - - Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState); - intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state); - intent.putExtra(BluetoothPan.EXTRA_LOCAL_ROLE, role); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state); - sendConnectionStateChange(device, state, prevState); - } - - /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, - int state, int role) { - handlePanDeviceStateChange(device, null, state, role); - } - - private String createNewTetheringAddressLocked() { - if (getConnectedPanDevices().size() == mMaxPanDevices) { - log("Max PAN device connections reached"); - return null; - } - String address = BLUETOOTH_IFACE_ADDR_START; - while (true) { - if (mBluetoothIfaceAddresses.contains(address)) { - String[] addr = address.split("\\."); - Integer newIp = Integer.parseInt(addr[2]) + 1; - address = address.replace(addr[2], newIp.toString()); - } else { - break; - } - } - mBluetoothIfaceAddresses.add(address); - return address; - } - - // configured when we start tethering - private synchronized String enableTethering(String iface) { - log("updateTetherState:" + iface); - - IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); - INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); - ConnectivityManager cm = - (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs(); - - // bring toggle the interfaces - String[] currentIfaces = new String[0]; - try { - currentIfaces = service.listInterfaces(); - } catch (Exception e) { - Log.e(TAG, "Error listing Interfaces :" + e); - return null; - } - - boolean found = false; - for (String currIface: currentIfaces) { - if (currIface.equals(iface)) { - found = true; - break; - } - } - - if (!found) return null; - - String address = createNewTetheringAddressLocked(); - if (address == null) return null; - - InterfaceConfiguration ifcg = null; - try { - ifcg = service.getInterfaceConfig(iface); - if (ifcg != null) { - InetAddress addr = null; - if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null || - addr.equals(InetAddress.getByName("0.0.0.0")) || - addr.equals(InetAddress.getByName("::0"))) { - addr = InetAddress.getByName(address); - } - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); - ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH); - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); - ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); - service.setInterfaceConfig(iface, ifcg); - if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { - Log.e(TAG, "Error tethering "+iface); - } - } - } catch (Exception e) { - Log.e(TAG, "Error configuring interface " + iface + ", :" + e); - return null; - } - return address; - } - - public synchronized boolean connectInputDevice(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - String objectPath = getObjectPathFromAddress(device.getAddress()); - if (objectPath == null || - getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED || - getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { - return false; - } - BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); - if (state != null) { - Message msg = new Message(); - msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING; - msg.obj = state; - mHidProfileState.sendMessage(msg); - return true; - } - return false; - } - - public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { - String objectPath = getObjectPathFromAddress(device.getAddress()); - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); - if (!connectInputDeviceNative(objectPath)) { - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED); - return false; - } - return true; - } - - public synchronized boolean disconnectInputDevice(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - - String objectPath = getObjectPathFromAddress(device.getAddress()); - if (objectPath == null || - getInputDeviceState(device) == BluetoothInputDevice.STATE_DISCONNECTED) { - return false; - } - BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); - if (state != null) { - Message msg = new Message(); - msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING; - msg.obj = state; - mHidProfileState.sendMessage(msg); - return true; - } - return false; - } - - public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { - String objectPath = getObjectPathFromAddress(device.getAddress()); - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); - if (!disconnectInputDeviceNative(objectPath)) { - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED); - return false; - } - return true; - } - - public synchronized int getInputDeviceState(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - - if (mInputDevices.get(device) == null) { - return BluetoothInputDevice.STATE_DISCONNECTED; - } - return mInputDevices.get(device); - } - - public synchronized List getConnectedInputDevices() { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - List devices = lookupInputDevicesMatchingStates( - new int[] {BluetoothInputDevice.STATE_CONNECTED}); - return devices; - } - - public synchronized int getInputDevicePriority(BluetoothDevice device) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), - BluetoothInputDevice.PRIORITY_UNDEFINED); - } - - public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { - mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, - "Need BLUETOOTH_ADMIN permission"); - if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) { - return false; - } - return Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()), - priority); - } - - /*package*/synchronized List lookupInputDevicesMatchingStates(int[] states) { - List inputDevices = new ArrayList(); - - for (BluetoothDevice device: mInputDevices.keySet()) { - int inputDeviceState = getInputDeviceState(device); - for (int state : states) { - if (state == inputDeviceState) { - inputDevices.add(device); - break; - } - } - } - return inputDevices; - } - - private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) { - int prevState; - if (mInputDevices.get(device) == null) { - prevState = BluetoothInputDevice.STATE_DISCONNECTED; - } else { - prevState = mInputDevices.get(device); - } - if (prevState == state) return; - - mInputDevices.put(device, state); - - if (getInputDevicePriority(device) > - BluetoothInputDevice.PRIORITY_OFF && - state == BluetoothInputDevice.STATE_CONNECTING || - state == BluetoothInputDevice.STATE_CONNECTED) { - // We have connected or attempting to connect. - // Bump priority - setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT); - } - - Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); - intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState); - intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state); - mContext.sendBroadcast(intent, BLUETOOTH_PERM); - - if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state); - sendConnectionStateChange(device, state, prevState); - } - - /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) { - int state = connected ? BluetoothInputDevice.STATE_CONNECTED : - BluetoothInputDevice.STATE_DISCONNECTED; - BluetoothDevice device = mAdapter.getRemoteDevice(address); - handleInputDeviceStateChange(device, state); - } - - private void setInitialInputDevicePriority(BluetoothDevice device, int state) { - switch (state) { - case BluetoothDevice.BOND_BONDED: - if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) { - setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON); - } - break; - case BluetoothDevice.BOND_NONE: - setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED); - break; - } - } - - /*package*/ boolean isRemoteDeviceInCache(String address) { - return (mDeviceProperties.get(address) != null); - } - /*package*/ String[] getRemoteDeviceProperties(String address) { if (!isEnabledInternal()) return null; @@ -2674,6 +2237,114 @@ public class BluetoothService extends IBluetooth.Stub { if (!result) log("Set Link Timeout to:" + num_slots + " slots failed"); } + /**** Handlers for PAN Profile ****/ + + public synchronized boolean isTetheringOn() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.isTetheringOn(); + } + + /*package*/ synchronized boolean allowIncomingTethering() { + return mBluetoothPanProfileHandler.allowIncomingTethering(); + } + + public synchronized void setBluetoothTethering(boolean value) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + mBluetoothPanProfileHandler.setBluetoothTethering(value); + } + + public synchronized int getPanDeviceState(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.getPanDeviceState(device); + } + + public synchronized boolean connectPanDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return mBluetoothPanProfileHandler.connectPanDevice(device); + } + + public synchronized List getConnectedPanDevices() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothPanProfileHandler.getConnectedPanDevices(); + } + + public synchronized boolean disconnectPanDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return mBluetoothPanProfileHandler.disconnectPanDevice(device); + } + + /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + String iface, + int state, + int role) { + mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role); + } + + /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device, + int state, int role) { + mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role); + } + + /**** Handlers for Input Device Profile ****/ + + public synchronized boolean connectInputDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); + return mBluetoothInputProfileHandler.connectInputDevice(device, state); + } + + public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { + return mBluetoothInputProfileHandler.connectInputDeviceInternal(device); + } + + public synchronized boolean disconnectInputDevice(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); + return mBluetoothInputProfileHandler.disconnectInputDevice(device, state); + } + + public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { + return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device); + } + + public synchronized int getInputDeviceState(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getInputDeviceState(device); + + } + + public synchronized List getConnectedInputDevices() { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getConnectedInputDevices(); + } + + public synchronized int getInputDevicePriority(BluetoothDevice device) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); + return mBluetoothInputProfileHandler.getInputDevicePriority(device); + } + + public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) { + mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, + "Need BLUETOOTH_ADMIN permission"); + return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority); + } + + /*package*/synchronized List lookupInputDevicesMatchingStates(int[] states) { + return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states); + } + + /*package*/ synchronized void handleInputDevicePropertyChange(String address, boolean connected) { + mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected); + } + + /*package*/ boolean isRemoteDeviceInCache(String address) { + return (mDeviceProperties.get(address) != null); + } + public boolean connectHeadset(String address) { if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false; @@ -2927,12 +2598,14 @@ public class BluetoothService extends IBluetooth.Stub { short channel); private native boolean removeServiceRecordNative(int handle); private native boolean setLinkTimeoutNative(String path, int num_slots); - private native boolean connectInputDeviceNative(String path); - private native boolean disconnectInputDeviceNative(String path); + native boolean connectInputDeviceNative(String path); + native boolean disconnectInputDeviceNative(String path); - private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge); - private native boolean connectPanDeviceNative(String path, String dstRole); - private native boolean disconnectPanDeviceNative(String path); + native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge); + native boolean connectPanDeviceNative(String path, String dstRole); + native boolean disconnectPanDeviceNative(String path); + native boolean disconnectPanServerDeviceNative(String path, + String address, String iface); private native int[] addReservedServiceRecordsNative(int[] uuuids); private native boolean removeReservedServiceRecordsNative(int[] handles); diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index 2c398713295bc..bf0504f7c8cc9 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -1214,6 +1214,45 @@ static jboolean disconnectPanDeviceNative(JNIEnv *env, jobject object, return JNI_FALSE; } +static jboolean disconnectPanServerDeviceNative(JNIEnv *env, jobject object, + jstring path, jstring address, + jstring iface) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + LOGE("disconnectPanServerDeviceNative"); + 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_address = env->GetStringUTFChars(address, NULL); + const char *c_path = env->GetStringUTFChars(path, NULL); + const char *c_iface = env->GetStringUTFChars(iface, 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, + get_adapter_path(env, object), + DBUS_NETWORKSERVER_IFACE, + "DisconnectDevice", + DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_STRING, &c_iface, + DBUS_TYPE_INVALID); + + env->ReleaseStringUTFChars(address, c_address); + env->ReleaseStringUTFChars(iface, c_iface); + 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}, @@ -1274,6 +1313,8 @@ static JNINativeMethod sMethods[] = { {"connectPanDeviceNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)connectPanDeviceNative}, {"disconnectPanDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectPanDeviceNative}, + {"disconnectPanServerDeviceNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z", + (void *)disconnectPanServerDeviceNative}, };