blob: 57a019755f8f54d19fa1faea3baa04df298d8d3c [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.
Jack Hea355e5e2017-08-22 16:06:54 -070039 * <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}
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070056 */
57public final class BluetoothHealth implements BluetoothProfile {
58 private static final String TAG = "BluetoothHealth";
fredc0f420372012-04-12 00:02:00 -070059 private static final boolean DBG = true;
Matthew Xie563e4142012-10-09 22:10:37 -070060 private static final boolean VDBG = false;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070061
62 /**
63 * Health Profile Source Role - the health device.
64 */
65 public static final int SOURCE_ROLE = 1 << 0;
66
67 /**
68 * Health Profile Sink Role the device talking to the health device.
69 */
70 public static final int SINK_ROLE = 1 << 1;
71
72 /**
73 * Health Profile - Channel Type used - Reliable
74 */
75 public static final int CHANNEL_TYPE_RELIABLE = 10;
76
77 /**
78 * Health Profile - Channel Type used - Streaming
79 */
80 public static final int CHANNEL_TYPE_STREAMING = 11;
81
82 /**
83 * @hide
84 */
85 public static final int CHANNEL_TYPE_ANY = 12;
86
Jaikumar Ganeshb5d2d452011-09-07 14:16:52 -070087 /** @hide */
88 public static final int HEALTH_OPERATION_SUCCESS = 6000;
89 /** @hide */
90 public static final int HEALTH_OPERATION_ERROR = 6001;
91 /** @hide */
92 public static final int HEALTH_OPERATION_INVALID_ARGS = 6002;
93 /** @hide */
94 public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003;
95 /** @hide */
96 public static final int HEALTH_OPERATION_NOT_FOUND = 6004;
97 /** @hide */
98 public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005;
99
Jack He2992cd02017-08-22 21:21:23 -0700100 private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
fredc0f420372012-04-12 00:02:00 -0700101 new IBluetoothStateChangeCallback.Stub() {
102 public void onBluetoothStateChange(boolean up) {
103 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
104 if (!up) {
Jack Hea355e5e2017-08-22 16:06:54 -0700105 if (VDBG) Log.d(TAG, "Unbinding service...");
fredc0f420372012-04-12 00:02:00 -0700106 synchronized (mConnection) {
107 try {
108 mService = null;
109 mContext.unbindService(mConnection);
110 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700111 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700112 }
113 }
114 } else {
115 synchronized (mConnection) {
116 try {
117 if (mService == null) {
Jack Hea355e5e2017-08-22 16:06:54 -0700118 if (VDBG) Log.d(TAG, "Binding service...");
Dianne Hackborn221ea892013-08-04 16:50:16 -0700119 doBind();
fredc0f420372012-04-12 00:02:00 -0700120 }
121 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700122 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700123 }
124 }
125 }
126 }
Jack Hea355e5e2017-08-22 16:06:54 -0700127 };
fredc0f420372012-04-12 00:02:00 -0700128
Jaikumar Ganeshb5d2d452011-09-07 14:16:52 -0700129
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700130 /**
131 * Register an application configuration that acts as a Health SINK.
132 * This is the configuration that will be used to communicate with health devices
133 * which will act as the {@link #SOURCE_ROLE}. This is an asynchronous call and so
134 * the callback is used to notify success or failure if the function returns true.
135 *
136 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
137 *
138 * @param name The friendly name associated with the application or configuration.
Jack Hea355e5e2017-08-22 16:06:54 -0700139 * @param dataType The dataType of the Source role of Health Profile to which the sink wants to
140 * connect to.
141 * @param callback A callback to indicate success or failure of the registration and all
142 * operations done on this application configuration.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700143 * @return If true, callback will be called.
144 */
145 public boolean registerSinkAppConfiguration(String name, int dataType,
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700146 BluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700147 if (!isEnabled() || name == null) return false;
148
Matthew Xie563e4142012-10-09 22:10:37 -0700149 if (VDBG) log("registerSinkApplication(" + name + ":" + dataType + ")");
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700150 return registerAppConfiguration(name, dataType, SINK_ROLE,
151 CHANNEL_TYPE_ANY, callback);
152 }
153
154 /**
155 * Register an application configuration that acts as a Health SINK or in a Health
156 * SOURCE role.This is an asynchronous call and so
157 * the callback is used to notify success or failure if the function returns true.
158 *
159 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
160 *
161 * @param name The friendly name associated with the application or configuration.
162 * @param dataType The dataType of the Source role of Health Profile.
Jack Hea355e5e2017-08-22 16:06:54 -0700163 * @param channelType The channel type. Will be one of {@link #CHANNEL_TYPE_RELIABLE} or {@link
164 * #CHANNEL_TYPE_STREAMING}
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700165 * @param callback - A callback to indicate success or failure.
166 * @return If true, callback will be called.
167 * @hide
168 */
169 public boolean registerAppConfiguration(String name, int dataType, int role,
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700170 int channelType, BluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700171 boolean result = false;
172 if (!isEnabled() || !checkAppParam(name, role, channelType, callback)) return result;
173
Matthew Xie563e4142012-10-09 22:10:37 -0700174 if (VDBG) log("registerApplication(" + name + ":" + dataType + ")");
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700175 BluetoothHealthCallbackWrapper wrapper = new BluetoothHealthCallbackWrapper(callback);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700176 BluetoothHealthAppConfiguration config =
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700177 new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700178
Jack He16eeac32017-08-17 12:11:18 -0700179 final IBluetoothHealth service = mService;
180 if (service != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700181 try {
Jack He16eeac32017-08-17 12:11:18 -0700182 result = service.registerAppConfiguration(config, wrapper);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700183 } catch (RemoteException e) {
184 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700185 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700186 } else {
187 Log.w(TAG, "Proxy not attached to service");
188 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
189 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700190 return result;
191 }
192
193 /**
194 * Unregister an application configuration that has been registered using
195 * {@link #registerSinkAppConfiguration}
196 *
197 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
198 *
Jack Hea355e5e2017-08-22 16:06:54 -0700199 * @param config The health app configuration
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700200 * @return Success or failure.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700201 */
202 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
203 boolean result = false;
Jack He16eeac32017-08-17 12:11:18 -0700204 final IBluetoothHealth service = mService;
205 if (service != null && isEnabled() && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700206 try {
Jack He16eeac32017-08-17 12:11:18 -0700207 result = service.unregisterAppConfiguration(config);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700208 } catch (RemoteException e) {
209 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700210 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700211 } else {
212 Log.w(TAG, "Proxy not attached to service");
213 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
214 }
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700215
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700216 return result;
217 }
218
219 /**
220 * Connect to a health device which has the {@link #SOURCE_ROLE}.
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700221 * This is an asynchronous call. If this function returns true, the callback
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700222 * associated with the application configuration will be called.
223 *
224 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
225 *
226 * @param device The remote Bluetooth device.
Jack Hea355e5e2017-08-22 16:06:54 -0700227 * @param config The application configuration which has been registered using {@link
228 * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700229 * @return If true, the callback associated with the application config will be called.
230 */
231 public boolean connectChannelToSource(BluetoothDevice device,
232 BluetoothHealthAppConfiguration config) {
Jack He16eeac32017-08-17 12:11:18 -0700233 final IBluetoothHealth service = mService;
234 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700235 try {
Jack He16eeac32017-08-17 12:11:18 -0700236 return service.connectChannelToSource(device, config);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700237 } catch (RemoteException e) {
238 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700239 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700240 } else {
241 Log.w(TAG, "Proxy not attached to service");
242 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
243 }
244 return false;
245 }
246
247 /**
248 * Connect to a health device which has the {@link #SINK_ROLE}.
249 * This is an asynchronous call. If this function returns true, the callback
250 * associated with the application configuration will be called.
251 *
Jack Hea355e5e2017-08-22 16:06:54 -0700252 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700253 *
254 * @param device The remote Bluetooth device.
Jack Hea355e5e2017-08-22 16:06:54 -0700255 * @param config The application configuration which has been registered using {@link
256 * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700257 * @return If true, the callback associated with the application config will be called.
258 * @hide
259 */
260 public boolean connectChannelToSink(BluetoothDevice device,
261 BluetoothHealthAppConfiguration config, int channelType) {
Jack He16eeac32017-08-17 12:11:18 -0700262 final IBluetoothHealth service = mService;
263 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700264 try {
Jack He16eeac32017-08-17 12:11:18 -0700265 return service.connectChannelToSink(device, config, channelType);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700266 } catch (RemoteException e) {
267 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700268 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700269 } else {
270 Log.w(TAG, "Proxy not attached to service");
271 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
272 }
273 return false;
274 }
275
276 /**
277 * Disconnect a connected health channel.
278 * This is an asynchronous call. If this function returns true, the callback
279 * associated with the application configuration will be called.
280 *
Jack Hea355e5e2017-08-22 16:06:54 -0700281 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700282 *
283 * @param device The remote Bluetooth device.
Jack Hea355e5e2017-08-22 16:06:54 -0700284 * @param config The application configuration which has been registered using {@link
285 * #registerSinkAppConfiguration(String, int, BluetoothHealthCallback) }
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700286 * @param channelId The channel id associated with the channel
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700287 * @return If true, the callback associated with the application config will be called.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700288 */
289 public boolean disconnectChannel(BluetoothDevice device,
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700290 BluetoothHealthAppConfiguration config, int channelId) {
Jack He16eeac32017-08-17 12:11:18 -0700291 final IBluetoothHealth service = mService;
292 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700293 try {
Jack He16eeac32017-08-17 12:11:18 -0700294 return service.disconnectChannel(device, config, channelId);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700295 } catch (RemoteException e) {
296 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700297 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700298 } else {
299 Log.w(TAG, "Proxy not attached to service");
300 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
301 }
302 return false;
303 }
304
305 /**
306 * Get the file descriptor of the main channel associated with the remote device
307 * and application configuration.
308 *
309 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
310 *
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700311 * <p> Its the responsibility of the caller to close the ParcelFileDescriptor
312 * when done.
313 *
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700314 * @param device The remote Bluetooth health device
315 * @param config The application configuration
316 * @return null on failure, ParcelFileDescriptor on success.
317 */
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700318 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
319 BluetoothHealthAppConfiguration config) {
Jack He16eeac32017-08-17 12:11:18 -0700320 final IBluetoothHealth service = mService;
321 if (service != null && isEnabled() && isValidDevice(device) && config != null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700322 try {
Jack He16eeac32017-08-17 12:11:18 -0700323 return service.getMainChannelFd(device, config);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700324 } catch (RemoteException e) {
325 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700326 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700327 } else {
328 Log.w(TAG, "Proxy not attached to service");
329 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
330 }
331 return null;
332 }
333
334 /**
335 * Get the current connection state of the profile.
336 *
337 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
338 *
339 * This is not specific to any application configuration but represents the connection
340 * state of the local Bluetooth adapter with the remote device. This can be used
341 * by applications like status bar which would just like to know the state of the
342 * local adapter.
343 *
344 * @param device Remote bluetooth device.
Jack Hea355e5e2017-08-22 16:06:54 -0700345 * @return State of the profile connection. One of {@link #STATE_CONNECTED}, {@link
346 * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700347 */
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700348 @Override
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700349 public int getConnectionState(BluetoothDevice device) {
Jack He16eeac32017-08-17 12:11:18 -0700350 final IBluetoothHealth service = mService;
351 if (service != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700352 try {
Jack He16eeac32017-08-17 12:11:18 -0700353 return service.getHealthDeviceConnectionState(device);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700354 } catch (RemoteException e) {
355 Log.e(TAG, e.toString());
Matthew Xie13450df2012-03-22 17:18:37 -0700356 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700357 } else {
358 Log.w(TAG, "Proxy not attached to service");
359 if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
360 }
361 return STATE_DISCONNECTED;
362 }
363
364 /**
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700365 * Get connected devices for the health profile.
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700366 *
367 * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
368 *
369 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
370 *
371 * This is not specific to any application configuration but represents the connection
372 * state of the local Bluetooth adapter for this profile. This can be used
373 * by applications like status bar which would just like to know the state of the
374 * local adapter.
Jack Hea355e5e2017-08-22 16:06:54 -0700375 *
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700376 * @return List of devices. The list will be empty on error.
377 */
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700378 @Override
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700379 public List<BluetoothDevice> getConnectedDevices() {
Jack He16eeac32017-08-17 12:11:18 -0700380 final IBluetoothHealth service = mService;
381 if (service != null && isEnabled()) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700382 try {
Jack He16eeac32017-08-17 12:11:18 -0700383 return service.getConnectedHealthDevices();
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700384 } catch (RemoteException e) {
385 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
386 return new ArrayList<BluetoothDevice>();
Matthew Xie13450df2012-03-22 17:18:37 -0700387 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700388 }
Jack He16eeac32017-08-17 12:11:18 -0700389 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700390 return new ArrayList<BluetoothDevice>();
391 }
392
393 /**
394 * Get a list of devices that match any of the given connection
395 * states.
396 *
397 * <p> If none of the devices match any of the given states,
398 * an empty list will be returned.
399 *
400 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
401 * This is not specific to any application configuration but represents the connection
402 * state of the local Bluetooth adapter for this profile. This can be used
403 * by applications like status bar which would just like to know the state of the
404 * local adapter.
405 *
Jack Hea355e5e2017-08-22 16:06:54 -0700406 * @param states Array of states. States can be one of {@link #STATE_CONNECTED}, {@link
407 * #STATE_CONNECTING}, {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700408 * @return List of devices. The list will be empty on error.
409 */
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700410 @Override
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700411 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Jack He16eeac32017-08-17 12:11:18 -0700412 final IBluetoothHealth service = mService;
413 if (service != null && isEnabled()) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700414 try {
Jack He16eeac32017-08-17 12:11:18 -0700415 return service.getHealthDevicesMatchingConnectionStates(states);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700416 } catch (RemoteException e) {
417 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
418 return new ArrayList<BluetoothDevice>();
Matthew Xie13450df2012-03-22 17:18:37 -0700419 }
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700420 }
Jack He16eeac32017-08-17 12:11:18 -0700421 if (service == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700422 return new ArrayList<BluetoothDevice>();
423 }
424
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700425 private static class BluetoothHealthCallbackWrapper extends IBluetoothHealthCallback.Stub {
426 private BluetoothHealthCallback mCallback;
427
428 public BluetoothHealthCallbackWrapper(BluetoothHealthCallback callback) {
429 mCallback = callback;
430 }
431
432 @Override
433 public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
Jack Hea355e5e2017-08-22 16:06:54 -0700434 int status) {
435 mCallback.onHealthAppConfigurationStatusChange(config, status);
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700436 }
437
438 @Override
439 public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
Jack Hea355e5e2017-08-22 16:06:54 -0700440 BluetoothDevice device, int prevState, int newState,
441 ParcelFileDescriptor fd, int channelId) {
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700442 mCallback.onHealthChannelStateChange(config, device, prevState, newState, fd,
Jack Hea355e5e2017-08-22 16:06:54 -0700443 channelId);
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700444 }
445 }
446
Jack Hea355e5e2017-08-22 16:06:54 -0700447 /** Health Channel Connection State - Disconnected */
448 public static final int STATE_CHANNEL_DISCONNECTED = 0;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700449 /** Health Channel Connection State - Connecting */
Jack Hea355e5e2017-08-22 16:06:54 -0700450 public static final int STATE_CHANNEL_CONNECTING = 1;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700451 /** Health Channel Connection State - Connected */
Jack Hea355e5e2017-08-22 16:06:54 -0700452 public static final int STATE_CHANNEL_CONNECTED = 2;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700453 /** Health Channel Connection State - Disconnecting */
454 public static final int STATE_CHANNEL_DISCONNECTING = 3;
455
456 /** Health App Configuration registration success */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700457 public static final int APP_CONFIG_REGISTRATION_SUCCESS = 0;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700458 /** Health App Configuration registration failure */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700459 public static final int APP_CONFIG_REGISTRATION_FAILURE = 1;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700460 /** Health App Configuration un-registration success */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700461 public static final int APP_CONFIG_UNREGISTRATION_SUCCESS = 2;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700462 /** Health App Configuration un-registration failure */
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -0700463 public static final int APP_CONFIG_UNREGISTRATION_FAILURE = 3;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700464
Matthew Xie13450df2012-03-22 17:18:37 -0700465 private Context mContext;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700466 private ServiceListener mServiceListener;
Jack He16eeac32017-08-17 12:11:18 -0700467 private volatile IBluetoothHealth mService;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700468 BluetoothAdapter mAdapter;
469
470 /**
471 * Create a BluetoothHealth proxy object.
472 */
Matthew Xie13450df2012-03-22 17:18:37 -0700473 /*package*/ BluetoothHealth(Context context, ServiceListener l) {
474 mContext = context;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700475 mServiceListener = l;
476 mAdapter = BluetoothAdapter.getDefaultAdapter();
fredc0f420372012-04-12 00:02:00 -0700477 IBluetoothManager mgr = mAdapter.getBluetoothManager();
478 if (mgr != null) {
479 try {
480 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
481 } catch (RemoteException e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700482 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700483 }
484 }
485
Dianne Hackborn221ea892013-08-04 16:50:16 -0700486 doBind();
487 }
488
489 boolean doBind() {
490 Intent intent = new Intent(IBluetoothHealth.class.getName());
491 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
492 intent.setComponent(comp);
Dianne Hackborn466ce962014-03-19 18:06:58 -0700493 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
494 android.os.Process.myUserHandle())) {
Dianne Hackborn221ea892013-08-04 16:50:16 -0700495 Log.e(TAG, "Could not bind to Bluetooth Health Service with " + intent);
496 return false;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700497 }
Dianne Hackborn221ea892013-08-04 16:50:16 -0700498 return true;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700499 }
500
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800501 /*package*/ void close() {
Matthew Xie563e4142012-10-09 22:10:37 -0700502 if (VDBG) log("close()");
fredc0f420372012-04-12 00:02:00 -0700503 IBluetoothManager mgr = mAdapter.getBluetoothManager();
504 if (mgr != null) {
505 try {
506 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
507 } catch (Exception e) {
Jack Hea355e5e2017-08-22 16:06:54 -0700508 Log.e(TAG, "", e);
fredc0f420372012-04-12 00:02:00 -0700509 }
510 }
511
512 synchronized (mConnection) {
513 if (mService != null) {
514 try {
515 mService = null;
516 mContext.unbindService(mConnection);
517 } catch (Exception re) {
Jack Hea355e5e2017-08-22 16:06:54 -0700518 Log.e(TAG, "", re);
fredc0f420372012-04-12 00:02:00 -0700519 }
520 }
Matthew Xie13450df2012-03-22 17:18:37 -0700521 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800522 mServiceListener = null;
523 }
524
Matthew Xie9b693992013-10-10 11:21:40 -0700525 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xie13450df2012-03-22 17:18:37 -0700526 public void onServiceConnected(ComponentName className, IBinder service) {
527 if (DBG) Log.d(TAG, "Proxy object connected");
Jeff Sharkey0a17db12016-11-04 11:23:46 -0600528 mService = IBluetoothHealth.Stub.asInterface(Binder.allowBlocking(service));
Matthew Xie13450df2012-03-22 17:18:37 -0700529
530 if (mServiceListener != null) {
531 mServiceListener.onServiceConnected(BluetoothProfile.HEALTH, BluetoothHealth.this);
532 }
533 }
Jack Hea355e5e2017-08-22 16:06:54 -0700534
Matthew Xie13450df2012-03-22 17:18:37 -0700535 public void onServiceDisconnected(ComponentName className) {
536 if (DBG) Log.d(TAG, "Proxy object disconnected");
537 mService = null;
538 if (mServiceListener != null) {
539 mServiceListener.onServiceDisconnected(BluetoothProfile.HEALTH);
540 }
541 }
542 };
543
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700544 private boolean isEnabled() {
545 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
546
547 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
548 log("Bluetooth is Not enabled");
549 return false;
550 }
551
Jack He16eeac32017-08-17 12:11:18 -0700552 private static boolean isValidDevice(BluetoothDevice device) {
553 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700554 }
555
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700556 private boolean checkAppParam(String name, int role, int channelType,
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -0700557 BluetoothHealthCallback callback) {
Jack He2992cd02017-08-22 21:21:23 -0700558 if (name == null || (role != SOURCE_ROLE && role != SINK_ROLE)
559 || (channelType != CHANNEL_TYPE_RELIABLE && channelType != CHANNEL_TYPE_STREAMING
560 && channelType != CHANNEL_TYPE_ANY)
561 || callback == null) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700562 return false;
563 }
564 if (role == SOURCE_ROLE && channelType == CHANNEL_TYPE_ANY) return false;
565 return true;
566 }
567
568 private static void log(String msg) {
569 Log.d(TAG, msg);
570 }
571}