blob: 199cc7a019d1bfb56406689ad2b01965342aec88 [file] [log] [blame]
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001/*
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002 * Copyright (C) 2011 The Android Open Source Project
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07003 *
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.bluetooth;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
Matthew Xiebf246ef2012-03-21 23:15:06 -070021import android.content.ComponentName;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070022import android.content.Context;
Matthew Xiebf246ef2012-03-21 23:15:06 -070023import android.content.Intent;
24import android.content.ServiceConnection;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070025import android.os.IBinder;
26import android.os.RemoteException;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070027import android.util.Log;
28
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070029import java.util.ArrayList;
30import java.util.List;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070031
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080032
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070033/**
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080034 * This class provides the public APIs to control the Bluetooth Input
35 * Device Profile.
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070036 *
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080037 *<p>BluetoothInputDevice is a proxy object for controlling the Bluetooth
38 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
39 * the BluetoothInputDevice proxy object.
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070040 *
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080041 *<p>Each method is protected with its appropriate permission.
42 *@hide
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070043 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080044public final class BluetoothInputDevice implements BluetoothProfile {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070045 private static final String TAG = "BluetoothInputDevice";
46 private static final boolean DBG = false;
47
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080048 /**
49 * Intent used to broadcast the change in connection state of the Input
50 * Device profile.
51 *
52 * <p>This intent will have 3 extras:
53 * <ul>
54 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
55 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
56 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
57 * </ul>
58 *
59 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
60 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
61 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
62 *
63 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
64 * receive.
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070065 */
66 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080067 public static final String ACTION_CONNECTION_STATE_CHANGED =
68 "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070069
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080070 /**
71 * Return codes for the connect and disconnect Bluez / Dbus calls.
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080072 * @hide
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080073 */
74 public static final int INPUT_DISCONNECT_FAILED_NOT_CONNECTED = 5000;
75
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080076 /**
77 * @hide
78 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080079 public static final int INPUT_CONNECT_FAILED_ALREADY_CONNECTED = 5001;
80
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080081 /**
82 * @hide
83 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080084 public static final int INPUT_CONNECT_FAILED_ATTEMPT_FAILED = 5002;
85
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080086 /**
87 * @hide
88 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080089 public static final int INPUT_OPERATION_GENERIC_FAILURE = 5003;
90
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080091 /**
92 * @hide
93 */
Jaikumar Ganeshfbe807d2011-01-19 13:59:32 -080094 public static final int INPUT_OPERATION_SUCCESS = 5004;
95
Matthew Xiebf246ef2012-03-21 23:15:06 -070096 private Context mContext;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080097 private ServiceListener mServiceListener;
98 private BluetoothAdapter mAdapter;
Matthew Xiebf246ef2012-03-21 23:15:06 -070099 private IBluetoothInputDevice mService;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700100
101 /**
102 * Create a BluetoothInputDevice proxy object for interacting with the local
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800103 * Bluetooth Service which handles the InputDevice profile
104 *
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700105 */
Matthew Xiebf246ef2012-03-21 23:15:06 -0700106 /*package*/ BluetoothInputDevice(Context context, ServiceListener l) {
107 mContext = context;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800108 mServiceListener = l;
109 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xiebf246ef2012-03-21 23:15:06 -0700110 if (!context.bindService(new Intent(IBluetoothInputDevice.class.getName()),
111 mConnection, 0)) {
112 Log.e(TAG, "Could not bind to Bluetooth HID Service");
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700113 }
114 }
115
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800116 /*package*/ void close() {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700117 if (DBG) log("close()");
118 if (mConnection != null) {
119 mContext.unbindService(mConnection);
120 mConnection = null;
121 }
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -0800122 mServiceListener = null;
123 }
124
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800125 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700126 * Initiate connection to a profile of the remote bluetooth device.
127 *
128 * <p> The system supports connection to multiple input devices.
129 *
130 * <p> This API returns false in scenarios like the profile on the
131 * device is already connected or Bluetooth is not turned on.
132 * When this API returns true, it is guaranteed that
133 * connection state intent for the profile will be broadcasted with
134 * the state. Users can get the connection state of the profile
135 * from this intent.
136 *
137 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
138 * permission.
139 *
140 * @param device Remote Bluetooth Device
141 * @return false on immediate error,
142 * true otherwise
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800143 * @hide
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700144 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800145 public boolean connect(BluetoothDevice device) {
146 if (DBG) log("connect(" + device + ")");
Matthew Xiebf246ef2012-03-21 23:15:06 -0700147 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800148 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700149 return mService.connect(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800150 } catch (RemoteException e) {
151 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
152 return false;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700153 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700154 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800155 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700156 return false;
157 }
158
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800159 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700160 * Initiate disconnection from a profile
161 *
162 * <p> This API will return false in scenarios like the profile on the
163 * Bluetooth device is not in connected state etc. When this API returns,
164 * true, it is guaranteed that the connection state change
165 * intent will be broadcasted with the state. Users can get the
166 * disconnection state of the profile from this intent.
167 *
168 * <p> If the disconnection is initiated by a remote device, the state
169 * will transition from {@link #STATE_CONNECTED} to
170 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
171 * host (local) device the state will transition from
172 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
173 * state {@link #STATE_DISCONNECTED}. The transition to
174 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
175 * two scenarios.
176 *
177 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
178 * permission.
179 *
180 * @param device Remote Bluetooth Device
181 * @return false on immediate error,
182 * true otherwise
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700183 * @hide
184 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800185 public boolean disconnect(BluetoothDevice device) {
186 if (DBG) log("disconnect(" + device + ")");
Matthew Xiebf246ef2012-03-21 23:15:06 -0700187 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800188 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700189 return mService.disconnect(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800190 } catch (RemoteException e) {
191 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
192 return false;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700193 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700194 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800195 if (mService == null) Log.w(TAG, "Proxy not attached to service");
196 return false;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700197 }
198
199 /**
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800200 * {@inheritDoc}
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700201 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800202 public List<BluetoothDevice> getConnectedDevices() {
203 if (DBG) log("getConnectedDevices()");
204 if (mService != null && isEnabled()) {
205 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700206 return mService.getConnectedDevices();
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800207 } catch (RemoteException e) {
208 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
209 return new ArrayList<BluetoothDevice>();
Matthew Xiebf246ef2012-03-21 23:15:06 -0700210 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700211 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800212 if (mService == null) Log.w(TAG, "Proxy not attached to service");
213 return new ArrayList<BluetoothDevice>();
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700214 }
215
216 /**
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800217 * {@inheritDoc}
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700218 */
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800219 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
220 if (DBG) log("getDevicesMatchingStates()");
221 if (mService != null && isEnabled()) {
222 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700223 return mService.getDevicesMatchingConnectionStates(states);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800224 } catch (RemoteException e) {
225 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
226 return new ArrayList<BluetoothDevice>();
Matthew Xiebf246ef2012-03-21 23:15:06 -0700227 }
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700228 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800229 if (mService == null) Log.w(TAG, "Proxy not attached to service");
230 return new ArrayList<BluetoothDevice>();
231 }
232
233 /**
234 * {@inheritDoc}
235 */
236 public int getConnectionState(BluetoothDevice device) {
237 if (DBG) log("getState(" + device + ")");
Matthew Xiebf246ef2012-03-21 23:15:06 -0700238 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800239 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700240 return mService.getConnectionState(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800241 } catch (RemoteException e) {
242 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
243 return BluetoothProfile.STATE_DISCONNECTED;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700244 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800245 }
246 if (mService == null) Log.w(TAG, "Proxy not attached to service");
247 return BluetoothProfile.STATE_DISCONNECTED;
248 }
249
250 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700251 * Set priority of the profile
252 *
253 * <p> The device should already be paired.
254 * Priority can be one of {@link #PRIORITY_ON} or
255 * {@link #PRIORITY_OFF},
256 *
257 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
258 * permission.
259 *
260 * @param device Paired bluetooth device
261 * @param priority
262 * @return true if priority is set, false on error
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800263 * @hide
264 */
265 public boolean setPriority(BluetoothDevice device, int priority) {
266 if (DBG) log("setPriority(" + device + ", " + priority + ")");
Matthew Xiebf246ef2012-03-21 23:15:06 -0700267 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800268 if (priority != BluetoothProfile.PRIORITY_OFF &&
269 priority != BluetoothProfile.PRIORITY_ON) {
270 return false;
271 }
272 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700273 return mService.setPriority(device, priority);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800274 } catch (RemoteException e) {
275 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
276 return false;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700277 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800278 }
279 if (mService == null) Log.w(TAG, "Proxy not attached to service");
280 return false;
281 }
282
283 /**
Jaikumar Ganeshf8789162011-05-26 13:56:40 -0700284 * Get the priority of the profile.
285 *
286 * <p> The priority can be any of:
287 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
288 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
289 *
290 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
291 *
292 * @param device Bluetooth device
293 * @return priority of the device
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800294 * @hide
295 */
296 public int getPriority(BluetoothDevice device) {
297 if (DBG) log("getPriority(" + device + ")");
Matthew Xiebf246ef2012-03-21 23:15:06 -0700298 if (mService != null && isEnabled() && isValidDevice(device)) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800299 try {
Matthew Xiebf246ef2012-03-21 23:15:06 -0700300 return mService.getPriority(device);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800301 } catch (RemoteException e) {
302 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
303 return BluetoothProfile.PRIORITY_OFF;
Matthew Xiebf246ef2012-03-21 23:15:06 -0700304 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800305 }
306 if (mService == null) Log.w(TAG, "Proxy not attached to service");
307 return BluetoothProfile.PRIORITY_OFF;
308 }
309
Matthew Xiebf246ef2012-03-21 23:15:06 -0700310 private ServiceConnection mConnection = new ServiceConnection() {
311 public void onServiceConnected(ComponentName className, IBinder service) {
312 if (DBG) Log.d(TAG, "Proxy object connected");
313 mService = IBluetoothInputDevice.Stub.asInterface(service);
314
315 if (mServiceListener != null) {
316 mServiceListener.onServiceConnected(BluetoothProfile.INPUT_DEVICE, BluetoothInputDevice.this);
317 }
318 }
319 public void onServiceDisconnected(ComponentName className) {
320 if (DBG) Log.d(TAG, "Proxy object disconnected");
321 mService = null;
322 if (mServiceListener != null) {
323 mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_DEVICE);
324 }
325 }
326 };
327
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800328 private boolean isEnabled() {
329 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
330 return false;
331 }
332
333 private boolean isValidDevice(BluetoothDevice device) {
334 if (device == null) return false;
335
336 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
337 return false;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700338 }
339
340 private static void log(String msg) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -0800341 Log.d(TAG, msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700342 }
343}