blob: 0a01dcf80e1d61b2b929dbeedb2b37041333c144 [file] [log] [blame]
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bluetooth;
18
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -070019import android.content.Context;
20import android.os.IBinder;
21import android.os.ParcelFileDescriptor;
22import android.os.RemoteException;
23import android.os.ServiceManager;
24import android.util.Log;
25
26import java.util.ArrayList;
27import java.util.List;
28
29/**
30 * Public API for Bluetooth Health Profile.
31 *
32 * <p>BluetoothHealth is a proxy object for controlling the Bluetooth
33 * Service via IPC.
34 *
35 * <p> Use {@link BluetoothAdapter#getProfileProxy} to get
36 * the BluetoothHealth proxy object. Use
37 * {@link BluetoothAdapter#closeProfileProxy} to close the service connection.
38 * @hide
39 */
40public final class BluetoothHealth implements BluetoothProfile {
41 private static final String TAG = "BluetoothHealth";
42 private static final boolean DBG = false;
43
44 /**
45 * Health Profile Source Role - the health device.
46 */
47 public static final int SOURCE_ROLE = 1 << 0;
48
49 /**
50 * Health Profile Sink Role the device talking to the health device.
51 */
52 public static final int SINK_ROLE = 1 << 1;
53
54 /**
55 * Health Profile - Channel Type used - Reliable
56 */
57 public static final int CHANNEL_TYPE_RELIABLE = 10;
58
59 /**
60 * Health Profile - Channel Type used - Streaming
61 */
62 public static final int CHANNEL_TYPE_STREAMING = 11;
63
64 /**
65 * @hide
66 */
67 public static final int CHANNEL_TYPE_ANY = 12;
68
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -070069 /**
70 * Register an application configuration that acts as a Health SINK.
71 * This is the configuration that will be used to communicate with health devices
72 * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
73 * the callback is used to notify success or failure if the function returns true.
74 *
75 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
76 *
77 * @param name The friendly name associated with the application or configuration.
78 * @param dataType The dataType of the Source role of Health Profile to which
79 * the sink wants to connect to.
80 * @param callback A callback to indicate success or failure of the registration and
81 * all operations done on this application configuration.
82 * @return If true, callback will be called.
83 */
84 public boolean registerSinkAppConfiguration(String name, int dataType,
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -070085 BluetoothHealthCallback callback) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -070086 if (!isEnabled() || name == null) return false;
87
88 if (DBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
89 return registerAppConfiguration(name, dataType, SINK_ROLE,
90 CHANNEL_TYPE_ANY, callback);
91 }
92
93 /**
94 * Register an application configuration that acts as a Health SINK or in a Health
95 * SOURCE role.This is an asynchronous call and so
96 * the callback is used to notify success or failure if the function returns true.
97 *
98 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
99 *
100 * @param name The friendly name associated with the application or configuration.
101 * @param dataType The dataType of the Source role of Health Profile.
102 * @param channelType The channel type. Will be one of
103 * {@link #CHANNEL_TYPE_RELIABLE} or
104 * {@link #CHANNEL_TYPE_STREAMING}
105 * @param callback - A callback to indicate success or failure.
106 * @return If true, callback will be called.
107 * @hide
108 */
109 public boolean registerAppConfiguration(String name, int dataType, int role,
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700110 int channelType, BluetoothHealthCallback callback) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700111 boolean result = false;
112 if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
113
114 if (DBG) log("registerApplication(" + name + ":" + dataType + ")");
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700115 BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700116 BluetoothHealthAppConfiguration config =
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700117 new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700118
119 if (mService != null) {
120 try {
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700121 result = mService.registerAppConfiguration(config, wrapper);
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700122 } catch (RemoteException e) {
123 Log.e(TAG, e.toString());
124 }
125 } else {
126 Log.w(TAG, "Proxy not attached to service");
127 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
128 }
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700129 return result;
130 }
131
132 /**
133 * Unregister an application configuration that has been registered using
134 * {@link #registerSinkAppConfiguration}
135 *
136 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
137 *
138 * @param config The health app configuration
139 * @return Success or failure.
140 * @hide
141 */
142 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
143 boolean result = false;
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700144 if (mService != null && isEnabled() && config != null) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700145 try {
146 result = mService.unregisterAppConfiguration(config);
147 } catch (RemoteException e) {
148 Log.e(TAG, e.toString());
149 }
150 } else {
151 Log.w(TAG, "Proxy not attached to service");
152 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
153 }
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700154
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700155 return result;
156 }
157
158 /**
159 * Connect to a health device which has the {@link #SOURCE_ROLE}.
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700160 * This is an asynchronous call. If this function returns true, the callback
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700161 * associated with the application configuration will be called.
162 *
163 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
164 *
165 * @param device The remote Bluetooth device.
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700166 * @param config The application configuration which has been registered using
167 * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700168 * @return If true, the callback associated with the application config will be called.
169 */
170 public boolean connectChannelToSource(BluetoothDevice device,
171 BluetoothHealthAppConfiguration config) {
172 if (mService != null && isEnabled() && isValidDevice(device) &&
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700173 config != null) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700174 try {
175 return mService.connectChannelToSource(device, config);
176 } catch (RemoteException e) {
177 Log.e(TAG, e.toString());
178 }
179 } else {
180 Log.w(TAG, "Proxy not attached to service");
181 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
182 }
183 return false;
184 }
185
186 /**
187 * Connect to a health device which has the {@link #SINK_ROLE}.
188 * This is an asynchronous call. If this function returns true, the callback
189 * associated with the application configuration will be called.
190 *
191 *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
192 *
193 * @param device The remote Bluetooth device.
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700194 * @param config The application configuration which has been registered using
195 * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700196 * @return If true, the callback associated with the application config will be called.
197 * @hide
198 */
199 public boolean connectChannelToSink(BluetoothDevice device,
200 BluetoothHealthAppConfiguration config, int channelType) {
201 if (mService != null && isEnabled() && isValidDevice(device) &&
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700202 config != null) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700203 try {
204 return mService.connectChannelToSink(device, config, channelType);
205 } catch (RemoteException e) {
206 Log.e(TAG, e.toString());
207 }
208 } else {
209 Log.w(TAG, "Proxy not attached to service");
210 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
211 }
212 return false;
213 }
214
215 /**
216 * Disconnect a connected health channel.
217 * This is an asynchronous call. If this function returns true, the callback
218 * associated with the application configuration will be called.
219 *
220 *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
221 *
222 * @param device The remote Bluetooth device.
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700223 * @param config The application configuration which has been registered using
224 * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700225 * @param fd The file descriptor that was associated with the channel.
226 * @return If true, the callback associated with the application config will be called.
227 * @hide
228 */
229 public boolean disconnectChannel(BluetoothDevice device,
230 BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
231 if (mService != null && isEnabled() && isValidDevice(device) &&
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700232 config != null) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700233 try {
234 return mService.disconnectChannel(device, config, fd);
235 } catch (RemoteException e) {
236 Log.e(TAG, e.toString());
237 }
238 } else {
239 Log.w(TAG, "Proxy not attached to service");
240 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
241 }
242 return false;
243 }
244
245 /**
246 * Get the file descriptor of the main channel associated with the remote device
247 * and application configuration.
248 *
249 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
250 *
251 * @param device The remote Bluetooth health device
252 * @param config The application configuration
253 * @return null on failure, ParcelFileDescriptor on success.
254 */
255
256 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
257 BluetoothHealthAppConfiguration config) {
258 if (mService != null && isEnabled() && isValidDevice(device) &&
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700259 config != null) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700260 try {
261 return mService.getMainChannelFd(device, config);
262 } catch (RemoteException e) {
263 Log.e(TAG, e.toString());
264 }
265 } else {
266 Log.w(TAG, "Proxy not attached to service");
267 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
268 }
269 return null;
270 }
271
272 /**
273 * Get the current connection state of the profile.
274 *
275 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
276 *
277 * This is not specific to any application configuration but represents the connection
278 * state of the local Bluetooth adapter with the remote device. This can be used
279 * by applications like status bar which would just like to know the state of the
280 * local adapter.
281 *
282 * @param device Remote bluetooth device.
283 * @return State of the profile connection. One of
284 * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
285 * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
286 */
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700287 @Override
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700288 public int getConnectionState(BluetoothDevice device) {
289 if (mService != null && isEnabled() && isValidDevice(device)) {
290 try {
291 return mService.getHealthDeviceConnectionState(device);
292 } catch (RemoteException e) {
293 Log.e(TAG, e.toString());
294 }
295 } else {
296 Log.w(TAG, "Proxy not attached to service");
297 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
298 }
299 return STATE_DISCONNECTED;
300 }
301
302 /**
303 * Get connected devices for this specific profile.
304 *
305 * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
306 *
307 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
308 *
309 * This is not specific to any application configuration but represents the connection
310 * state of the local Bluetooth adapter for this profile. This can be used
311 * by applications like status bar which would just like to know the state of the
312 * local adapter.
313 * @return List of devices. The list will be empty on error.
314 */
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700315 @Override
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700316 public List<BluetoothDevice> getConnectedDevices() {
317 if (mService != null && isEnabled()) {
318 try {
319 return mService.getConnectedHealthDevices();
320 } catch (RemoteException e) {
321 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
322 return new ArrayList<BluetoothDevice>();
323 }
324 }
325 if (mService == null) Log.w(TAG, "Proxy not attached to service");
326 return new ArrayList<BluetoothDevice>();
327 }
328
329 /**
330 * Get a list of devices that match any of the given connection
331 * states.
332 *
333 * <p> If none of the devices match any of the given states,
334 * an empty list will be returned.
335 *
336 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
337 * This is not specific to any application configuration but represents the connection
338 * state of the local Bluetooth adapter for this profile. This can be used
339 * by applications like status bar which would just like to know the state of the
340 * local adapter.
341 *
342 * @param states Array of states. States can be one of
343 * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
344 * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
345 * @return List of devices. The list will be empty on error.
346 */
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700347 @Override
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700348 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
349 if (mService != null && isEnabled()) {
350 try {
351 return mService.getHealthDevicesMatchingConnectionStates(states);
352 } catch (RemoteException e) {
353 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
354 return new ArrayList<BluetoothDevice>();
355 }
356 }
357 if (mService == null) Log.w(TAG, "Proxy not attached to service");
358 return new ArrayList<BluetoothDevice>();
359 }
360
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700361 private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
362 private BluetoothHealthCallback mCallback;
363
364 public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
365 mCallback = callback;
366 }
367
368 @Override
369 public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
370 int status) {
371 mCallback.onHealthAppConfigurationStatusChange(config, status);
372 }
373
374 @Override
375 public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
376 BluetoothDevice device, int prevState, int newState,
377 ParcelFileDescriptor fd) {
378 mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd);
379 }
380 }
381
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700382 /** Health Channel Connection State - Disconnected */
383 public static final int STATE_CHANNEL_DISCONNECTED = 0;
384 /** Health Channel Connection State - Connecting */
385 public static final int STATE_CHANNEL_CONNECTING = 1;
386 /** Health Channel Connection State - Connected */
387 public static final int STATE_CHANNEL_CONNECTED = 2;
388 /** Health Channel Connection State - Disconnecting */
389 public static final int STATE_CHANNEL_DISCONNECTING = 3;
390
391 /** Health App Configuration registration success */
392 public static final int APPLICATION_REGISTRATION_SUCCESS = 0;
393 /** Health App Configuration registration failure */
394 public static final int APPLICATION_REGISTRATION_FAILURE = 1;
395 /** Health App Configuration un-registration success */
396 public static final int APPLICATION_UNREGISTRATION_SUCCESS = 2;
397 /** Health App Configuration un-registration failure */
398 public static final int APPLICATION_UNREGISTRATION_FAILURE = 3;
399
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700400 private ServiceListener mServiceListener;
401 private IBluetooth mService;
402 BluetoothAdapter mAdapter;
403
404 /**
405 * Create a BluetoothHealth proxy object.
406 */
407 /*package*/ BluetoothHealth(Context mContext, ServiceListener l) {
408 IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
409 mServiceListener = l;
410 mAdapter = BluetoothAdapter.getDefaultAdapter();
411 if (b != null) {
412 mService = IBluetooth.Stub.asInterface(b);
413 if (mServiceListener != null) {
414 mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, this);
415 }
416 } else {
417 Log.w(TAG, "Bluetooth Service not available!");
418
419 // Instead of throwing an exception which prevents people from going
420 // into Wireless settings in the emulator. Let it crash later when it is actually used.
421 mService = null;
422 }
423 }
424
425 private boolean isEnabled() {
426 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
427
428 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
429 log("Bluetooth is Not enabled");
430 return false;
431 }
432
433 private boolean isValidDevice(BluetoothDevice device) {
434 if (device == null) return false;
435
436 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
437 return false;
438 }
439
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700440 private boolean checkAppParam(String name, int role, int channelType,
Jaikumar Ganeshd3b7d1d2011-07-06 17:37:02 -0700441 BluetoothHealthCallback callback) {
Jaikumar Ganeshbf981ca2011-04-01 16:33:09 -0700442 if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) ||
443 (channelType != CHANNEL_TYPE_RELIABLE &&
444 channelType != CHANNEL_TYPE_STREAMING &&
445 channelType != CHANNEL_TYPE_ANY) || callback == null) {
446 return false;
447 }
448 if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false;
449 return true;
450 }
451
452 private static void log(String msg) {
453 Log.d(TAG, msg);
454 }
455}