HidService.java revision b5cc776c9353a203cdde97e62b25f05d9633d14c
1/*
2 * Copyright (C) 2012 Google Inc.
3 */
4
5package com.android.bluetooth.hid;
6
7import android.app.Service;
8import android.bluetooth.BluetoothAdapter;
9import android.bluetooth.BluetoothDevice;
10import android.bluetooth.BluetoothInputDevice;
11import android.bluetooth.BluetoothProfile;
12import android.bluetooth.IBluetooth;
13import android.bluetooth.IBluetoothInputDevice;
14import android.content.Intent;
15import android.os.Bundle;
16import android.os.IBinder;
17import android.os.Handler;
18import android.os.Message;
19import android.os.RemoteException;
20import android.os.ServiceManager;
21import android.provider.Settings;
22import android.util.Log;
23import java.util.ArrayList;
24import java.util.Collections;
25import java.util.HashMap;
26import java.util.List;
27import java.util.Map;
28import com.android.bluetooth.Utils;
29import android.content.pm.PackageManager;
30import com.android.bluetooth.btservice.AdapterService;
31import com.android.bluetooth.btservice.ProfileService;
32
33/**
34 * Provides Bluetooth Hid Host profile, as a service in
35 * the Bluetooth application.
36 * @hide
37 */
38public class HidService extends ProfileService {
39    private static final boolean DBG = true;
40    private static final String TAG = "HidService";
41    private Map<BluetoothDevice, Integer> mInputDevices;
42
43    private static final int MESSAGE_CONNECT = 1;
44    private static final int MESSAGE_DISCONNECT = 2;
45    private static final int MESSAGE_CONNECT_STATE_CHANGED = 3;
46    private static final int MESSAGE_GET_PROTOCOL_MODE = 4;
47    private static final int MESSAGE_VIRTUAL_UNPLUG = 5;
48    private static final int MESSAGE_ON_GET_PROTOCOL_MODE = 6;
49    private static final int MESSAGE_SET_PROTOCOL_MODE = 7;
50    private static final int MESSAGE_GET_REPORT = 8;
51    private static final int MESSAGE_ON_GET_REPORT = 9;
52    private static final int MESSAGE_SET_REPORT = 10;
53    private static final int MESSAGE_SEND_DATA = 11;
54    private static final int MESSAGE_ON_VIRTUAL_UNPLUG = 12;
55
56    static {
57        classInitNative();
58    }
59
60    public String getName() {
61        return TAG;
62    }
63
64    @Override
65    public IBinder onBind(Intent intent) {
66        log("onBind");
67        return mBinder;
68    }
69
70
71    protected boolean start() {
72        if(mInputDevices != null) {
73            mInputDevices.clear();
74            mInputDevices = null;
75        }
76        mInputDevices = Collections.synchronizedMap(new HashMap<BluetoothDevice, Integer>());
77        initializeNative();
78
79        return true;
80    }
81
82    protected boolean stop() {
83        if (DBG) log("Stopping Bluetooth HidService");
84        cleanupNative();
85
86        if(mInputDevices != null) {
87            mInputDevices.clear();
88            mInputDevices = null;
89        }
90
91        return true;
92    }
93
94
95
96    private final Handler mHandler = new Handler() {
97
98        @Override
99        public void handleMessage(Message msg) {
100            switch (msg.what) {
101                case MESSAGE_CONNECT:
102                {
103                    BluetoothDevice device = (BluetoothDevice) msg.obj;
104                    if (!connectHidNative(getByteAddress(device)) ) {
105                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
106                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
107                        break;
108                    }
109                }
110                    break;
111                case MESSAGE_DISCONNECT:
112                {
113                    BluetoothDevice device = (BluetoothDevice) msg.obj;
114                    if (!disconnectHidNative(getByteAddress(device)) ) {
115                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING);
116                        broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED);
117                        break;
118                    }
119                }
120                    break;
121                case MESSAGE_CONNECT_STATE_CHANGED:
122                {
123                    BluetoothDevice device = getDevice((byte[]) msg.obj);
124                    int halState = msg.arg1;
125                    broadcastConnectionState(device, convertHalState(halState));
126                }
127                    break;
128                case MESSAGE_GET_PROTOCOL_MODE:
129                {
130                    BluetoothDevice device = (BluetoothDevice) msg.obj;
131                    if(!getProtocolModeNative(getByteAddress(device)) ) {
132                        Log.e(TAG, "Error: get protocol mode native returns false");
133                    }
134                }
135                break;
136
137                case MESSAGE_ON_GET_PROTOCOL_MODE:
138                {
139                    BluetoothDevice device = getDevice((byte[]) msg.obj);
140                    int protocolMode = msg.arg1;
141                    broadcastProtocolMode(device, protocolMode);
142                }
143                break;
144                case MESSAGE_VIRTUAL_UNPLUG:
145                {
146                    BluetoothDevice device = (BluetoothDevice) msg.obj;
147                    if(!virtualUnPlugNative(getByteAddress(device))) {
148                        Log.e(TAG, "Error: virtual unplug native returns false");
149                    }
150                }
151                break;
152                case MESSAGE_SET_PROTOCOL_MODE:
153                {
154                    BluetoothDevice device = (BluetoothDevice) msg.obj;
155                    byte protocolMode = (byte) msg.arg1;
156                    log("sending set protocol mode(" + protocolMode + ")");
157                    if(!setProtocolModeNative(getByteAddress(device), protocolMode)) {
158                        Log.e(TAG, "Error: set protocol mode native returns false");
159                    }
160                }
161                break;
162                case MESSAGE_GET_REPORT:
163                {
164                    BluetoothDevice device = (BluetoothDevice) msg.obj;
165                    Bundle data = msg.getData();
166                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
167                    byte reportId = data.getByte(BluetoothInputDevice.EXTRA_REPORT_ID);
168                    int bufferSize = data.getInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE);
169                    if(!getReportNative(getByteAddress(device), reportType, reportId, bufferSize)) {
170                        Log.e(TAG, "Error: get report native returns false");
171                    }
172                }
173                break;
174                case MESSAGE_SET_REPORT:
175                {
176                    BluetoothDevice device = (BluetoothDevice) msg.obj;
177                    Bundle data = msg.getData();
178                    byte reportType = data.getByte(BluetoothInputDevice.EXTRA_REPORT_TYPE);
179                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
180                    if(!setReportNative(getByteAddress(device), reportType, report)) {
181                        Log.e(TAG, "Error: set report native returns false");
182                    }
183                }
184                break;
185                case MESSAGE_SEND_DATA:
186                {
187                    BluetoothDevice device = (BluetoothDevice) msg.obj;
188                    Bundle data = msg.getData();
189                    String report = data.getString(BluetoothInputDevice.EXTRA_REPORT);
190                    if(!sendDataNative(getByteAddress(device), report)) {
191                        Log.e(TAG, "Error: send data native returns false");
192                    }
193                }
194                break;
195                case MESSAGE_ON_VIRTUAL_UNPLUG:
196                {
197                    BluetoothDevice device = getDevice((byte[]) msg.obj);
198                    int status = msg.arg1;
199                    broadcastVirtualUnplugStatus(device, status);
200                }
201                break;
202            }
203        }
204    };
205
206    /**
207     * Handlers for incoming service calls
208     */
209    private final IBluetoothInputDevice.Stub mBinder = new IBluetoothInputDevice.Stub() {
210        public boolean connect(BluetoothDevice device) {
211            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
212            if (getConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED) {
213                Log.e(TAG, "Hid Device not disconnected: " + device);
214                return false;
215            }
216            if (getPriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
217                Log.e(TAG, "Hid Device PRIORITY_OFF: " + device);
218                return false;
219            }
220
221            Message msg = mHandler.obtainMessage(MESSAGE_CONNECT);
222            msg.obj = device;
223            mHandler.sendMessage(msg);
224            return true;
225        }
226
227        public boolean disconnect(BluetoothDevice device) {
228            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
229            Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECT);
230            msg.obj = device;
231            mHandler.sendMessage(msg);
232            return true;
233        }
234
235        public int getConnectionState(BluetoothDevice device) {
236            if (mInputDevices.get(device) == null) {
237                return BluetoothInputDevice.STATE_DISCONNECTED;
238            }
239            return mInputDevices.get(device);
240        }
241
242        public List<BluetoothDevice> getConnectedDevices() {
243            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
244            List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
245                    new int[] {BluetoothProfile.STATE_CONNECTED});
246            return devices;
247        }
248
249        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
250            enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
251            List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
252
253            for (BluetoothDevice device: mInputDevices.keySet()) {
254                int inputDeviceState = getConnectionState(device);
255                for (int state : states) {
256                    if (state == inputDeviceState) {
257                        inputDevices.add(device);
258                        break;
259                    }
260                }
261            }
262            return inputDevices;
263        }
264
265        public boolean setPriority(BluetoothDevice device, int priority) {
266            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
267                                           "Need BLUETOOTH_ADMIN permission");
268            Settings.Secure.putInt(getContentResolver(),
269                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
270                priority);
271            if (DBG) log("Saved priority " + device + " = " + priority);
272            return true;
273        }
274
275        public int getPriority(BluetoothDevice device) {
276            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
277                                           "Need BLUETOOTH_ADMIN permission");
278            int priority = Settings.Secure.getInt(getContentResolver(),
279                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
280                BluetoothProfile.PRIORITY_UNDEFINED);
281            return priority;
282        }
283        /* The following APIs regarding test app for compliance */
284        public boolean getProtocolMode(BluetoothDevice device) {
285            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
286                                           "Need BLUETOOTH_ADMIN permission");
287            int state = this.getConnectionState(device);
288            if (state != BluetoothInputDevice.STATE_CONNECTED) {
289                return false;
290            }
291            Message msg = mHandler.obtainMessage(MESSAGE_GET_PROTOCOL_MODE);
292            msg.obj = device;
293            mHandler.sendMessage(msg);
294            return true;
295            /* String objectPath = getObjectPathFromAddress(device.getAddress());
296                return getProtocolModeInputDeviceNative(objectPath);*/
297        }
298
299        public boolean virtualUnplug(BluetoothDevice device) {
300            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
301                                           "Need BLUETOOTH_ADMIN permission");
302            int state = this.getConnectionState(device);
303            if (state != BluetoothInputDevice.STATE_CONNECTED) {
304                return false;
305            }
306            Message msg = mHandler.obtainMessage(MESSAGE_VIRTUAL_UNPLUG);
307            msg.obj = device;
308            mHandler.sendMessage(msg);
309
310            return true;
311        }
312
313        public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
314            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
315                                           "Need BLUETOOTH_ADMIN permission");
316            int state = this.getConnectionState(device);
317            if (state != BluetoothInputDevice.STATE_CONNECTED) {
318                return false;
319            }
320            Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL_MODE);
321            msg.obj = device;
322            msg.arg1 = protocolMode;
323            mHandler.sendMessage(msg);
324            return true ;
325        }
326
327        public boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) {
328            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
329                                           "Need BLUETOOTH_ADMIN permission");
330            int state = this.getConnectionState(device);
331            if (state != BluetoothInputDevice.STATE_CONNECTED) {
332                return false;
333            }
334            Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
335            msg.obj = device;
336            Bundle data = new Bundle();
337            data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
338            data.putByte(BluetoothInputDevice.EXTRA_REPORT_ID, reportId);
339            data.putInt(BluetoothInputDevice.EXTRA_REPORT_BUFFER_SIZE, bufferSize);
340            msg.setData(data);
341            mHandler.sendMessage(msg);
342            return true ;
343        }
344
345        public boolean setReport(BluetoothDevice device, byte reportType, String report) {
346            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
347                                                       "Need BLUETOOTH_ADMIN permission");
348            int state = this.getConnectionState(device);
349            if (state != BluetoothInputDevice.STATE_CONNECTED) {
350                return false;
351            }
352            Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
353            msg.obj = device;
354            Bundle data = new Bundle();
355            data.putByte(BluetoothInputDevice.EXTRA_REPORT_TYPE, reportType);
356            data.putString(BluetoothInputDevice.EXTRA_REPORT, report);
357            msg.setData(data);
358            mHandler.sendMessage(msg);
359            return true ;
360
361        }
362
363        public boolean sendData(BluetoothDevice device, String report) {
364            enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
365                                                       "Need BLUETOOTH_ADMIN permission");
366            int state = this.getConnectionState(device);
367            if (state != BluetoothInputDevice.STATE_CONNECTED) {
368                return false;
369            }
370
371            return sendDataNative(getByteAddress(device), report);
372            /*Message msg = mHandler.obtainMessage(MESSAGE_SEND_DATA);
373            msg.obj = device;
374            Bundle data = new Bundle();
375            data.putString(BluetoothInputDevice.EXTRA_REPORT, report);
376            msg.setData(data);
377            mHandler.sendMessage(msg);
378            return true ;*/
379        }
380    };
381
382    private void onGetProtocolMode(byte[] address, int mode) {
383        Message msg = mHandler.obtainMessage(MESSAGE_ON_GET_PROTOCOL_MODE);
384        msg.obj = address;
385        msg.arg1 = mode;
386        mHandler.sendMessage(msg);
387    }
388
389    private void onVirtualUnplug(byte[] address, int status) {
390        Message msg = mHandler.obtainMessage(MESSAGE_ON_VIRTUAL_UNPLUG);
391        msg.obj = address;
392        msg.arg1 = status;
393        mHandler.sendMessage(msg);
394    }
395
396    private void onConnectStateChanged(byte[] address, int state) {
397        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
398        msg.obj = address;
399        msg.arg1 = state;
400        mHandler.sendMessage(msg);
401    }
402
403    // This method does not check for error conditon (newState == prevState)
404    private void broadcastConnectionState(BluetoothDevice device, int newState) {
405        Integer prevStateInteger = mInputDevices.get(device);
406        int prevState = (prevStateInteger == null) ? BluetoothInputDevice.STATE_DISCONNECTED :
407                                                     prevStateInteger;
408        if (prevState == newState) {
409            Log.w(TAG, "no state change: " + newState);
410            return;
411        }
412        mInputDevices.put(device, newState);
413
414        Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
415        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
416        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
417        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
418        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
419        sendBroadcast(intent, BLUETOOTH_PERM);
420        if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState);
421        AdapterService svc = AdapterService.getAdapterService();
422        if (svc != null) {
423            svc.onProfileConnectionStateChanged(device, BluetoothProfile.INPUT_DEVICE, newState, prevState);
424        }
425    }
426
427    private void broadcastProtocolMode(BluetoothDevice device, int protocolMode) {
428        Intent intent = new Intent(BluetoothInputDevice.ACTION_PROTOCOL_MODE_CHANGED);
429        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
430        intent.putExtra(BluetoothInputDevice.EXTRA_PROTOCOL_MODE, protocolMode);
431        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
432        sendBroadcast(intent, BLUETOOTH_PERM);
433        if (DBG) log("Protocol Mode (" + device + "): " + protocolMode);
434    }
435
436    private void broadcastVirtualUnplugStatus(BluetoothDevice device, int status) {
437        Intent intent = new Intent(BluetoothInputDevice.ACTION_VIRTUAL_UNPLUG_STATUS);
438        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
439        intent.putExtra(BluetoothInputDevice.EXTRA_VIRTUAL_UNPLUG_STATUS, status);
440        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
441        sendBroadcast(intent, BLUETOOTH_PERM);
442    }
443
444    private BluetoothDevice getDevice(byte[] address) {
445        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
446    }
447
448    private byte[] getByteAddress(BluetoothDevice device) {
449        return Utils.getBytesFromAddress(device.getAddress());
450    }
451
452    private int convertHalState(int halState) {
453        switch (halState) {
454            case CONN_STATE_CONNECTED:
455                return BluetoothProfile.STATE_CONNECTED;
456            case CONN_STATE_CONNECTING:
457                return BluetoothProfile.STATE_CONNECTING;
458            case CONN_STATE_DISCONNECTED:
459                return BluetoothProfile.STATE_DISCONNECTED;
460            case CONN_STATE_DISCONNECTING:
461                return BluetoothProfile.STATE_DISCONNECTING;
462            default:
463                Log.e(TAG, "bad hid connection state: " + halState);
464                return BluetoothProfile.STATE_DISCONNECTED;
465        }
466    }
467
468
469    // Constants matching Hal header file bt_hh.h
470    // bthh_connection_state_t
471    private final static int CONN_STATE_CONNECTED = 0;
472    private final static int CONN_STATE_CONNECTING = 1;
473    private final static int CONN_STATE_DISCONNECTED = 2;
474    private final static int CONN_STATE_DISCONNECTING = 3;
475
476    private native static void classInitNative();
477    private native void initializeNative();
478    private native void cleanupNative();
479    private native boolean connectHidNative(byte[] btAddress);
480    private native boolean disconnectHidNative(byte[] btAddress);
481    private native boolean getProtocolModeNative(byte[] btAddress);
482    private native boolean virtualUnPlugNative(byte[] btAddress);
483    private native boolean setProtocolModeNative(byte[] btAddress, byte protocolMode);
484    private native boolean getReportNative(byte[]btAddress, byte reportType, byte reportId, int bufferSize);
485    private native boolean setReportNative(byte[] btAddress, byte reportType, String report);
486    private native boolean sendDataNative(byte[] btAddress, String report);
487}
488