BluetoothMap.java revision fe3807a5b23f54f6539436d71aa0cd931a2b76f0
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 android.content.ComponentName; 20import android.content.Context; 21import android.content.Intent; 22import android.content.ServiceConnection; 23import android.os.RemoteException; 24import android.os.IBinder; 25import android.os.ServiceManager; 26import android.util.Log; 27 28/** 29 * This class provides the APIs to control the Bluetooth MAP 30 * Profile. 31 *@hide 32 */ 33public class BluetoothMap { 34 35 private static final String TAG = "BluetoothMap"; 36 private static final boolean DBG = true; 37 private static final boolean VDBG = false; 38 39 /** int extra for MAP_STATE_CHANGED_ACTION */ 40 public static final String MAP_STATE = 41 "android.bluetooth.map.intent.MAP_STATE"; 42 /** int extra for MAP_STATE_CHANGED_ACTION */ 43 public static final String MAP_PREVIOUS_STATE = 44 "android.bluetooth.map.intent.MAP_PREVIOUS_STATE"; 45 46 /** Indicates the state of a Map connection state has changed. 47 * This intent will always contain MAP_STATE, MAP_PREVIOUS_STATE and 48 * BluetoothIntent.ADDRESS extras. 49 */ 50 public static final String MAP_STATE_CHANGED_ACTION = 51 "android.bluetooth.map.intent.action.MAP_STATE_CHANGED"; 52 53 private IBluetoothMap mService; 54 private final Context mContext; 55 private ServiceListener mServiceListener; 56 private BluetoothAdapter mAdapter; 57 58 /** There was an error trying to obtain the state */ 59 public static final int STATE_ERROR = -1; 60 /** No client currently connected */ 61 public static final int STATE_DISCONNECTED = 0; 62 /** Connection attempt in progress */ 63 public static final int STATE_CONNECTING = 1; 64 /** Client is currently connected */ 65 public static final int STATE_CONNECTED = 2; 66 67 public static final int RESULT_FAILURE = 0; 68 public static final int RESULT_SUCCESS = 1; 69 /** Connection canceled before completion. */ 70 public static final int RESULT_CANCELED = 2; 71 72 /** 73 * An interface for notifying Bluetooth PCE IPC clients when they have 74 * been connected to the BluetoothMap service. 75 */ 76 public interface ServiceListener { 77 /** 78 * Called to notify the client when this proxy object has been 79 * connected to the BluetoothMap service. Clients must wait for 80 * this callback before making IPC calls on the BluetoothMap 81 * service. 82 */ 83 public void onServiceConnected(BluetoothMap proxy); 84 85 /** 86 * Called to notify the client that this proxy object has been 87 * disconnected from the BluetoothMap service. Clients must not 88 * make IPC calls on the BluetoothMap service after this callback. 89 * This callback will currently only occur if the application hosting 90 * the BluetoothMap service, but may be called more often in future. 91 */ 92 public void onServiceDisconnected(); 93 } 94 95 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 96 new IBluetoothStateChangeCallback.Stub() { 97 public void onBluetoothStateChange(boolean up) { 98 if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up); 99 if (!up) { 100 if (VDBG) Log.d(TAG,"Unbinding service..."); 101 synchronized (mConnection) { 102 try { 103 mService = null; 104 mContext.unbindService(mConnection); 105 } catch (Exception re) { 106 Log.e(TAG,"",re); 107 } 108 } 109 } else { 110 synchronized (mConnection) { 111 try { 112 if (mService == null) { 113 if (VDBG) Log.d(TAG,"Binding service..."); 114 if (!mContext.bindService( 115 new Intent(IBluetoothMap.class.getName()), 116 mConnection, 0)) { 117 Log.e(TAG, "Could not bind to Bluetooth MAP Service"); 118 } 119 } 120 } catch (Exception re) { 121 Log.e(TAG,"",re); 122 } 123 } 124 } 125 } 126 }; 127 128 /** 129 * Create a BluetoothMap proxy object. 130 */ 131 public BluetoothMap(Context context, ServiceListener l) { 132 mContext = context; 133 mServiceListener = l; 134 mAdapter = BluetoothAdapter.getDefaultAdapter(); 135 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 136 if (mgr != null) { 137 try { 138 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 139 } catch (RemoteException e) { 140 Log.e(TAG,"",e); 141 } 142 } 143 if (!context.bindService(new Intent(IBluetoothMap.class.getName()), mConnection, 0)) { 144 Log.e(TAG, "Could not bind to Bluetooth Map Service"); 145 } 146 } 147 148 protected void finalize() throws Throwable { 149 try { 150 close(); 151 } finally { 152 super.finalize(); 153 } 154 } 155 156 /** 157 * Close the connection to the backing service. 158 * Other public functions of BluetoothMap will return default error 159 * results once close() has been called. Multiple invocations of close() 160 * are ok. 161 */ 162 public synchronized void close() { 163 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 164 if (mgr != null) { 165 try { 166 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 167 } catch (Exception e) { 168 Log.e(TAG,"",e); 169 } 170 } 171 172 synchronized (mConnection) { 173 if (mService != null) { 174 try { 175 mService = null; 176 mContext.unbindService(mConnection); 177 mConnection = null; 178 } catch (Exception re) { 179 Log.e(TAG,"",re); 180 } 181 } 182 } 183 mServiceListener = null; 184 } 185 186 /** 187 * Get the current state of the BluetoothMap service. 188 * @return One of the STATE_ return codes, or STATE_ERROR if this proxy 189 * object is currently not connected to the Map service. 190 */ 191 public int getState() { 192 if (VDBG) log("getState()"); 193 if (mService != null) { 194 try { 195 return mService.getState(); 196 } catch (RemoteException e) {Log.e(TAG, e.toString());} 197 } else { 198 Log.w(TAG, "Proxy not attached to service"); 199 if (DBG) log(Log.getStackTraceString(new Throwable())); 200 } 201 return BluetoothMap.STATE_ERROR; 202 } 203 204 /** 205 * Get the currently connected remote Bluetooth device (PCE). 206 * @return The remote Bluetooth device, or null if not in connected or 207 * connecting state, or if this proxy object is not connected to 208 * the Map service. 209 */ 210 public BluetoothDevice getClient() { 211 if (VDBG) log("getClient()"); 212 if (mService != null) { 213 try { 214 return mService.getClient(); 215 } catch (RemoteException e) {Log.e(TAG, e.toString());} 216 } else { 217 Log.w(TAG, "Proxy not attached to service"); 218 if (DBG) log(Log.getStackTraceString(new Throwable())); 219 } 220 return null; 221 } 222 223 /** 224 * Returns true if the specified Bluetooth device is connected (does not 225 * include connecting). Returns false if not connected, or if this proxy 226 * object is not currently connected to the Map service. 227 */ 228 public boolean isConnected(BluetoothDevice device) { 229 if (VDBG) log("isConnected(" + device + ")"); 230 if (mService != null) { 231 try { 232 return mService.isConnected(device); 233 } catch (RemoteException e) {Log.e(TAG, e.toString());} 234 } else { 235 Log.w(TAG, "Proxy not attached to service"); 236 if (DBG) log(Log.getStackTraceString(new Throwable())); 237 } 238 return false; 239 } 240 241 /** 242 * Disconnects the current Map Client. Currently this call blocks, 243 * it may soon be made asynchronous. Returns false if this proxy object is 244 * not currently connected to the Map service. 245 */ 246 public boolean disconnect() { 247 if (DBG) log("disconnect()"); 248 if (mService != null) { 249 try { 250 mService.disconnect(); 251 return true; 252 } catch (RemoteException e) {Log.e(TAG, e.toString());} 253 } else { 254 Log.w(TAG, "Proxy not attached to service"); 255 if (DBG) log(Log.getStackTraceString(new Throwable())); 256 } 257 return false; 258 } 259 260 /** 261 * Check class bits for possible Map support. 262 * This is a simple heuristic that tries to guess if a device with the 263 * given class bits might support Map. It is not accurate for all 264 * devices. It tries to err on the side of false positives. 265 * @return True if this device might support Map. 266 */ 267 public static boolean doesClassMatchSink(BluetoothClass btClass) { 268 // TODO optimize the rule 269 switch (btClass.getDeviceClass()) { 270 case BluetoothClass.Device.COMPUTER_DESKTOP: 271 case BluetoothClass.Device.COMPUTER_LAPTOP: 272 case BluetoothClass.Device.COMPUTER_SERVER: 273 case BluetoothClass.Device.COMPUTER_UNCATEGORIZED: 274 return true; 275 default: 276 return false; 277 } 278 } 279 280 private ServiceConnection mConnection = new ServiceConnection() { 281 public void onServiceConnected(ComponentName className, IBinder service) { 282 if (DBG) log("Proxy object connected"); 283 mService = IBluetoothMap.Stub.asInterface(service); 284 if (mServiceListener != null) { 285 mServiceListener.onServiceConnected(BluetoothMap.this); 286 } 287 } 288 public void onServiceDisconnected(ComponentName className) { 289 if (DBG) log("Proxy object disconnected"); 290 mService = null; 291 if (mServiceListener != null) { 292 mServiceListener.onServiceDisconnected(); 293 } 294 } 295 }; 296 297 private static void log(String msg) { 298 Log.d(TAG, msg); 299 } 300} 301