HidService.java revision eb7b90f5b93db1230a5b64caa3d8d05a642e33a6
1/*
2 * Copyright (C) 2012 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 com.android.bluetooth.hid;
18
19import android.bluetooth.BluetoothDevice;
20import android.bluetooth.BluetoothInputDevice;
21import android.bluetooth.BluetoothProfile;
22import android.bluetooth.IBluetoothInputDevice;
23import android.content.Intent;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Message;
27import android.provider.Settings;
28import android.util.Log;
29
30import com.android.bluetooth.btservice.AdapterService;
31import com.android.bluetooth.btservice.ProfileService;
32import com.android.bluetooth.Utils;
33
34import java.util.ArrayList;
35import java.util.Collections;
36import java.util.HashMap;
37import java.util.List;
38import java.util.Map;
39
40/**
41 * Provides Bluetooth Hid Host profile, as a service in
42 * the Bluetooth application.
43 * @hide
44 */
45public class HidService extends ProfileService {
46    private static final boolean DBG = true;
47    private static final String TAG = "HidService";
48
49    private Map<BluetoothDevice, Integer> mInputDevices;
50    private boolean mNativeAvailable;
51    private static HidService sHidService;
52    private BluetoothDevice mTargetDevice = null;
53
54    private static final int MESSAGE_CONNECT = 1;
55    private static final int MESSAGE_DISCONNECT = 2;
56    private static final int MESSAGE_CONNECT_STATE_CHANGED = 3;
57    private static final int MESSAGE_GET_PROTOCOL_MODE = 4;
58    private static final int MESSAGE_VIRTUAL_UNPLUG = 5;
59    private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6;
60    private static final int MESSAGE_SET_PROTOCOL_MODE = 7;
61    private static final int MESSAGE_GET_REPORT = 8;
62    private static final int MESSAGE_ON_GET_REPORT = 9;
63    private static final int MESSAGE_SET_REPORT = 10;
64    private static final int MESSAGE_SEND_DATA = 11;
65    private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12;
66    private static final int MESSAGE_ON_HANDSHAKE = 13;
67
68    static {
69        classInitNative();
70    }
71
72    public String getName() {
73        return TAG;
74    }
75
76    public IProfileServiceBinder initBinder() {
77        return new BluetoothInputDeviceBinder(this);
78    }
79
80    protected boolean start() {
81        mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
82        initializeNative();
83        mNativeAvailable=true;
84        setHidService(this);
85        return true;
86    }
87
88    protected boolean stop() {
89        if (DBG) log("Stopping Bluetooth HidService");
90        return true;
91    }
92
93    protected boolean cleanup() {
94        if (mNativeAvailable) {
95            cleanupNative();
96            mNativeAvailable=false;
97        }
98
99        if(mInputDevices != null) {
100            mInputDevices.clear();
101        }
102        clearHidService();
103        return true;
104    }
105
106    public static synchronized HidService getHidService(){
107        if (sHidService != null && sHidService.isAvailable()) {
108            if (DBG) Log.d(TAG, "getHidService(): returning " + sHidService);
109            return sHidService;
110        }
111        if (DBG)  {
112            if (sHidService == null) {
113                Log.d(TAG, "getHidService(): service is NULL");
114            } else if (!(sHidService.isAvailable())) {
115                Log.d(TAG,"getHidService(): service is not available");
116            }
117        }
118        return null;
119    }
120
121    private static synchronized void setHidService(HidService instance) {
122        if (instance != null && instance.isAvailable()) {
123            if (DBG) Log.d(TAG, "setHidService(): set to: " + sHidService);
124            sHidService = instance;
125        } else {
126            if (DBG)  {
127                if (sHidService == null) {
128                    Log.d(TAG, "setHidService(): service not available");
129                } else if (!sHidService.isAvailable()) {
130                    Log.d(TAG,"setHidService(): service is cleaning up");
131                }
132            }
133        }
134    }
135
136    private static synchronized void clearHidService() {
137        sHidService = null;
138    }
139
140
141    private final Handler mHandler = new Handler() {
142
143        @Override
144        public void handleMessage(Message msg) {
145            switch (msg.what) {
146                case MESSAGE_CONNECT:
147                {
148                    BluetoothDevice device = (BluetoothDevice) msg.obj;
149                    if (!connectHidNative(Utils.getByteAddress(device)) ) {
150                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
151                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
152                        break;
153                    }
154                    mTargetDevice = device;
155                }
156                    break;
157                case MESSAGE_DISCONNECT:
158                {
159                    BluetoothDevice device = (BluetoothDevice) msg.obj;
160                    if (!disconnectHidNative(Utils.getByteAddress(device)) ) {
161                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
162                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
163                        break;
164                    }
165                }
166                    break;
167                case MESSAGE_CONNECT_STATE_CHANGED:
168                {
169                    BluetoothDevice device = getDevice((byte[]) msg.obj);
170                    int halState = msg.arg1;
171                    Integer prevStateInteger = mInputDevices.get(device);
172                    int prevState = (prevStateInteger == null) ?
173                        BluetoothInputDevice.STATE_DISCONNECTED :prevStateInteger;
174                    if(DBG) Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:"+
175                        convertHalState(halState)+", prevState:"+prevState);
176                    if(halState == CONN_STATE_CONNECTED &&
177                       prevState == BluetoothInputDevice.STATE_DISCONNECTED &&
178                       (!okToConnect(device))) {
179                        if (DBG) Log.d(TAG,"Incoming HID connection rejected");
180                        disconnectHidNative(Utils.getByteAddress(device));
181                    } else {
182                        broadcastConnectionState(device, convertHalState(halState));
183                    }
184                    if (halState == CONN_STATE_CONNECTED &&
185                        (mTargetDevice != null && mTargetDevice.equals(device))) {
186                        mTargetDevice = null;
187                        // local device originated connection to hid device, move out
188                        // of quiet mode
189                        AdapterService adapterService = AdapterService.getAdapterService();
190                        adapterService.enable(false);
191                    }
192                }
193                    break;
194                case MESSAGE_GET_PROTOCOL_MODE:
195                {
196                    BluetoothDevice device = (BluetoothDevice) msg.obj;
197                    if(!getProtocolModeNative(Utils.getByteAddress(device)) ) {
198                        Log.e(TAG, "Error: get protocol mode native returns false");
199                    }
200                }
201                break;
202
203                case MESSAGE_ON_GET_PROTOCOL_MODE:
204                {
205                    BluetoothDevice device = getDevice((byte[]) msg.obj);
206                    int protocolMode = msg.arg1;
207                    broadcastProtocolMode(device, protocolMode);
208                }
209                break;
210                case MESSAGE_VIRTUAL_UNPLUG:
211                {
212                    BluetoothDevice device = (BluetoothDevice) msg.obj;
213                    if(!virtualUnPlugNative(Utils.getByteAddress(device))) {
214                        Log.e(TAG, "Error: virtual unplug native returns false");
215                    }
216                }
217                break;
218                case MESSAGE_SET_PROTOCOL_MODE:
219                {
220                    BluetoothDevice device = (BluetoothDevice) msg.obj;
221                    byte protocolMode = (byte) msg.arg1;
222                    log("sending set protocol mode(" + protocolMode + ")");
223                    if(!setProtocolModeNative(Utils.getByteAddress(device), protocolMode)) {
224                        Log.e(TAG, "Error: set protocol mode native returns false");
225                    }
226                }
227                break;
228                case MESSAGE_GET_REPORT:
229                {
230                    BluetoothDevice device = (BluetoothDevice) msg.obj;
231                    Bundle data = msg.getData();
232                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
233                    byte reportId = data.getByte(BluetoothInputDevice.EXTRA_REPORT_ID);
234                    int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
235                    if(!getReportNative(Utils.getByteAddress(device), reportType, reportId, bufferSize)) {
236                        Log.e(TAG, "Error: get report native returns false");
237                    }
238                }
239                break;
240                case MESSAGE_ON_GET_REPORT:
241                {
242                    BluetoothDevice device = getDevice((byte[])msg.obj);
243                    Bundle data = msg.getData();
244                    byte[] report = data.getByteArray(BluetoothInputDevice.EXTRA_REPORT);
245                    int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
246                    broadcastReport(device, report, bufferSize);
247                }
248                break;
249                case MESSAGE_ON_HANDSHAKE:
250                {
251                    BluetoothDevice device = getDevice((byte[])msg.obj);
252                    int status = msg.arg1;
253                    broadcastHandshake(device, status);
254                }
255                break;
256                case MESSAGE_SET_REPORT:
257                {
258                    BluetoothDevice device = (BluetoothDevice) msg.obj;
259                    Bundle data = msg.getData();
260                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
261                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
262                    if(!setReportNative(Utils.getByteAddress(device), reportType, report)) {
263                        Log.e(TAG, "Error: set report native returns false");
264                    }
265                }
266                break;
267                case MESSAGE_SEND_DATA:
268                {
269                    BluetoothDevice device = (BluetoothDevice) msg.obj;
270                    Bundle data = msg.getData();
271                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
272                    if(!sendDataNative(Utils.getByteAddress(device), report)) {
273                        Log.e(TAG, "Error: send data native returns false");
274                    }
275                }
276                break;
277                case MESSAGE_ON_VIRTUAL_UNPLUG:
278                {
279                    BluetoothDevice device = getDevice((byte[]) msg.obj);
280                    int status = msg.arg1;
281                    broadcastVirtualUnplugStatus(device, status);
282                }
283                break;
284            }
285        }
286    };
287
288    /**
289     * Handlers for incoming service calls
290     */
291    private static class BluetoothInputDeviceBinder extends IBluetoothInputDevice.Stub implements IProfileServiceBinder{
292        private HidService mService;
293        public BluetoothInputDeviceBinder(HidService svc) {
294            mService = svc;
295        }
296
297        public boolean cleanup() {
298            mService = null;
299            return true;
300        }
301
302        private HidService getService() {
303            if (!Utils.checkCaller()) {
304                Log.w(TAG,"InputDevice call not allowed for non-active user");
305                return null;
306            }
307
308            if (mService  != null && mService.isAvailable()) {
309                return mService;
310            }
311            return null;
312        }
313
314        public boolean connect(BluetoothDevice device) {
315            HidService service = getService();
316            if (service == null) return false;
317            return service.connect(device);
318        }
319
320        public boolean disconnect(BluetoothDevice device) {
321            HidService service = getService();
322            if (service == null) return false;
323            return service.disconnect(device);
324        }
325
326        public int getConnectionState(BluetoothDevice device) {
327            HidService service = getService();
328            if (service == null) return BluetoothInputDevice.STATE_DISCONNECTED;
329            return service.getConnectionState(device);
330        }
331
332        public List<BluetoothDevice> getConnectedDevices() {
333            return getDevicesMatchingConnectionStates(
334                    new int[] {BluetoothProfile.STATE_CONNECTED});
335        }
336
337        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
338            HidService service = getService();
339            if (service == null) return new ArrayList<BluetoothDevice>(0);
340            return service.getDevicesMatchingConnectionStates(states);
341        }
342
343        public boolean setPriority(BluetoothDevice device, int priority) {
344            HidService service = getService();
345            if (service == null) return false;
346            return service.setPriority(device, priority);
347        }
348
349        public int getPriority(BluetoothDevice device) {
350            HidService service = getService();
351            if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED;
352            return service.getPriority(device);
353        }
354
355        /* The following APIs regarding test app for compliance */
356        public boolean getProtocolMode(BluetoothDevice device) {
357            HidService service = getService();
358            if (service == null) return false;
359            return service.getProtocolMode(device);
360        }
361
362        public boolean virtualUnplug(BluetoothDevice device) {
363            HidService service = getService();
364            if (service == null) return false;
365            return service.virtualUnplug(device);
366        }
367
368        public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
369            HidService service = getService();
370            if (service == null) return false;
371            return service.setProtocolMode(device, protocolMode);
372        }
373
374        public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
375            HidService service = getService();
376            if (service == null) return false;
377            return service.getReport(device, reportType, reportId, bufferSize) ;
378        }
379
380        public boolean setReport(BluetoothDevice device, byte reportType, String report) {
381            HidService service = getService();
382            if (service == null) return false;
383            return service.setReport(device, reportType, report);
384        }
385
386        public boolean sendData(BluetoothDevice device, String report) {
387            HidService service = getService();
388            if (service == null) return false;
389            return service.sendData(device, report);
390        }
391    };
392
393    //APIs
394    boolean connect(BluetoothDevice device) {
395        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
396        if (getConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED) {
397            Log.e(TAG, "Hid Device not disconnected: " + device);
398            return false;
399        }
400        if (getPriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
401            Log.e(TAG, "Hid Device PRIORITY_OFF: " + device);
402            return false;
403        }
404
405        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device);
406        mHandler.sendMessage(msg);
407        return true;
408    }
409
410    boolean disconnect(BluetoothDevice device) {
411        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
412        Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT,device);
413        mHandler.sendMessage(msg);
414        return true;
415    }
416
417    int getConnectionState(BluetoothDevice device) {
418        if (mInputDevices.get(device) == null) {
419            return BluetoothInputDevice.STATE_DISCONNECTED;
420        }
421        return mInputDevices.get(device);
422    }
423
424    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
425        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
426        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
427
428        for (BluetoothDevice device: mInputDevices.keySet()) {
429            int inputDeviceState = getConnectionState(device);
430            for (int state : states) {
431                if (state == inputDeviceState) {
432                    inputDevices.add(device);
433                    break;
434                }
435            }
436        }
437        return inputDevices;
438    }
439
440    public boolean setPriority(BluetoothDevice device, int priority) {
441        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
442                                       "Need BLUETOOTH_ADMIN permission");
443        Settings.Global.putInt(getContentResolver(),
444            Settings.Global.getBluetoothInputDevicePriorityKey(device.getAddress()),
445            priority);
446        if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority);
447        return true;
448    }
449
450    public  int getPriority(BluetoothDevice device) {
451        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
452                                       "Need BLUETOOTH_ADMIN permission");
453        int priority = Settings.Global.getInt(getContentResolver(),
454            Settings.Global.getBluetoothInputDevicePriorityKey(device.getAddress()),
455            BluetoothProfile.PRIORITY_UNDEFINED);
456        return priority;
457    }
458
459    /* The following APIs regarding test app for compliance */
460    boolean getProtocolMode(BluetoothDevice device) {
461        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
462                                       "Need BLUETOOTH_ADMIN permission");
463        int state = this.getConnectionState(device);
464        if (state != BluetoothInputDevice.STATE_CONNECTED) {
465            return false;
466        }
467        Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE,device);
468        mHandler.sendMessage(msg);
469        return true;
470        /* String objectPath = getObjectPathFromAddress(device.getAddress());
471            return getProtocolModeInputDeviceNative(objectPath);*/
472    }
473
474    boolean virtualUnplug(BluetoothDevice device) {
475        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
476                                       "Need BLUETOOTH_ADMIN permission");
477        int state = this.getConnectionState(device);
478        if (state != BluetoothInputDevice.STATE_CONNECTED) {
479            return false;
480        }
481        Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG,device);
482        mHandler.sendMessage(msg);
483        return true;
484    }
485
486    boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
487        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
488                                       "Need BLUETOOTH_ADMIN permission");
489        int state = this.getConnectionState(device);
490        if (state != BluetoothInputDevice.STATE_CONNECTED) {
491            return false;
492        }
493        Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
494        msg.obj = device;
495        msg.arg1 = protocolMode;
496        mHandler.sendMessage(msg);
497        return true ;
498    }
499
500    boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
501        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
502                                       "Need BLUETOOTH_ADMIN permission");
503        int state = this.getConnectionState(device);
504        if (state != BluetoothInputDevice.STATE_CONNECTED) {
505            return false;
506        }
507        Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
508        msg.obj = device;
509        Bundle data = new Bundle();
510        data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
511        data.putByte(BluetoothInputDevice.EXTRA_REPORT_ID, reportId);
512        data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
513        msg.setData(data);
514        mHandler.sendMessage(msg);
515        return true ;
516    }
517
518    boolean setReport(BluetoothDevice device, byte reportType, String report) {
519        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
520                                                   "Need BLUETOOTH_ADMIN permission");
521        int state = this.getConnectionState(device);
522        if (state != BluetoothInputDevice.STATE_CONNECTED) {
523            return false;
524        }
525        Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
526        msg.obj = device;
527        Bundle data = new Bundle();
528        data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
529        data.putString(BluetoothInputDevice.EXTRA_REPORT, report);
530        msg.setData(data);
531        mHandler.sendMessage(msg);
532        return true ;
533
534    }
535
536    boolean sendData(BluetoothDevice device, String report) {
537        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
538                                                   "Need BLUETOOTH_ADMIN permission");
539        int state = this.getConnectionState(device);
540        if (state != BluetoothInputDevice.STATE_CONNECTED) {
541            return false;
542        }
543
544        return sendDataNative(Utils.getByteAddress(device), report);
545    }
546
547    private void onGetProtocolMode(byte[] address, int mode) {
548        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
549        msg.obj = address;
550        msg.arg1 = mode;
551        mHandler.sendMessage(msg);
552    }
553
554    private void onGetReport(byte[] address, byte[] report, int rpt_size) {
555        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT);
556        msg.obj = address;
557        Bundle data = new Bundle();
558        data.putByteArray(BluetoothInputDevice.EXTRA_REPORT, report);
559        data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, rpt_size);
560        msg.setData(data);
561        mHandler.sendMessage(msg);
562    }
563
564    private void onHandshake(byte[] address, int status) {
565        Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE);
566        msg.obj = address;
567        msg.arg1 = status;
568        mHandler.sendMessage(msg);
569    }
570
571    private void onVirtualUnplug(byte[] address, int status) {
572        Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
573        msg.obj = address;
574        msg.arg1 = status;
575        mHandler.sendMessage(msg);
576    }
577
578    private void onConnectStateChanged(byte[] address, int state) {
579        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
580        msg.obj = address;
581        msg.arg1 = state;
582        mHandler.sendMessage(msg);
583    }
584
585    // This method does not check for error conditon (newState == prevState)
586    private void broadcastConnectionState(BluetoothDevice device, int newState) {
587        Integer prevStateInteger = mInputDevices.get(device);
588        int prevState = (prevStateInteger == null) ? BluetoothInputDevice.STATE_DISCONNECTED :
589                                                     prevStateInteger;
590        if (prevState == newState) {
591            Log.w(TAG, "no state change: " + newState);
592            return;
593        }
594        mInputDevices.put(device, newState);
595
596        /* Notifying the connection state change of the profile before sending the intent for
597           connection state change, as it was causing a race condition, with the UI not being
598           updated with the correct connection state. */
599        log("Connection state " + device + ": " + prevState + "->" + newState);
600        notifyProfileConnectionStateChanged(device, BluetoothProfile.INPUT_DEVICE,
601                                            newState, prevState);
602        Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
603        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
604        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
605        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
606        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
607        sendBroadcast(intent, BLUETOOTH_PERM);
608    }
609
610    private void broadcastHandshake(BluetoothDevice device, int status) {
611        Intent intent = new Intent(BluetoothInputDevice.ACTION_HANDSHAKE);
612        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
613        intent.putExtra(BluetoothInputDevice.EXTRA_STATUS, status);
614        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
615        sendBroadcast(intent, BLUETOOTH_PERM);
616    }
617
618    private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
619        Intent intent = new Intent(BluetoothInputDevice.ACTION_PROTOCOL_MODE_CHANGED);
620        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
621        intent.putExtra(BluetoothInputDevice.EXTRA_PROTOCOL_MODE, protocolMode);
622        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
623        sendBroadcast(intent, BLUETOOTH_PERM);
624        if (DBG) log("Protocol Mode (" + device + "): " + protocolMode);
625    }
626
627    private void broadcastReport(BluetoothDevice device, byte[] report, int rpt_size) {
628        Intent intent = new Intent(BluetoothInputDevice.ACTION_REPORT);
629        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
630        intent.putExtra(BluetoothInputDevice.EXTRA_REPORT, report);
631        intent.putExtra(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, rpt_size);
632        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
633        sendBroadcast(intent, BLUETOOTH_PERM);
634    }
635
636    private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
637        Intent intent = new Intent(BluetoothInputDevice.ACTION_VIRTUAL_UNPLUG_STATUS);
638        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
639        intent.putExtra(BluetoothInputDevice.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
640        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
641        sendBroadcast(intent, BLUETOOTH_PERM);
642    }
643
644    private boolean okToConnect(BluetoothDevice device) {
645        AdapterService adapterService = AdapterService.getAdapterService();
646        //check if it is inbound connection in Quiet mode, priority and Bond status
647        //to decide if its ok to allow this connection
648        if((adapterService == null)||
649           ((adapterService.isQuietModeEnabled()) &&(mTargetDevice == null)) ||
650           (BluetoothProfile.PRIORITY_OFF == getPriority(device)) ||
651           (device.getBondState() == BluetoothDevice.BOND_NONE))
652            return false;
653
654        return true;
655    }
656    private static int convertHalState(int halState) {
657        switch (halState) {
658            case CONN_STATE_CONNECTED:
659                return BluetoothProfile.STATE_CONNECTED;
660            case CONN_STATE_CONNECTING:
661                return BluetoothProfile.STATE_CONNECTING;
662            case CONN_STATE_DISCONNECTED:
663                return BluetoothProfile.STATE_DISCONNECTED;
664            case CONN_STATE_DISCONNECTING:
665                return BluetoothProfile.STATE_DISCONNECTING;
666            default:
667                Log.e(TAG, "bad hid connection state: " + halState);
668                return BluetoothProfile.STATE_DISCONNECTED;
669        }
670    }
671
672    @Override
673    public void dump(StringBuilder sb) {
674        super.dump(sb);
675        println(sb, "mTargetDevice: " + mTargetDevice);
676        println(sb, "mInputDevices:");
677        for (BluetoothDevice device : mInputDevices.keySet()) {
678            println(sb, "  " + device + " : " + mInputDevices.get(device));
679        }
680    }
681
682    // Constants matching Hal header file bt_hh.h
683    // bthh_connection_state_t
684    private final static int CONN_STATE_CONNECTED = 0;
685    private final static int CONN_STATE_CONNECTING = 1;
686    private final static int CONN_STATE_DISCONNECTED = 2;
687    private final static int CONN_STATE_DISCONNECTING = 3;
688
689    private native static void classInitNative();
690    private native void initializeNative();
691    private native void cleanupNative();
692    private native boolean connectHidNative(byte[] btAddress);
693    private native boolean disconnectHidNative(byte[] btAddress);
694    private native boolean getProtocolModeNative(byte[] btAddress);
695    private native boolean virtualUnPlugNative(byte[] btAddress);
696    private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
697    private native boolean getReportNative(byte[]btAddress, byte reportType, byte reportId, int bufferSize);
698    private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
699    private native boolean sendDataNative(byte[] btAddress, String report);
700}
701