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