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