BluetoothMap.java revision 9b6939939901cb82bc6fca93aad3810a4936dfc6
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
17package android.bluetooth;
18
19import java.util.List;
20import java.util.ArrayList;
21import 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 */
35public final class BluetoothMap implements BluetoothProfile {
36
37    private static final String TAG = "BluetoothMap";
38    private static final boolean DBG = true;
39    private static final boolean VDBG = false;
40
41    public static final String ACTION_CONNECTION_STATE_CHANGED =
42        "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
43
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;
51
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
57    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...");
76                                    doBind();
77                                }
78                            } catch (Exception re) {
79                                Log.e(TAG,"",re);
80                            }
81                        }
82                    }
83                }
84        };
85
86    /**
87     * Create a BluetoothMap proxy object.
88     */
89    /*package*/ BluetoothMap(Context context, ServiceListener l) {
90        if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
91        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        }
102        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;
112        }
113        return true;
114    }
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);
145                } 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    /**
191     * 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.
194     */
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    /**
209     * Initiate connection. Initiation of outgoing connections is not
210     * supported for MAP server.
211     */
212    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)) {
228            try {
229                return mService.disconnect(device);
230            } catch (RemoteException e) {
231              Log.e(TAG, Log.getStackTraceString(new Throwable()));
232              return false;
233            }
234        }
235        if (mService == null) Log.w(TAG, "Proxy not attached to service");
236        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
259    /**
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
372    private final ServiceConnection mConnection = new ServiceConnection() {
373        public void onServiceConnected(ComponentName className, IBinder service) {
374            if (DBG) log("Proxy object connected");
375            mService = IBluetoothMap.Stub.asInterface(service);
376            if (mServiceListener != null) {
377                mServiceListener.onServiceConnected(BluetoothProfile.MAP, BluetoothMap.this);
378            }
379        }
380        public void onServiceDisconnected(ComponentName className) {
381            if (DBG) log("Proxy object disconnected");
382            mService = null;
383            if (mServiceListener != null) {
384                mServiceListener.onServiceDisconnected(BluetoothProfile.MAP);
385            }
386        }
387    };
388
389    private static void log(String msg) {
390        Log.d(TAG, msg);
391    }
392
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
407}
408