blob: 07be63f01efccf6b5b0be42a7fe640d831dea350 [file] [log] [blame]
Jaikumar Ganesh2ea1e852011-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
Matthew Xie13450df2012-03-22 17:18:37 -070019import android.content.ComponentName;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070020import android.content.Context;
Matthew Xie13450df2012-03-22 17:18:37 -070021import android.content.Intent;
22import android.content.ServiceConnection;
Jeff Sharkey0a17db12016-11-04 11:23:46 -060023import android.os.Binder;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070024import android.os.IBinder;
25import android.os.ParcelFileDescriptor;
26import android.os.RemoteException;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070027import android.util.Log;
28
29import java.util.ArrayList;
30import java.util.List;
31
32/**
33 * Public API for Bluetooth Health Profile.
34 *
35 * <p>BluetoothHealth is a proxy object for controlling the Bluetooth
36 * Service via IPC.
37 *
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -070038 * <p> How to connect to a health device which is acting in the source role.
39 * <li> Use {@link BluetoothAdapter#getProfileProxy} to get
40 * the BluetoothHealth proxy object. </li>
41 * <li> Create an {@link BluetoothHealth} callback and call
42 * {@link #registerSinkAppConfiguration} to register an application
43 * configuration </li>
44 * <li> Pair with the remote device. This currently needs to be done manually
45 * from Bluetooth Settings </li>
46 * <li> Connect to a health device using {@link #connectChannelToSource}. Some
47 * devices will connect the channel automatically. The {@link BluetoothHealth}
48 * callback will inform the application of channel state change. </li>
49 * <li> Use the file descriptor provided with a connected channel to read and
50 * write data to the health channel. </li>
51 * <li> The received data needs to be interpreted using a health manager which
52 * implements the IEEE 11073-xxxxx specifications.
53 * <li> When done, close the health channel by calling {@link #disconnectChannel}
54 * and unregister the application configuration calling
55 * {@link #unregisterAppConfiguration}
56 *
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070057 */
58public final class BluetoothHealth implements BluetoothProfile {
59 private static final String TAG = "BluetoothHealth";
fredc0f420372012-04-12 00:02:00 -070060 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070061 private static final boolean VDBG = false;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070062
63 /**
64 * Health Profile Source Role - the health device.
65 */
66 public static final int SOURCE_ROLE = 1 << 0;
67
68 /**
69 * Health Profile Sink Role the device talking to the health device.
70 */
71 public static final int SINK_ROLE = 1 << 1;
72
73 /**
74 * Health Profile - Channel Type used - Reliable
75 */
76 public static final int CHANNEL_TYPE_RELIABLE = 10;
77
78 /**
79 * Health Profile - Channel Type used - Streaming
80 */
81 public static final int CHANNEL_TYPE_STREAMING = 11;
82
83 /**
84 * @hide
85 */
86 public static final int CHANNEL_TYPE_ANY = 12;
87
Jaikumar Ganeshb5d2d452011-09-07 14:16:52 -070088 /** @hide */
89 public static final int HEALTH_OPERATION_SUCCESS = 6000;
90 /** @hide */
91 public static final int HEALTH_OPERATION_ERROR = 6001;
92 /** @hide */
93 public static final int HEALTH_OPERATION_INVALID_ARGS = 6002;
94 /** @hide */
95 public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003;
96 /** @hide */
97 public static final int HEALTH_OPERATION_NOT_FOUND = 6004;
98 /** @hide */
99 public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005;
100
fredc0f420372012-04-12 00:02:00 -0700101 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
102 new IBluetoothStateChangeCallback.Stub() {
103 public void onBluetoothStateChange(boolean up) {
104 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
105 if (!up) {
Matthew Xie563e4142012-10-09 22:10:37 -0700106 if (VDBG) Log.d(TAG,"Unbinding service...");
fredc0f420372012-04-12 00:02:00 -0700107 synchronized (mConnection) {
108 try {
109 mService = null;
110 mContext.unbindService(mConnection);
111 } catch (Exception re) {
112 Log.e(TAG,"",re);
113 }
114 }
115 } else {
116 synchronized (mConnection) {
117 try {
118 if (mService == null) {
Matthew Xie563e4142012-10-09 22:10:37 -0700119 if (VDBG) Log.d(TAG,"Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700120 doBind();
fredc0f420372012-04-12 00:02:00 -0700121 }
122 } catch (Exception re) {
123 Log.e(TAG,"",re);
124 }
125 }
126 }
127 }
128 };
129
Jaikumar Ganeshb5d2d452011-09-07 14:16:52 -0700130
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700131 /**
132 * Register an application configuration that acts as a Health SINK.
133 * This is the configuration that will be used to communicate with health devices
134 * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
135 * the callback is used to notify success or failure if the function returns true.
136 *
137 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
138 *
139 * @param name The friendly name associated with the application or configuration.
140 * @param dataType The dataType of the Source role of Health Profile to which
141 * the sink wants to connect to.
142 * @param callback A callback to indicate success or failure of the registration and
143 * all operations done on this application configuration.
144 * @return If true, callback will be called.
145 */
146 public boolean registerSinkAppConfiguration(String name, int dataType,
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700147 BluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700148 if (!isEnabled() || name == null) return false;
149
Matthew Xie563e4142012-10-09 22:10:37 -0700150 if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700151 return registerAppConfiguration(name, dataType, SINK_ROLE,
152 CHANNEL_TYPE_ANY, callback);
153 }
154
155 /**
156 * Register an application configuration that acts as a Health SINK or in a Health
157 * SOURCE role.This is an asynchronous call and so
158 * the callback is used to notify success or failure if the function returns true.
159 *
160 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
161 *
162 * @param name The friendly name associated with the application or configuration.
163 * @param dataType The dataType of the Source role of Health Profile.
164 * @param channelType The channel type. Will be one of
165 * {@link #CHANNEL_TYPE_RELIABLE} or
166 * {@link #CHANNEL_TYPE_STREAMING}
167 * @param callback - A callback to indicate success or failure.
168 * @return If true, callback will be called.
169 * @hide
170 */
171 public boolean registerAppConfiguration(String name, int dataType, int role,
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700172 int channelType, BluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700173 boolean result = false;
174 if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
175
Matthew Xie563e4142012-10-09 22:10:37 -0700176 if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700177 BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700178 BluetoothHealthAppConfiguration config =
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700179 new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700180
Jack He01464632017-08-17 12:11:18 -0700181 final IBluetoothHealth service = mService;
182 if (service != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700183 try {
Jack He01464632017-08-17 12:11:18 -0700184 result = service.registerAppConfiguration(config, wrapper);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700185 } catch (RemoteException e) {
186 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700187 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700188 } else {
189 Log.w(TAG, "Proxy not attached to service");
190 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
191 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700192 return result;
193 }
194
195 /**
196 * Unregister an application configuration that has been registered using
197 * {@link #registerSinkAppConfiguration}
198 *
199 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
200 *
201 * @param config The health app configuration
202 * @return Success or failure.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700203 */
204 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
205 boolean result = false;
Jack He01464632017-08-17 12:11:18 -0700206 final IBluetoothHealth service = mService;
207 if (service != null && isEnabled() && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700208 try {
Jack He01464632017-08-17 12:11:18 -0700209 result = service.unregisterAppConfiguration(config);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700210 } catch (RemoteException e) {
211 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700212 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700213 } else {
214 Log.w(TAG, "Proxy not attached to service");
215 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
216 }
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700217
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700218 return result;
219 }
220
221 /**
222 * Connect to a health device which has the {@link #SOURCE_ROLE}.
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700223 * This is an asynchronous call. If this function returns true, the callback
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700224 * associated with the application configuration will be called.
225 *
226 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
227 *
228 * @param device The remote Bluetooth device.
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700229 * @param config The application configuration which has been registered using
230 * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700231 * @return If true, the callback associated with the application config will be called.
232 */
233 public boolean connectChannelToSource(BluetoothDevice device,
234 BluetoothHealthAppConfiguration config) {
Jack He01464632017-08-17 12:11:18 -0700235 final IBluetoothHealth service = mService;
236 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700237 try {
Jack He01464632017-08-17 12:11:18 -0700238 return service.connectChannelToSource(device, config);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700239 } catch (RemoteException e) {
240 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700241 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700242 } else {
243 Log.w(TAG, "Proxy not attached to service");
244 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
245 }
246 return false;
247 }
248
249 /**
250 * Connect to a health device which has the {@link #SINK_ROLE}.
251 * This is an asynchronous call. If this function returns true, the callback
252 * associated with the application configuration will be called.
253 *
254 *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
255 *
256 * @param device The remote Bluetooth device.
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700257 * @param config The application configuration which has been registered using
258 * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700259 * @return If true, the callback associated with the application config will be called.
260 * @hide
261 */
262 public boolean connectChannelToSink(BluetoothDevice device,
263 BluetoothHealthAppConfiguration config, int channelType) {
Jack He01464632017-08-17 12:11:18 -0700264 final IBluetoothHealth service = mService;
265 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700266 try {
Jack He01464632017-08-17 12:11:18 -0700267 return service.connectChannelToSink(device, config, channelType);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700268 } catch (RemoteException e) {
269 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700270 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700271 } else {
272 Log.w(TAG, "Proxy not attached to service");
273 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
274 }
275 return false;
276 }
277
278 /**
279 * Disconnect a connected health channel.
280 * This is an asynchronous call. If this function returns true, the callback
281 * associated with the application configuration will be called.
282 *
283 *<p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
284 *
285 * @param device The remote Bluetooth device.
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700286 * @param config The application configuration which has been registered using
287 * {@link #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700288 * @param channelId The channel id associated with the channel
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700289 * @return If true, the callback associated with the application config will be called.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700290 */
291 public boolean disconnectChannel(BluetoothDevice device,
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700292 BluetoothHealthAppConfiguration config, int channelId) {
Jack He01464632017-08-17 12:11:18 -0700293 final IBluetoothHealth service = mService;
294 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700295 try {
Jack He01464632017-08-17 12:11:18 -0700296 return service.disconnectChannel(device, config, channelId);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700297 } catch (RemoteException e) {
298 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700299 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700300 } else {
301 Log.w(TAG, "Proxy not attached to service");
302 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
303 }
304 return false;
305 }
306
307 /**
308 * Get the file descriptor of the main channel associated with the remote device
309 * and application configuration.
310 *
311 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
312 *
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700313 * <p> Its the responsibility of the caller to close the ParcelFileDescriptor
314 * when done.
315 *
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700316 * @param device The remote Bluetooth health device
317 * @param config The application configuration
318 * @return null on failure, ParcelFileDescriptor on success.
319 */
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700320 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
321 BluetoothHealthAppConfiguration config) {
Jack He01464632017-08-17 12:11:18 -0700322 final IBluetoothHealth service = mService;
323 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700324 try {
Jack He01464632017-08-17 12:11:18 -0700325 return service.getMainChannelFd(device, config);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700326 } catch (RemoteException e) {
327 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700328 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700329 } else {
330 Log.w(TAG, "Proxy not attached to service");
331 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
332 }
333 return null;
334 }
335
336 /**
337 * Get the current connection state of the profile.
338 *
339 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
340 *
341 * This is not specific to any application configuration but represents the connection
342 * state of the local Bluetooth adapter with the remote device. This can be used
343 * by applications like status bar which would just like to know the state of the
344 * local adapter.
345 *
346 * @param device Remote bluetooth device.
347 * @return State of the profile connection. One of
348 * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
349 * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
350 */
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700351 @Override
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700352 public int getConnectionState(BluetoothDevice device) {
Jack He01464632017-08-17 12:11:18 -0700353 final IBluetoothHealth service = mService;
354 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700355 try {
Jack He01464632017-08-17 12:11:18 -0700356 return service.getHealthDeviceConnectionState(device);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700357 } catch (RemoteException e) {
358 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700359 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700360 } else {
361 Log.w(TAG, "Proxy not attached to service");
362 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
363 }
364 return STATE_DISCONNECTED;
365 }
366
367 /**
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700368 * Get connected devices for the health profile.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700369 *
370 * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
371 *
372 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
373 *
374 * This is not specific to any application configuration but represents the connection
375 * state of the local Bluetooth adapter for this profile. This can be used
376 * by applications like status bar which would just like to know the state of the
377 * local adapter.
378 * @return List of devices. The list will be empty on error.
379 */
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700380 @Override
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700381 public List<BluetoothDevice> getConnectedDevices() {
Jack He01464632017-08-17 12:11:18 -0700382 final IBluetoothHealth service = mService;
383 if (service != null && isEnabled()) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700384 try {
Jack He01464632017-08-17 12:11:18 -0700385 return service.getConnectedHealthDevices();
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700386 } catch (RemoteException e) {
387 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
388 return new ArrayList<BluetoothDevice>();
Matthew Xie13450df2012-03-22 17:18:37 -0700389 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700390 }
Jack He01464632017-08-17 12:11:18 -0700391 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700392 return new ArrayList<BluetoothDevice>();
393 }
394
395 /**
396 * Get a list of devices that match any of the given connection
397 * states.
398 *
399 * <p> If none of the devices match any of the given states,
400 * an empty list will be returned.
401 *
402 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
403 * This is not specific to any application configuration but represents the connection
404 * state of the local Bluetooth adapter for this profile. This can be used
405 * by applications like status bar which would just like to know the state of the
406 * local adapter.
407 *
408 * @param states Array of states. States can be one of
409 * {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
410 * {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
411 * @return List of devices. The list will be empty on error.
412 */
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700413 @Override
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700414 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He01464632017-08-17 12:11:18 -0700415 final IBluetoothHealth service = mService;
416 if (service != null && isEnabled()) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700417 try {
Jack He01464632017-08-17 12:11:18 -0700418 return service.getHealthDevicesMatchingConnectionStates(states);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700419 } catch (RemoteException e) {
420 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
421 return new ArrayList<BluetoothDevice>();
Matthew Xie13450df2012-03-22 17:18:37 -0700422 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700423 }
Jack He01464632017-08-17 12:11:18 -0700424 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700425 return new ArrayList<BluetoothDevice>();
426 }
427
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700428 private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
429 private BluetoothHealthCallback mCallback;
430
431 public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
432 mCallback = callback;
433 }
434
435 @Override
436 public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
437 int status) {
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700438 mCallback.onHealthAppConfigurationStatusChange(config, status);
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700439 }
440
441 @Override
442 public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
443 BluetoothDevice device, int prevState, int newState,
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700444 ParcelFileDescriptor fd, int channelId) {
445 mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd,
446 channelId);
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700447 }
448 }
449
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700450 /** Health Channel Connection State - Disconnected */
451 public static final int STATE_CHANNEL_DISCONNECTED = 0;
452 /** Health Channel Connection State - Connecting */
453 public static final int STATE_CHANNEL_CONNECTING = 1;
454 /** Health Channel Connection State - Connected */
455 public static final int STATE_CHANNEL_CONNECTED = 2;
456 /** Health Channel Connection State - Disconnecting */
457 public static final int STATE_CHANNEL_DISCONNECTING = 3;
458
459 /** Health App Configuration registration success */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700460 public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700461 /** Health App Configuration registration failure */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700462 public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700463 /** Health App Configuration un-registration success */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700464 public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700465 /** Health App Configuration un-registration failure */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700466 public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700467
Matthew Xie13450df2012-03-22 17:18:37 -0700468 private Context mContext;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700469 private ServiceListener mServiceListener;
Jack He01464632017-08-17 12:11:18 -0700470 private volatile IBluetoothHealth mService;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700471 BluetoothAdapter mAdapter;
472
473 /**
474 * Create a BluetoothHealth proxy object.
475 */
Matthew Xie13450df2012-03-22 17:18:37 -0700476 /*package*/ BluetoothHealth(Context context, ServiceListener l) {
477 mContext = context;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700478 mServiceListener = l;
479 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700480 IBluetoothManager mgr = mAdapter.getBluetoothManager();
481 if (mgr != null) {
482 try {
483 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
484 } catch (RemoteException e) {
485 Log.e(TAG,"",e);
486 }
487 }
488
Dianne Hackborn221ea892013-08-04 16:50:16 -0700489 doBind();
490 }
491
492 boolean doBind() {
493 Intent intent = new Intent(IBluetoothHealth.class.getName());
494 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
495 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700496 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
497 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700498 Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
499 return false;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700500 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700501 return true;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700502 }
503
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800504 /*package*/ void close() {
Matthew Xie563e4142012-10-09 22:10:37 -0700505 if (VDBG) log("close()");
fredc0f420372012-04-12 00:02:00 -0700506 IBluetoothManager mgr = mAdapter.getBluetoothManager();
507 if (mgr != null) {
508 try {
509 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
510 } catch (Exception e) {
511 Log.e(TAG,"",e);
512 }
513 }
514
515 synchronized (mConnection) {
516 if (mService != null) {
517 try {
518 mService = null;
519 mContext.unbindService(mConnection);
520 } catch (Exception re) {
521 Log.e(TAG,"",re);
522 }
523 }
Matthew Xie13450df2012-03-22 17:18:37 -0700524 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800525 mServiceListener = null;
526 }
527
Matthew Xie9b693992013-10-10 11:21:40 -0700528 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie13450df2012-03-22 17:18:37 -0700529 public void onServiceConnected(ComponentName className, IBinder service) {
530 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600531 mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service));
Matthew Xie13450df2012-03-22 17:18:37 -0700532
533 if (mServiceListener != null) {
534 mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this);
535 }
536 }
537 public void onServiceDisconnected(ComponentName className) {
538 if (DBG) Log.d(TAG, "Proxy object disconnected");
539 mService = null;
540 if (mServiceListener != null) {
541 mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH);
542 }
543 }
544 };
545
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700546 private boolean isEnabled() {
547 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
548
549 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
550 log("Bluetooth is Not enabled");
551 return false;
552 }
553
Jack He01464632017-08-17 12:11:18 -0700554 private static boolean isValidDevice(BluetoothDevice device) {
555 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700556 }
557
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700558 private boolean checkAppParam(String name, int role, int channelType,
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700559 BluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700560 if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE) ||
561 (channelType != CHANNEL_TYPE_RELIABLE &&
562 channelType != CHANNEL_TYPE_STREAMING &&
563 channelType != CHANNEL_TYPE_ANY) || callback == null) {
564 return false;
565 }
566 if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false;
567 return true;
568 }
569
570 private static void log(String msg) {
571 Log.d(TAG, msg);
572 }
573}