HidDeviceService.java revision 4a6fdddc84107ab900afb6d8b09b68129ed4133e
1/*
2 * Copyright (C) 2016 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.app.ActivityManager;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothHidDevice;
22import android.bluetooth.BluetoothHidDeviceAppQosSettings;
23import android.bluetooth.BluetoothHidDeviceAppSdpSettings;
24import android.bluetooth.BluetoothProfile;
25import android.bluetooth.IBluetoothHidDevice;
26import android.bluetooth.IBluetoothHidDeviceCallback;
27import android.content.Context;
28import android.content.Intent;
29import android.os.Binder;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Message;
33import android.os.Process;
34import android.os.RemoteException;
35import android.util.Log;
36
37import com.android.bluetooth.Utils;
38import com.android.bluetooth.btservice.ProfileService;
39import com.android.internal.annotations.VisibleForTesting;
40
41import java.nio.ByteBuffer;
42import java.util.ArrayList;
43import java.util.Arrays;
44import java.util.List;
45import java.util.NoSuchElementException;
46
47/** @hide */
48public class HidDeviceService extends ProfileService {
49    private static final boolean DBG = false;
50    private static final String TAG = HidDeviceService.class.getSimpleName();
51
52    private static final int MESSAGE_APPLICATION_STATE_CHANGED = 1;
53    private static final int MESSAGE_CONNECT_STATE_CHANGED = 2;
54    private static final int MESSAGE_GET_REPORT = 3;
55    private static final int MESSAGE_SET_REPORT = 4;
56    private static final int MESSAGE_SET_PROTOCOL = 5;
57    private static final int MESSAGE_INTR_DATA = 6;
58    private static final int MESSAGE_VC_UNPLUG = 7;
59    private static final int MESSAGE_IMPORTANCE_CHANGE = 8;
60
61    private static final int FOREGROUND_IMPORTANCE_CUTOFF =
62            ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
63
64    private static HidDeviceService sHidDeviceService;
65
66    private HidDeviceNativeInterface mHidDeviceNativeInterface;
67
68    private boolean mNativeAvailable = false;
69    private BluetoothDevice mHidDevice;
70    private int mHidDeviceState = BluetoothHidDevice.STATE_DISCONNECTED;
71    private int mUserUid = 0;
72    private IBluetoothHidDeviceCallback mCallback;
73    private BluetoothHidDeviceDeathRecipient mDeathRcpt;
74    private ActivityManager mActivityManager;
75
76    private HidDeviceServiceHandler mHandler;
77
78    private class HidDeviceServiceHandler extends Handler {
79        @Override
80        public void handleMessage(Message msg) {
81            if (DBG) {
82                Log.d(TAG, "handleMessage(): msg.what=" + msg.what);
83            }
84
85            switch (msg.what) {
86                case MESSAGE_APPLICATION_STATE_CHANGED: {
87                    BluetoothDevice device = msg.obj != null ? (BluetoothDevice) msg.obj : null;
88                    boolean success = (msg.arg1 != 0);
89
90                    if (success) {
91                        Log.d(TAG, "App registered, set device to: " + device);
92                        mHidDevice = device;
93                    } else {
94                        mHidDevice = null;
95                    }
96
97                    try {
98                        if (mCallback != null) {
99                            mCallback.onAppStatusChanged(device, success);
100                        } else {
101                            break;
102                        }
103                    } catch (RemoteException e) {
104                        Log.e(TAG, "e=" + e.toString());
105                        e.printStackTrace();
106                    }
107
108                    if (success) {
109                        mDeathRcpt = new BluetoothHidDeviceDeathRecipient(HidDeviceService.this);
110                        if (mCallback != null) {
111                            IBinder binder = mCallback.asBinder();
112                            try {
113                                binder.linkToDeath(mDeathRcpt, 0);
114                                Log.i(TAG, "IBinder.linkToDeath() ok");
115                            } catch (RemoteException e) {
116                                e.printStackTrace();
117                            }
118                        }
119                    } else if (mDeathRcpt != null) {
120                        if (mCallback != null) {
121                            IBinder binder = mCallback.asBinder();
122                            try {
123                                binder.unlinkToDeath(mDeathRcpt, 0);
124                                Log.i(TAG, "IBinder.unlinkToDeath() ok");
125                            } catch (NoSuchElementException e) {
126                                e.printStackTrace();
127                            }
128                            mDeathRcpt.cleanup();
129                            mDeathRcpt = null;
130                        }
131                    }
132
133                    if (!success) {
134                        mCallback = null;
135                    }
136
137                    break;
138                }
139
140                case MESSAGE_CONNECT_STATE_CHANGED: {
141                    BluetoothDevice device = (BluetoothDevice) msg.obj;
142                    int halState = msg.arg1;
143                    int state = convertHalState(halState);
144
145                    if (state != BluetoothHidDevice.STATE_DISCONNECTED) {
146                        mHidDevice = device;
147                    }
148
149                    setAndBroadcastConnectionState(device, state);
150
151                    try {
152                        if (mCallback != null) {
153                            mCallback.onConnectionStateChanged(device, state);
154                        }
155                    } catch (RemoteException e) {
156                        e.printStackTrace();
157                    }
158                    break;
159                }
160
161                case MESSAGE_GET_REPORT:
162                    byte type = (byte) msg.arg1;
163                    byte id = (byte) msg.arg2;
164                    int bufferSize = msg.obj == null ? 0 : ((Integer) msg.obj).intValue();
165
166                    try {
167                        if (mCallback != null) {
168                            mCallback.onGetReport(mHidDevice, type, id, bufferSize);
169                        }
170                    } catch (RemoteException e) {
171                        e.printStackTrace();
172                    }
173                    break;
174
175                case MESSAGE_SET_REPORT: {
176                    byte reportType = (byte) msg.arg1;
177                    byte reportId = (byte) msg.arg2;
178                    byte[] data = ((ByteBuffer) msg.obj).array();
179
180                    try {
181                        if (mCallback != null) {
182                            mCallback.onSetReport(mHidDevice, reportType, reportId, data);
183                        }
184                    } catch (RemoteException e) {
185                        e.printStackTrace();
186                    }
187                    break;
188                }
189
190                case MESSAGE_SET_PROTOCOL:
191                    byte protocol = (byte) msg.arg1;
192
193                    try {
194                        if (mCallback != null) {
195                            mCallback.onSetProtocol(mHidDevice, protocol);
196                        }
197                    } catch (RemoteException e) {
198                        e.printStackTrace();
199                    }
200                    break;
201
202                case MESSAGE_INTR_DATA:
203                    byte reportId = (byte) msg.arg1;
204                    byte[] data = ((ByteBuffer) msg.obj).array();
205
206                    try {
207                        if (mCallback != null) {
208                            mCallback.onInterruptData(mHidDevice, reportId, data);
209                        }
210                    } catch (RemoteException e) {
211                        e.printStackTrace();
212                    }
213                    break;
214
215                case MESSAGE_VC_UNPLUG:
216                    try {
217                        if (mCallback != null) {
218                            mCallback.onVirtualCableUnplug(mHidDevice);
219                        }
220                    } catch (RemoteException e) {
221                        e.printStackTrace();
222                    }
223                    mHidDevice = null;
224                    break;
225
226                case MESSAGE_IMPORTANCE_CHANGE:
227                    int importance = msg.arg1;
228                    int uid = msg.arg2;
229                    if (importance > FOREGROUND_IMPORTANCE_CUTOFF
230                            && uid >= Process.FIRST_APPLICATION_UID) {
231                        unregisterAppUid(uid);
232                    }
233                    break;
234            }
235        }
236    }
237
238    private static class BluetoothHidDeviceDeathRecipient implements IBinder.DeathRecipient {
239        private HidDeviceService mService;
240
241        BluetoothHidDeviceDeathRecipient(HidDeviceService service) {
242            mService = service;
243        }
244
245        @Override
246        public void binderDied() {
247            Log.w(TAG, "Binder died, need to unregister app :(");
248            mService.unregisterApp();
249        }
250
251        public void cleanup() {
252            mService = null;
253        }
254    }
255
256    private ActivityManager.OnUidImportanceListener mUidImportanceListener =
257            new ActivityManager.OnUidImportanceListener() {
258                @Override
259                public void onUidImportance(final int uid, final int importance) {
260                    Message message = mHandler.obtainMessage(MESSAGE_IMPORTANCE_CHANGE);
261                    message.arg1 = importance;
262                    message.arg2 = uid;
263                    mHandler.sendMessage(message);
264                }
265            };
266
267    @VisibleForTesting
268    static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub
269            implements IProfileServiceBinder {
270
271        private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName();
272
273        private HidDeviceService mService;
274
275        BluetoothHidDeviceBinder(HidDeviceService service) {
276            mService = service;
277        }
278
279        @VisibleForTesting
280        HidDeviceService getServiceForTesting() {
281            if (mService != null && mService.isAvailable()) {
282                return mService;
283            }
284            return null;
285        }
286
287        @Override
288        public void cleanup() {
289            mService = null;
290        }
291
292        private HidDeviceService getService() {
293            if (!Utils.checkCaller()) {
294                Log.w(TAG, "HidDevice call not allowed for non-active user");
295                return null;
296            }
297
298            if (mService != null && mService.isAvailable()) {
299                return mService;
300            }
301
302            return null;
303        }
304
305        @Override
306        public boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
307                BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
308                IBluetoothHidDeviceCallback callback) {
309            if (DBG) {
310                Log.d(TAG, "registerApp()");
311            }
312
313            HidDeviceService service = getService();
314            if (service == null) {
315                return false;
316            }
317
318            return service.registerApp(sdp, inQos, outQos, callback);
319        }
320
321        @Override
322        public boolean unregisterApp() {
323            if (DBG) {
324                Log.d(TAG, "unregisterApp()");
325            }
326
327            HidDeviceService service = getService();
328            if (service == null) {
329                return false;
330            }
331
332            return service.unregisterApp();
333        }
334
335        @Override
336        public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
337            if (DBG) {
338                Log.d(TAG, "sendReport(): device=" + device + "  id=" + id);
339            }
340
341            HidDeviceService service = getService();
342            if (service == null) {
343                return false;
344            }
345
346            return service.sendReport(device, id, data);
347        }
348
349        @Override
350        public boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
351            if (DBG) {
352                Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
353            }
354
355            HidDeviceService service = getService();
356            if (service == null) {
357                return false;
358            }
359
360            return service.replyReport(device, type, id, data);
361        }
362
363        @Override
364        public boolean unplug(BluetoothDevice device) {
365            if (DBG) {
366                Log.d(TAG, "unplug(): device=" + device);
367            }
368
369            HidDeviceService service = getService();
370            if (service == null) {
371                return false;
372            }
373
374            return service.unplug(device);
375        }
376
377        @Override
378        public boolean connect(BluetoothDevice device) {
379            if (DBG) {
380                Log.d(TAG, "connect(): device=" + device);
381            }
382
383            HidDeviceService service = getService();
384            if (service == null) {
385                return false;
386            }
387
388            return service.connect(device);
389        }
390
391        @Override
392        public boolean disconnect(BluetoothDevice device) {
393            if (DBG) {
394                Log.d(TAG, "disconnect(): device=" + device);
395            }
396
397            HidDeviceService service = getService();
398            if (service == null) {
399                return false;
400            }
401
402            return service.disconnect(device);
403        }
404
405        @Override
406        public boolean reportError(BluetoothDevice device, byte error) {
407            if (DBG) {
408                Log.d(TAG, "reportError(): device=" + device + " error=" + error);
409            }
410
411            HidDeviceService service = getService();
412            if (service == null) {
413                return false;
414            }
415
416            return service.reportError(device, error);
417        }
418
419        @Override
420        public int getConnectionState(BluetoothDevice device) {
421            if (DBG) {
422                Log.d(TAG, "getConnectionState(): device=" + device);
423            }
424
425            HidDeviceService service = getService();
426            if (service == null) {
427                return BluetoothHidDevice.STATE_DISCONNECTED;
428            }
429
430            return service.getConnectionState(device);
431        }
432
433        @Override
434        public List<BluetoothDevice> getConnectedDevices() {
435            if (DBG) {
436                Log.d(TAG, "getConnectedDevices()");
437            }
438
439            return getDevicesMatchingConnectionStates(new int[]{BluetoothProfile.STATE_CONNECTED});
440        }
441
442        @Override
443        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
444            if (DBG) {
445                Log.d(TAG,
446                        "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
447            }
448
449            HidDeviceService service = getService();
450            if (service == null) {
451                return new ArrayList<BluetoothDevice>(0);
452            }
453
454            return service.getDevicesMatchingConnectionStates(states);
455        }
456    }
457
458    @Override
459    protected IProfileServiceBinder initBinder() {
460        return new BluetoothHidDeviceBinder(this);
461    }
462
463    private boolean checkDevice(BluetoothDevice device) {
464        if (mHidDevice == null || !mHidDevice.equals(device)) {
465            Log.w(TAG, "Unknown device: " + device);
466            return false;
467        }
468        return true;
469    }
470
471    private boolean checkCallingUid() {
472        int callingUid = Binder.getCallingUid();
473        if (callingUid != mUserUid) {
474            Log.w(TAG, "checkCallingUid(): caller UID doesn't match registered user UID");
475            return false;
476        }
477        return true;
478    }
479
480    synchronized boolean registerApp(BluetoothHidDeviceAppSdpSettings sdp,
481            BluetoothHidDeviceAppQosSettings inQos, BluetoothHidDeviceAppQosSettings outQos,
482            IBluetoothHidDeviceCallback callback) {
483        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
484        if (mUserUid != 0) {
485            Log.w(TAG, "registerApp(): failed because another app is registered");
486            return false;
487        }
488
489        int callingUid = Binder.getCallingUid();
490        if (DBG) {
491            Log.d(TAG, "registerApp(): calling uid=" + callingUid);
492        }
493        if (callingUid >= Process.FIRST_APPLICATION_UID
494                && mActivityManager.getUidImportance(callingUid) > FOREGROUND_IMPORTANCE_CUTOFF) {
495            Log.w(TAG, "registerApp(): failed because the app is not foreground");
496            return false;
497        }
498        mUserUid = callingUid;
499        mCallback = callback;
500
501        return mHidDeviceNativeInterface.registerApp(
502                sdp.getName(),
503                sdp.getDescription(),
504                sdp.getProvider(),
505                sdp.getSubclass(),
506                sdp.getDescriptors(),
507                inQos == null
508                        ? null
509                        : new int[] {
510                            inQos.getServiceType(),
511                            inQos.getTokenRate(),
512                            inQos.getTokenBucketSize(),
513                            inQos.getPeakBandwidth(),
514                            inQos.getLatency(),
515                            inQos.getDelayVariation()
516                        },
517                outQos == null
518                        ? null
519                        : new int[] {
520                            outQos.getServiceType(),
521                            outQos.getTokenRate(),
522                            outQos.getTokenBucketSize(),
523                            outQos.getPeakBandwidth(),
524                            outQos.getLatency(),
525                            outQos.getDelayVariation()
526                        });
527    }
528
529    synchronized boolean unregisterApp() {
530        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
531        if (DBG) {
532            Log.d(TAG, "unregisterApp()");
533        }
534
535        int callingUid = Binder.getCallingUid();
536        return unregisterAppUid(callingUid);
537    }
538
539    private synchronized boolean unregisterAppUid(int uid) {
540        if (DBG) {
541            Log.d(TAG, "unregisterAppUid(): uid=" + uid);
542        }
543
544        if (mUserUid != 0 && (uid == mUserUid || uid < Process.FIRST_APPLICATION_UID)) {
545            mUserUid = 0;
546            return mHidDeviceNativeInterface.unregisterApp();
547        }
548        if (DBG) {
549            Log.d(TAG, "unregisterAppUid(): caller UID doesn't match user UID");
550        }
551        return false;
552    }
553
554    synchronized boolean sendReport(BluetoothDevice device, int id, byte[] data) {
555        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
556        if (DBG) {
557            Log.d(TAG, "sendReport(): device=" + device + " id=" + id);
558        }
559
560        return checkDevice(device) && checkCallingUid()
561                && mHidDeviceNativeInterface.sendReport(id, data);
562    }
563
564    synchronized boolean replyReport(BluetoothDevice device, byte type, byte id, byte[] data) {
565        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
566        if (DBG) {
567            Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id);
568        }
569
570        return checkDevice(device) && checkCallingUid()
571                && mHidDeviceNativeInterface.replyReport(type, id, data);
572    }
573
574    synchronized boolean unplug(BluetoothDevice device) {
575        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
576        if (DBG) {
577            Log.d(TAG, "unplug(): device=" + device);
578        }
579
580        return checkDevice(device) && checkCallingUid()
581                && mHidDeviceNativeInterface.unplug();
582    }
583
584    synchronized boolean connect(BluetoothDevice device) {
585        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
586        if (DBG) {
587            Log.d(TAG, "connect(): device=" + device);
588        }
589
590        return checkCallingUid() && mHidDeviceNativeInterface.connect(device);
591    }
592
593    synchronized boolean disconnect(BluetoothDevice device) {
594        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
595        if (DBG) {
596            Log.d(TAG, "disconnect(): device=" + device);
597        }
598
599        int callingUid = Binder.getCallingUid();
600        if (callingUid != mUserUid && callingUid >= Process.FIRST_APPLICATION_UID) {
601            Log.w(TAG, "disconnect(): caller UID doesn't match user UID");
602            return false;
603        }
604        return checkDevice(device) && mHidDeviceNativeInterface.disconnect();
605    }
606
607    synchronized boolean reportError(BluetoothDevice device, byte error) {
608        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission");
609        if (DBG) {
610            Log.d(TAG, "reportError(): device=" + device + " error=" + error);
611        }
612
613        return checkDevice(device) && checkCallingUid()
614                && mHidDeviceNativeInterface.reportError(error);
615    }
616
617    @Override
618    protected boolean start() {
619        if (DBG) {
620            Log.d(TAG, "start()");
621        }
622
623        mHandler = new HidDeviceServiceHandler();
624        mHidDeviceNativeInterface = HidDeviceNativeInterface.getInstance();
625        mHidDeviceNativeInterface.init();
626        mNativeAvailable = true;
627        mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
628        mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
629                FOREGROUND_IMPORTANCE_CUTOFF);
630        setHidDeviceService(this);
631        return true;
632    }
633
634    @Override
635    protected boolean stop() {
636        if (DBG) {
637            Log.d(TAG, "stop()");
638        }
639
640        setHidDeviceService(null);
641        if (mNativeAvailable) {
642            mHidDeviceNativeInterface.cleanup();
643            mNativeAvailable = false;
644        }
645        mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
646        return true;
647    }
648
649    @Override
650    public boolean onUnbind(Intent intent) {
651        Log.d(TAG, "Need to unregister app");
652        unregisterApp();
653        return super.onUnbind(intent);
654    }
655
656    /**
657     * Get the HID Device Service instance
658     * @return HID Device Service instance
659     */
660    public static synchronized HidDeviceService getHidDeviceService() {
661        if (sHidDeviceService == null) {
662            Log.d(TAG, "getHidDeviceService(): service is NULL");
663            return null;
664        }
665        if (!sHidDeviceService.isAvailable()) {
666            Log.d(TAG, "getHidDeviceService(): service is not available");
667            return null;
668        }
669        return sHidDeviceService;
670    }
671
672    private static synchronized void setHidDeviceService(HidDeviceService instance) {
673        if (DBG) {
674            Log.d(TAG, "setHidDeviceService(): set to: " + instance);
675        }
676        sHidDeviceService = instance;
677    }
678
679    int getConnectionState(BluetoothDevice device) {
680        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
681        if (mHidDevice != null && mHidDevice.equals(device)) {
682            return mHidDeviceState;
683        }
684        return BluetoothHidDevice.STATE_DISCONNECTED;
685    }
686
687    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
688        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
689        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
690
691        if (mHidDevice != null) {
692            for (int state : states) {
693                if (state == mHidDeviceState) {
694                    inputDevices.add(mHidDevice);
695                    break;
696                }
697            }
698        }
699        return inputDevices;
700    }
701
702    synchronized void onApplicationStateChangedFromNative(BluetoothDevice device,
703            boolean registered) {
704        if (DBG) {
705            Log.d(TAG, "onApplicationStateChanged(): registered=" + registered);
706        }
707
708        Message msg = mHandler.obtainMessage(MESSAGE_APPLICATION_STATE_CHANGED);
709        msg.obj = device;
710        msg.arg1 = registered ? 1 : 0;
711        mHandler.sendMessage(msg);
712    }
713
714    synchronized void onConnectStateChangedFromNative(BluetoothDevice device, int state) {
715        if (DBG) {
716            Log.d(TAG, "onConnectStateChanged(): device="
717                    + device + " state=" + state);
718        }
719
720        Message msg = mHandler.obtainMessage(MESSAGE_CONNECT_STATE_CHANGED);
721        msg.obj = device;
722        msg.arg1 = state;
723        mHandler.sendMessage(msg);
724    }
725
726    synchronized void onGetReportFromNative(byte type, byte id, short bufferSize) {
727        if (DBG) {
728            Log.d(TAG, "onGetReport(): type=" + type + " id=" + id + " bufferSize=" + bufferSize);
729        }
730
731        Message msg = mHandler.obtainMessage(MESSAGE_GET_REPORT);
732        msg.obj = bufferSize > 0 ? new Integer(bufferSize) : null;
733        msg.arg1 = type;
734        msg.arg2 = id;
735        mHandler.sendMessage(msg);
736    }
737
738    synchronized void onSetReportFromNative(byte reportType, byte reportId, byte[] data) {
739        if (DBG) {
740            Log.d(TAG, "onSetReport(): reportType=" + reportType + " reportId=" + reportId);
741        }
742
743        ByteBuffer bb = ByteBuffer.wrap(data);
744
745        Message msg = mHandler.obtainMessage(MESSAGE_SET_REPORT);
746        msg.arg1 = reportType;
747        msg.arg2 = reportId;
748        msg.obj = bb;
749        mHandler.sendMessage(msg);
750    }
751
752    synchronized void onSetProtocolFromNative(byte protocol) {
753        if (DBG) {
754            Log.d(TAG, "onSetProtocol(): protocol=" + protocol);
755        }
756
757        Message msg = mHandler.obtainMessage(MESSAGE_SET_PROTOCOL);
758        msg.arg1 = protocol;
759        mHandler.sendMessage(msg);
760    }
761
762    synchronized void onInterruptDataFromNative(byte reportId, byte[] data) {
763        if (DBG) {
764            Log.d(TAG, "onInterruptData(): reportId=" + reportId);
765        }
766
767        ByteBuffer bb = ByteBuffer.wrap(data);
768
769        Message msg = mHandler.obtainMessage(MESSAGE_INTR_DATA);
770        msg.arg1 = reportId;
771        msg.obj = bb;
772        mHandler.sendMessage(msg);
773    }
774
775    synchronized void onVirtualCableUnplugFromNative() {
776        if (DBG) {
777            Log.d(TAG, "onVirtualCableUnplug()");
778        }
779
780        Message msg = mHandler.obtainMessage(MESSAGE_VC_UNPLUG);
781        mHandler.sendMessage(msg);
782    }
783
784    private void setAndBroadcastConnectionState(BluetoothDevice device, int newState) {
785        if (DBG) {
786            Log.d(TAG, "setAndBroadcastConnectionState(): device=" + device.getAddress()
787                    + " oldState=" + mHidDeviceState + " newState=" + newState);
788        }
789
790        if (mHidDevice != null && !mHidDevice.equals(device)) {
791            Log.w(TAG, "Connection state changed for unknown device, ignoring");
792            return;
793        }
794
795        int prevState = mHidDeviceState;
796        mHidDeviceState = newState;
797
798        if (prevState == newState) {
799            Log.w(TAG, "Connection state is unchanged, ignoring");
800            return;
801        }
802
803        Intent intent = new Intent(BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED);
804        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
805        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
806        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
807        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
808        sendBroadcast(intent, BLUETOOTH_PERM);
809    }
810
811    private static int convertHalState(int halState) {
812        switch (halState) {
813            case HAL_CONN_STATE_CONNECTED:
814                return BluetoothProfile.STATE_CONNECTED;
815            case HAL_CONN_STATE_CONNECTING:
816                return BluetoothProfile.STATE_CONNECTING;
817            case HAL_CONN_STATE_DISCONNECTED:
818                return BluetoothProfile.STATE_DISCONNECTED;
819            case HAL_CONN_STATE_DISCONNECTING:
820                return BluetoothProfile.STATE_DISCONNECTING;
821            default:
822                return BluetoothProfile.STATE_DISCONNECTED;
823        }
824    }
825
826    static final int HAL_CONN_STATE_CONNECTED = 0;
827    static final int HAL_CONN_STATE_CONNECTING = 1;
828    static final int HAL_CONN_STATE_DISCONNECTED = 2;
829    static final int HAL_CONN_STATE_DISCONNECTING = 3;
830}
831