• 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 java.util.List;
20 import java.util.ArrayList;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.os.*;
26 import android.util.Log;
27 
28 /**
29  * This class provides the APIs to control the Bluetooth MAP
30  * Profile.
31  *@hide
32  */
33 public final class BluetoothMap implements BluetoothProfile {
34 
35     private static final String TAG = "BluetoothMap";
36     private static final boolean DBG = true;
37     private static final boolean VDBG = false;
38 
39     public static final String ACTION_CONNECTION_STATE_CHANGED =
40         "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
41 
42     private IBluetoothMap mService;
43     private final Context mContext;
44     private ServiceListener mServiceListener;
45     private BluetoothAdapter mAdapter;
46 
47     /** There was an error trying to obtain the state */
48     public static final int STATE_ERROR        = -1;
49 
50     public static final int RESULT_FAILURE = 0;
51     public static final int RESULT_SUCCESS = 1;
52     /** Connection canceled before completion. */
53     public static final int RESULT_CANCELED = 2;
54 
55     final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
56             new IBluetoothStateChangeCallback.Stub() {
57                 public void onBluetoothStateChange(boolean up) {
58                     if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
59                     if (!up) {
60                         if (VDBG) Log.d(TAG,"Unbinding service...");
61                         synchronized (mConnection) {
62                             try {
63                                 mService = null;
64                                 mContext.unbindService(mConnection);
65                             } catch (Exception re) {
66                                 Log.e(TAG,"",re);
67                             }
68                         }
69                     } else {
70                         synchronized (mConnection) {
71                             try {
72                                 if (mService == null) {
73                                     if (VDBG) Log.d(TAG,"Binding service...");
74                                     doBind();
75                                 }
76                             } catch (Exception re) {
77                                 Log.e(TAG,"",re);
78                             }
79                         }
80                     }
81                 }
82         };
83 
84     /**
85      * Create a BluetoothMap proxy object.
86      */
BluetoothMap(Context context, ServiceListener l)87     /*package*/ BluetoothMap(Context context, ServiceListener l) {
88         if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
89         mContext = context;
90         mServiceListener = l;
91         mAdapter = BluetoothAdapter.getDefaultAdapter();
92         IBluetoothManager mgr = mAdapter.getBluetoothManager();
93         if (mgr != null) {
94             try {
95                 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
96             } catch (RemoteException e) {
97                 Log.e(TAG,"",e);
98             }
99         }
100         doBind();
101     }
102 
doBind()103     boolean doBind() {
104         Intent intent = new Intent(IBluetoothMap.class.getName());
105         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
106         intent.setComponent(comp);
107         if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
108                 android.os.Process.myUserHandle())) {
109             Log.e(TAG, "Could not bind to Bluetooth MAP Service with " + intent);
110             return false;
111         }
112         return true;
113     }
114 
finalize()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      */
close()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);
144                 } 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      */
getState()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      */
getClient()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     /**
190      * 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.
193      */
isConnected(BluetoothDevice device)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     /**
208      * Initiate connection. Initiation of outgoing connections is not
209      * supported for MAP server.
210      */
connect(BluetoothDevice device)211     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      */
disconnect(BluetoothDevice device)223     public boolean disconnect(BluetoothDevice device) {
224         if (DBG) log("disconnect(" + device + ")");
225         if (mService != null && isEnabled() &&
226             isValidDevice(device)) {
227             try {
228                 return mService.disconnect(device);
229             } catch (RemoteException e) {
230               Log.e(TAG, Log.getStackTraceString(new Throwable()));
231               return false;
232             }
233         }
234         if (mService == null) Log.w(TAG, "Proxy not attached to service");
235         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      */
doesClassMatchSink(BluetoothClass btClass)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 
258     /**
259      * Get the list of connected devices. Currently at most one.
260      *
261      * @return list of connected devices
262      */
getConnectedDevices()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      */
getDevicesMatchingConnectionStates(int[] states)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      */
getConnectionState(BluetoothDevice device)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      */
setPriority(BluetoothDevice device, int priority)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      */
getPriority(BluetoothDevice device)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 
371     private final ServiceConnection mConnection = new ServiceConnection() {
372         public void onServiceConnected(ComponentName className, IBinder service) {
373             if (DBG) log("Proxy object connected");
374             mService = IBluetoothMap.Stub.asInterface(service);
375             if (mServiceListener != null) {
376                 mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
377             }
378         }
379         public void onServiceDisconnected(ComponentName className) {
380             if (DBG) log("Proxy object disconnected");
381             mService = null;
382             if (mServiceListener != null) {
383                 mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
384             }
385         }
386     };
387 
log(String msg)388     private static void log(String msg) {
389         Log.d(TAG, msg);
390     }
391 
isEnabled()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     }
isValidDevice(BluetoothDevice device)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 
406 }
407