HealthService.java revision 676cb1bdd1c14c7af56562bab51f168e7f8f6d62
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.bluetooth.hdp;
6
7import android.app.Service;
8import android.bluetooth.BluetoothAdapter;
9import android.bluetooth.BluetoothDevice;
10import android.bluetooth.BluetoothHealth;
11import android.bluetooth.BluetoothHealthAppConfiguration;
12import android.bluetooth.BluetoothProfile;
13import android.bluetooth.IBluetooth;
14import android.bluetooth.IBluetoothHealth;
15import android.bluetooth.IBluetoothHealthCallback;
16import android.content.Intent;
17import android.os.IBinder;
18import android.os.Handler;
19import android.os.HandlerThread;
20import android.os.Looper;
21import android.os.Message;
22import android.os.ParcelFileDescriptor;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.util.Log;
26import java.io.FileDescriptor;
27import java.io.IOException;
28import java.util.ArrayList;
29import java.util.Collections;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import java.util.Map.Entry;
34import com.android.bluetooth.Utils;
35
36/**
37 * Provides Bluetooth Health Device profile, as a service in
38 * the Bluetooth application.
39 * @hide
40 */
41public class HealthService extends Service {
42    private static final String TAG = "BluetoothHealthService";
43    private static final boolean DBG = true;
44
45    static final String BLUETOOTH_ADMIN_PERM =
46        android.Manifest.permission.BLUETOOTH_ADMIN;
47    static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
48
49    private BluetoothAdapter mAdapter;
50    private List<HealthChannel> mHealthChannels;
51    private Map <BluetoothHealthAppConfiguration, AppInfo> mApps;
52    private Map <BluetoothDevice, Integer> mHealthDevices;
53    private HealthServiceMessageHandler mHandler;
54    private IBluetooth mAdapterService;
55
56    private static final int MESSAGE_REGISTER_APPLICATION = 1;
57    private static final int MESSAGE_UNREGISTER_APPLICATION = 2;
58    private static final int MESSAGE_CONNECT_CHANNEL = 3;
59    private static final int MESSAGE_DISCONNECT_CHANNEL = 4;
60    private static final int MESSAGE_APP_REGISTRATION_CALLBACK = 11;
61    private static final int MESSAGE_CHANNEL_STATE_CALLBACK = 12;
62
63    static {
64        classInitNative();
65    }
66
67    @Override
68    public void onCreate() {
69        mAdapter = BluetoothAdapter.getDefaultAdapter();
70        mHealthChannels = Collections.synchronizedList(new ArrayList<HealthChannel>());
71        mApps = Collections.synchronizedMap(new HashMap<BluetoothHealthAppConfiguration,
72                                            AppInfo>());
73        mHealthDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
74
75        HandlerThread thread = new HandlerThread("BluetoothHdpHandler");
76        thread.start();
77        Looper looper = thread.getLooper();
78        mHandler = new HealthServiceMessageHandler(looper);
79        mAdapterService = IBluetooth.Stub.asInterface(ServiceManager.getService("bluetooth"));
80
81        initializeNativeDataNative();
82    }
83
84    @Override
85    public IBinder onBind(Intent intent) {
86        log("onBind");
87        return mBinder;
88    }
89
90    @Override
91    public void onStart(Intent intent, int startId) {
92        log("onStart");
93        if (mAdapter == null) {
94            Log.w(TAG, "Stopping Bluetooth HealthService: device does not have BT");
95            stopSelf();
96        }
97    }
98
99    @Override
100    public void onDestroy() {
101        super.onDestroy();
102        if (DBG) log("Stopping Bluetooth HealthService");
103        // TBD
104    }
105
106    private final class HealthServiceMessageHandler extends Handler {
107        private HealthServiceMessageHandler(Looper looper) {
108            super(looper);
109        }
110
111        @Override
112        public void handleMessage(Message msg) {
113            switch (msg.what) {
114                case MESSAGE_REGISTER_APPLICATION:
115                {
116                    BluetoothHealthAppConfiguration appConfig =
117                        (BluetoothHealthAppConfiguration) msg.obj;
118                    int halRole = convertRoleToHal(appConfig.getRole());
119                    int halChannelType = convertChannelTypeToHal(appConfig.getChannelType());
120                    if (DBG) log("register datatype: " + appConfig.getDataType() + " role: " +
121                                 halRole + " name: " + appConfig.getName() + " channeltype: " +
122                                 halChannelType);
123                    int appId = registerHealthAppNative(appConfig.getDataType(), halRole,
124                                                        appConfig.getName(), halChannelType);
125
126                    if (appId == -1) {
127                        callStatusCallback(appConfig,
128                                           BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE);
129                        mApps.remove(appConfig);
130                    } else {
131                        (mApps.get(appConfig)).mAppId = appId;
132                        callStatusCallback(appConfig,
133                                           BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS);
134                    }
135                }
136                    break;
137                case MESSAGE_UNREGISTER_APPLICATION:
138                {
139                    BluetoothHealthAppConfiguration appConfig =
140                        (BluetoothHealthAppConfiguration) msg.obj;
141                    int appId = (mApps.get(appConfig)).mAppId;
142                    if (!unregisterHealthAppNative(appId)) {
143                        Log.e(TAG, "Failed to unregister application: id: " + appId);
144                        callStatusCallback(appConfig,
145                                           BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE);
146                    }
147                }
148                    break;
149                case MESSAGE_CONNECT_CHANNEL:
150                {
151                    HealthChannel chan = (HealthChannel) msg.obj;
152                    byte[] devAddr = getByteAddress(chan.mDevice);
153                    int appId = (mApps.get(chan.mConfig)).mAppId;
154                    chan.mChannelId = connectChannelNative(devAddr, appId);
155                    if (chan.mChannelId == -1) {
156                        callHealthChannelCallback(chan.mConfig, chan.mDevice,
157                                                  BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
158                                                  BluetoothHealth.STATE_CHANNEL_DISCONNECTED,
159                                                  chan.mChannelFd, chan.mChannelId);
160                        callHealthChannelCallback(chan.mConfig, chan.mDevice,
161                                                  BluetoothHealth.STATE_CHANNEL_DISCONNECTED,
162                                                  BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
163                                                  chan.mChannelFd, chan.mChannelId);
164                        mHealthChannels.remove(chan);
165                    }
166                }
167                    break;
168                case MESSAGE_DISCONNECT_CHANNEL:
169                {
170                    HealthChannel chan = (HealthChannel) msg.obj;
171                    if (!disconnectChannelNative(chan.mChannelId)) {
172                        callHealthChannelCallback(chan.mConfig, chan.mDevice,
173                                                  BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
174                                                  BluetoothHealth.STATE_CHANNEL_CONNECTED,
175                                                  chan.mChannelFd, chan.mChannelId);
176                        callHealthChannelCallback(chan.mConfig, chan.mDevice,
177                                                  BluetoothHealth.STATE_CHANNEL_CONNECTED,
178                                                  BluetoothHealth.STATE_CHANNEL_DISCONNECTING,
179                                                  chan.mChannelFd, chan.mChannelId);
180                    }
181                }
182                    break;
183                case MESSAGE_APP_REGISTRATION_CALLBACK:
184                {
185                    BluetoothHealthAppConfiguration appConfig = findAppConfigByAppId(msg.arg1);
186                    if (appConfig == null) break;
187
188                    int regStatus = convertHalRegStatus(msg.arg2);
189                    callStatusCallback(appConfig, regStatus);
190                    if (regStatus == BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE ||
191                        regStatus == BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS) {
192                        mApps.remove(appConfig);
193                    }
194                }
195                    break;
196                case MESSAGE_CHANNEL_STATE_CALLBACK:
197                {
198                    ChannelStateEvent channelStateEvent = (ChannelStateEvent) msg.obj;
199                    HealthChannel chan = findChannelById(channelStateEvent.mChannelId);
200                    int newState;
201                    if (chan == null) {
202                        // incoming connection
203                        BluetoothHealthAppConfiguration appConfig =
204                            findAppConfigByAppId(channelStateEvent.mAppId);
205                        BluetoothDevice device = getDevice(channelStateEvent.mAddr);
206                        chan = new HealthChannel(device, appConfig, appConfig.getChannelType());
207                        chan.mChannelId = channelStateEvent.mChannelId;
208                    }
209                    newState = convertHalChannelState(channelStateEvent.mState);
210                    if (newState == BluetoothHealth.STATE_CHANNEL_CONNECTED) {
211                        try {
212                            chan.mChannelFd = ParcelFileDescriptor.dup(channelStateEvent.mFd);
213                        } catch (IOException e) {
214                            Log.e(TAG, "failed to dup ParcelFileDescriptor");
215                            break;
216                        }
217                    }
218                    callHealthChannelCallback(chan.mConfig, chan.mDevice, newState,
219                                              chan.mState, chan.mChannelFd, chan.mChannelId);
220                    chan.mState = newState;
221                    if (channelStateEvent.mState == CONN_STATE_DESTROYED) {
222                        mHealthChannels.remove(chan);
223                    }
224                }
225                    break;
226            }
227        }
228    }
229
230    /**
231     * Handlers for incoming service calls
232     */
233    private final IBluetoothHealth.Stub mBinder = new IBluetoothHealth.Stub() {
234        public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
235                                                IBluetoothHealthCallback callback) {
236            enforceCallingOrSelfPermission(BLUETOOTH_PERM,
237                                           "Need BLUETOOTH permission");
238            if (mApps.get(config) != null) {
239                if (DBG) log("Config has already been registered");
240                return false;
241            }
242            mApps.put(config, new AppInfo(callback));
243            Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_APPLICATION);
244            msg.obj = config;
245            mHandler.sendMessage(msg);
246            return true;
247        }
248
249        public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
250            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
251            if (mApps.get(config) == null) {
252                if (DBG) log("unregisterAppConfiguration: no app found");
253                return false;
254            }
255
256            Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_APPLICATION);
257            msg.obj = config;
258            mHandler.sendMessage(msg);
259            return true;
260        }
261
262        public boolean connectChannelToSource(BluetoothDevice device,
263                                              BluetoothHealthAppConfiguration config) {
264            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
265            return connectChannel(device, config, BluetoothHealth.CHANNEL_TYPE_ANY);
266        }
267
268        public boolean connectChannelToSink(BluetoothDevice device,
269                           BluetoothHealthAppConfiguration config, int channelType) {
270            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
271            return connectChannel(device, config, channelType);
272        }
273
274        public boolean disconnectChannel(BluetoothDevice device,
275                                         BluetoothHealthAppConfiguration config, int channelId) {
276            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
277            HealthChannel chan = findChannelById(channelId);
278            if (chan == null) {
279                if (DBG) log("disconnectChannel: no channel found");
280                return false;
281            }
282            Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT_CHANNEL);
283            msg.obj = chan;
284            mHandler.sendMessage(msg);
285            return true;
286        }
287
288        public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
289                                                     BluetoothHealthAppConfiguration config) {
290            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
291            HealthChannel healthChan = null;
292            for (HealthChannel chan: mHealthChannels) {
293                if (chan.mDevice.equals(device) && chan.mConfig.equals(config)) {
294                    healthChan = chan;
295                }
296            }
297            if (healthChan == null) {
298                Log.e(TAG, "No channel found for device: " + device + " config: " + config);
299                return null;
300            }
301            return healthChan.mChannelFd;
302        }
303
304        public int getHealthDeviceConnectionState(BluetoothDevice device) {
305            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
306            return getConnectionState(device);
307        }
308
309        public List<BluetoothDevice> getConnectedHealthDevices() {
310            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
311            List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(
312                    new int[] {BluetoothHealth.STATE_CONNECTED});
313            return devices;
314        }
315
316        public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(int[] states) {
317            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
318            List<BluetoothDevice> devices = lookupHealthDevicesMatchingStates(states);
319            return devices;
320        }
321    };
322
323    private void onAppRegistrationState(int appId, int state) {
324        Message msg = mHandler.obtainMessage(MESSAGE_APP_REGISTRATION_CALLBACK);
325        msg.arg1 = appId;
326        msg.arg2 = state;
327        mHandler.sendMessage(msg);
328    }
329
330    private void onChannelStateChanged(int appId, byte[] addr, int cfgIndex,
331                                       int channelId, int state, FileDescriptor pfd) {
332        Message msg = mHandler.obtainMessage(MESSAGE_CHANNEL_STATE_CALLBACK);
333        ChannelStateEvent channelStateEvent = new ChannelStateEvent(appId, addr, cfgIndex,
334                                                                    channelId, state, pfd);
335        msg.obj = channelStateEvent;
336        mHandler.sendMessage(msg);
337    }
338
339    private String getStringChannelType(int type) {
340        if (type == BluetoothHealth.CHANNEL_TYPE_RELIABLE) {
341            return "Reliable";
342        } else if (type == BluetoothHealth.CHANNEL_TYPE_STREAMING) {
343            return "Streaming";
344        } else {
345            return "Any";
346        }
347    }
348
349    private void callStatusCallback(BluetoothHealthAppConfiguration config, int status) {
350        if (DBG) log ("Health Device Application: " + config + " State Change: status:" + status);
351        IBluetoothHealthCallback callback = (mApps.get(config)).mCallback;
352        if (callback == null) {
353            Log.e(TAG, "Callback object null");
354        }
355
356        try {
357            callback.onHealthAppConfigurationStatusChange(config, status);
358        } catch (RemoteException e) {
359            Log.e(TAG, "Remote Exception:" + e);
360        }
361    }
362
363    private BluetoothHealthAppConfiguration findAppConfigByAppId(int appId) {
364        BluetoothHealthAppConfiguration appConfig = null;
365        for (Entry<BluetoothHealthAppConfiguration, AppInfo> e : mApps.entrySet()) {
366            if (appId == (e.getValue()).mAppId) {
367                appConfig = e.getKey();
368                break;
369            }
370        }
371        if (appConfig == null) {
372            Log.e(TAG, "No appConfig found for " + appId);
373        }
374        return appConfig;
375    }
376
377    private int convertHalRegStatus(int halRegStatus) {
378        switch (halRegStatus) {
379            case APP_REG_STATE_REG_SUCCESS:
380                return BluetoothHealth.APP_CONFIG_REGISTRATION_SUCCESS;
381            case APP_REG_STATE_REG_FAILED:
382                return BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE;
383            case APP_REG_STATE_DEREG_SUCCESS:
384                return BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS;
385            case APP_REG_STATE_DEREG_FAILED:
386                return BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE;
387        }
388        Log.e(TAG, "Unexpected App Registration state: " + halRegStatus);
389        return BluetoothHealth.APP_CONFIG_REGISTRATION_FAILURE;
390    }
391
392    private int convertHalChannelState(int halChannelState) {
393        switch (halChannelState) {
394            case CONN_STATE_CONNECTED:
395                return BluetoothHealth.STATE_CHANNEL_CONNECTED;
396            case CONN_STATE_CONNECTING:
397                return BluetoothHealth.STATE_CHANNEL_CONNECTING;
398            case CONN_STATE_DISCONNECTING:
399                return BluetoothHealth.STATE_CHANNEL_DISCONNECTING;
400            case CONN_STATE_DISCONNECTED:
401                return BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
402            case CONN_STATE_DESTROYED:
403                // TODO(BT) add BluetoothHealth.STATE_CHANNEL_DESTROYED;
404                return BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
405            default:
406                Log.e(TAG, "Unexpected channel state: " + halChannelState);
407                return BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
408        }
409    }
410
411    private boolean connectChannel(BluetoothDevice device,
412                                   BluetoothHealthAppConfiguration config, int channelType) {
413        if (mApps.get(config) == null) {
414            Log.e(TAG, "connectChannel fail to get a app id from config");
415            return false;
416        }
417
418        HealthChannel chan = new HealthChannel(device, config, channelType);
419        mHealthChannels.add(chan);
420
421        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_CHANNEL);
422        msg.obj = chan;
423        mHandler.sendMessage(msg);
424
425        return true;
426    }
427
428    private void callHealthChannelCallback(BluetoothHealthAppConfiguration config,
429            BluetoothDevice device, int state, int prevState, ParcelFileDescriptor fd, int id) {
430        broadcastHealthDeviceStateChange(device, state);
431
432        if (DBG) log("Health Device Callback: " + device + " State Change: " + prevState + "->" +
433                     state);
434
435        ParcelFileDescriptor dupedFd = null;
436        if (fd != null) {
437            try {
438                dupedFd = fd.dup();
439            } catch (IOException e) {
440                dupedFd = null;
441                Log.e(TAG, "Exception while duping: " + e);
442            }
443        }
444
445        IBluetoothHealthCallback callback = (mApps.get(config)).mCallback;
446        if (callback == null) {
447            Log.e(TAG, "No callback found for config: " + config);
448            return;
449        }
450
451        try {
452            callback.onHealthChannelStateChange(config, device, prevState, state, dupedFd, id);
453        } catch (RemoteException e) {
454            Log.e(TAG, "Remote Exception:" + e);
455        }
456    }
457
458    /**
459     * This function sends the intent for the updates on the connection status to the remote device.
460     * Note that multiple channels can be connected to the remote device by multiple applications.
461     * This sends an intent for the update to the device connection status and not the channel
462     * connection status. Only the following state transitions are possible:
463     *
464     * {@link BluetoothHealth#STATE_DISCONNECTED} to {@link BluetoothHealth#STATE_CONNECTING}
465     * {@link BluetoothHealth#STATE_CONNECTING} to {@link BluetoothHealth#STATE_CONNECTED}
466     * {@link BluetoothHealth#STATE_CONNECTED} to {@link BluetoothHealth#STATE_DISCONNECTING}
467     * {@link BluetoothHealth#STATE_DISCONNECTING} to {@link BluetoothHealth#STATE_DISCONNECTED}
468     * {@link BluetoothHealth#STATE_DISCONNECTED} to {@link BluetoothHealth#STATE_CONNECTED}
469     * {@link BluetoothHealth#STATE_CONNECTED} to {@link BluetoothHealth#STATE_DISCONNECTED}
470     * {@link BluetoothHealth#STATE_CONNECTING} to {{@link BluetoothHealth#STATE_DISCONNECTED}
471     *
472     * @param device
473     * @param prevChannelState
474     * @param newChannelState
475     * @hide
476     */
477    private void broadcastHealthDeviceStateChange(BluetoothDevice device, int newChannelState) {
478        if (mHealthDevices.get(device) == null) {
479            mHealthDevices.put(device, BluetoothHealth.STATE_DISCONNECTED);
480        }
481
482        int currDeviceState = mHealthDevices.get(device);
483        int newDeviceState = convertState(newChannelState);
484
485        if (currDeviceState == newDeviceState) return;
486
487        boolean sendIntent = false;
488        List<HealthChannel> chan;
489        switch (currDeviceState) {
490            case BluetoothHealth.STATE_DISCONNECTED:
491                // there was no connection or connect/disconnect attemp with the remote device
492                sendIntent = true;
493                break;
494            case BluetoothHealth.STATE_CONNECTING:
495                // there was no connection, there was a connecting attempt going on
496
497                // Channel got connected.
498                if (newDeviceState == BluetoothHealth.STATE_CONNECTED) {
499                    sendIntent = true;
500                } else {
501                    // Channel got disconnected
502                    chan = findChannelByStates(device, new int [] {
503                            BluetoothHealth.STATE_CHANNEL_CONNECTING,
504                            BluetoothHealth.STATE_CHANNEL_DISCONNECTING});
505                    if (chan.isEmpty()) {
506                        sendIntent = true;
507                    }
508                }
509                break;
510            case BluetoothHealth.STATE_CONNECTED:
511                // there was at least one connection
512
513                // Channel got disconnected or is in disconnecting state.
514                chan = findChannelByStates(device, new int [] {
515                        BluetoothHealth.STATE_CHANNEL_CONNECTING,
516                        BluetoothHealth.STATE_CHANNEL_CONNECTED});
517                if (chan.isEmpty()) {
518                    sendIntent = true;
519                }
520                break;
521            case BluetoothHealth.STATE_DISCONNECTING:
522                // there was no connected channel with the remote device
523                // We were disconnecting all the channels with the remote device
524
525                // Channel got disconnected.
526                chan = findChannelByStates(device, new int [] {
527                        BluetoothHealth.STATE_CHANNEL_CONNECTING,
528                        BluetoothHealth.STATE_CHANNEL_DISCONNECTING});
529                if (chan.isEmpty()) {
530                    updateAndSendIntent(device, newDeviceState, currDeviceState);
531                }
532                break;
533        }
534        if (sendIntent)
535            updateAndSendIntent(device, newDeviceState, currDeviceState);
536    }
537
538    private void updateAndSendIntent(BluetoothDevice device, int newDeviceState,
539            int prevDeviceState) {
540        if (newDeviceState == BluetoothHealth.STATE_DISCONNECTED) {
541            mHealthDevices.remove(device);
542        } else {
543            mHealthDevices.put(device, newDeviceState);
544        }
545        try {
546            mAdapterService.sendConnectionStateChange(device, BluetoothProfile.HEALTH,
547                                                      newDeviceState, prevDeviceState);
548        } catch (RemoteException e) {
549            Log.e(TAG, Log.getStackTraceString(new Throwable()));
550        }
551    }
552
553    /**
554     * This function converts the channel connection state to device connection state.
555     *
556     * @param state
557     * @return
558     */
559    private int convertState(int state) {
560        switch (state) {
561            case BluetoothHealth.STATE_CHANNEL_CONNECTED:
562                return BluetoothHealth.STATE_CONNECTED;
563            case BluetoothHealth.STATE_CHANNEL_CONNECTING:
564                return BluetoothHealth.STATE_CONNECTING;
565            case BluetoothHealth.STATE_CHANNEL_DISCONNECTING:
566                return BluetoothHealth.STATE_DISCONNECTING;
567            case BluetoothHealth.STATE_CHANNEL_DISCONNECTED:
568                return BluetoothHealth.STATE_DISCONNECTED;
569        }
570        Log.e(TAG, "Mismatch in Channel and Health Device State: " + state);
571        return BluetoothHealth.STATE_DISCONNECTED;
572    }
573
574    private int convertRoleToHal(int role) {
575        if (role == BluetoothHealth.SOURCE_ROLE) return MDEP_ROLE_SOURCE;
576        if (role == BluetoothHealth.SINK_ROLE) return MDEP_ROLE_SINK;
577        Log.e(TAG, "unkonw role: " + role);
578        return MDEP_ROLE_SINK;
579    }
580
581    private int convertChannelTypeToHal(int channelType) {
582        if (channelType == BluetoothHealth.CHANNEL_TYPE_RELIABLE) return CHANNEL_TYPE_RELIABLE;
583        if (channelType == BluetoothHealth.CHANNEL_TYPE_STREAMING) return CHANNEL_TYPE_STREAMING;
584        if (channelType == BluetoothHealth.CHANNEL_TYPE_ANY) return CHANNEL_TYPE_ANY;
585        Log.e(TAG, "unkonw channel type: " + channelType);
586        return CHANNEL_TYPE_ANY;
587    }
588
589    private HealthChannel findChannelById(int id) {
590        for (HealthChannel chan : mHealthChannels) {
591            if (chan.mChannelId == id) return chan;
592        }
593        Log.e(TAG, "No channel found by id: " + id);
594        return null;
595    }
596
597    private List<HealthChannel> findChannelByStates(BluetoothDevice device, int[] states) {
598        List<HealthChannel> channels = new ArrayList<HealthChannel>();
599        for (HealthChannel chan: mHealthChannels) {
600            if (chan.mDevice.equals(device)) {
601                for (int state : states) {
602                    if (chan.mState == state) {
603                        channels.add(chan);
604                    }
605                }
606            }
607        }
608        return channels;
609    }
610
611    private BluetoothDevice getDevice(byte[] address) {
612        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
613    }
614
615    private byte[] getByteAddress(BluetoothDevice device) {
616        return Utils.getBytesFromAddress(device.getAddress());
617    }
618
619    private int getConnectionState(BluetoothDevice device) {
620        if (mHealthDevices.get(device) == null) {
621            return BluetoothHealth.STATE_DISCONNECTED;
622        }
623        return mHealthDevices.get(device);
624    }
625
626    List<BluetoothDevice> lookupHealthDevicesMatchingStates(int[] states) {
627        List<BluetoothDevice> healthDevices = new ArrayList<BluetoothDevice>();
628
629        for (BluetoothDevice device: mHealthDevices.keySet()) {
630            int healthDeviceState = getConnectionState(device);
631            for (int state : states) {
632                if (state == healthDeviceState) {
633                    healthDevices.add(device);
634                    break;
635                }
636            }
637        }
638        return healthDevices;
639    }
640
641    private void log(String msg) {
642        Log.d(TAG, msg);
643    }
644
645    private class AppInfo {
646        private IBluetoothHealthCallback mCallback;
647        private int mAppId;
648
649        private AppInfo(IBluetoothHealthCallback callback) {
650            mCallback = callback;
651            mAppId = -1;
652        }
653    }
654
655    private class HealthChannel {
656        private ParcelFileDescriptor mChannelFd;
657        private BluetoothDevice mDevice;
658        private BluetoothHealthAppConfiguration mConfig;
659        // BluetoothHealth channel state
660        private int mState;
661        private int mChannelType;
662        private int mChannelId;
663
664        private HealthChannel(BluetoothDevice device, BluetoothHealthAppConfiguration config,
665                      int channelType) {
666             mChannelFd = null;
667             mDevice = device;
668             mConfig = config;
669             mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
670             mChannelType = channelType;
671             mChannelId = -1;
672        }
673    }
674
675    // Channel state event from Hal
676    private class ChannelStateEvent {
677        int mAppId;
678        byte[] mAddr;
679        int mCfgIndex;
680        int mChannelId;
681        int mState;
682        FileDescriptor mFd;
683
684        private ChannelStateEvent(int appId, byte[] addr, int cfgIndex,
685                                  int channelId, int state, FileDescriptor fileDescriptor) {
686            mAppId = appId;
687            mAddr = addr;
688            mCfgIndex = cfgIndex;
689            mState = state;
690            mChannelId = channelId;
691            mFd = fileDescriptor;
692        }
693    }
694
695    // Constants matching Hal header file bt_hl.h
696    // bthl_app_reg_state_t
697    private static final int APP_REG_STATE_REG_SUCCESS = 0;
698    private static final int APP_REG_STATE_REG_FAILED = 1;
699    private static final int APP_REG_STATE_DEREG_SUCCESS = 2;
700    private static final int APP_REG_STATE_DEREG_FAILED = 3;
701
702    // bthl_channel_state_t
703    private static final int CONN_STATE_CONNECTING = 0;
704    private static final int CONN_STATE_CONNECTED = 1;
705    private static final int CONN_STATE_DISCONNECTING = 2;
706    private static final int CONN_STATE_DISCONNECTED = 3;
707    private static final int CONN_STATE_DESTROYED = 4;
708
709    // bthl_mdep_role_t
710    private static final int MDEP_ROLE_SOURCE = 0;
711    private static final int MDEP_ROLE_SINK = 1;
712
713    // bthl_channel_type_t
714    private static final int CHANNEL_TYPE_RELIABLE = 0;
715    private static final int CHANNEL_TYPE_STREAMING = 1;
716    private static final int CHANNEL_TYPE_ANY =2;
717
718    private native static void classInitNative();
719    private native void initializeNativeDataNative();
720    private native int registerHealthAppNative(int dataType, int role, String name, int channelType);
721    private native boolean unregisterHealthAppNative(int appId);
722    private native int connectChannelNative(byte[] btAddress, int appId);
723    private native boolean disconnectChannelNative(int channelId);
724}
725