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