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