diff options
author | 2016-11-02 00:55:40 +0000 | |
---|---|---|
committer | 2016-11-02 00:55:40 +0000 | |
commit | cf4dc9236e6319f233edc9c16730c6db4b416ae4 (patch) | |
tree | 670a375274709e2a292a60a1123f2d57e3650e39 | |
parent | 7f64c195f73065fb5350887ebdd1ff865d3c7697 (diff) | |
parent | b8fc0679e84f9cadc08524ea89f6e1b28631f3e5 (diff) |
Merge changes Icac17694,Ic0b651f3,Ifa2cdea7
* changes:
MAP MCE
Pbap disconnect durring connect
MAP MCE
9 files changed, 735 insertions, 8 deletions
diff --git a/Android.mk b/Android.mk index ada4ac5a54f7..53d4b3eb02fa 100644 --- a/Android.mk +++ b/Android.mk @@ -116,9 +116,10 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothPan.aidl \ core/java/android/bluetooth/IBluetoothManager.aidl \ core/java/android/bluetooth/IBluetoothManagerCallback.aidl \ + core/java/android/bluetooth/IBluetoothMap.aidl \ + core/java/android/bluetooth/IBluetoothMapClient.aidl \ core/java/android/bluetooth/IBluetoothPbap.aidl \ core/java/android/bluetooth/IBluetoothPbapClient.aidl \ - core/java/android/bluetooth/IBluetoothMap.aidl \ core/java/android/bluetooth/IBluetoothSap.aidl \ core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \ core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \ diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 59edadce9620..11c27355c787 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1973,6 +1973,9 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.PBAP_CLIENT) { BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener); return true; + } else if (profile == BluetoothProfile.MAP_CLIENT) { + BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); + return true; } else { return false; } @@ -2045,6 +2048,10 @@ public final class BluetoothAdapter { BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy; pbapClient.close(); break; + case BluetoothProfile.MAP_CLIENT: + BluetoothMapClient mapClient = (BluetoothMapClient)proxy; + mapClient.close(); + break; } } diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java new file mode 100644 index 000000000000..425248224e1e --- /dev/null +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -0,0 +1,415 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class provides the APIs to control the Bluetooth MAP MCE Profile. + * + * @hide + */ +public final class BluetoothMapClient implements BluetoothProfile { + + private static final String TAG = "BluetoothMapClient"; + private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); + + public static final String ACTION_CONNECTION_STATE_CHANGED = + "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; + public static final String ACTION_MESSAGE_RECEIVED = + "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED"; + /* Actions to be used for pending intents */ + public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY = + "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY"; + public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY = + "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY"; + + private IBluetoothMapClient mService; + private final Context mContext; + private ServiceListener mServiceListener; + private BluetoothAdapter mAdapter; + + /** There was an error trying to obtain the state */ + public static final int STATE_ERROR = -1; + + public static final int RESULT_FAILURE = 0; + public static final int RESULT_SUCCESS = 1; + /** Connection canceled before completion. */ + public static final int RESULT_CANCELED = 2; + + final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = + new IBluetoothStateChangeCallback.Stub() { + public void onBluetoothStateChange(boolean up) { + if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); + if (!up) { + if (VDBG) Log.d(TAG, "Unbinding service..."); + synchronized (mConnection) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } + } + } else { + synchronized (mConnection) { + try { + if (mService == null) { + if (VDBG) Log.d(TAG, "Binding service..."); + doBind(); + } + } catch (Exception re) { + Log.e(TAG, "", re); + } + } + } + } + }; + + /** + * Create a BluetoothMapClient proxy object. + */ + /*package*/ BluetoothMapClient(Context context, ServiceListener l) { + if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object"); + mContext = context; + mServiceListener = l; + mAdapter = BluetoothAdapter.getDefaultAdapter(); + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } + } + doBind(); + } + + boolean doBind() { + Intent intent = new Intent(IBluetoothMapClient.class.getName()); + ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); + intent.setComponent(comp); + if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, + android.os.Process.myUserHandle())) { + Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent); + return false; + } + return true; + } + + protected void finalize() throws Throwable { + try { + close(); + } finally { + super.finalize(); + } + } + + /** + * Close the connection to the backing service. + * Other public functions of BluetoothMap will return default error + * results once close() has been called. Multiple invocations of close() + * are ok. + */ + public void close() { + IBluetoothManager mgr = mAdapter.getBluetoothManager(); + if (mgr != null) { + try { + mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); + } catch (Exception e) { + Log.e(TAG, "", e); + } + } + + synchronized (mConnection) { + if (mService != null) { + try { + mService = null; + mContext.unbindService(mConnection); + } catch (Exception re) { + Log.e(TAG, "", re); + } + } + } + mServiceListener = null; + } + + /** + * Returns true if the specified Bluetooth device is connected. + * Returns false if not connected, or if this proxy object is not + * currently connected to the Map service. + */ + public boolean isConnected(BluetoothDevice device) { + if (VDBG) Log.d(TAG, "isConnected(" + device + ")"); + if (mService != null) { + try { + return mService.isConnected(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Initiate connection. Initiation of outgoing connections is not + * supported for MAP server. + */ + public boolean connect(BluetoothDevice device) { + if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE"); + if (mService != null) { + try { + return mService.connect(device); + } catch (RemoteException e) { + Log.e(TAG, e.toString()); + } + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return false; + } + + /** + * Initiate disconnect. + * + * @param device Remote Bluetooth Device + * @return false on error, true otherwise + */ + public boolean disconnect(BluetoothDevice device) { + if (DBG) Log.d(TAG, "disconnect(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.disconnect(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the list of connected devices. Currently at most one. + * + * @return list of connected devices + */ + @Override + public List<BluetoothDevice> getConnectedDevices() { + if (DBG) Log.d(TAG, "getConnectedDevices()"); + if (mService != null && isEnabled()) { + try { + return mService.getConnectedDevices(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } + + /** + * Get the list of devices matching specified states. Currently at most one. + * + * @return list of matching devices + */ + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { + if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); + if (mService != null && isEnabled()) { + try { + return mService.getDevicesMatchingConnectionStates(states); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return new ArrayList<>(); + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return new ArrayList<>(); + } + + /** + * Get connection state of device + * + * @return device connection state + */ + @Override + public int getConnectionState(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getConnectionState(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return BluetoothProfile.STATE_DISCONNECTED; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return BluetoothProfile.STATE_DISCONNECTED; + } + + /** + * Set priority of the profile + * + * <p> The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or + * {@link #PRIORITY_OFF}, + * + * @param device Paired bluetooth device + * @return true if priority is set, false on error + */ + public boolean setPriority(BluetoothDevice device, int priority) { + if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + if (priority != BluetoothProfile.PRIORITY_OFF && + priority != BluetoothProfile.PRIORITY_ON) { + return false; + } + try { + return mService.setPriority(device, priority); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return false; + } + + /** + * Get the priority of the profile. + * + * <p> The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * @param device Bluetooth device + * @return priority of the device + */ + public int getPriority(BluetoothDevice device) { + if (VDBG) Log.d(TAG, "getPriority(" + device + ")"); + if (mService != null && isEnabled() && + isValidDevice(device)) { + try { + return mService.getPriority(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return PRIORITY_OFF; + } + } + if (mService == null) Log.w(TAG, "Proxy not attached to service"); + return PRIORITY_OFF; + } + + /** + * Send a message. + * + * Send an SMS message to either the contacts primary number or the telephone number specified. + * + * @param device Bluetooth device + * @param contacts Uri[] of the contacts + * @param message Message to be sent + * @param sentIntent intent issued when message is sent + * @param deliveredIntent intent issued when message is delivered + * @return true if the message is enqueued, false on error + */ + public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, + PendingIntent sentIntent, PendingIntent deliveredIntent) { + if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + /** + * Get unread messages. Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}. + * + * @param device Bluetooth device + * @return true if the message is enqueued, false on error + */ + public boolean getUnreadMessages(BluetoothDevice device) { + if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")"); + if (mService != null && isEnabled() && isValidDevice(device)) { + try { + return mService.getUnreadMessages(device); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(new Throwable())); + return false; + } + } + return false; + } + + private final ServiceConnection mConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName className, IBinder service) { + if (DBG) Log.d(TAG, "Proxy object connected"); + mService = IBluetoothMapClient.Stub.asInterface(service); + if (mServiceListener != null) { + mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT, + BluetoothMapClient.this); + } + } + + public void onServiceDisconnected(ComponentName className) { + if (DBG) Log.d(TAG, "Proxy object disconnected"); + mService = null; + if (mServiceListener != null) { + mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT); + } + } + }; + + private boolean isEnabled() { + BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true; + if (DBG) Log.d(TAG, "Bluetooth is Not enabled"); + return false; + } + + private boolean isValidDevice(BluetoothDevice device) { + if (device == null) return false; + + if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; + return false; + } + + +} diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 20d95ccc3835..f3636070fa5c 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2014 The Android Open Source Project + * Copyright (C) 2010-2016 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. @@ -137,6 +137,12 @@ public interface BluetoothProfile { public static final int PBAP_CLIENT = 17; /** + * MAP Messaging Client Equipment (MCE) + * @hide + */ + public static final int MAP_CLIENT = 18; + + /** * Max profile ID. This value should be updated whenever a new profile is added to match * the largest value assigned to a profile. * @hide diff --git a/core/java/android/bluetooth/IBluetoothMapClient.aidl b/core/java/android/bluetooth/IBluetoothMapClient.aidl new file mode 100644 index 000000000000..df45af91c92c --- /dev/null +++ b/core/java/android/bluetooth/IBluetoothMapClient.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; +import android.net.Uri; + +/** + * System private API for Bluetooth MAP MCE service + * + * {@hide} + */ +interface IBluetoothMapClient { + boolean connect(in BluetoothDevice device); + boolean disconnect(in BluetoothDevice device); + boolean isConnected(in BluetoothDevice device); + List<BluetoothDevice> getConnectedDevices(); + List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); + int getConnectionState(in BluetoothDevice device); + boolean setPriority(in BluetoothDevice device,in int priority); + int getPriority(in BluetoothDevice device); + boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message, + in PendingIntent sentIntent, in PendingIntent deliveryIntent); + boolean getUnreadMessages(in BluetoothDevice device); +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index eaf56ac544f2..5e9380ed159a 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7932,6 +7932,9 @@ public final class Settings { BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_"; /** {@hide} */ public static final String + BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_"; + /** {@hide} */ + public static final String BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_"; /** {@hide} */ public static final String @@ -8132,6 +8135,14 @@ public final class Settings { } /** + * Get the key that retrieves a bluetooth map client priority. + * @hide + */ + public static final String getBluetoothMapClientPriorityKey(String address) { + return BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT); + } + + /** * Get the key that retrieves a bluetooth pbap client priority. * @hide */ @@ -8140,7 +8151,7 @@ public final class Settings { } /** - * Get the key that retrieves a bluetooth map priority. + * Get the key that retrieves a bluetooth sap priority. * @hide */ public static final String getBluetoothSapPriorityKey(String address) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 7f0e27a96c0a..1ea592d7785d 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothMap; +import android.bluetooth.BluetoothMapClient; import android.bluetooth.BluetoothInputDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothPbapClient; @@ -81,12 +82,14 @@ public final class LocalBluetoothProfileManager { private HeadsetProfile mHeadsetProfile; private HfpClientProfile mHfpClientProfile; private MapProfile mMapProfile; + private MapClientProfile mMapClientProfile; private final HidProfile mHidProfile; private OppProfile mOppProfile; private final PanProfile mPanProfile; private PbapClientProfile mPbapClientProfile; private final PbapServerProfile mPbapProfile; private final boolean mUsePbapPce; + private final boolean mUseMapClient; /** * Mapping from profile name, e.g. "HEADSET" to profile object. @@ -104,6 +107,8 @@ public final class LocalBluetoothProfileManager { mDeviceManager = deviceManager; mEventManager = eventManager; mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile); + // MAP Client is typically used in the same situations as PBAP Client + mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile); // pass this reference to adapter and event manager (circular dependency) mLocalAdapter.setProfileManager(this); mEventManager.setProfileManager(this); @@ -125,10 +130,15 @@ public final class LocalBluetoothProfileManager { 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); + if (mUseMapClient) { + mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this); + addProfile(mMapClientProfile, MapClientProfile.NAME, + BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED); + } else { + 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 @@ -199,6 +209,22 @@ public final class LocalBluetoothProfileManager { Log.d(TAG, "Handsfree Uuid not found."); } + // Message Access Profile Client + if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) { + if (mMapClientProfile == null) { + if(DEBUG) Log.d(TAG, "Adding local Map Client profile"); + mMapClientProfile = + new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this); + addProfile(mMapClientProfile, MapClientProfile.NAME, + BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED); + } + } else if (mMapClientProfile != null) { + Log.w(TAG, + "Warning: MAP Client profile was previously added but the UUID is now missing."); + } else { + Log.d(TAG, "MAP Client Uuid not found."); + } + // OPP if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) { if (mOppProfile == null) { @@ -383,6 +409,10 @@ public final class LocalBluetoothProfileManager { return mMapProfile; } + public MapClientProfile getMapClientProfile() { + return mMapClientProfile; + } + /** * Fill in a list of LocalBluetoothProfile objects that are supported by * the local device and the remote device. @@ -465,6 +495,11 @@ public final class LocalBluetoothProfileManager { mMapProfile.setPreferred(device, true); } + if (mMapClientProfile != null) { + profiles.add(mMapClientProfile); + removedProfiles.remove(mMapClientProfile); + } + if (mUsePbapPce) { profiles.add(mPbapClientProfile); removedProfiles.remove(mPbapClientProfile); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java new file mode 100644 index 000000000000..a7621fcf02fb --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java @@ -0,0 +1,212 @@ +/* + * 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.BluetoothMapClient; +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; + +/** + * MapClientProfile handles Bluetooth MAP profile. + */ +public final class MapClientProfile implements LocalBluetoothProfile { + private static final String TAG = "MapClientProfile"; + private static boolean V = false; + + private BluetoothMapClient 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 Client"; + + // Order of this profile in device profiles list + private static final int ORDINAL = 0; + + // These callbacks run on the main thread. + private final class MapClientServiceListener + implements BluetoothProfile.ServiceListener { + + public void onServiceConnected(int profile, BluetoothProfile proxy) { + if (V) Log.d(TAG,"Bluetooth service connected"); + mService = (BluetoothMapClient) proxy; + // We just bound to the service, so refresh the UI for any connected MAP devices. + List<BluetoothDevice> 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(MapClientProfile.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; + } + + MapClientProfile(Context context, LocalBluetoothAdapter adapter, + CachedBluetoothDeviceManager deviceManager, + LocalBluetoothProfileManager profileManager) { + mLocalAdapter = adapter; + mDeviceManager = deviceManager; + mProfileManager = profileManager; + mLocalAdapter.getProfileProxy(context, new MapClientServiceListener(), + BluetoothProfile.MAP_CLIENT); + } + + public boolean isConnectable() { + return true; + } + + public boolean isAutoConnectable() { + return true; + } + + public boolean connect(BluetoothDevice device) { + if (mService == null) return false; + List<BluetoothDevice> connectedDevices = getConnectedDevices(); + if (connectedDevices != null) { + for (BluetoothDevice connectedDevice : connectedDevices) { + mService.disconnect(connectedDevice); + } + } + return mService.connect(device); + } + + public boolean disconnect(BluetoothDevice device) { + if (mService == null) return false; + // Downgrade priority as user is disconnecting. + if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { + mService.setPriority(device, BluetoothProfile.PRIORITY_ON); + } + 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) { + 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<BluetoothDevice> getConnectedDevices() { + if (mService == null) return new ArrayList<BluetoothDevice>(0); + return mService.getDevicesMatchingConnectionStates( + new int[] {BluetoothProfile.STATE_CONNECTED, + BluetoothProfile.STATE_CONNECTING, + BluetoothProfile.STATE_DISCONNECTING}); + } + + public String toString() { + return NAME; + } + + public int getOrdinal() { + return ORDINAL; + } + + 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_CLIENT, + mService); + mService = null; + }catch (Throwable t) { + Log.w(TAG, "Error cleaning up MAP Client proxy", t); + } + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java index d7c9eab6074b..72a3b3a00e22 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java @@ -145,7 +145,7 @@ final class PbapClientProfile implements LocalBluetoothProfile { } } for (BluetoothDevice src : srcs) { - mService.disconnect(device); + mService.disconnect(src); } } Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress()); |