blob: 92a2f1e4253c9a04f0d25a98356a24a804c3ca18 [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);
Matthew Xiefe3807a2013-07-18 17:31:50 -0700145 } catch (Exception re) {
146 Log.e(TAG,"",re);
147 }
148 }
149 }
150 mServiceListener = null;
151 }
152
153 /**
154 * Get the current state of the BluetoothMap service.
155 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
156 * object is currently not connected to the Map service.
157 */
158 public int getState() {
159 if (VDBG) log("getState()");
160 if (mService != null) {
161 try {
162 return mService.getState();
163 } catch (RemoteException e) {Log.e(TAG, e.toString());}
164 } else {
165 Log.w(TAG, "Proxy not attached to service");
166 if (DBG) log(Log.getStackTraceString(new Throwable()));
167 }
168 return BluetoothMap.STATE_ERROR;
169 }
170
171 /**
172 * Get the currently connected remote Bluetooth device (PCE).
173 * @return The remote Bluetooth device, or null if not in connected or
174 * connecting state, or if this proxy object is not connected to
175 * the Map service.
176 */
177 public BluetoothDevice getClient() {
178 if (VDBG) log("getClient()");
179 if (mService != null) {
180 try {
181 return mService.getClient();
182 } catch (RemoteException e) {Log.e(TAG, e.toString());}
183 } else {
184 Log.w(TAG, "Proxy not attached to service");
185 if (DBG) log(Log.getStackTraceString(new Throwable()));
186 }
187 return null;
188 }
189
190 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200191 * Returns true if the specified Bluetooth device is connected.
192 * Returns false if not connected, or if this proxy object is not
193 * currently connected to the Map service.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700194 */
195 public boolean isConnected(BluetoothDevice device) {
196 if (VDBG) log("isConnected(" + device + ")");
197 if (mService != null) {
198 try {
199 return mService.isConnected(device);
200 } catch (RemoteException e) {Log.e(TAG, e.toString());}
201 } else {
202 Log.w(TAG, "Proxy not attached to service");
203 if (DBG) log(Log.getStackTraceString(new Throwable()));
204 }
205 return false;
206 }
207
208 /**
Kim Schulz0d376052013-08-22 11:18:02 +0200209 * Initiate connection. Initiation of outgoing connections is not
210 * supported for MAP server.
Matthew Xiefe3807a2013-07-18 17:31:50 -0700211 */
Kim Schulz0d376052013-08-22 11:18:02 +0200212 public boolean connect(BluetoothDevice device) {
213 if (DBG) log("connect(" + device + ")" + "not supported for MAPS");
214 return false;
215 }
216
217 /**
218 * Initiate disconnect.
219 *
220 * @param device Remote Bluetooth Device
221 * @return false on error,
222 * true otherwise
223 */
224 public boolean disconnect(BluetoothDevice device) {
225 if (DBG) log("disconnect(" + device + ")");
226 if (mService != null && isEnabled() &&
227 isValidDevice(device)) {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700228 try {
Kim Schulz0d376052013-08-22 11:18:02 +0200229 return mService.disconnect(device);
230 } catch (RemoteException e) {
231 Log.e(TAG, Log.getStackTraceString(new Throwable()));
232 return false;
233 }
Matthew Xiefe3807a2013-07-18 17:31:50 -0700234 }
Kim Schulz0d376052013-08-22 11:18:02 +0200235 if (mService == null) Log.w(TAG, "Proxy not attached to service");
Matthew Xiefe3807a2013-07-18 17:31:50 -0700236 return false;
237 }
238
239 /**
240 * Check class bits for possible Map support.
241 * This is a simple heuristic that tries to guess if a device with the
242 * given class bits might support Map. It is not accurate for all
243 * devices. It tries to err on the side of false positives.
244 * @return True if this device might support Map.
245 */
246 public static boolean doesClassMatchSink(BluetoothClass btClass) {
247 // TODO optimize the rule
248 switch (btClass.getDeviceClass()) {
249 case BluetoothClass.Device.COMPUTER_DESKTOP:
250 case BluetoothClass.Device.COMPUTER_LAPTOP:
251 case BluetoothClass.Device.COMPUTER_SERVER:
252 case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
253 return true;
254 default:
255 return false;
256 }
257 }
258
Kim Schulz0d376052013-08-22 11:18:02 +0200259 /**
260 * Get the list of connected devices. Currently at most one.
261 *
262 * @return list of connected devices
263 */
264 public List<BluetoothDevice> getConnectedDevices() {
265 if (DBG) log("getConnectedDevices()");
266 if (mService != null && isEnabled()) {
267 try {
268 return mService.getConnectedDevices();
269 } catch (RemoteException e) {
270 Log.e(TAG, Log.getStackTraceString(new Throwable()));
271 return new ArrayList<BluetoothDevice>();
272 }
273 }
274 if (mService == null) Log.w(TAG, "Proxy not attached to service");
275 return new ArrayList<BluetoothDevice>();
276 }
277
278 /**
279 * Get the list of devices matching specified states. Currently at most one.
280 *
281 * @return list of matching devices
282 */
283 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
284 if (DBG) log("getDevicesMatchingStates()");
285 if (mService != null && isEnabled()) {
286 try {
287 return mService.getDevicesMatchingConnectionStates(states);
288 } catch (RemoteException e) {
289 Log.e(TAG, Log.getStackTraceString(new Throwable()));
290 return new ArrayList<BluetoothDevice>();
291 }
292 }
293 if (mService == null) Log.w(TAG, "Proxy not attached to service");
294 return new ArrayList<BluetoothDevice>();
295 }
296
297 /**
298 * Get connection state of device
299 *
300 * @return device connection state
301 */
302 public int getConnectionState(BluetoothDevice device) {
303 if (DBG) log("getConnectionState(" + device + ")");
304 if (mService != null && isEnabled() &&
305 isValidDevice(device)) {
306 try {
307 return mService.getConnectionState(device);
308 } catch (RemoteException e) {
309 Log.e(TAG, Log.getStackTraceString(new Throwable()));
310 return BluetoothProfile.STATE_DISCONNECTED;
311 }
312 }
313 if (mService == null) Log.w(TAG, "Proxy not attached to service");
314 return BluetoothProfile.STATE_DISCONNECTED;
315 }
316
317 /**
318 * Set priority of the profile
319 *
320 * <p> The device should already be paired.
321 * Priority can be one of {@link #PRIORITY_ON} or
322 * {@link #PRIORITY_OFF},
323 *
324 * @param device Paired bluetooth device
325 * @param priority
326 * @return true if priority is set, false on error
327 */
328 public boolean setPriority(BluetoothDevice device, int priority) {
329 if (DBG) log("setPriority(" + device + ", " + priority + ")");
330 if (mService != null && isEnabled() &&
331 isValidDevice(device)) {
332 if (priority != BluetoothProfile.PRIORITY_OFF &&
333 priority != BluetoothProfile.PRIORITY_ON) {
334 return false;
335 }
336 try {
337 return mService.setPriority(device, priority);
338 } catch (RemoteException e) {
339 Log.e(TAG, Log.getStackTraceString(new Throwable()));
340 return false;
341 }
342 }
343 if (mService == null) Log.w(TAG, "Proxy not attached to service");
344 return false;
345 }
346
347 /**
348 * Get the priority of the profile.
349 *
350 * <p> The priority can be any of:
351 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
352 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
353 *
354 * @param device Bluetooth device
355 * @return priority of the device
356 */
357 public int getPriority(BluetoothDevice device) {
358 if (VDBG) log("getPriority(" + device + ")");
359 if (mService != null && isEnabled() &&
360 isValidDevice(device)) {
361 try {
362 return mService.getPriority(device);
363 } catch (RemoteException e) {
364 Log.e(TAG, Log.getStackTraceString(new Throwable()));
365 return PRIORITY_OFF;
366 }
367 }
368 if (mService == null) Log.w(TAG, "Proxy not attached to service");
369 return PRIORITY_OFF;
370 }
371
Matthew Xie9b693992013-10-10 11:21:40 -0700372 private final ServiceConnection mConnection = new ServiceConnection() {
Matthew Xiefe3807a2013-07-18 17:31:50 -0700373 public void onServiceConnected(ComponentName className, IBinder service) {
374 if (DBG) log("Proxy object connected");
375 mService = IBluetoothMap.Stub.asInterface(service);
376 if (mServiceListener != null) {
Kim Schulz0d376052013-08-22 11:18:02 +0200377 mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
Matthew Xiefe3807a2013-07-18 17:31:50 -0700378 }
379 }
380 public void onServiceDisconnected(ComponentName className) {
381 if (DBG) log("Proxy object disconnected");
382 mService = null;
383 if (mServiceListener != null) {
Kim Schulz0d376052013-08-22 11:18:02 +0200384 mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
Matthew Xiefe3807a2013-07-18 17:31:50 -0700385 }
386 }
387 };
388
389 private static void log(String msg) {
390 Log.d(TAG, msg);
391 }
Kim Schulz0d376052013-08-22 11:18:02 +0200392
393 private boolean isEnabled() {
394 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
395 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
396 log("Bluetooth is Not enabled");
397 return false;
398 }
399 private boolean isValidDevice(BluetoothDevice device) {
400 if (device == null) return false;
401
402 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
403 return false;
404 }
405
406
Matthew Xiefe3807a2013-07-18 17:31:50 -0700407}