deviceList = mService.getConnectedDevices();
+
+ return !deviceList.isEmpty() && deviceList.get(0).equals(device)
+ ? mService.getConnectionState(device)
+ : BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ // TODO: distinguish between keyboard and mouse?
+ return R.string.bluetooth_profile_hid;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_hid_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_hid_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ if (btClass == null) {
+ return R.drawable.ic_lockscreen_ime;
+ }
+ return getHidClassDrawable(btClass);
+ }
+
+ public static int getHidClassDrawable(BluetoothClass btClass) {
+ switch (btClass.getDeviceClass()) {
+ case BluetoothClass.Device.PERIPHERAL_KEYBOARD:
+ case BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING:
+ return R.drawable.ic_lockscreen_ime;
+ case BluetoothClass.Device.PERIPHERAL_POINTING:
+ return R.drawable.ic_bt_pointing_hid;
+ default:
+ return R.drawable.ic_bt_misc_hid;
+ }
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.INPUT_DEVICE,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up HID proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
new file mode 100644
index 0000000000000..0c1adec33d545
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
@@ -0,0 +1,216 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import java.util.Set;
+
+/**
+ * LocalBluetoothAdapter provides an interface between the Settings app
+ * and the functionality of the local {@link BluetoothAdapter}, specifically
+ * those related to state transitions of the adapter itself.
+ *
+ * Connection and bonding state changes affecting specific devices
+ * are handled by {@link CachedBluetoothDeviceManager},
+ * {@link BluetoothEventManager}, and {@link LocalBluetoothProfileManager}.
+ */
+public final class LocalBluetoothAdapter {
+ private static final String TAG = "LocalBluetoothAdapter";
+
+ /** This class does not allow direct access to the BluetoothAdapter. */
+ private final BluetoothAdapter mAdapter;
+
+ private LocalBluetoothProfileManager mProfileManager;
+
+ private static LocalBluetoothAdapter sInstance;
+
+ private int mState = BluetoothAdapter.ERROR;
+
+ private static final int SCAN_EXPIRATION_MS = 5 * 60 * 1000; // 5 mins
+
+ private long mLastScan;
+
+ private LocalBluetoothAdapter(BluetoothAdapter adapter) {
+ mAdapter = adapter;
+ }
+
+ void setProfileManager(LocalBluetoothProfileManager manager) {
+ mProfileManager = manager;
+ }
+
+ /**
+ * Get the singleton instance of the LocalBluetoothAdapter. If this device
+ * doesn't support Bluetooth, then null will be returned. Callers must be
+ * prepared to handle a null return value.
+ * @return the LocalBluetoothAdapter object, or null if not supported
+ */
+ static synchronized LocalBluetoothAdapter getInstance() {
+ if (sInstance == null) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ sInstance = new LocalBluetoothAdapter(adapter);
+ }
+ }
+
+ return sInstance;
+ }
+
+ // Pass-through BluetoothAdapter methods that we can intercept if necessary
+
+ public void cancelDiscovery() {
+ mAdapter.cancelDiscovery();
+ }
+
+ public boolean enable() {
+ return mAdapter.enable();
+ }
+
+ public boolean disable() {
+ return mAdapter.disable();
+ }
+
+ void getProfileProxy(Context context,
+ BluetoothProfile.ServiceListener listener, int profile) {
+ mAdapter.getProfileProxy(context, listener, profile);
+ }
+
+ public Set getBondedDevices() {
+ return mAdapter.getBondedDevices();
+ }
+
+ public String getName() {
+ return mAdapter.getName();
+ }
+
+ public int getScanMode() {
+ return mAdapter.getScanMode();
+ }
+
+ public int getState() {
+ return mAdapter.getState();
+ }
+
+ public ParcelUuid[] getUuids() {
+ return mAdapter.getUuids();
+ }
+
+ public boolean isDiscovering() {
+ return mAdapter.isDiscovering();
+ }
+
+ public boolean isEnabled() {
+ return mAdapter.isEnabled();
+ }
+
+ public void setDiscoverableTimeout(int timeout) {
+ mAdapter.setDiscoverableTimeout(timeout);
+ }
+
+ public void setName(String name) {
+ mAdapter.setName(name);
+ }
+
+ public void setScanMode(int mode) {
+ mAdapter.setScanMode(mode);
+ }
+
+ public boolean setScanMode(int mode, int duration) {
+ return mAdapter.setScanMode(mode, duration);
+ }
+
+ public void startScanning(boolean force) {
+ // Only start if we're not already scanning
+ if (!mAdapter.isDiscovering()) {
+ if (!force) {
+ // Don't scan more than frequently than SCAN_EXPIRATION_MS,
+ // unless forced
+ if (mLastScan + SCAN_EXPIRATION_MS > System.currentTimeMillis()) {
+ return;
+ }
+
+ // If we are playing music, don't scan unless forced.
+ A2dpProfile a2dp = mProfileManager.getA2dpProfile();
+ if (a2dp != null && a2dp.isA2dpPlaying()) {
+ return;
+ }
+ }
+
+ if (mAdapter.startDiscovery()) {
+ mLastScan = System.currentTimeMillis();
+ }
+ }
+ }
+
+ public void stopScanning() {
+ if (mAdapter.isDiscovering()) {
+ mAdapter.cancelDiscovery();
+ }
+ }
+
+ public synchronized int getBluetoothState() {
+ // Always sync state, in case it changed while paused
+ syncBluetoothState();
+ return mState;
+ }
+
+ synchronized void setBluetoothStateInt(int state) {
+ mState = state;
+
+ if (state == BluetoothAdapter.STATE_ON) {
+ // if mProfileManager hasn't been constructed yet, it will
+ // get the adapter UUIDs in its constructor when it is.
+ if (mProfileManager != null) {
+ mProfileManager.setBluetoothStateOn();
+ }
+ }
+ }
+
+ // Returns true if the state changed; false otherwise.
+ boolean syncBluetoothState() {
+ int currentState = mAdapter.getState();
+ if (currentState != mState) {
+ setBluetoothStateInt(mAdapter.getState());
+ return true;
+ }
+ return false;
+ }
+
+ public void setBluetoothEnabled(boolean enabled) {
+ boolean success = enabled
+ ? mAdapter.enable()
+ : mAdapter.disable();
+
+ if (success) {
+ setBluetoothStateInt(enabled
+ ? BluetoothAdapter.STATE_TURNING_ON
+ : BluetoothAdapter.STATE_TURNING_OFF);
+ } else {
+ if (Utils.V) {
+ Log.v(TAG, "setBluetoothEnabled call, manager didn't return " +
+ "success for enabled: " + enabled);
+ }
+
+ syncBluetoothState();
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
new file mode 100644
index 0000000000000..4adc62e77ee65
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothManager.java
@@ -0,0 +1,122 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import android.content.Context;
+import android.util.Log;
+
+/**
+ * LocalBluetoothManager provides a simplified interface on top of a subset of
+ * the Bluetooth API. Note that {@link #getInstance} will return null
+ * if there is no Bluetooth adapter on this device, and callers must be
+ * prepared to handle this case.
+ */
+public final class LocalBluetoothManager {
+ private static final String TAG = "LocalBluetoothManager";
+
+ /** Singleton instance. */
+ private static LocalBluetoothManager sInstance;
+
+ private final Context mContext;
+
+ /** If a BT-related activity is in the foreground, this will be it. */
+ private Context mForegroundActivity;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+
+ private final CachedBluetoothDeviceManager mCachedDeviceManager;
+
+ /** The Bluetooth profile manager. */
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ /** The broadcast receiver event manager. */
+ private final BluetoothEventManager mEventManager;
+
+ public static synchronized LocalBluetoothManager getInstance(Context context,
+ BluetoothManagerCallback onInitCallback) {
+ if (sInstance == null) {
+ LocalBluetoothAdapter adapter = LocalBluetoothAdapter.getInstance();
+ if (adapter == null) {
+ return null;
+ }
+ // This will be around as long as this process is
+ Context appContext = context.getApplicationContext();
+ sInstance = new LocalBluetoothManager(adapter, appContext);
+ if (onInitCallback != null) {
+ onInitCallback.onBluetoothManagerInitialized(appContext, sInstance);
+ }
+ }
+
+ return sInstance;
+ }
+
+ private LocalBluetoothManager(LocalBluetoothAdapter adapter, Context context) {
+ mContext = context;
+ mLocalAdapter = adapter;
+
+ mCachedDeviceManager = new CachedBluetoothDeviceManager(context);
+ mEventManager = new BluetoothEventManager(mLocalAdapter,
+ mCachedDeviceManager, context);
+ mProfileManager = new LocalBluetoothProfileManager(context,
+ mLocalAdapter, mCachedDeviceManager, mEventManager);
+ }
+
+ public LocalBluetoothAdapter getBluetoothAdapter() {
+ return mLocalAdapter;
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public Context getForegroundActivity() {
+ return mForegroundActivity;
+ }
+
+ public boolean isForegroundActivity() {
+ return mForegroundActivity != null;
+ }
+
+ public synchronized void setForegroundActivity(Context context) {
+ if (context != null) {
+ Log.d(TAG, "setting foreground activity to non-null context");
+ mForegroundActivity = context;
+ } else {
+ if (mForegroundActivity != null) {
+ Log.d(TAG, "setting foreground activity to null");
+ mForegroundActivity = null;
+ }
+ }
+ }
+
+ public CachedBluetoothDeviceManager getCachedDeviceManager() {
+ return mCachedDeviceManager;
+ }
+
+ public BluetoothEventManager getEventManager() {
+ return mEventManager;
+ }
+
+ public LocalBluetoothProfileManager getProfileManager() {
+ return mProfileManager;
+ }
+
+ public interface BluetoothManagerCallback {
+ void onBluetoothManagerInitialized(Context appContext,
+ LocalBluetoothManager bluetoothManager);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
new file mode 100755
index 0000000000000..abcb989802e9c
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfile.java
@@ -0,0 +1,71 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * LocalBluetoothProfile is an interface defining the basic
+ * functionality related to a Bluetooth profile.
+ */
+public interface LocalBluetoothProfile {
+
+ /**
+ * Returns true if the user can initiate a connection, false otherwise.
+ */
+ boolean isConnectable();
+
+ /**
+ * Returns true if the user can enable auto connection for this profile.
+ */
+ boolean isAutoConnectable();
+
+ boolean connect(BluetoothDevice device);
+
+ boolean disconnect(BluetoothDevice device);
+
+ int getConnectionStatus(BluetoothDevice device);
+
+ boolean isPreferred(BluetoothDevice device);
+
+ int getPreferred(BluetoothDevice device);
+
+ void setPreferred(BluetoothDevice device, boolean preferred);
+
+ boolean isProfileReady();
+
+ /** Display order for device profile settings. */
+ int getOrdinal();
+
+ /**
+ * Returns the string resource ID for the localized name for this profile.
+ * @param device the Bluetooth device (to distinguish between PAN roles)
+ */
+ int getNameResource(BluetoothDevice device);
+
+ /**
+ * Returns the string resource ID for the summary text for this profile
+ * for the specified device, e.g. "Use for media audio" or
+ * "Connected to media audio".
+ * @param device the device to query for profile connection status
+ * @return a string resource ID for the profile summary text
+ */
+ int getSummaryResourceForDevice(BluetoothDevice device);
+
+ int getDrawableResource(BluetoothClass btClass);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
new file mode 100644
index 0000000000000..b0a7b27bfae23
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -0,0 +1,383 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothInputDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothPbap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.content.Intent;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.os.Handler;
+import android.os.Message;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+
+/**
+ * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
+ * objects for the available Bluetooth profiles.
+ */
+public final class LocalBluetoothProfileManager {
+ private static final String TAG = "LocalBluetoothProfileManager";
+ private static final boolean DEBUG = Utils.D;
+ /** Singleton instance. */
+ private static LocalBluetoothProfileManager sInstance;
+
+ /**
+ * An interface for notifying BluetoothHeadset IPC clients when they have
+ * been connected to the BluetoothHeadset service.
+ * Only used by com.android.settings.bluetooth.DockService.
+ */
+ public interface ServiceListener {
+ /**
+ * Called to notify the client when this proxy object has been
+ * connected to the BluetoothHeadset service. Clients must wait for
+ * this callback before making IPC calls on the BluetoothHeadset
+ * service.
+ */
+ void onServiceConnected();
+
+ /**
+ * Called to notify the client that this proxy object has been
+ * disconnected from the BluetoothHeadset service. Clients must not
+ * make IPC calls on the BluetoothHeadset service after this callback.
+ * This callback will currently only occur if the application hosting
+ * the BluetoothHeadset service, but may be called more often in future.
+ */
+ void onServiceDisconnected();
+ }
+
+ private final Context mContext;
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final BluetoothEventManager mEventManager;
+
+ private A2dpProfile mA2dpProfile;
+ private HeadsetProfile mHeadsetProfile;
+ private MapProfile mMapProfile;
+ private final HidProfile mHidProfile;
+ private OppProfile mOppProfile;
+ private final PanProfile mPanProfile;
+ private final PbapServerProfile mPbapProfile;
+
+ /**
+ * Mapping from profile name, e.g. "HEADSET" to profile object.
+ */
+ private final Map
+ mProfileNameMap = new HashMap();
+
+ LocalBluetoothProfileManager(Context context,
+ LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ BluetoothEventManager eventManager) {
+ mContext = context;
+
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mEventManager = eventManager;
+ // pass this reference to adapter and event manager (circular dependency)
+ mLocalAdapter.setProfileManager(this);
+ mEventManager.setProfileManager(this);
+
+ ParcelUuid[] uuids = adapter.getUuids();
+
+ // uuids may be null if Bluetooth is turned off
+ if (uuids != null) {
+ updateLocalProfiles(uuids);
+ }
+
+ // Always add HID and PAN profiles
+ mHidProfile = new HidProfile(context, mLocalAdapter, mDeviceManager, this);
+ addProfile(mHidProfile, HidProfile.NAME,
+ BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
+
+ mPanProfile = new PanProfile(context);
+ addPanProfile(mPanProfile, PanProfile.NAME,
+ BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
+
+ if(DEBUG) Log.d(TAG, "Adding local MAP profile");
+ mMapProfile = new MapProfile(mContext, mLocalAdapter,
+ mDeviceManager, this);
+ addProfile(mMapProfile, MapProfile.NAME,
+ BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+
+ //Create PBAP server profile, but do not add it to list of profiles
+ // as we do not need to monitor the profile as part of profile list
+ mPbapProfile = new PbapServerProfile(context);
+
+ if (DEBUG) Log.d(TAG, "LocalBluetoothProfileManager construction complete");
+ }
+
+ /**
+ * Initialize or update the local profile objects. If a UUID was previously
+ * present but has been removed, we print a warning but don't remove the
+ * profile object as it might be referenced elsewhere, or the UUID might
+ * come back and we don't want multiple copies of the profile objects.
+ * @param uuids
+ */
+ void updateLocalProfiles(ParcelUuid[] uuids) {
+ // A2DP
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.AudioSource)) {
+ if (mA2dpProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local A2DP profile");
+ mA2dpProfile = new A2dpProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mA2dpProfile, A2dpProfile.NAME,
+ BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mA2dpProfile != null) {
+ Log.w(TAG, "Warning: A2DP profile was previously added but the UUID is now missing.");
+ }
+
+ // Headset / Handsfree
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP_AG)) {
+ if (mHeadsetProfile == null) {
+ if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
+ mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
+ mDeviceManager, this);
+ addProfile(mHeadsetProfile, HeadsetProfile.NAME,
+ BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mHeadsetProfile != null) {
+ Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
+ }
+
+ // OPP
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+ if (mOppProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local OPP profile");
+ mOppProfile = new OppProfile();
+ // Note: no event handler for OPP, only name map.
+ mProfileNameMap.put(OppProfile.NAME, mOppProfile);
+ }
+ } else if (mOppProfile != null) {
+ Log.w(TAG, "Warning: OPP profile was previously added but the UUID is now missing.");
+ }
+ mEventManager.registerProfileIntentReceiver();
+
+ // There is no local SDP record for HID and Settings app doesn't control PBAP
+ }
+
+ private final Collection mServiceListeners =
+ new ArrayList();
+
+ private void addProfile(LocalBluetoothProfile profile,
+ String profileName, String stateChangedAction) {
+ mEventManager.addProfileHandler(stateChangedAction, new StateChangedHandler(profile));
+ mProfileNameMap.put(profileName, profile);
+ }
+
+ private void addPanProfile(LocalBluetoothProfile profile,
+ String profileName, String stateChangedAction) {
+ mEventManager.addProfileHandler(stateChangedAction,
+ new PanStateChangedHandler(profile));
+ mProfileNameMap.put(profileName, profile);
+ }
+
+ public LocalBluetoothProfile getProfileByName(String name) {
+ return mProfileNameMap.get(name);
+ }
+
+ // Called from LocalBluetoothAdapter when state changes to ON
+ void setBluetoothStateOn() {
+ ParcelUuid[] uuids = mLocalAdapter.getUuids();
+ if (uuids != null) {
+ updateLocalProfiles(uuids);
+ }
+ mEventManager.readPairedDevices();
+ }
+
+ /**
+ * Generic handler for connection state change events for the specified profile.
+ */
+ private class StateChangedHandler implements BluetoothEventManager.Handler {
+ final LocalBluetoothProfile mProfile;
+
+ StateChangedHandler(LocalBluetoothProfile profile) {
+ mProfile = profile;
+ }
+
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice == null) {
+ Log.w(TAG, "StateChangedHandler found new device: " + device);
+ cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
+ LocalBluetoothProfileManager.this, device);
+ }
+ int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+ int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
+ if (newState == BluetoothProfile.STATE_DISCONNECTED &&
+ oldState == BluetoothProfile.STATE_CONNECTING) {
+ Log.i(TAG, "Failed to connect " + mProfile + " device");
+ }
+
+ cachedDevice.onProfileStateChanged(mProfile, newState);
+ cachedDevice.refresh();
+ }
+ }
+
+ /** State change handler for NAP and PANU profiles. */
+ private class PanStateChangedHandler extends StateChangedHandler {
+
+ PanStateChangedHandler(LocalBluetoothProfile profile) {
+ super(profile);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+ PanProfile panProfile = (PanProfile) mProfile;
+ int role = intent.getIntExtra(BluetoothPan.EXTRA_LOCAL_ROLE, 0);
+ panProfile.setLocalRole(device, role);
+ super.onReceive(context, intent, device);
+ }
+ }
+
+ // called from DockService
+ public void addServiceListener(ServiceListener l) {
+ mServiceListeners.add(l);
+ }
+
+ // called from DockService
+ public void removeServiceListener(ServiceListener l) {
+ mServiceListeners.remove(l);
+ }
+
+ // not synchronized: use only from UI thread! (TODO: verify)
+ void callServiceConnectedListeners() {
+ for (ServiceListener l : mServiceListeners) {
+ l.onServiceConnected();
+ }
+ }
+
+ // not synchronized: use only from UI thread! (TODO: verify)
+ void callServiceDisconnectedListeners() {
+ for (ServiceListener listener : mServiceListeners) {
+ listener.onServiceDisconnected();
+ }
+ }
+
+ // This is called by DockService, so check Headset and A2DP.
+ public synchronized boolean isManagerReady() {
+ // Getting just the headset profile is fine for now. Will need to deal with A2DP
+ // and others if they aren't always in a ready state.
+ LocalBluetoothProfile profile = mHeadsetProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
+ }
+ profile = mA2dpProfile;
+ if (profile != null) {
+ return profile.isProfileReady();
+ }
+ return false;
+ }
+
+ public A2dpProfile getA2dpProfile() {
+ return mA2dpProfile;
+ }
+
+ public HeadsetProfile getHeadsetProfile() {
+ return mHeadsetProfile;
+ }
+
+ public PbapServerProfile getPbapProfile(){
+ return mPbapProfile;
+ }
+
+ public MapProfile getMapProfile(){
+ return mMapProfile;
+ }
+
+ /**
+ * Fill in a list of LocalBluetoothProfile objects that are supported by
+ * the local device and the remote device.
+ *
+ * @param uuids of the remote device
+ * @param localUuids UUIDs of the local device
+ * @param profiles The list of profiles to fill
+ * @param removedProfiles list of profiles that were removed
+ */
+ synchronized void updateProfiles(ParcelUuid[] uuids, ParcelUuid[] localUuids,
+ Collection profiles,
+ Collection removedProfiles,
+ boolean isPanNapConnected, BluetoothDevice device) {
+ // Copy previous profile list into removedProfiles
+ removedProfiles.clear();
+ removedProfiles.addAll(profiles);
+ profiles.clear();
+
+ if (uuids == null) {
+ return;
+ }
+
+ if (mHeadsetProfile != null) {
+ if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
+ (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
+ profiles.add(mHeadsetProfile);
+ removedProfiles.remove(mHeadsetProfile);
+ }
+ }
+
+ if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
+ mA2dpProfile != null) {
+ profiles.add(mA2dpProfile);
+ removedProfiles.remove(mA2dpProfile);
+ }
+
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
+ mOppProfile != null) {
+ profiles.add(mOppProfile);
+ removedProfiles.remove(mOppProfile);
+ }
+
+ if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
+ BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
+ mHidProfile != null) {
+ profiles.add(mHidProfile);
+ removedProfiles.remove(mHidProfile);
+ }
+
+ if(isPanNapConnected)
+ if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
+ if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
+ mPanProfile != null) || isPanNapConnected) {
+ profiles.add(mPanProfile);
+ removedProfiles.remove(mPanProfile);
+ }
+
+ if ((mMapProfile != null) &&
+ (mMapProfile.getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED)) {
+ profiles.add(mMapProfile);
+ removedProfiles.remove(mMapProfile);
+ mMapProfile.setPreferred(device, true);
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
new file mode 100644
index 0000000000000..e6a152f7205b1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapProfile.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2012 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MapProfile handles Bluetooth MAP profile.
+ */
+public final class MapProfile implements LocalBluetoothProfile {
+ private static final String TAG = "MapProfile";
+ private static boolean V = true;
+
+ private BluetoothMap mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final ParcelUuid[] UUIDS = {
+ BluetoothUuid.MAP,
+ BluetoothUuid.MNS,
+ BluetoothUuid.MAS,
+ };
+
+ static final String NAME = "MAP";
+
+ // Order of this profile in device profiles list
+
+ // These callbacks run on the main thread.
+ private final class MapServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothMap) proxy;
+ // We just bound to the service, so refresh the UI for any connected MAP devices.
+ List deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "MapProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(MapProfile.this,
+ BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ return mIsProfileReady;
+ }
+
+ MapProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new MapServiceListener(),
+ BluetoothProfile.MAP);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if(V)Log.d(TAG,"connect() - should not get called");
+ return false; // MAP never connects out
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List deviceList = mService.getConnectedDevices();
+ if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) {
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ } else {
+ return false;
+ }
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+ List deviceList = mService.getConnectedDevices();
+ if(V) Log.d(TAG,"getConnectionStatus: status is: "+ mService.getConnectionState(device));
+
+ return !deviceList.isEmpty() && deviceList.get(0).equals(device)
+ ? mService.getConnectionState(device)
+ : BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public List getConnectedDevices() {
+ if (mService == null) return new ArrayList(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return BluetoothProfile.MAP;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_map;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_map_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_map_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up MAP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
new file mode 100755
index 0000000000000..31e675c2355bd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/OppProfile.java
@@ -0,0 +1,89 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import com.android.settingslib.R;
+
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+
+/**
+ * OppProfile handles Bluetooth OPP.
+ */
+final class OppProfile implements LocalBluetoothProfile {
+
+ static final String NAME = "OPP";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 2;
+
+ public boolean isConnectable() {
+ return false;
+ }
+
+ public boolean isAutoConnectable() {
+ return false;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ return false;
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ return BluetoothProfile.STATE_DISCONNECTED; // Settings app doesn't handle OPP
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return BluetoothProfile.PRIORITY_OFF; // Settings app doesn't handle OPP
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ }
+
+ public boolean isProfileReady() {
+ return true;
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_opp;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ return 0; // OPP profile not displayed in UI
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return 0; // no icon for OPP
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
new file mode 100755
index 0000000000000..3af89e6d25031
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPan;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PanProfile handles Bluetooth PAN profile (NAP and PANU).
+ */
+final class PanProfile implements LocalBluetoothProfile {
+ private static final String TAG = "PanProfile";
+ private static boolean V = true;
+
+ private BluetoothPan mService;
+ private boolean mIsProfileReady;
+
+ // Tethering direction for each device
+ private final HashMap mDeviceRoleMap =
+ new HashMap();
+
+ static final String NAME = "PAN";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 4;
+
+ // These callbacks run on the main thread.
+ private final class PanServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothPan) proxy;
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ PanProfile(Context context) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ adapter.getProfileProxy(context, new PanServiceListener(),
+ BluetoothProfile.PAN);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return false;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List sinks = mService.getConnectedDevices();
+ if (sinks != null) {
+ for (BluetoothDevice sink : sinks) {
+ mService.disconnect(sink);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ // return current connection status so profile checkbox is set correctly
+ return getConnectionStatus(device) == BluetoothProfile.STATE_CONNECTED;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return -1;
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ // ignore: isPreferred is always true for PAN
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ if (isLocalRoleNap(device)) {
+ return R.string.bluetooth_profile_pan_nap;
+ } else {
+ return R.string.bluetooth_profile_pan;
+ }
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_pan_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ if (isLocalRoleNap(device)) {
+ return R.string.bluetooth_pan_nap_profile_summary_connected;
+ } else {
+ return R.string.bluetooth_pan_user_profile_summary_connected;
+ }
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_network_pan;
+ }
+
+ // Tethering direction determines UI strings.
+ void setLocalRole(BluetoothDevice device, int role) {
+ mDeviceRoleMap.put(device, role);
+ }
+
+ boolean isLocalRoleNap(BluetoothDevice device) {
+ if (mDeviceRoleMap.containsKey(device)) {
+ return mDeviceRoleMap.get(device) == BluetoothPan.LOCAL_NAP_ROLE;
+ } else {
+ return false;
+ }
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.PAN, mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up PAN proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
new file mode 100755
index 0000000000000..a552b24a713cd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -0,0 +1,152 @@
+/*
+ * 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 com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothPbap;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * PBAPServer Profile
+ */
+public final class PbapServerProfile implements LocalBluetoothProfile {
+ private static final String TAG = "PbapServerProfile";
+ private static boolean V = true;
+
+ private BluetoothPbap mService;
+ private boolean mIsProfileReady;
+
+ static final String NAME = "PBAP Server";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 6;
+
+ // The UUIDs indicate that remote device might access pbap server
+ static final ParcelUuid[] PBAB_CLIENT_UUIDS = {
+ BluetoothUuid.HSP,
+ BluetoothUuid.Handsfree,
+ BluetoothUuid.PBAP_PCE
+ };
+
+ // These callbacks run on the main thread.
+ private final class PbapServiceListener
+ implements BluetoothPbap.ServiceListener {
+
+ public void onServiceConnected(BluetoothPbap proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothPbap) proxy;
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected() {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ return mIsProfileReady;
+ }
+
+ PbapServerProfile(Context context) {
+ BluetoothPbap pbap = new BluetoothPbap(context, new PbapServiceListener());
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return false;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ /*Can't connect from server */
+ return false;
+
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.disconnect();
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) {
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ if (mService.isConnected(device))
+ return BluetoothProfile.STATE_CONNECTED;
+ else
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ return false;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ return -1;
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ // ignore: isPreferred is always true for PBAP
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_pbap;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ return R.string.bluetooth_profile_pbap_summary;
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ mService.close();
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up PBAP proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
new file mode 100644
index 0000000000000..c9194268543a3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/Utils.java
@@ -0,0 +1,43 @@
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.R;
+
+public class Utils {
+ public static final boolean V = false; // verbose logging
+ public static final boolean D = true; // regular logging
+
+ private static ErrorListener sErrorListener;
+
+ public static int getConnectionStateSummary(int connectionState) {
+ switch (connectionState) {
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_connected;
+ case BluetoothProfile.STATE_CONNECTING:
+ return R.string.bluetooth_connecting;
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_disconnected;
+ case BluetoothProfile.STATE_DISCONNECTING:
+ return R.string.bluetooth_disconnecting;
+ default:
+ return 0;
+ }
+ }
+
+ static void showError(Context context, String name, int messageResId) {
+ if (sErrorListener != null) {
+ sErrorListener.onShowError(context, name, messageResId);
+ }
+ }
+
+ public static void setErrorListener(ErrorListener listener) {
+ sErrorListener = listener;
+ }
+
+ public interface ErrorListener {
+ void onShowError(Context context, String name, int messageResId);
+ }
+
+}