BluetoothInputProfileHandler.java revision 4ab0e7746fe74a9e4d75d374f73b7af87420b2f6
1/*
2 * Copyright (C) 2011 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 android.server;
18
19import android.bluetooth.BluetoothAdapter;
20import android.bluetooth.BluetoothDevice;
21import android.bluetooth.BluetoothDeviceProfileState;
22import android.bluetooth.BluetoothInputDevice;
23import android.bluetooth.BluetoothProfileState;
24import android.content.Context;
25import android.content.Intent;
26import android.os.Message;
27import android.provider.Settings;
28import android.util.Log;
29
30import java.util.ArrayList;
31import java.util.HashMap;
32import java.util.List;
33
34/**
35 * This handles all the operations on the HID profile.
36 * All functions are called by BluetoothService, as Bluetooth Service
37 * is the Service handler for the HID profile.
38 */
39final class BluetoothInputProfileHandler {
40    private static final String TAG = "BluetoothInputProfileHandler";
41    private static final boolean DBG = true;
42
43    public static BluetoothInputProfileHandler sInstance;
44    private Context mContext;
45    private BluetoothService mBluetoothService;
46    private final HashMap<BluetoothDevice, Integer> mInputDevices;
47    private final BluetoothProfileState mHidProfileState;
48
49    private BluetoothInputProfileHandler(Context context, BluetoothService service) {
50        mContext = context;
51        mBluetoothService = service;
52        mInputDevices = new HashMap<BluetoothDevice, Integer>();
53        mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
54        mHidProfileState.start();
55    }
56
57    static synchronized BluetoothInputProfileHandler getInstance(Context context,
58            BluetoothService service) {
59        if (sInstance == null) sInstance = new BluetoothInputProfileHandler(context, service);
60        return sInstance;
61    }
62
63    synchronized boolean connectInputDevice(BluetoothDevice device,
64                                            BluetoothDeviceProfileState state) {
65        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
66        if (objectPath == null ||
67            getInputDeviceConnectionState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
68            getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
69            return false;
70        }
71        if (state != null) {
72            Message msg = new Message();
73            msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
74            msg.obj = state;
75            mHidProfileState.sendMessage(msg);
76            return true;
77        }
78        return false;
79    }
80
81    synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
82        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
83        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
84        if (!mBluetoothService.connectInputDeviceNative(objectPath)) {
85            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
86            return false;
87        }
88        return true;
89    }
90
91    synchronized boolean disconnectInputDevice(BluetoothDevice device,
92                                               BluetoothDeviceProfileState state) {
93        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
94        if (objectPath == null ||
95                getInputDeviceConnectionState(device) == BluetoothInputDevice.STATE_DISCONNECTED) {
96            return false;
97        }
98        if (state != null) {
99            Message msg = new Message();
100            msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
101            msg.obj = state;
102            mHidProfileState.sendMessage(msg);
103            return true;
104        }
105        return false;
106    }
107
108    synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
109        String objectPath = mBluetoothService.getObjectPathFromAddress(device.getAddress());
110        handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
111        if (!mBluetoothService.disconnectInputDeviceNative(objectPath)) {
112            handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
113            return false;
114        }
115        return true;
116    }
117
118    synchronized int getInputDeviceConnectionState(BluetoothDevice device) {
119        if (mInputDevices.get(device) == null) {
120            return BluetoothInputDevice.STATE_DISCONNECTED;
121        }
122        return mInputDevices.get(device);
123    }
124
125    synchronized List<BluetoothDevice> getConnectedInputDevices() {
126        List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
127            new int[] {BluetoothInputDevice.STATE_CONNECTED});
128        return devices;
129    }
130
131    synchronized List<BluetoothDevice> getInputDevicesMatchingConnectionStates(int[] states) {
132        List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(states);
133        return devices;
134    }
135
136    synchronized int getInputDevicePriority(BluetoothDevice device) {
137        return Settings.Secure.getInt(mContext.getContentResolver(),
138                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
139                BluetoothInputDevice.PRIORITY_UNDEFINED);
140    }
141
142    synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
143        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
144            return false;
145        }
146        return Settings.Secure.putInt(mContext.getContentResolver(),
147                Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
148                priority);
149    }
150
151    synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
152        List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
153
154        for (BluetoothDevice device: mInputDevices.keySet()) {
155            int inputDeviceState = getInputDeviceConnectionState(device);
156            for (int state : states) {
157                if (state == inputDeviceState) {
158                    inputDevices.add(device);
159                    break;
160                }
161            }
162        }
163        return inputDevices;
164    }
165
166    private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
167        int prevState;
168        if (mInputDevices.get(device) == null) {
169            prevState = BluetoothInputDevice.STATE_DISCONNECTED;
170        } else {
171            prevState = mInputDevices.get(device);
172        }
173        if (prevState == state) return;
174
175        mInputDevices.put(device, state);
176
177        if (getInputDevicePriority(device) >
178              BluetoothInputDevice.PRIORITY_OFF &&
179            state == BluetoothInputDevice.STATE_CONNECTING ||
180            state == BluetoothInputDevice.STATE_CONNECTED) {
181            // We have connected or attempting to connect.
182            // Bump priority
183            setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
184        }
185
186        Intent intent = new Intent(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
187        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
188        intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_STATE, prevState);
189        intent.putExtra(BluetoothInputDevice.EXTRA_STATE, state);
190        mContext.sendBroadcast(intent, BluetoothService.BLUETOOTH_PERM);
191
192        debugLog("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
193        mBluetoothService.sendConnectionStateChange(device, state, prevState);
194    }
195
196    synchronized void handleInputDevicePropertyChange(String address, boolean connected) {
197        int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
198            BluetoothInputDevice.STATE_DISCONNECTED;
199        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
200        BluetoothDevice device = adapter.getRemoteDevice(address);
201        handleInputDeviceStateChange(device, state);
202    }
203
204    synchronized void setInitialInputDevicePriority(BluetoothDevice device, int state) {
205        switch (state) {
206            case BluetoothDevice.BOND_BONDED:
207                if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
208                    setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
209                }
210                break;
211            case BluetoothDevice.BOND_NONE:
212                setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
213                break;
214        }
215    }
216
217    private static void debugLog(String msg) {
218        if (DBG) Log.d(TAG, msg);
219    }
220
221    private static void errorLog(String msg) {
222        Log.e(TAG, msg);
223    }
224}
225