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