blob: fac8fd51848dcec183e5a7d64c6b8c301ef4bdd1 [file] [log] [blame]
Matthew Xiefe3807a2013-07-18 17:31:50 -07001/*
2 * Copyright (C) 2008 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.bluetooth;
18
Kim Schulz0d376052013-08-22 11:18:02 +020019import java.util.List;
20import java.util.ArrayList;
Matthew Xiefe3807a2013-07-18 17:31:50 -070021import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.os.RemoteException;
26import android.os.IBinder;
27import android.os.ServiceManager;
28import android.util.Log;
29
30/**
31 * This class provides the APIs to control the Bluetooth MAP
32 * Profile.
33 *@hide
34 */
Kim Schulz0d376052013-08-22 11:18:02 +020035public final class BluetoothMap implements BluetoothProfile {
Matthew Xiefe3807a2013-07-18 17:31:50 -070036
37 private static final String TAG = "BluetoothMap";
38 private static final boolean DBG = true;
39 private static final boolean VDBG = false;
40
Kim Schulz0d376052013-08-22 11:18:02 +020041 public static final String ACTION_CONNECTION_STATE_CHANGED =
42 "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
Matthew Xiefe3807a2013-07-18 17:31:50 -070043
44 private IBluetoothMap mService;
45 private final Context mContext;
46 private ServiceListener mServiceListener;
47 private BluetoothAdapter mAdapter;
48
49 /** There was an error trying to obtain the state */
50 public static final int STATE_ERROR = -1;
Matthew Xiefe3807a2013-07-18 17:31:50 -070051
52 public static final int RESULT_FAILURE = 0;
53 public static final int RESULT_SUCCESS = 1;
54 /** Connection canceled before completion. */
55 public static final int RESULT_CANCELED = 2;
56
Matthew Xiefe3807a2013-07-18 17:31:50 -070057 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
58 new IBluetoothStateChangeCallback.Stub() {
59 public void onBluetoothStateChange(boolean up) {
60 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
61 if (!up) {
62 if (VDBG) Log.d(TAG,"Unbinding service...");
63 synchronized (mConnection) {
64 try {
65 mService = null;
66 mContext.unbindService(mConnection);
67 } catch (Exception re) {
68 Log.e(TAG,"",re);
69 }
70 }
71 } else {
72 synchronized (mConnection) {
73 try {
74 if (mService == null) {
75 if (VDBG) Log.d(TAG,"Binding service...");
Kim Schulz0d376052013-08-22 11:18:02 +020076 doBind();
Matthew Xiefe3807a2013-07-18 17:31:50 -070077 }
78 } catch (Exception re) {
79 Log.e(TAG,"",re);
80 }
81 }
82 }
83 }
84 };
85
86 /**
87 * Create a BluetoothMap proxy object.
88 */
Kim Schulz0d376052013-08-22 11:18:02 +020089 /*package*/ BluetoothMap(Context context, ServiceListener l) {
90 if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
Matthew Xiefe3807a2013-07-18 17:31:50 -070091 mContext = context;
92 mServiceListener = l;
93 mAdapter = BluetoothAdapter.getDefaultAdapter();
94 IBluetoothManager mgr = mAdapter.getBluetoothManager();
95 if (mgr != null) {
96 try {
97 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
98 } catch (RemoteException e) {
99 Log.e(TAG,"",e);
100 }
101 }
Kim Schulz0d376052013-08-22 11:18:02 +0200102 doBind();
103 }
104
105 boolean doBind() {
106 Intent intent = new Intent(IBluetoothMap.class.getName());
107 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
108 intent.setComponent(comp);
109 if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
110 Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
111 return false;
Matthew Xiefe3807a2013-07-18 17:31:50 -0700112 }
Kim Schulz0d376052013-08-22 11:18:02 +0200113 return true;
Matthew Xiefe3807a2013-07-18 17:31:50 -0700114 }
115
116 protected void finalize() throws Throwable {
117 try {
118 close();
119 } finally {
120 super.finalize();
121 }
122 }
123
124 /**
125 * Close the connection to the backing service.
126 * Other public functions of BluetoothMap will return default error
127 * results once close() has been called. Multiple invocations of close()
128 * are ok.
129 */
130 public synchronized void close() {
131 IBluetoothManager mgr = mAdapter.getBluetoothManager();
132 if (mgr != null) {
133 try {
134 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
135 } catch (Exception e) {
136 Log.e(TAG,"",e);
137 }
138 }
139
140 synchronized (mConnection) {
141 if (mService != null) {
142 try {
143 mService = null;
144 mContext.unbindService(mConnection);
145 mConnection = null;
146 } catch (Exception re) {
147 Log.e(TAG,"",re);
148 }
149 }
150 }
151 mServiceListener = null;
152 }
153
154 /**
155 * Get the current state of the BluetoothMap service.
156 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
157 * object is currently not connected to the Map service.
158 */
159 public int getState() {
160 if (VDBG) log("getState()");
161 if (mService != null) {
162 try {
163 return mService.getState();
164 } catch (RemoteException e) {Log.e(TAG, e.toString());}
165 } else {
166 Log.w(TAG, "Proxy not attached to service");
167 if (DBG) log(Log.getStackTraceString(new Throwable()));
168 }
169 return BluetoothMap.STATE_ERROR;
170 }
171
172 /**
173 * Get the currently connected remote Bluetooth device (PCE).
174 * @return The remote Bluetooth device, or null if not in connected or
175 * connecting state, or if this proxy object is not connected to
176 * the Map service.
177 */
178 public BluetoothDevice getClient() {
179 if (VDBG) log("getClient()");
180 if (mService != null) {
181 try {
182 return mService.getClient();
183 } catch (RemoteException e) {Log.e(TAG, e.toString());}
184 } else {
185 Log.w(TAG, "Proxy not attached to service");
186 if (DBG) log(Log.getStackTraceString(new Throwable()));
187 }
188 return null;
189 }
190
191 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200192 * Returns true if the specified Bluetooth device is connected.
193 * Returns false if not connected, or if this proxy object is not
194 * currently connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700195 */
196 public boolean isConnected(BluetoothDevice device) {
197 if (VDBG) log("isConnected(" + device + ")");
198 if (mService != null) {
199 try {
200 return mService.isConnected(device);
201 } catch (RemoteException e) {Log.e(TAG, e.toString());}
202 } else {
203 Log.w(TAG, "Proxy not attached to service");
204 if (DBG) log(Log.getStackTraceString(new Throwable()));
205 }
206 return false;
207 }
208
209 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200210 * Initiate connection. Initiation of outgoing connections is not
211 * supported for MAP server.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700212 */
Kim Schulz0d376052013-08-22 11:18:02 +0200213 public boolean connect(BluetoothDevice device) {
214 if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
215 return false;
216 }
217
218 /**
219 * Initiate disconnect.
220 *
221 * @param device Remote Bluetooth Device
222 * @return false on error,
223 * true otherwise
224 */
225 public boolean disconnect(BluetoothDevice device) {
226 if (DBG) log("disconnect(" + device + ")");
227 if (mService != null && isEnabled() &&
228 isValidDevice(device)) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700229 try {
Kim Schulz0d376052013-08-22 11:18:02 +0200230 return mService.disconnect(device);
231 } catch (RemoteException e) {
232 Log.e(TAG, Log.getStackTraceString(new Throwable()));
233 return false;
234 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700235 }
Kim Schulz0d376052013-08-22 11:18:02 +0200236 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xiefe3807a2013-07-18 17:31:50 -0700237 return false;
238 }
239
240 /**
241 * Check class bits for possible Map support.
242 * This is a simple heuristic that tries to guess if a device with the
243 * given class bits might support Map. It is not accurate for all
244 * devices. It tries to err on the side of false positives.
245 * @return True if this device might support Map.
246 */
247 public static boolean doesClassMatchSink(BluetoothClass btClass) {
248 // TODO optimize the rule
249 switch (btClass.getDeviceClass()) {
250 case BluetoothClass.Device.COMPUTER_DESKTOP:
251 case BluetoothClass.Device.COMPUTER_LAPTOP:
252 case BluetoothClass.Device.COMPUTER_SERVER:
253 case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
254 return true;
255 default:
256 return false;
257 }
258 }
259
Kim Schulz0d376052013-08-22 11:18:02 +0200260 /**
261 * Get the list of connected devices. Currently at most one.
262 *
263 * @return list of connected devices
264 */
265 public List<BluetoothDevice> getConnectedDevices() {
266 if (DBG) log("getConnectedDevices()");
267 if (mService != null && isEnabled()) {
268 try {
269 return mService.getConnectedDevices();
270 } catch (RemoteException e) {
271 Log.e(TAG, Log.getStackTraceString(new Throwable()));
272 return new ArrayList<BluetoothDevice>();
273 }
274 }
275 if (mService == null) Log.w(TAG, "Proxy not attached to service");
276 return new ArrayList<BluetoothDevice>();
277 }
278
279 /**
280 * Get the list of devices matching specified states. Currently at most one.
281 *
282 * @return list of matching devices
283 */
284 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
285 if (DBG) log("getDevicesMatchingStates()");
286 if (mService != null && isEnabled()) {
287 try {
288 return mService.getDevicesMatchingConnectionStates(states);
289 } catch (RemoteException e) {
290 Log.e(TAG, Log.getStackTraceString(new Throwable()));
291 return new ArrayList<BluetoothDevice>();
292 }
293 }
294 if (mService == null) Log.w(TAG, "Proxy not attached to service");
295 return new ArrayList<BluetoothDevice>();
296 }
297
298 /**
299 * Get connection state of device
300 *
301 * @return device connection state
302 */
303 public int getConnectionState(BluetoothDevice device) {
304 if (DBG) log("getConnectionState(" + device + ")");
305 if (mService != null && isEnabled() &&
306 isValidDevice(device)) {
307 try {
308 return mService.getConnectionState(device);
309 } catch (RemoteException e) {
310 Log.e(TAG, Log.getStackTraceString(new Throwable()));
311 return BluetoothProfile.STATE_DISCONNECTED;
312 }
313 }
314 if (mService == null) Log.w(TAG, "Proxy not attached to service");
315 return BluetoothProfile.STATE_DISCONNECTED;
316 }
317
318 /**
319 * Set priority of the profile
320 *
321 * <p> The device should already be paired.
322 * Priority can be one of {@link #PRIORITY_ON} or
323 * {@link #PRIORITY_OFF},
324 *
325 * @param device Paired bluetooth device
326 * @param priority
327 * @return true if priority is set, false on error
328 */
329 public boolean setPriority(BluetoothDevice device, int priority) {
330 if (DBG) log("setPriority(" + device + ", " + priority + ")");
331 if (mService != null && isEnabled() &&
332 isValidDevice(device)) {
333 if (priority != BluetoothProfile.PRIORITY_OFF &&
334 priority != BluetoothProfile.PRIORITY_ON) {
335 return false;
336 }
337 try {
338 return mService.setPriority(device, priority);
339 } catch (RemoteException e) {
340 Log.e(TAG, Log.getStackTraceString(new Throwable()));
341 return false;
342 }
343 }
344 if (mService == null) Log.w(TAG, "Proxy not attached to service");
345 return false;
346 }
347
348 /**
349 * Get the priority of the profile.
350 *
351 * <p> The priority can be any of:
352 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
353 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
354 *
355 * @param device Bluetooth device
356 * @return priority of the device
357 */
358 public int getPriority(BluetoothDevice device) {
359 if (VDBG) log("getPriority(" + device + ")");
360 if (mService != null && isEnabled() &&
361 isValidDevice(device)) {
362 try {
363 return mService.getPriority(device);
364 } catch (RemoteException e) {
365 Log.e(TAG, Log.getStackTraceString(new Throwable()));
366 return PRIORITY_OFF;
367 }
368 }
369 if (mService == null) Log.w(TAG, "Proxy not attached to service");
370 return PRIORITY_OFF;
371 }
372
Matthew Xiefe3807a2013-07-18 17:31:50 -0700373 private ServiceConnection mConnection = new ServiceConnection() {
374 public void onServiceConnected(ComponentName className, IBinder service) {
375 if (DBG) log("Proxy object connected");
376 mService = IBluetoothMap.Stub.asInterface(service);
377 if (mServiceListener != null) {
Kim Schulz0d376052013-08-22 11:18:02 +0200378 mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
Matthew Xiefe3807a2013-07-18 17:31:50 -0700379 }
380 }
381 public void onServiceDisconnected(ComponentName className) {
382 if (DBG) log("Proxy object disconnected");
383 mService = null;
384 if (mServiceListener != null) {
Kim Schulz0d376052013-08-22 11:18:02 +0200385 mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
Matthew Xiefe3807a2013-07-18 17:31:50 -0700386 }
387 }
388 };
389
390 private static void log(String msg) {
391 Log.d(TAG, msg);
392 }
Kim Schulz0d376052013-08-22 11:18:02 +0200393
394 private boolean isEnabled() {
395 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
396 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
397 log("Bluetooth is Not enabled");
398 return false;
399 }
400 private boolean isValidDevice(BluetoothDevice device) {
401 if (device == null) return false;
402
403 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
404 return false;
405 }
406
407
Matthew Xiefe3807a2013-07-18 17:31:50 -0700408}