Merge "Make Bluetooth Health APIs public."

This commit is contained in:
Jaikumar Ganesh
2011-09-08 15:19:37 -07:00
committed by Android (Google) Code Review
9 changed files with 266 additions and 137 deletions

View File

@ -4528,6 +4528,44 @@ package android.bluetooth {
field public static final java.lang.String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid"; field public static final java.lang.String VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY = "android.bluetooth.headset.intent.category.companyid";
} }
public final class BluetoothHealth implements android.bluetooth.BluetoothProfile {
method public boolean connectChannelToSource(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
method public boolean disconnectChannel(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration, int);
method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public int getConnectionState(android.bluetooth.BluetoothDevice);
method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.os.ParcelFileDescriptor getMainChannelFd(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothHealthAppConfiguration);
method public boolean registerSinkAppConfiguration(java.lang.String, int, android.bluetooth.BluetoothHealthCallback);
method public boolean unregisterAppConfiguration(android.bluetooth.BluetoothHealthAppConfiguration);
field public static final int APP_CONFIG_REGISTRATION_FAILURE = 1; // 0x1
field public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0; // 0x0
field public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3; // 0x3
field public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2; // 0x2
field public static final int CHANNEL_TYPE_RELIABLE = 10; // 0xa
field public static final int CHANNEL_TYPE_STREAMING = 11; // 0xb
field public static final int SINK_ROLE = 2; // 0x2
field public static final int SOURCE_ROLE = 1; // 0x1
field public static final int STATE_CHANNEL_CONNECTED = 2; // 0x2
field public static final int STATE_CHANNEL_CONNECTING = 1; // 0x1
field public static final int STATE_CHANNEL_DISCONNECTED = 0; // 0x0
field public static final int STATE_CHANNEL_DISCONNECTING = 3; // 0x3
}
public final class BluetoothHealthAppConfiguration implements android.os.Parcelable {
method public int describeContents();
method public int getDataType();
method public java.lang.String getName();
method public int getRole();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
public abstract class BluetoothHealthCallback {
ctor public BluetoothHealthCallback();
method public void onHealthAppConfigurationStatusChange(android.bluetooth.BluetoothHealthAppConfiguration, int);
method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
}
public abstract interface BluetoothProfile { public abstract interface BluetoothProfile {
method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method public abstract int getConnectionState(android.bluetooth.BluetoothDevice); method public abstract int getConnectionState(android.bluetooth.BluetoothDevice);

View File

@ -32,10 +32,25 @@ import java.util.List;
* <p>BluetoothHealth is a proxy object for controlling the Bluetooth * <p>BluetoothHealth is a proxy object for controlling the Bluetooth
* Service via IPC. * Service via IPC.
* *
* <p> Use {@link BluetoothAdapter#getProfileProxy} to get * <p> How to connect to a health device which is acting in the source role.
* the BluetoothHealth proxy object. Use * <li> Use {@link BluetoothAdapter#getProfileProxy} to get
* {@link BluetoothAdapter#closeProfileProxy} to close the service connection. * the BluetoothHealth proxy object. </li>
* @hide * <li> Create an {@link BluetoothHealth} callback and call
* {@link #registerSinkAppConfiguration} to register an application
* configuration </li>
* <li> Pair with the remote device. This currently needs to be done manually
* from Bluetooth Settings </li>
* <li> Connect to a health device using {@link #connectChannelToSource}. Some
* devices will connect the channel automatically. The {@link BluetoothHealth}
* callback will inform the application of channel state change. </li>
* <li> Use the file descriptor provided with a connected channel to read and
* write data to the health channel. </li>
* <li> The received data needs to be interpreted using a health manager which
* implements the IEEE 11073-xxxxx specifications.
* <li> When done, close the health channel by calling {@link #disconnectChannel}
* and unregister the application configuration calling
* {@link #unregisterAppConfiguration}
*
*/ */
public final class BluetoothHealth implements BluetoothProfile { public final class BluetoothHealth implements BluetoothProfile {
private static final String TAG = "BluetoothHealth"; private static final String TAG = "BluetoothHealth";
@ -137,7 +152,6 @@ public final class BluetoothHealth implements BluetoothProfile {
* *
* @param config The health app configuration * @param config The health app configuration
* @return Success or failure. * @return Success or failure.
* @hide
*/ */
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) { public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
boolean result = false; boolean result = false;
@ -222,16 +236,15 @@ public final class BluetoothHealth implements BluetoothProfile {
* @param device The remote Bluetooth device. * @param device The remote Bluetooth device.
* @param config The application configuration which has been registered using * @param config The application configuration which has been registered using
* {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) } * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
* @param fd The file descriptor that was associated with the channel. * @param channelId The channel id associated with the channel
* @return If true, the callback associated with the application config will be called. * @return If true, the callback associated with the application config will be called.
* @hide
*/ */
public boolean disconnectChannel(BluetoothDevice device, public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { BluetoothHealthAppConfiguration config, int channelId) {
if (mService != null && isEnabled() && isValidDevice(device) && if (mService != null && isEnabled() && isValidDevice(device) &&
config != null) { config != null) {
try { try {
return mService.disconnectChannel(device, config, fd); return mService.disconnectChannel(device, config, channelId);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
} }
@ -248,11 +261,13 @@ public final class BluetoothHealth implements BluetoothProfile {
* *
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
* *
* <p> Its the responsibility of the caller to close the ParcelFileDescriptor
* when done.
*
* @param device The remote Bluetooth health device * @param device The remote Bluetooth health device
* @param config The application configuration * @param config The application configuration
* @return null on failure, ParcelFileDescriptor on success. * @return null on failure, ParcelFileDescriptor on success.
*/ */
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) { BluetoothHealthAppConfiguration config) {
if (mService != null && isEnabled() && isValidDevice(device) && if (mService != null && isEnabled() && isValidDevice(device) &&
@ -300,7 +315,7 @@ public final class BluetoothHealth implements BluetoothProfile {
} }
/** /**
* Get connected devices for this specific profile. * Get connected devices for the health profile.
* *
* <p> Return the set of devices which are in state {@link #STATE_CONNECTED} * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
* *
@ -374,8 +389,9 @@ public final class BluetoothHealth implements BluetoothProfile {
@Override @Override
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState, BluetoothDevice device, int prevState, int newState,
ParcelFileDescriptor fd) { ParcelFileDescriptor fd, int channelId) {
mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd); mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd,
channelId);
} }
} }
@ -389,13 +405,13 @@ public final class BluetoothHealth implements BluetoothProfile {
public static final int STATE_CHANNEL_DISCONNECTING = 3; public static final int STATE_CHANNEL_DISCONNECTING = 3;
/** Health App Configuration registration success */ /** Health App Configuration registration success */
public static final int APPLICATION_REGISTRATION_SUCCESS = 0; public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
/** Health App Configuration registration failure */ /** Health App Configuration registration failure */
public static final int APPLICATION_REGISTRATION_FAILURE = 1; public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
/** Health App Configuration un-registration success */ /** Health App Configuration un-registration success */
public static final int APPLICATION_UNREGISTRATION_SUCCESS = 2; public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
/** Health App Configuration un-registration failure */ /** Health App Configuration un-registration failure */
public static final int APPLICATION_UNREGISTRATION_FAILURE = 3; public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
private ServiceListener mServiceListener; private ServiceListener mServiceListener;
private IBluetooth mService; private IBluetooth mService;

View File

@ -26,7 +26,6 @@ import android.os.Parcelable;
* that the Bluetooth Health third party application will register to communicate with the * that the Bluetooth Health third party application will register to communicate with the
* remote Bluetooth health device. * remote Bluetooth health device.
* *
* @hide
*/ */
public final class BluetoothHealthAppConfiguration implements Parcelable { public final class BluetoothHealthAppConfiguration implements Parcelable {
private final String mName; private final String mName;
@ -39,6 +38,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
* *
* @param name Friendly name associated with the application configuration * @param name Friendly name associated with the application configuration
* @param dataType Data Type of the remote Bluetooth Health device * @param dataType Data Type of the remote Bluetooth Health device
* @hide
*/ */
BluetoothHealthAppConfiguration(String name, int dataType) { BluetoothHealthAppConfiguration(String name, int dataType) {
mName = name; mName = name;
@ -54,6 +54,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
* @param dataType Data Type of the remote Bluetooth Health device * @param dataType Data Type of the remote Bluetooth Health device
* @param role {@link BluetoothHealth#SOURCE_ROLE} or * @param role {@link BluetoothHealth#SOURCE_ROLE} or
* {@link BluetoothHealth#SINK_ROLE} * {@link BluetoothHealth#SINK_ROLE}
* @hide
*/ */
BluetoothHealthAppConfiguration(String name, int dataType, int role, int BluetoothHealthAppConfiguration(String name, int dataType, int role, int
channelType) { channelType) {
@ -93,7 +94,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
mChannelType + "]"; mChannelType + "]";
} }
@Override
public int describeContents() { public int describeContents() {
return 0; return 0;
} }
@ -132,6 +132,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
* @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or * @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or
* {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or * {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or
* {@link BluetoothHealth#CHANNEL_TYPE_ANY}. * {@link BluetoothHealth#CHANNEL_TYPE_ANY}.
* @hide
*/ */
public int getChannelType() { public int getChannelType() {
return mChannelType; return mChannelType;
@ -155,13 +156,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
} }
}; };
@Override
public void writeToParcel(Parcel out, int flags) { public void writeToParcel(Parcel out, int flags) {
out.writeString(mName); out.writeString(mName);
out.writeInt(mDataType); out.writeInt(mDataType);
out.writeInt(mRole); out.writeInt(mRole);
out.writeInt(mChannelType); out.writeInt(mChannelType);
} }
} }

View File

@ -21,22 +21,48 @@ import android.os.ParcelFileDescriptor;
import android.util.Log; import android.util.Log;
/** /**
* This class is used for all the {@link BluetoothHealth} callbacks. * This abstract class is used to implement {@link BluetoothHealth} callbacks.
* @hide
*/ */
public abstract class BluetoothHealthCallback { public abstract class BluetoothHealthCallback {
private static final String TAG = "BluetoothHealthCallback"; private static final String TAG = "BluetoothHealthCallback";
/**
* Callback to inform change in registration state of the health
* application.
* <p> This callback is called on the binder thread (not on the UI thread)
*
* @param config Bluetooth Health app configuration
* @param status Success or failure of the registration or unregistration
* calls. Can be one of
* {@link BluetoothHealth#APP_CONFIG_REGISTRATION_SUCCESS} or
* {@link BluetoothHealth#APP_CONFIG_REGISTRATION_FAILURE} or
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
*/
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config, public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
int status) { int status) {
Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status); Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
} }
/**
* Callback to inform change in channel state.
* <p> Its the responsibility of the implementor of this callback to close the
* parcel file descriptor when done. This callback is called on the Binder
* thread (not the UI thread)
*
* @param config The Health app configutation
* @param device The Bluetooth Device
* @param prevState The previous state of the channel
* @param newState The new state of the channel.
* @param fd The Parcel File Descriptor when the channel state is connected.
* @param channelId The id associated with the channel. This id will be used
* in future calls like when disconnecting the channel.
*/
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config, public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState, BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
ParcelFileDescriptor fd) { int channelId) {
Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device + Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device +
"PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd); "prevState:" + prevState + "newState:" + newState + "ParcelFd:" + fd +
"ChannelId:" + channelId);
} }
} }

View File

@ -116,7 +116,7 @@ interface IBluetooth
boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); boolean connectChannelToSource(in BluetoothDevice device, in BluetoothHealthAppConfiguration config);
boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, boolean connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config,
int channelType); int channelType);
boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, in ParcelFileDescriptor fd); boolean disconnectChannel(in BluetoothDevice device, in BluetoothHealthAppConfiguration config, int id);
ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config); ParcelFileDescriptor getMainChannelFd(in BluetoothDevice device, in BluetoothHealthAppConfiguration config);
List<BluetoothDevice> getConnectedHealthDevices(); List<BluetoothDevice> getConnectedHealthDevices();
List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(in int[] states); List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(in int[] states);

View File

@ -27,5 +27,6 @@ interface IBluetoothHealthCallback
{ {
void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status); void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status);
void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config, void onHealthChannelStateChange(in BluetoothHealthAppConfiguration config,
in BluetoothDevice device, int prevState, int newState, in ParcelFileDescriptor fd); in BluetoothDevice device, int prevState, int newState, in
ParcelFileDescriptor fd, int id);
} }

View File

@ -29,6 +29,8 @@ import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -47,7 +49,6 @@ final class BluetoothHealthProfileHandler {
private static final boolean DBG = true; private static final boolean DBG = true;
private static BluetoothHealthProfileHandler sInstance; private static BluetoothHealthProfileHandler sInstance;
private Context mContext;
private BluetoothService mBluetoothService; private BluetoothService mBluetoothService;
private ArrayList<HealthChannel> mHealthChannels; private ArrayList<HealthChannel> mHealthChannels;
private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs; private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs;
@ -76,6 +77,17 @@ final class BluetoothHealthProfileHandler {
mConfig = config; mConfig = config;
mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
} }
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (mChannelPath == null ? 0 : mChannelPath.hashCode());
result = 31 * result + mDevice.hashCode();
result = 31 * result + mConfig.hashCode();
result = 31 * result + mState;
result = 31 * result + mChannelType;
return result;
}
} }
private final Handler mHandler = new Handler() { private final Handler mHandler = new Handler() {
@ -98,28 +110,38 @@ final class BluetoothHealthProfileHandler {
} }
if (path == null) { if (path == null) {
mCallbacks.remove(registerApp);
callHealthApplicationStatusCallback(registerApp, callHealthApplicationStatusCallback(registerApp,
BluetoothHealth.APPLICATION_REGISTRATION_FAILURE); BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE);
mCallbacks.remove(registerApp);
} else { } else {
mHealthAppConfigs.put(registerApp, path); mHealthAppConfigs.put(registerApp, path);
callHealthApplicationStatusCallback(registerApp, callHealthApplicationStatusCallback(registerApp,
BluetoothHealth.APPLICATION_REGISTRATION_SUCCESS); BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS);
} }
break; break;
case MESSAGE_UNREGISTER_APPLICATION: case MESSAGE_UNREGISTER_APPLICATION:
BluetoothHealthAppConfiguration unregisterApp = BluetoothHealthAppConfiguration unregisterApp =
(BluetoothHealthAppConfiguration) msg.obj; (BluetoothHealthAppConfiguration) msg.obj;
// Disconnect all the channels
for (HealthChannel chan : mHealthChannels) {
if (chan.mConfig.equals(unregisterApp) &&
chan.mState != BluetoothHealth.STATE_CHANNEL_DISCONNECTED) {
disconnectChannel(chan.mDevice, unregisterApp, chan.hashCode());
}
}
boolean result = mBluetoothService.unregisterHealthApplicationNative( boolean result = mBluetoothService.unregisterHealthApplicationNative(
mHealthAppConfigs.get(unregisterApp)); mHealthAppConfigs.get(unregisterApp));
if (result) { if (result) {
mCallbacks.remove(unregisterApp);
callHealthApplicationStatusCallback(unregisterApp, callHealthApplicationStatusCallback(unregisterApp,
BluetoothHealth.APPLICATION_UNREGISTRATION_SUCCESS); BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS);
mCallbacks.remove(unregisterApp);
mHealthAppConfigs.remove(unregisterApp);
} else { } else {
callHealthApplicationStatusCallback(unregisterApp, callHealthApplicationStatusCallback(unregisterApp,
BluetoothHealth.APPLICATION_UNREGISTRATION_FAILURE); BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE);
} }
break; break;
case MESSAGE_CONNECT_CHANNEL: case MESSAGE_CONNECT_CHANNEL:
@ -133,7 +155,8 @@ final class BluetoothHealthProfileHandler {
channelType)) { channelType)) {
int prevState = chan.mState; int prevState = chan.mState;
int state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; int state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
callHealthChannelCallback(chan.mConfig, chan.mDevice, prevState, state, null); callHealthChannelCallback(chan.mConfig, chan.mDevice, prevState, state, null,
chan.hashCode());
mHealthChannels.remove(chan); mHealthChannels.remove(chan);
} }
} }
@ -141,7 +164,6 @@ final class BluetoothHealthProfileHandler {
}; };
private BluetoothHealthProfileHandler(Context context, BluetoothService service) { private BluetoothHealthProfileHandler(Context context, BluetoothService service) {
mContext = context;
mBluetoothService = service; mBluetoothService = service;
mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>(); mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>();
mHealthChannels = new ArrayList<HealthChannel>(); mHealthChannels = new ArrayList<HealthChannel>();
@ -205,7 +227,7 @@ final class BluetoothHealthProfileHandler {
int prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; int prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
int state = BluetoothHealth.STATE_CHANNEL_CONNECTING; int state = BluetoothHealth.STATE_CHANNEL_CONNECTING;
callHealthChannelCallback(config, device, prevState, state, null); callHealthChannelCallback(config, device, prevState, state, null, chan.hashCode());
Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_CHANNEL); Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_CHANNEL);
msg.obj = chan; msg.obj = chan;
@ -235,37 +257,44 @@ final class BluetoothHealthProfileHandler {
} }
boolean disconnectChannel(BluetoothDevice device, boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { BluetoothHealthAppConfiguration config, int id) {
HealthChannel chan = findChannelByFd(device, config, fd); HealthChannel chan = findChannelById(device, config, id);
if (chan == null) return false; if (chan == null) {
return false;
}
String deviceObjectPath = String deviceObjectPath =
mBluetoothService.getObjectPathFromAddress(device.getAddress()); mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) {
mBluetoothService.releaseChannelFdNative(chan.mChannelPath);
int prevState = chan.mState; int prevState = chan.mState;
chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING; chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING;
callHealthChannelCallback(config, device, prevState, chan.mState, callHealthChannelCallback(config, device, prevState, chan.mState,
chan.mChannelFd); null, chan.hashCode());
return true;
} else { if (!mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) {
prevState = chan.mState;
chan.mState = BluetoothHealth.STATE_CHANNEL_CONNECTED;
callHealthChannelCallback(config, device, prevState, chan.mState,
chan.mChannelFd, chan.hashCode());
return false; return false;
} else {
return true;
} }
} }
private HealthChannel findChannelByFd(BluetoothDevice device, private HealthChannel findChannelById(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { BluetoothHealthAppConfiguration config, int id) {
for (HealthChannel chan : mHealthChannels) { for (HealthChannel chan : mHealthChannels) {
if (chan.mChannelFd.equals(fd) && chan.mDevice.equals(device) && if (chan.hashCode() == id) return chan;
chan.mConfig.equals(config)) return chan;
} }
return null; return null;
} }
private HealthChannel findChannelByPath(BluetoothDevice device, private HealthChannel findChannelByPath(BluetoothDevice device, String path) {
BluetoothHealthAppConfiguration config, String path) {
for (HealthChannel chan : mHealthChannels) { for (HealthChannel chan : mHealthChannels) {
if (chan.mChannelPath.equals(path) && chan.mDevice.equals(device) && if (chan.mChannelPath.equals(path) && chan.mDevice.equals(device)) return chan;
chan.mConfig.equals(config)) return chan;
} }
return null; return null;
} }
@ -296,7 +325,15 @@ final class BluetoothHealthProfileHandler {
ParcelFileDescriptor getMainChannelFd(BluetoothDevice device, ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) { BluetoothHealthAppConfiguration config) {
HealthChannel chan = getMainChannel(device, config); HealthChannel chan = getMainChannel(device, config);
if (chan != null) return chan.mChannelFd; if (chan != null) {
ParcelFileDescriptor pfd = null;
try {
pfd = chan.mChannelFd.dup();
return pfd;
} catch (IOException e) {
return null;
}
}
String objectPath = String objectPath =
mBluetoothService.getObjectPathFromAddress(device.getAddress()); mBluetoothService.getObjectPathFromAddress(device.getAddress());
@ -308,14 +345,18 @@ final class BluetoothHealthProfileHandler {
// We had no record of the main channel but querying Bluez we got a // We had no record of the main channel but querying Bluez we got a
// main channel. We might not have received the PropertyChanged yet for // main channel. We might not have received the PropertyChanged yet for
// the main channel creation so update our data structure here. // the main channel creation so update our data structure here.
chan = findChannelByPath(device, config, mainChannelPath); chan = findChannelByPath(device, mainChannelPath);
if (chan == null) { if (chan == null) {
errorLog("Main Channel present but we don't have any account of it:" + errorLog("Main Channel present but we don't have any account of it:" +
device +":" + config); device +":" + config);
return null; return null;
} }
chan.mMainChannel = true; chan.mMainChannel = true;
return chan.mChannelFd; try {
return chan.mChannelFd.dup();
} catch (IOException e) {
return null;
}
} }
/*package*/ void onHealthDevicePropertyChanged(String devicePath, /*package*/ void onHealthDevicePropertyChanged(String devicePath,
@ -334,7 +375,7 @@ final class BluetoothHealthProfileHandler {
BluetoothHealthAppConfiguration config = findHealthApplication(device, BluetoothHealthAppConfiguration config = findHealthApplication(device,
channelPath); channelPath);
if (config != null) { if (config != null) {
HealthChannel chan = findChannelByPath(device, config, channelPath); HealthChannel chan = findChannelByPath(device, channelPath);
if (chan == null) { if (chan == null) {
errorLog("Health Channel is not present:" + channelPath); errorLog("Health Channel is not present:" + channelPath);
} else { } else {
@ -346,11 +387,14 @@ final class BluetoothHealthProfileHandler {
private BluetoothHealthAppConfiguration findHealthApplication( private BluetoothHealthAppConfiguration findHealthApplication(
BluetoothDevice device, String channelPath) { BluetoothDevice device, String channelPath) {
BluetoothHealthAppConfiguration config = null; BluetoothHealthAppConfiguration config = null;
String configPath = mBluetoothService.getChannelApplicationNative(channelPath); HealthChannel chan = findChannelByPath(device, channelPath);
if (chan != null) {
config = chan.mConfig;
} else {
String configPath = mBluetoothService.getChannelApplicationNative(channelPath);
if (configPath == null) { if (configPath == null) {
errorLog("No associated application for Health Channel:" + channelPath); errorLog("Config path is null for application");
return null;
} else { } else {
for (Entry<BluetoothHealthAppConfiguration, String> e : for (Entry<BluetoothHealthAppConfiguration, String> e :
mHealthAppConfigs.entrySet()) { mHealthAppConfigs.entrySet()) {
@ -358,9 +402,7 @@ final class BluetoothHealthProfileHandler {
config = e.getKey(); config = e.getKey();
} }
} }
if (config == null) { if (config == null) errorLog("No associated application for path:" + configPath);
errorLog("No associated application for application path:" + configPath);
return null;
} }
} }
return config; return config;
@ -375,22 +417,18 @@ final class BluetoothHealthProfileHandler {
if (address == null) return; if (address == null) return;
BluetoothDevice device = adapter.getRemoteDevice(address); BluetoothDevice device = adapter.getRemoteDevice(address);
BluetoothHealthAppConfiguration config;
BluetoothHealthAppConfiguration config = findHealthApplication(device,
channelPath);
int state, prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; int state, prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
ParcelFileDescriptor fd; ParcelFileDescriptor fd;
HealthChannel channel; HealthChannel channel;
config = findHealthApplication(device, channelPath);
if (config != null) {
if (exists) { if (exists) {
fd = mBluetoothService.getChannelFdNative(channelPath); fd = mBluetoothService.getChannelFdNative(channelPath);
if (fd == null) { if (fd == null) {
errorLog("Error obtaining fd for channel:" + channelPath); errorLog("Error obtaining fd for channel:" + channelPath);
return; return;
} }
boolean mainChannel = boolean mainChannel =
getMainChannel(device, config) == null ? false : true; getMainChannel(device, config) == null ? false : true;
if (!mainChannel) { if (!mainChannel) {
@ -402,7 +440,6 @@ final class BluetoothHealthProfileHandler {
} }
if (mainChannelPath.equals(channelPath)) mainChannel = true; if (mainChannelPath.equals(channelPath)) mainChannel = true;
} }
channel = findConnectingChannel(device, config); channel = findConnectingChannel(device, config);
if (channel != null) { if (channel != null) {
channel.mChannelFd = fd; channel.mChannelFd = fd;
@ -417,36 +454,46 @@ final class BluetoothHealthProfileHandler {
} }
state = BluetoothHealth.STATE_CHANNEL_CONNECTED; state = BluetoothHealth.STATE_CHANNEL_CONNECTED;
} else { } else {
channel = findChannelByPath(device, config, channelPath); channel = findChannelByPath(device, channelPath);
if (channel == null) { if (channel == null) {
errorLog("Channel not found:" + config + ":" + channelPath); errorLog("Channel not found:" + config + ":" + channelPath);
return; return;
} }
fd = channel.mChannelFd;
// CLOSE FD
mBluetoothService.releaseChannelFdNative(channel.mChannelPath);
mHealthChannels.remove(channel); mHealthChannels.remove(channel);
channel.mChannelFd = null;
prevState = channel.mState; prevState = channel.mState;
state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED; state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
} }
channel.mState = state; channel.mState = state;
callHealthChannelCallback(config, device, prevState, state, fd); callHealthChannelCallback(config, device, prevState, state, channel.mChannelFd,
} channel.hashCode());
} }
private void callHealthChannelCallback(BluetoothHealthAppConfiguration config, private void callHealthChannelCallback(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int state, ParcelFileDescriptor fd) { BluetoothDevice device, int prevState, int state, ParcelFileDescriptor fd, int id) {
broadcastHealthDeviceStateChange(device, prevState, state); broadcastHealthDeviceStateChange(device, prevState, state);
debugLog("Health Device Callback: " + device + " State Change: " debugLog("Health Device Callback: " + device + " State Change: "
+ prevState + "->" + state); + prevState + "->" + state);
ParcelFileDescriptor dupedFd = null;
if (fd != null) {
try {
dupedFd = fd.dup();
} catch (IOException e) {
dupedFd = null;
errorLog("Exception while duping: " + e);
}
}
IBluetoothHealthCallback callback = mCallbacks.get(config); IBluetoothHealthCallback callback = mCallbacks.get(config);
if (callback != null) { if (callback != null) {
try { try {
callback.onHealthChannelStateChange(config, device, prevState, state, fd); callback.onHealthChannelStateChange(config, device, prevState, state, dupedFd, id);
} catch (RemoteException e) {} } catch (RemoteException e) {
errorLog("Remote Exception:" + e);
}
} }
} }
@ -458,7 +505,9 @@ final class BluetoothHealthProfileHandler {
if (callback != null) { if (callback != null) {
try { try {
callback.onHealthAppConfigurationStatusChange(config, status); callback.onHealthAppConfigurationStatusChange(config, status);
} catch (RemoteException e) {} } catch (RemoteException e) {
errorLog("Remote Exception:" + e);
}
} }
} }

View File

@ -2243,11 +2243,11 @@ public class BluetoothService extends IBluetooth.Stub {
} }
public boolean disconnectChannel(BluetoothDevice device, public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) { BluetoothHealthAppConfiguration config, int id) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission"); "Need BLUETOOTH permission");
synchronized (mBluetoothHealthProfileHandler) { synchronized (mBluetoothHealthProfileHandler) {
return mBluetoothHealthProfileHandler.disconnectChannel(device, config, fd); return mBluetoothHealthProfileHandler.disconnectChannel(device, config, id);
} }
} }

View File

@ -1659,6 +1659,7 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa
if (fileDesc == NULL) { if (fileDesc == NULL) {
// FileDescriptor constructor has thrown an exception // FileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath); releaseChannelFdNative(env, object, channelPath);
close(fd);
return NULL; return NULL;
} }
@ -1667,7 +1668,7 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa
if (parcelFileDesc == NULL) { if (parcelFileDesc == NULL) {
// ParcelFileDescriptor constructor has thrown an exception // ParcelFileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath); releaseChannelFdNative(env, object, channelPath);
LOGE("---Parcel File Desc is null"); close(fd);
return NULL; return NULL;
} }