HidHostService.java revision 0ad53ef457f156314595a27c63660454450d3c84
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.BluetoothHidHost;
21import android.bluetooth.BluetoothProfile;
22import android.bluetooth.IBluetoothHidHost;
23import android.content.Intent;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.Message;
27import android.os.UserHandle;
28import android.provider.Settings;
29import android.util.Log;
30
31import com.android.bluetooth.BluetoothMetricsProto;
32import com.android.bluetooth.Utils;
33import com.android.bluetooth.btservice.AdapterService;
34import com.android.bluetooth.btservice.MetricsLogger;
35import com.android.bluetooth.btservice.ProfileService;
36
37import java.util.ArrayList;
38import java.util.Collections;
39import java.util.HashMap;
40import java.util.List;
41import java.util.Map;
42
43/**
44 * Provides Bluetooth Hid Host profile, as a service in
45 * the Bluetooth application.
46 * @hide
47 */
48public class HidHostService extends ProfileService {
49    private static final boolean DBG = false;
50    private static final String TAG = "BluetoothHidHostService";
51
52    private Map<BluetoothDevice, Integer> mInputDevices;
53    private boolean mNativeAvailable;
54    private static HidHostService sHidHostService;
55    private BluetoothDevice mTargetDevice = null;
56
57    private static final int MESSAGE_CONNECT = 1;
58    private static final int MESSAGE_DISCONNECT = 2;
59    private static final int MESSAGE_CONNECT_STATE_CHANGED = 3;
60    private static final int MESSAGE_GET_PROTOCOL_MODE = 4;
61    private static final int MESSAGE_VIRTUAL_UNPLUG = 5;
62    private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6;
63    private static final int MESSAGE_SET_PROTOCOL_MODE = 7;
64    private static final int MESSAGE_GET_REPORT = 8;
65    private static final int MESSAGE_ON_GET_REPORT = 9;
66    private static final int MESSAGE_SET_REPORT = 10;
67    private static final int MESSAGE_SEND_DATA = 11;
68    private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12;
69    private static final int MESSAGE_ON_HANDSHAKE = 13;
70    private static final int MESSAGE_GET_IDLE_TIME = 14;
71    private static final int MESSAGE_ON_GET_IDLE_TIME = 15;
72    private static final int MESSAGE_SET_IDLE_TIME = 16;
73
74    static {
75        classInitNative();
76    }
77
78    @Override
79    public IProfileServiceBinder initBinder() {
80        return new BluetoothHidHostBinder(this);
81    }
82
83    @Override
84    protected boolean start() {
85        mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
86        initializeNative();
87        mNativeAvailable = true;
88        setHidHostService(this);
89        return true;
90    }
91
92    @Override
93    protected boolean stop() {
94        if (DBG) {
95            Log.d(TAG, "Stopping Bluetooth HidHostService");
96        }
97        return true;
98    }
99
100    @Override
101    protected void cleanup() {
102        if (DBG) Log.d(TAG, "Stopping Bluetooth HidHostService");
103        if (mNativeAvailable) {
104            cleanupNative();
105            mNativeAvailable = false;
106        }
107
108        if (mInputDevices != null) {
109            for (BluetoothDevice device : mInputDevices.keySet()) {
110                int inputDeviceState = getConnectionState(device);
111                if (inputDeviceState != BluetoothProfile.STATE_DISCONNECTED) {
112                    broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
113                }
114            }
115            mInputDevices.clear();
116        }
117        // TODO(b/72948646): this should be moved to stop()
118        setHidHostService(null);
119    }
120
121    public static synchronized HidHostService getHidHostService() {
122        if (sHidHostService == null) {
123            Log.w(TAG, "getHidHostService(): service is null");
124            return null;
125        }
126        if (!sHidHostService.isAvailable()) {
127            Log.w(TAG, "getHidHostService(): service is not available ");
128            return null;
129        }
130        return sHidHostService;
131    }
132
133    private static synchronized void setHidHostService(HidHostService instance) {
134        if (DBG) {
135            Log.d(TAG, "setHidHostService(): set to: " + instance);
136        }
137        sHidHostService = instance;
138    }
139
140    private final Handler mHandler = new Handler() {
141
142        @Override
143        public void handleMessage(Message msg) {
144            if (DBG) Log.v(TAG, "handleMessage(): msg.what=" + msg.what);
145
146            switch (msg.what) {
147                case MESSAGE_CONNECT: {
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                    BluetoothDevice device = (BluetoothDevice) msg.obj;
159                    if (!disconnectHidNative(Utils.getByteAddress(device))) {
160                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
161                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
162                        break;
163                    }
164                }
165                break;
166                case MESSAGE_CONNECT_STATE_CHANGED: {
167                    BluetoothDevice device = getDevice((byte[]) msg.obj);
168                    int halState = msg.arg1;
169                    Integer prevStateInteger = mInputDevices.get(device);
170                    int prevState =
171                            (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED
172                                    : prevStateInteger;
173                    if (DBG) {
174                        Log.d(TAG, "MESSAGE_CONNECT_STATE_CHANGED newState:" + convertHalState(
175                                halState) + ", prevState:" + prevState);
176                    }
177                    if (halState == CONN_STATE_CONNECTED
178                            && prevState == BluetoothHidHost.STATE_DISCONNECTED
179                            && (!okToConnect(device))) {
180                        if (DBG) {
181                            Log.d(TAG, "Incoming HID connection rejected");
182                        }
183                        disconnectHidNative(Utils.getByteAddress(device));
184                    } else {
185                        broadcastConnectionState(device, convertHalState(halState));
186                    }
187                    if (halState == CONN_STATE_CONNECTED && (mTargetDevice != null
188                            && mTargetDevice.equals(device))) {
189                        mTargetDevice = null;
190                        // local device originated connection to hid device, move out
191                        // of quiet mode
192                        AdapterService adapterService = AdapterService.getAdapterService();
193                        adapterService.enable(false);
194                    }
195                }
196                break;
197                case MESSAGE_GET_PROTOCOL_MODE: {
198                    BluetoothDevice device = (BluetoothDevice) msg.obj;
199                    if (!getProtocolModeNative(Utils.getByteAddress(device))) {
200                        Log.e(TAG, "Error: get protocol mode native returns false");
201                    }
202                }
203                break;
204
205                case MESSAGE_ON_GET_PROTOCOL_MODE: {
206                    BluetoothDevice device = getDevice((byte[]) msg.obj);
207                    int protocolMode = msg.arg1;
208                    broadcastProtocolMode(device, protocolMode);
209                }
210                break;
211                case MESSAGE_VIRTUAL_UNPLUG: {
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                    BluetoothDevice device = (BluetoothDevice) msg.obj;
220                    byte protocolMode = (byte) msg.arg1;
221                    Log.d(TAG, "sending set protocol mode(" + protocolMode + ")");
222                    if (!setProtocolModeNative(Utils.getByteAddress(device), protocolMode)) {
223                        Log.e(TAG, "Error: set protocol mode native returns false");
224                    }
225                }
226                break;
227                case MESSAGE_GET_REPORT: {
228                    BluetoothDevice device = (BluetoothDevice) msg.obj;
229                    Bundle data = msg.getData();
230                    byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
231                    byte reportId = data.getByte(BluetoothHidHost.EXTRA_REPORT_ID);
232                    int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
233                    if (!getReportNative(Utils.getByteAddress(device), reportType, reportId,
234                            bufferSize)) {
235                        Log.e(TAG, "Error: get report native returns false");
236                    }
237                }
238                break;
239                case MESSAGE_ON_GET_REPORT: {
240                    BluetoothDevice device = getDevice((byte[]) msg.obj);
241                    Bundle data = msg.getData();
242                    byte[] report = data.getByteArray(BluetoothHidHost.EXTRA_REPORT);
243                    int bufferSize = data.getInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE);
244                    broadcastReport(device, report, bufferSize);
245                }
246                break;
247                case MESSAGE_ON_HANDSHAKE: {
248                    BluetoothDevice device = getDevice((byte[]) msg.obj);
249                    int status = msg.arg1;
250                    broadcastHandshake(device, status);
251                }
252                break;
253                case MESSAGE_SET_REPORT: {
254                    BluetoothDevice device = (BluetoothDevice) msg.obj;
255                    Bundle data = msg.getData();
256                    byte reportType = data.getByte(BluetoothHidHost.EXTRA_REPORT_TYPE);
257                    String report = data.getString(BluetoothHidHost.EXTRA_REPORT);
258                    if (!setReportNative(Utils.getByteAddress(device), reportType, report)) {
259                        Log.e(TAG, "Error: set report native returns false");
260                    }
261                }
262                break;
263                case MESSAGE_SEND_DATA: {
264                    BluetoothDevice device = (BluetoothDevice) msg.obj;
265                    Bundle data = msg.getData();
266                    String report = data.getString(BluetoothHidHost.EXTRA_REPORT);
267                    if (!sendDataNative(Utils.getByteAddress(device), report)) {
268                        Log.e(TAG, "Error: send data native returns false");
269                    }
270                }
271                break;
272                case MESSAGE_ON_VIRTUAL_UNPLUG: {
273                    BluetoothDevice device = getDevice((byte[]) msg.obj);
274                    int status = msg.arg1;
275                    broadcastVirtualUnplugStatus(device, status);
276                }
277                break;
278                case MESSAGE_GET_IDLE_TIME: {
279                    BluetoothDevice device = (BluetoothDevice) msg.obj;
280                    if (!getIdleTimeNative(Utils.getByteAddress(device))) {
281                        Log.e(TAG, "Error: get idle time native returns false");
282                    }
283                }
284                break;
285                case MESSAGE_ON_GET_IDLE_TIME: {
286                    BluetoothDevice device = getDevice((byte[]) msg.obj);
287                    int idleTime = msg.arg1;
288                    broadcastIdleTime(device, idleTime);
289                }
290                break;
291                case MESSAGE_SET_IDLE_TIME: {
292                    BluetoothDevice device = (BluetoothDevice) msg.obj;
293                    Bundle data = msg.getData();
294                    byte idleTime = data.getByte(BluetoothHidHost.EXTRA_IDLE_TIME);
295                    if (!setIdleTimeNative(Utils.getByteAddress(device), idleTime)) {
296                        Log.e(TAG, "Error: get idle time native returns false");
297                    }
298                }
299                break;
300            }
301        }
302    };
303
304    /**
305     * Handlers for incoming service calls
306     */
307    private static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub
308            implements IProfileServiceBinder {
309        private HidHostService mService;
310
311        BluetoothHidHostBinder(HidHostService svc) {
312            mService = svc;
313        }
314
315        @Override
316        public void cleanup() {
317            mService = null;
318        }
319
320        private HidHostService getService() {
321            if (!Utils.checkCaller()) {
322                Log.w(TAG, "InputDevice call not allowed for non-active user");
323                return null;
324            }
325
326            if (mService != null && mService.isAvailable()) {
327                return mService;
328            }
329            Log.w(TAG, "Service is null");
330            return null;
331        }
332
333        @Override
334        public boolean connect(BluetoothDevice device) {
335            HidHostService service = getService();
336            if (service == null) {
337                return false;
338            }
339            return service.connect(device);
340        }
341
342        @Override
343        public boolean disconnect(BluetoothDevice device) {
344            HidHostService service = getService();
345            if (service == null) {
346                return false;
347            }
348            return service.disconnect(device);
349        }
350
351        @Override
352        public int getConnectionState(BluetoothDevice device) {
353            HidHostService service = getService();
354            if (service == null) {
355                return BluetoothHidHost.STATE_DISCONNECTED;
356            }
357            return service.getConnectionState(device);
358        }
359
360        @Override
361        public List<BluetoothDevice> getConnectedDevices() {
362            return getDevicesMatchingConnectionStates(new int[]{BluetoothProfile.STATE_CONNECTED});
363        }
364
365        @Override
366        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
367            HidHostService service = getService();
368            if (service == null) {
369                return new ArrayList<BluetoothDevice>(0);
370            }
371            return service.getDevicesMatchingConnectionStates(states);
372        }
373
374        @Override
375        public boolean setPriority(BluetoothDevice device, int priority) {
376            HidHostService service = getService();
377            if (service == null) {
378                return false;
379            }
380            return service.setPriority(device, priority);
381        }
382
383        @Override
384        public int getPriority(BluetoothDevice device) {
385            HidHostService service = getService();
386            if (service == null) {
387                return BluetoothProfile.PRIORITY_UNDEFINED;
388            }
389            return service.getPriority(device);
390        }
391
392        /* The following APIs regarding test app for compliance */
393        @Override
394        public boolean getProtocolMode(BluetoothDevice device) {
395            HidHostService service = getService();
396            if (service == null) {
397                return false;
398            }
399            return service.getProtocolMode(device);
400        }
401
402        @Override
403        public boolean virtualUnplug(BluetoothDevice device) {
404            HidHostService service = getService();
405            if (service == null) {
406                return false;
407            }
408            return service.virtualUnplug(device);
409        }
410
411        @Override
412        public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
413            HidHostService service = getService();
414            if (service == null) {
415                return false;
416            }
417            return service.setProtocolMode(device, protocolMode);
418        }
419
420        @Override
421        public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
422                int bufferSize) {
423            HidHostService service = getService();
424            if (service == null) {
425                return false;
426            }
427            return service.getReport(device, reportType, reportId, bufferSize);
428        }
429
430        @Override
431        public boolean setReport(BluetoothDevice device, byte reportType, String report) {
432            HidHostService service = getService();
433            if (service == null) {
434                return false;
435            }
436            return service.setReport(device, reportType, report);
437        }
438
439        @Override
440        public boolean sendData(BluetoothDevice device, String report) {
441            HidHostService service = getService();
442            if (service == null) {
443                return false;
444            }
445            return service.sendData(device, report);
446        }
447
448        @Override
449        public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
450            HidHostService service = getService();
451            if (service == null) {
452                return false;
453            }
454            return service.setIdleTime(device, idleTime);
455        }
456
457        @Override
458        public boolean getIdleTime(BluetoothDevice device) {
459            HidHostService service = getService();
460            if (service == null) {
461                return false;
462            }
463            return service.getIdleTime(device);
464        }
465    }
466
467    ;
468
469    //APIs
470    boolean connect(BluetoothDevice device) {
471        if (DBG) Log.d(TAG, "connect: " + device.getAddress());
472        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
473        if (getConnectionState(device) != BluetoothHidHost.STATE_DISCONNECTED) {
474            Log.e(TAG, "Hid Device not disconnected: " + device);
475            return false;
476        }
477        if (getPriority(device) == BluetoothHidHost.PRIORITY_OFF) {
478            Log.e(TAG, "Hid Device PRIORITY_OFF: " + device);
479            return false;
480        }
481
482        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT, device);
483        mHandler.sendMessage(msg);
484        return true;
485    }
486
487    boolean disconnect(BluetoothDevice device) {
488        if (DBG) Log.d(TAG, "disconnect: " + device.getAddress());
489        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
490        Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT, device);
491        mHandler.sendMessage(msg);
492        return true;
493    }
494
495    int getConnectionState(BluetoothDevice device) {
496        if (DBG) Log.d(TAG, "getConnectionState: " + device.getAddress());
497        if (mInputDevices.get(device) == null) {
498            return BluetoothHidHost.STATE_DISCONNECTED;
499        }
500        return mInputDevices.get(device);
501    }
502
503    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
504        if (DBG) Log.d(TAG, "getDevicesMatchingConnectionStates()");
505        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
506        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
507
508        for (BluetoothDevice device : mInputDevices.keySet()) {
509            int inputDeviceState = getConnectionState(device);
510            for (int state : states) {
511                if (state == inputDeviceState) {
512                    inputDevices.add(device);
513                    break;
514                }
515            }
516        }
517        return inputDevices;
518    }
519
520    public boolean setPriority(BluetoothDevice device, int priority) {
521        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
522        if (DBG) {
523            Log.d(TAG, "setPriority: " + device.getAddress());
524        }
525        Settings.Global.putInt(getContentResolver(),
526                Settings.Global.getBluetoothHidHostPriorityKey(device.getAddress()), priority);
527        if (DBG) {
528            Log.d(TAG, "Saved priority " + device + " = " + priority);
529        }
530        return true;
531    }
532
533    public int getPriority(BluetoothDevice device) {
534        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
535        if (DBG) {
536            Log.d(TAG, "getPriority: " + device.getAddress());
537        }
538        int priority = Settings.Global.getInt(getContentResolver(),
539                Settings.Global.getBluetoothHidHostPriorityKey(device.getAddress()),
540                BluetoothProfile.PRIORITY_UNDEFINED);
541        return priority;
542    }
543
544    /* The following APIs regarding test app for compliance */
545    boolean getProtocolMode(BluetoothDevice device) {
546        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
547        if (DBG) {
548            Log.d(TAG, "getProtocolMode: " + device.getAddress());
549        }
550        int state = this.getConnectionState(device);
551        if (state != BluetoothHidHost.STATE_CONNECTED) {
552            return false;
553        }
554        Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE, device);
555        mHandler.sendMessage(msg);
556        return true;
557        /* String objectPath = getObjectPathFromAddress(device.getAddress());
558            return getProtocolModeInputDeviceNative(objectPath);*/
559    }
560
561    boolean virtualUnplug(BluetoothDevice device) {
562        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
563        if (DBG) {
564            Log.d(TAG, "virtualUnplug: " + device.getAddress());
565        }
566        int state = this.getConnectionState(device);
567        if (state != BluetoothHidHost.STATE_CONNECTED) {
568            return false;
569        }
570        Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG, device);
571        mHandler.sendMessage(msg);
572        return true;
573    }
574
575    boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
576        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
577        if (DBG) {
578            Log.d(TAG, "setProtocolMode: " + device.getAddress());
579        }
580        int state = this.getConnectionState(device);
581        if (state != BluetoothHidHost.STATE_CONNECTED) {
582            return false;
583        }
584        Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
585        msg.obj = device;
586        msg.arg1 = protocolMode;
587        mHandler.sendMessage(msg);
588        return true;
589    }
590
591    boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
592        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
593        if (DBG) {
594            Log.d(TAG, "getReport: " + device.getAddress());
595        }
596        int state = this.getConnectionState(device);
597        if (state != BluetoothHidHost.STATE_CONNECTED) {
598            return false;
599        }
600        Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
601        msg.obj = device;
602        Bundle data = new Bundle();
603        data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType);
604        data.putByte(BluetoothHidHost.EXTRA_REPORT_ID, reportId);
605        data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
606        msg.setData(data);
607        mHandler.sendMessage(msg);
608        return true;
609    }
610
611    boolean setReport(BluetoothDevice device, byte reportType, String report) {
612        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
613        if (DBG) {
614            Log.d(TAG, "setReport: " + device.getAddress());
615        }
616        int state = this.getConnectionState(device);
617        if (state != BluetoothHidHost.STATE_CONNECTED) {
618            return false;
619        }
620        Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
621        msg.obj = device;
622        Bundle data = new Bundle();
623        data.putByte(BluetoothHidHost.EXTRA_REPORT_TYPE, reportType);
624        data.putString(BluetoothHidHost.EXTRA_REPORT, report);
625        msg.setData(data);
626        mHandler.sendMessage(msg);
627        return true;
628
629    }
630
631    boolean sendData(BluetoothDevice device, String report) {
632        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
633        if (DBG) {
634            Log.d(TAG, "sendData: " + device.getAddress());
635        }
636        int state = this.getConnectionState(device);
637        if (state != BluetoothHidHost.STATE_CONNECTED) {
638            return false;
639        }
640
641        return sendDataNative(Utils.getByteAddress(device), report);
642    }
643
644    boolean getIdleTime(BluetoothDevice device) {
645        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
646        if (DBG) Log.d(TAG, "getIdleTime: " + device.getAddress());
647        int state = this.getConnectionState(device);
648        if (state != BluetoothHidHost.STATE_CONNECTED) {
649            return false;
650        }
651        Message msg = mHandler.obtainMessage(MESSAGE_GET_IDLE_TIME, device);
652        mHandler.sendMessage(msg);
653        return true;
654    }
655
656    boolean setIdleTime(BluetoothDevice device, byte idleTime) {
657        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
658        if (DBG) Log.d(TAG, "setIdleTime: " + device.getAddress());
659        int state = this.getConnectionState(device);
660        if (state != BluetoothHidHost.STATE_CONNECTED) {
661            return false;
662        }
663        Message msg = mHandler.obtainMessage(MESSAGE_SET_IDLE_TIME);
664        msg.obj = device;
665        Bundle data = new Bundle();
666        data.putByte(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime);
667        msg.setData(data);
668        mHandler.sendMessage(msg);
669        return true;
670    }
671
672    private void onGetProtocolMode(byte[] address, int mode) {
673        if (DBG) Log.d(TAG, "onGetProtocolMode()");
674        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
675        msg.obj = address;
676        msg.arg1 = mode;
677        mHandler.sendMessage(msg);
678    }
679
680    private void onGetIdleTime(byte[] address, int idleTime) {
681        if (DBG) Log.d(TAG, "onGetIdleTime()");
682        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_IDLE_TIME);
683        msg.obj = address;
684        msg.arg1 = idleTime;
685        mHandler.sendMessage(msg);
686    }
687
688    private void onGetReport(byte[] address, byte[] report, int rptSize) {
689        if (DBG) Log.d(TAG, "onGetReport()");
690        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_REPORT);
691        msg.obj = address;
692        Bundle data = new Bundle();
693        data.putByteArray(BluetoothHidHost.EXTRA_REPORT, report);
694        data.putInt(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize);
695        msg.setData(data);
696        mHandler.sendMessage(msg);
697    }
698
699    private void onHandshake(byte[] address, int status) {
700        if (DBG) Log.d(TAG, "onHandshake: status=" + status);
701        Message msg = mHandler.obtainMessage(MESSAGE_ON_HANDSHAKE);
702        msg.obj = address;
703        msg.arg1 = status;
704        mHandler.sendMessage(msg);
705    }
706
707    private void onVirtualUnplug(byte[] address, int status) {
708        if (DBG) Log.d(TAG, "onVirtualUnplug: status=" + status);
709        Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
710        msg.obj = address;
711        msg.arg1 = status;
712        mHandler.sendMessage(msg);
713    }
714
715    private void onConnectStateChanged(byte[] address, int state) {
716        if (DBG) Log.d(TAG, "onConnectStateChanged: state=" + state);
717        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
718        msg.obj = address;
719        msg.arg1 = state;
720        mHandler.sendMessage(msg);
721    }
722
723    // This method does not check for error conditon (newState == prevState)
724    private void broadcastConnectionState(BluetoothDevice device, int newState) {
725        Integer prevStateInteger = mInputDevices.get(device);
726        int prevState = (prevStateInteger == null) ? BluetoothHidHost.STATE_DISCONNECTED
727                : prevStateInteger;
728        if (prevState == newState) {
729            Log.w(TAG, "no state change: " + newState);
730            return;
731        }
732        if (newState == BluetoothProfile.STATE_CONNECTED) {
733            MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.HID_HOST);
734        }
735        mInputDevices.put(device, newState);
736
737        /* Notifying the connection state change of the profile before sending the intent for
738           connection state change, as it was causing a race condition, with the UI not being
739           updated with the correct connection state. */
740        Log.d(TAG, "Connection state " + device + ": " + prevState + "->" + newState);
741        Intent intent = new Intent(BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED);
742        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
743        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
744        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
745        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
746        sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
747    }
748
749    private void broadcastHandshake(BluetoothDevice device, int status) {
750        Intent intent = new Intent(BluetoothHidHost.ACTION_HANDSHAKE);
751        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
752        intent.putExtra(BluetoothHidHost.EXTRA_STATUS, status);
753        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
754        sendBroadcast(intent, BLUETOOTH_PERM);
755    }
756
757    private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
758        Intent intent = new Intent(BluetoothHidHost.ACTION_PROTOCOL_MODE_CHANGED);
759        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
760        intent.putExtra(BluetoothHidHost.EXTRA_PROTOCOL_MODE, protocolMode);
761        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
762        sendBroadcast(intent, BLUETOOTH_PERM);
763        if (DBG) {
764            Log.d(TAG, "Protocol Mode (" + device + "): " + protocolMode);
765        }
766    }
767
768    private void broadcastReport(BluetoothDevice device, byte[] report, int rptSize) {
769        Intent intent = new Intent(BluetoothHidHost.ACTION_REPORT);
770        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
771        intent.putExtra(BluetoothHidHost.EXTRA_REPORT, report);
772        intent.putExtra(BluetoothHidHost.EXTRA_REPORT_BUFFER_SIZE, rptSize);
773        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
774        sendBroadcast(intent, BLUETOOTH_PERM);
775    }
776
777    private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
778        Intent intent = new Intent(BluetoothHidHost.ACTION_VIRTUAL_UNPLUG_STATUS);
779        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
780        intent.putExtra(BluetoothHidHost.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
781        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
782        sendBroadcast(intent, BLUETOOTH_PERM);
783    }
784
785    private void broadcastIdleTime(BluetoothDevice device, int idleTime) {
786        Intent intent = new Intent(BluetoothHidHost.ACTION_IDLE_TIME_CHANGED);
787        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
788        intent.putExtra(BluetoothHidHost.EXTRA_IDLE_TIME, idleTime);
789        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
790        sendBroadcast(intent, BLUETOOTH_PERM);
791        if (DBG) {
792            Log.d(TAG, "Idle time (" + device + "): " + idleTime);
793        }
794    }
795
796    private boolean okToConnect(BluetoothDevice device) {
797        AdapterService adapterService = AdapterService.getAdapterService();
798        //check if it is inbound connection in Quiet mode, priority and Bond status
799        //to decide if its ok to allow this connection
800        if ((adapterService == null) || ((adapterService.isQuietModeEnabled()) && (mTargetDevice
801                == null)) || (BluetoothProfile.PRIORITY_OFF == getPriority(device)) || (
802                device.getBondState() == BluetoothDevice.BOND_NONE)) {
803            return false;
804        }
805
806        return true;
807    }
808
809    private static int convertHalState(int halState) {
810        switch (halState) {
811            case CONN_STATE_CONNECTED:
812                return BluetoothProfile.STATE_CONNECTED;
813            case CONN_STATE_CONNECTING:
814                return BluetoothProfile.STATE_CONNECTING;
815            case CONN_STATE_DISCONNECTED:
816                return BluetoothProfile.STATE_DISCONNECTED;
817            case CONN_STATE_DISCONNECTING:
818                return BluetoothProfile.STATE_DISCONNECTING;
819            default:
820                Log.e(TAG, "bad hid connection state: " + halState);
821                return BluetoothProfile.STATE_DISCONNECTED;
822        }
823    }
824
825    @Override
826    public void dump(StringBuilder sb) {
827        super.dump(sb);
828        println(sb, "mTargetDevice: " + mTargetDevice);
829        println(sb, "mInputDevices:");
830        for (BluetoothDevice device : mInputDevices.keySet()) {
831            println(sb, "  " + device + " : " + mInputDevices.get(device));
832        }
833    }
834
835    // Constants matching Hal header file bt_hh.h
836    // bthh_connection_state_t
837    private static final int CONN_STATE_CONNECTED = 0;
838    private static final int CONN_STATE_CONNECTING = 1;
839    private static final int CONN_STATE_DISCONNECTED = 2;
840    private static final int CONN_STATE_DISCONNECTING = 3;
841
842    private static native void classInitNative();
843
844    private native void initializeNative();
845
846    private native void cleanupNative();
847
848    private native boolean connectHidNative(byte[] btAddress);
849
850    private native boolean disconnectHidNative(byte[] btAddress);
851
852    private native boolean getProtocolModeNative(byte[] btAddress);
853
854    private native boolean virtualUnPlugNative(byte[] btAddress);
855
856    private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
857
858    private native boolean getReportNative(byte[] btAddress, byte reportType, byte reportId,
859            int bufferSize);
860
861    private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
862
863    private native boolean sendDataNative(byte[] btAddress, String report);
864
865    private native boolean setIdleTimeNative(byte[] btAddress, byte idleTime);
866
867    private native boolean getIdleTimeNative(byte[] btAddress);
868}
869