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