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";
}
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 {
method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
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
* Service via IPC.
*
* <p> Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothHealth proxy object. Use
* {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
* @hide
* <p> How to connect to a health device which is acting in the source role.
* <li> Use {@link BluetoothAdapter#getProfileProxy} to get
* the BluetoothHealth proxy object. </li>
* <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 {
private static final String TAG = "BluetoothHealth";
@ -137,7 +152,6 @@ public final class BluetoothHealth implements BluetoothProfile {
*
* @param config The health app configuration
* @return Success or failure.
* @hide
*/
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
boolean result = false;
@ -222,16 +236,15 @@ public final class BluetoothHealth implements BluetoothProfile {
* @param device The remote Bluetooth device.
* @param config The application configuration which has been registered using
* {@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.
* @hide
*/
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
BluetoothHealthAppConfiguration config, int channelId) {
if (mService != null && isEnabled() && isValidDevice(device) &&
config != null) {
try {
return mService.disconnectChannel(device, config, fd);
return mService.disconnectChannel(device, config, channelId);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@ -248,11 +261,13 @@ public final class BluetoothHealth implements BluetoothProfile {
*
* <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 config The application configuration
* @return null on failure, ParcelFileDescriptor on success.
*/
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
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}
*
@ -368,14 +383,15 @@ public final class BluetoothHealth implements BluetoothProfile {
@Override
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
int status) {
mCallback.onHealthAppConfigurationStatusChange(config, status);
mCallback.onHealthAppConfigurationStatusChange(config, status);
}
@Override
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState,
ParcelFileDescriptor fd) {
mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd);
ParcelFileDescriptor fd, int channelId) {
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;
/** 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 */
public static final int APPLICATION_REGISTRATION_FAILURE = 1;
public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
/** 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 */
public static final int APPLICATION_UNREGISTRATION_FAILURE = 3;
public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
private ServiceListener mServiceListener;
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
* remote Bluetooth health device.
*
* @hide
*/
public final class BluetoothHealthAppConfiguration implements Parcelable {
private final String mName;
@ -39,6 +38,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
*
* @param name Friendly name associated with the application configuration
* @param dataType Data Type of the remote Bluetooth Health device
* @hide
*/
BluetoothHealthAppConfiguration(String name, int dataType) {
mName = name;
@ -54,6 +54,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
* @param dataType Data Type of the remote Bluetooth Health device
* @param role {@link BluetoothHealth#SOURCE_ROLE} or
* {@link BluetoothHealth#SINK_ROLE}
* @hide
*/
BluetoothHealthAppConfiguration(String name, int dataType, int role, int
channelType) {
@ -93,7 +94,6 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
mChannelType + "]";
}
@Override
public int describeContents() {
return 0;
}
@ -132,6 +132,7 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
* @return One of {@link BluetoothHealth#CHANNEL_TYPE_RELIABLE} or
* {@link BluetoothHealth#CHANNEL_TYPE_STREAMING} or
* {@link BluetoothHealth#CHANNEL_TYPE_ANY}.
* @hide
*/
public int getChannelType() {
return mChannelType;
@ -155,13 +156,10 @@ public final class BluetoothHealthAppConfiguration implements Parcelable {
}
};
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeString(mName);
out.writeInt(mDataType);
out.writeInt(mRole);
out.writeInt(mChannelType);
}
}

View File

@ -21,22 +21,48 @@ import android.os.ParcelFileDescriptor;
import android.util.Log;
/**
* This class is used for all the {@link BluetoothHealth} callbacks.
* @hide
* This abstract class is used to implement {@link BluetoothHealth} callbacks.
*/
public abstract class 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,
int status) {
Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + " Status:" + status);
int 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,
BluetoothDevice device, int prevState, int newState,
ParcelFileDescriptor fd) {
Log.d(TAG, "onHealthChannelStateChange: " + config + " Device:" + device +
"PrevState:" + prevState + "NewState:" + newState + "FileDescriptor:" + fd);
BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
int channelId) {
Log.d(TAG, "onHealthChannelStateChange: " + config + "Device: " + device +
"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 connectChannelToSink(in BluetoothDevice device, in BluetoothHealthAppConfiguration config,
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);
List<BluetoothDevice> getConnectedHealthDevices();
List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(in int[] states);

View File

@ -27,5 +27,6 @@ interface IBluetoothHealthCallback
{
void onHealthAppConfigurationStatusChange(in BluetoothHealthAppConfiguration config, int status);
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.util.Log;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -47,7 +49,6 @@ final class BluetoothHealthProfileHandler {
private static final boolean DBG = true;
private static BluetoothHealthProfileHandler sInstance;
private Context mContext;
private BluetoothService mBluetoothService;
private ArrayList<HealthChannel> mHealthChannels;
private HashMap <BluetoothHealthAppConfiguration, String> mHealthAppConfigs;
@ -76,6 +77,17 @@ final class BluetoothHealthProfileHandler {
mConfig = config;
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() {
@ -98,28 +110,38 @@ final class BluetoothHealthProfileHandler {
}
if (path == null) {
mCallbacks.remove(registerApp);
callHealthApplicationStatusCallback(registerApp,
BluetoothHealth.APPLICATION_REGISTRATION_FAILURE);
BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE);
mCallbacks.remove(registerApp);
} else {
mHealthAppConfigs.put(registerApp, path);
callHealthApplicationStatusCallback(registerApp,
BluetoothHealth.APPLICATION_REGISTRATION_SUCCESS);
BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS);
}
break;
case MESSAGE_UNREGISTER_APPLICATION:
BluetoothHealthAppConfiguration unregisterApp =
(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(
mHealthAppConfigs.get(unregisterApp));
if (result) {
mCallbacks.remove(unregisterApp);
callHealthApplicationStatusCallback(unregisterApp,
BluetoothHealth.APPLICATION_UNREGISTRATION_SUCCESS);
BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS);
mCallbacks.remove(unregisterApp);
mHealthAppConfigs.remove(unregisterApp);
} else {
callHealthApplicationStatusCallback(unregisterApp,
BluetoothHealth.APPLICATION_UNREGISTRATION_FAILURE);
BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE);
}
break;
case MESSAGE_CONNECT_CHANNEL:
@ -133,7 +155,8 @@ final class BluetoothHealthProfileHandler {
channelType)) {
int prevState = chan.mState;
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);
}
}
@ -141,7 +164,6 @@ final class BluetoothHealthProfileHandler {
};
private BluetoothHealthProfileHandler(Context context, BluetoothService service) {
mContext = context;
mBluetoothService = service;
mHealthAppConfigs = new HashMap<BluetoothHealthAppConfiguration, String>();
mHealthChannels = new ArrayList<HealthChannel>();
@ -205,7 +227,7 @@ final class BluetoothHealthProfileHandler {
int prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
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);
msg.obj = chan;
@ -235,37 +257,44 @@ final class BluetoothHealthProfileHandler {
}
boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
HealthChannel chan = findChannelByFd(device, config, fd);
if (chan == null) return false;
BluetoothHealthAppConfiguration config, int id) {
HealthChannel chan = findChannelById(device, config, id);
if (chan == null) {
return false;
}
String deviceObjectPath =
mBluetoothService.getObjectPathFromAddress(device.getAddress());
if (mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) {
int prevState = chan.mState;
chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING;
mBluetoothService.releaseChannelFdNative(chan.mChannelPath);
int prevState = chan.mState;
chan.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTING;
callHealthChannelCallback(config, device, prevState, chan.mState,
null, chan.hashCode());
if (!mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) {
prevState = chan.mState;
chan.mState = BluetoothHealth.STATE_CHANNEL_CONNECTED;
callHealthChannelCallback(config, device, prevState, chan.mState,
chan.mChannelFd);
return true;
} else {
chan.mChannelFd, chan.hashCode());
return false;
} else {
return true;
}
}
private HealthChannel findChannelByFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
private HealthChannel findChannelById(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int id) {
for (HealthChannel chan : mHealthChannels) {
if (chan.mChannelFd.equals(fd) && chan.mDevice.equals(device) &&
chan.mConfig.equals(config)) return chan;
if (chan.hashCode() == id) return chan;
}
return null;
}
private HealthChannel findChannelByPath(BluetoothDevice device,
BluetoothHealthAppConfiguration config, String path) {
private HealthChannel findChannelByPath(BluetoothDevice device, String path) {
for (HealthChannel chan : mHealthChannels) {
if (chan.mChannelPath.equals(path) && chan.mDevice.equals(device) &&
chan.mConfig.equals(config)) return chan;
if (chan.mChannelPath.equals(path) && chan.mDevice.equals(device)) return chan;
}
return null;
}
@ -296,7 +325,15 @@ final class BluetoothHealthProfileHandler {
ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration 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 =
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
// main channel. We might not have received the PropertyChanged yet for
// the main channel creation so update our data structure here.
chan = findChannelByPath(device, config, mainChannelPath);
chan = findChannelByPath(device, mainChannelPath);
if (chan == null) {
errorLog("Main Channel present but we don't have any account of it:" +
device +":" + config);
return null;
}
chan.mMainChannel = true;
return chan.mChannelFd;
try {
return chan.mChannelFd.dup();
} catch (IOException e) {
return null;
}
}
/*package*/ void onHealthDevicePropertyChanged(String devicePath,
@ -334,7 +375,7 @@ final class BluetoothHealthProfileHandler {
BluetoothHealthAppConfiguration config = findHealthApplication(device,
channelPath);
if (config != null) {
HealthChannel chan = findChannelByPath(device, config, channelPath);
HealthChannel chan = findChannelByPath(device, channelPath);
if (chan == null) {
errorLog("Health Channel is not present:" + channelPath);
} else {
@ -346,21 +387,22 @@ final class BluetoothHealthProfileHandler {
private BluetoothHealthAppConfiguration findHealthApplication(
BluetoothDevice device, String channelPath) {
BluetoothHealthAppConfiguration config = null;
String configPath = mBluetoothService.getChannelApplicationNative(channelPath);
HealthChannel chan = findChannelByPath(device, channelPath);
if (configPath == null) {
errorLog("No associated application for Health Channel:" + channelPath);
return null;
if (chan != null) {
config = chan.mConfig;
} else {
for (Entry<BluetoothHealthAppConfiguration, String> e :
mHealthAppConfigs.entrySet()) {
if (e.getValue().equals(configPath)) {
config = e.getKey();
String configPath = mBluetoothService.getChannelApplicationNative(channelPath);
if (configPath == null) {
errorLog("Config path is null for application");
} else {
for (Entry<BluetoothHealthAppConfiguration, String> e :
mHealthAppConfigs.entrySet()) {
if (e.getValue().equals(configPath)) {
config = e.getKey();
}
}
}
if (config == null) {
errorLog("No associated application for application path:" + configPath);
return null;
if (config == null) errorLog("No associated application for path:" + configPath);
}
}
return config;
@ -375,78 +417,83 @@ final class BluetoothHealthProfileHandler {
if (address == null) return;
BluetoothDevice device = adapter.getRemoteDevice(address);
BluetoothHealthAppConfiguration config = findHealthApplication(device,
channelPath);
BluetoothHealthAppConfiguration config;
int state, prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
ParcelFileDescriptor fd;
HealthChannel channel;
config = findHealthApplication(device, channelPath);
if (config != null) {
if (exists) {
fd = mBluetoothService.getChannelFdNative(channelPath);
if (exists) {
fd = mBluetoothService.getChannelFdNative(channelPath);
if (fd == null) {
errorLog("Error obtaining fd for channel:" + channelPath);
return;
}
boolean mainChannel =
getMainChannel(device, config) == null ? false : true;
if (!mainChannel) {
String mainChannelPath =
mBluetoothService.getMainChannelNative(devicePath);
if (mainChannelPath == null) {
errorLog("Main Channel Path is null for devicePath:" + devicePath);
return;
}
if (mainChannelPath.equals(channelPath)) mainChannel = true;
}
channel = findConnectingChannel(device, config);
if (channel != null) {
channel.mChannelFd = fd;
channel.mMainChannel = mainChannel;
channel.mChannelPath = channelPath;
prevState = channel.mState;
} else {
channel = new HealthChannel(device, config, fd, mainChannel,
channelPath);
mHealthChannels.add(channel);
prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
}
state = BluetoothHealth.STATE_CHANNEL_CONNECTED;
} else {
channel = findChannelByPath(device, channelPath);
if (channel == null) {
errorLog("Channel not found:" + config + ":" + channelPath);
return;
}
mHealthChannels.remove(channel);
if (fd == null) {
errorLog("Error obtaining fd for channel:" + channelPath);
return;
}
boolean mainChannel =
getMainChannel(device, config) == null ? false : true;
if (!mainChannel) {
String mainChannelPath =
mBluetoothService.getMainChannelNative(devicePath);
if (mainChannelPath == null) {
errorLog("Main Channel Path is null for devicePath:" + devicePath);
return;
}
if (mainChannelPath.equals(channelPath)) mainChannel = true;
}
channel = findConnectingChannel(device, config);
if (channel != null) {
channel.mChannelFd = fd;
channel.mMainChannel = mainChannel;
channel.mChannelPath = channelPath;
prevState = channel.mState;
} else {
channel = new HealthChannel(device, config, fd, mainChannel,
channelPath);
mHealthChannels.add(channel);
prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
}
state = BluetoothHealth.STATE_CHANNEL_CONNECTED;
} else {
channel = findChannelByPath(device, config, channelPath);
if (channel == null) {
errorLog("Channel not found:" + config + ":" + channelPath);
return;
}
fd = channel.mChannelFd;
// CLOSE FD
mBluetoothService.releaseChannelFdNative(channel.mChannelPath);
mHealthChannels.remove(channel);
prevState = channel.mState;
state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
}
channel.mState = state;
callHealthChannelCallback(config, device, prevState, state, fd);
channel.mChannelFd = null;
prevState = channel.mState;
state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
}
channel.mState = state;
callHealthChannelCallback(config, device, prevState, state, channel.mChannelFd,
channel.hashCode());
}
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);
debugLog("Health Device Callback: " + device + " State Change: "
+ 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);
if (callback != null) {
try {
callback.onHealthChannelStateChange(config, device, prevState, state, fd);
} catch (RemoteException e) {}
callback.onHealthChannelStateChange(config, device, prevState, state, dupedFd, id);
} catch (RemoteException e) {
errorLog("Remote Exception:" + e);
}
}
}
@ -458,7 +505,9 @@ final class BluetoothHealthProfileHandler {
if (callback != null) {
try {
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,
BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
BluetoothHealthAppConfiguration config, int id) {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
"Need BLUETOOTH permission");
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) {
// FileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath);
close(fd);
return NULL;
}
@ -1667,7 +1668,7 @@ static jobject getChannelFdNative(JNIEnv *env, jobject object, jstring channelPa
if (parcelFileDesc == NULL) {
// ParcelFileDescriptor constructor has thrown an exception
releaseChannelFdNative(env, object, channelPath);
LOGE("---Parcel File Desc is null");
close(fd);
return NULL;
}