blob: de14b5b4e0bb564a138a1ebc81c727ad9c15e927 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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/**
18 * TODO: Move this to
Nick Pellybd022f42009-08-14 18:33:38 -070019 * java/services/com/android/server/BluetoothService.java
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020 * and make the contructor package private again.
21 *
22 * @hide
23 */
24
25package android.server;
26
Nick Pellybd022f42009-08-14 18:33:38 -070027import android.bluetooth.BluetoothAdapter;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -070028import android.bluetooth.BluetoothClass;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.bluetooth.BluetoothDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.bluetooth.BluetoothHeadset;
Jaikumar Ganesh10eac972009-09-21 12:48:51 -070031import android.bluetooth.BluetoothUuid;
Nick Pellybd022f42009-08-14 18:33:38 -070032import android.bluetooth.IBluetooth;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -070033import android.bluetooth.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.BroadcastReceiver;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.os.Binder;
40import android.os.Handler;
41import android.os.Message;
42import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070043import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.os.SystemService;
45import android.provider.Settings;
46import android.util.Log;
47
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070048import com.android.internal.app.IBatteryStats;
49
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import java.io.FileDescriptor;
51import java.io.PrintWriter;
52import java.io.UnsupportedEncodingException;
53import java.util.ArrayList;
54import java.util.Arrays;
55import java.util.HashMap;
56import java.util.Map;
57
Nick Pellybd022f42009-08-14 18:33:38 -070058public class BluetoothService extends IBluetooth.Stub {
59 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 private static final boolean DBG = true;
61
62 private int mNativeData;
63 private BluetoothEventLoop mEventLoop;
64 private IntentFilter mIntentFilter;
65 private boolean mIsAirplaneSensitive;
The Android Open Source Project10592532009-03-18 17:39:46 -070066 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070067 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 private boolean mIsDiscovering;
69
Nick Pellybd022f42009-08-14 18:33:38 -070070 private BluetoothAdapter mAdapter; // constant after init()
71 private final BondState mBondState = new BondState(); // local cache of bondings
72 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 private final Context mContext;
74
75 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
76 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
77
The Android Open Source Project10592532009-03-18 17:39:46 -070078 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
79 private static final int MESSAGE_FINISH_DISABLE = 2;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -070080 private static final int MESSAGE_UUID_INTENT = 3;
81
82 // The timeout used to sent the UUIDs Intent
83 // This timeout should be greater than the page timeout
84 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -070085
Nick Pellybd022f42009-08-14 18:33:38 -070086 private final Map<String, String> mAdapterProperties;
87 private final HashMap <String, Map<String, String>> mDeviceProperties;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070088
Jaikumar Ganesh10eac972009-09-21 12:48:51 -070089 private final HashMap <String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -070090 private final ArrayList <String> mUuidIntentTracker;
91
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 static {
93 classInitNative();
94 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095
Nick Pellybd022f42009-08-14 18:33:38 -070096 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -070098
99 // Need to do this in place of:
100 // mBatteryStats = BatteryStatsService.getService();
101 // Since we can not import BatteryStatsService from here. This class really needs to be
102 // moved to java/services/com/android/server/
103 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700106
107 if (isEnabledNative() == 1) {
108 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
109 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700111
Nick Pellyde893f52009-09-08 13:15:33 -0700112 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700114 mAdapterProperties = new HashMap<String, String>();
115 mDeviceProperties = new HashMap<String, Map<String,String>>();
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700116
117 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700118 mUuidIntentTracker = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 registerForAirplaneMode();
Nick Pellybd022f42009-08-14 18:33:38 -0700120 }
121
122 public synchronized void initAfterRegistration() {
123 mAdapter = (BluetoothAdapter) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
124 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
127 @Override
128 protected void finalize() throws Throwable {
129 if (mIsAirplaneSensitive) {
130 mContext.unregisterReceiver(mReceiver);
131 }
132 try {
133 cleanupNativeDataNative();
134 } finally {
135 super.finalize();
136 }
137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138
139 public boolean isEnabled() {
140 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellyde893f52009-09-08 13:15:33 -0700141 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143
The Android Open Source Project10592532009-03-18 17:39:46 -0700144 public int getBluetoothState() {
145 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
146 return mBluetoothState;
147 }
148
149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 /**
151 * Bring down bluetooth and disable BT in settings. Returns true on success.
152 */
153 public boolean disable() {
154 return disable(true);
155 }
156
157 /**
158 * Bring down bluetooth. Returns true on success.
159 *
160 * @param saveSetting If true, disable BT in settings
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 */
162 public synchronized boolean disable(boolean saveSetting) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700163 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
The Android Open Source Project10592532009-03-18 17:39:46 -0700165 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700166 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700167 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700168 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700169 break;
170 default:
171 return false;
172 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 if (mEnableThread != null && mEnableThread.isAlive()) {
174 return false;
175 }
Nick Pellyde893f52009-09-08 13:15:33 -0700176 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700177
178 // Allow 3 seconds for profiles to gracefully disconnect
179 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700180 // BluetoothService when it is done shutting down
The Android Open Source Project10592532009-03-18 17:39:46 -0700181 mHandler.sendMessageDelayed(
182 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
183 return true;
184 }
185
186
187 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700188 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700189 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
191 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700192 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 disableNative();
194
195 // mark in progress bondings as cancelled
196 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700197 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
199 }
200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700202 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
203 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
205
The Android Open Source Project10592532009-03-18 17:39:46 -0700206 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700207 mAdapterProperties.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 if (saveSetting) {
210 persistBluetoothOnSetting(false);
211 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700212
Nick Pellyde893f52009-09-08 13:15:33 -0700213 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700214
215 // Log bluetooth off to battery stats.
216 long ident = Binder.clearCallingIdentity();
217 try {
218 mBatteryStats.noteBluetoothOff();
219 } catch (RemoteException e) {
220 } finally {
221 Binder.restoreCallingIdentity(ident);
222 }
Nick Pelly997c7612009-03-24 20:44:48 -0700223
224 if (mRestart) {
225 mRestart = false;
226 enable();
227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 }
229
The Android Open Source Project10592532009-03-18 17:39:46 -0700230 /** Bring up BT and persist BT on in settings */
231 public boolean enable() {
232 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 }
234
235 /**
236 * Enable this Bluetooth device, asynchronously.
237 * This turns on/off the underlying hardware.
238 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700239 * @param saveSetting If true, persist the new state of BT in settings
240 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700242 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
244 "Need BLUETOOTH_ADMIN permission");
245
246 // Airplane mode can prevent Bluetooth radio from being turned on.
247 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
248 return false;
249 }
Nick Pellyde893f52009-09-08 13:15:33 -0700250 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 return false;
252 }
253 if (mEnableThread != null && mEnableThread.isAlive()) {
254 return false;
255 }
Nick Pellyde893f52009-09-08 13:15:33 -0700256 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700257 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 mEnableThread.start();
259 return true;
260 }
261
Nick Pelly997c7612009-03-24 20:44:48 -0700262 /** Forcibly restart Bluetooth if it is on */
263 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700264 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700265 return;
266 }
267 mRestart = true;
268 if (!disable(false)) {
269 mRestart = false;
270 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700271 }
Nick Pelly997c7612009-03-24 20:44:48 -0700272
The Android Open Source Project10592532009-03-18 17:39:46 -0700273 private synchronized void setBluetoothState(int state) {
274 if (state == mBluetoothState) {
275 return;
276 }
277
278 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
279
Nick Pellyde893f52009-09-08 13:15:33 -0700280 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
281 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
282 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700283 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
284
285 mBluetoothState = state;
286
287 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
288 }
289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 private final Handler mHandler = new Handler() {
291 @Override
292 public void handleMessage(Message msg) {
293 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700294 case MESSAGE_REGISTER_SDP_RECORDS:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 //TODO: Don't assume HSP/HFP is running, don't use sdptool,
296 if (isEnabled()) {
297 SystemService.start("hsag");
298 SystemService.start("hfag");
Nick Pelly03c707a2009-07-14 21:32:14 -0700299 SystemService.start("opush");
Jaikumar Ganesh67542962009-07-23 21:32:45 -0700300 SystemService.start("pbap");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700302 break;
303 case MESSAGE_FINISH_DISABLE:
304 finishDisable(msg.arg1 != 0);
305 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700306 case MESSAGE_UUID_INTENT:
307 String address = (String)msg.obj;
308 if (address != null)
309 sendUuidIntent(address);
310 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 }
312 }
313 };
314
315 private EnableThread mEnableThread;
316
317 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700319 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 mSaveSetting = saveSetting;
321 }
322 public void run() {
323 boolean res = (enableNative() == 0);
324 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700325 int retryCount = 2;
326 boolean running = false;
327 while ((retryCount-- > 0) && !running) {
328 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700329 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700330 // thing. Check periodically for a while.
331 int pollCount = 5;
332 while ((pollCount-- > 0) && !running) {
333 if (mEventLoop.isEventLoopRunning()) {
334 running = true;
335 break;
336 }
337 try {
338 Thread.sleep(100);
339 } catch (InterruptedException e) {}
340 }
341 }
342 if (!running) {
343 log("bt EnableThread giving up");
344 res = false;
345 disableNative();
346 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 }
348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349
350 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700351 if (!setupNativeDataNative()) {
352 return;
353 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 if (mSaveSetting) {
355 persistBluetoothOnSetting(true);
356 }
357 mIsDiscovering = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 mBondState.loadBondState();
The Android Open Source Project10592532009-03-18 17:39:46 -0700359 mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS),
360 3000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361
The Android Open Source Project10592532009-03-18 17:39:46 -0700362 // Log bluetooth on to battery stats.
363 long ident = Binder.clearCallingIdentity();
364 try {
365 mBatteryStats.noteBluetoothOn();
366 } catch (RemoteException e) {
367 } finally {
368 Binder.restoreCallingIdentity(ident);
369 }
370 }
371
372 mEnableThread = null;
373
374 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700375 BluetoothAdapter.STATE_ON :
376 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700377
378 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700380 String[] propVal = {"Pairable", getProperty("Pairable")};
381 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700383
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900384 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
385 disable(false);
386 }
387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
389 }
390
391 private void persistBluetoothOnSetting(boolean bluetoothOn) {
392 long origCallerIdentityToken = Binder.clearCallingIdentity();
393 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
394 bluetoothOn ? 1 : 0);
395 Binder.restoreCallingIdentity(origCallerIdentityToken);
396 }
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 /* package */ BondState getBondState() {
399 return mBondState;
400 }
401
402 /** local cache of bonding state.
403 /* we keep our own state to track the intermediate state BONDING, which
404 /* bluez does not track.
405 * All addreses must be passed in upper case.
406 */
407 public class BondState {
408 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
409 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
410 private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
The Android Open Source Project10592532009-03-18 17:39:46 -0700411 // List of all the vendor_id prefix of Bluetooth addresses for
412 // which auto pairing is not attempted.
413 // The following companies are included in the list below:
414 // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
415 // Parrot, Zhongshan General K-mate Electronics, Great Well
416 // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
417 // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
Nick Pellyadbaef22009-09-14 19:04:47 -0700418 // Continental Automotive, Harman/Becker, Panasonic/Kyushu Ten
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700419 private final ArrayList<String> mAutoPairingAddressBlacklist =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 new ArrayList<String>(Arrays.asList(
The Android Open Source Project10592532009-03-18 17:39:46 -0700421 "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
422 "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
423 "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
424 "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59",
Nick Pellyadbaef22009-09-14 19:04:47 -0700425 "00:0A:30", "00:1E:AE", "00:1C:D7", "00:80:F0"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 ));
427
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700428 // List of names of Bluetooth devices for which auto pairing should be
429 // disabled.
430 private final ArrayList<String> mAutoPairingNameBlacklist =
431 new ArrayList<String>(Arrays.asList(
432 "Motorola IHF1000", "i.TechBlueBAND", "X5 Stereo v1.3"));
433
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700434 // If this is an outgoing connection, store the address.
435 // There can be only 1 pending outgoing connection at a time,
436 private String mPendingOutgoingBonding;
437
438 private synchronized void setPendingOutgoingBonding(String address) {
439 mPendingOutgoingBonding = address;
440 }
441
442 public synchronized String getPendingOutgoingBonding() {
443 return mPendingOutgoingBonding;
444 }
445
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700447 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 return;
449 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700450 String []bonds = null;
451 String val = getProperty("Devices");
452 if (val != null) {
453 bonds = val.split(",");
454 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 if (bonds == null) {
456 return;
457 }
458 mState.clear();
459 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700460 for (String device : bonds) {
461 mState.put(getAddressFromObjectPath(device).toUpperCase(),
462 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 }
464 }
465
466 public synchronized void setBondState(String address, int state) {
467 setBondState(address, state, 0);
468 }
469
470 /** reason is ignored unless state == BOND_NOT_BONDED */
471 public synchronized void setBondState(String address, int state, int reason) {
472 int oldState = getBondState(address);
473 if (oldState == state) {
474 return;
475 }
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700476
477 // Check if this was an pending outgoing bonding.
478 // If yes, reset the state.
479 if (oldState == BluetoothDevice.BOND_BONDING) {
480 if (address.equals(mPendingOutgoingBonding)) {
481 mPendingOutgoingBonding = null;
482 }
483 }
484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
486 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700487 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
488 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
489 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
490 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
491 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 if (reason <= 0) {
493 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
494 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
495 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
496 }
Nick Pelly005b2282009-09-10 10:21:56 -0700497 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 mState.remove(address);
499 } else {
500 mState.put(address, state);
501 }
502
503 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
504 }
505
506 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700507 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 if (address.startsWith(blacklistAddress)) return true;
509 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700510
511 String name = getRemoteName(address);
512 if (name != null) {
513 for (String blacklistName : mAutoPairingNameBlacklist) {
514 if (name.equals(blacklistName)) return true;
515 }
516 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 return false;
518 }
519
520 public synchronized int getBondState(String address) {
521 Integer state = mState.get(address);
522 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700523 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525 return state.intValue();
526 }
527
528 private synchronized String[] listInState(int state) {
529 ArrayList<String> result = new ArrayList<String>(mState.size());
530 for (Map.Entry<String, Integer> e : mState.entrySet()) {
531 if (e.getValue().intValue() == state) {
532 result.add(e.getKey());
533 }
534 }
535 return result.toArray(new String[result.size()]);
536 }
537
538 public synchronized void addAutoPairingFailure(String address) {
539 if (!mAutoPairingFailures.contains(address)) {
540 mAutoPairingFailures.add(address);
541 }
542 }
543
544 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
545 return getAttempt(address) != 0;
546 }
547
548 public synchronized void clearPinAttempts(String address) {
549 mPinAttempt.remove(address);
550 }
551
552 public synchronized boolean hasAutoPairingFailed(String address) {
553 return mAutoPairingFailures.contains(address);
554 }
555
556 public synchronized int getAttempt(String address) {
557 Integer attempt = mPinAttempt.get(address);
558 if (attempt == null) {
559 return 0;
560 }
561 return attempt.intValue();
562 }
563
564 public synchronized void attempt(String address) {
565 Integer attempt = mPinAttempt.get(address);
566 int newAttempt;
567 if (attempt == null) {
568 newAttempt = 1;
569 } else {
570 newAttempt = attempt.intValue() + 1;
571 }
572 mPinAttempt.put(address, new Integer(newAttempt));
573 }
574
575 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576
577 private static String toBondStateString(int bondState) {
578 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700579 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 return "not bonded";
581 case BluetoothDevice.BOND_BONDING:
582 return "bonding";
583 case BluetoothDevice.BOND_BONDED:
584 return "bonded";
585 default:
586 return "??????";
587 }
588 }
589
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -0700590 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
591 return mAdapterProperties.isEmpty();
592 }
593
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700594 /*package*/synchronized void getAllProperties() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -0700596 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700598 String properties[] = (String [])getAdapterPropertiesNative();
599 // The String Array consists of key-value pairs.
600 if (properties == null) {
601 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
602 return;
603 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700604
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700605 for (int i = 0; i < properties.length; i++) {
606 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700607 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700608 int len;
609 if (name == null) {
610 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
611 continue;
612 }
613 if (name.equals("Devices")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700614 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700615 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700616 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700617 str.append(properties[++i]);
618 str.append(",");
619 }
620 if (len > 0) {
621 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700622 }
623 } else {
624 newValue = properties[++i];
625 }
Nick Pellybd022f42009-08-14 18:33:38 -0700626 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700627 }
628
629 // Add adapter object path property.
630 String adapterPath = getAdapterPathNative();
631 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -0700632 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700634
635 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700636 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700637 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638
639 public synchronized boolean setName(String name) {
640 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
641 "Need BLUETOOTH_ADMIN permission");
642 if (name == null) {
643 return false;
644 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700645 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800647
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700648 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
649 // Either have a single property function with Object as the parameter
650 // or have a function for each property and then obfuscate in the JNI layer.
651 // The following looks dirty.
652 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800653 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700654 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700657 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700659 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 }
661
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700662 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700664 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666
667 /**
668 * Set the discoverability window for the device. A timeout of zero
669 * makes the device permanently discoverable (if the device is
670 * discoverable). Setting the timeout to a nonzero value does not make
671 * a device discoverable; you need to call setMode() to make the device
672 * explicitly discoverable.
673 *
674 * @param timeout_s The discoverable timeout in seconds.
675 */
676 public synchronized boolean setDiscoverableTimeout(int timeout) {
677 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
678 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700679 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700681
682 public synchronized boolean setScanMode(int mode) {
Nick Pelly18b1e792009-09-24 11:14:15 -0700683 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
684 "Need WRITE_SECURE_SETTINGS permission");
Nick Pellyde893f52009-09-08 13:15:33 -0700685 boolean pairable = false;
686 boolean discoverable = false;
687 switch (mode) {
688 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700689 pairable = false;
690 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700691 break;
692 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700693 pairable = true;
694 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700695 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700696 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700697 pairable = true;
698 discoverable = true;
Nick Pelly005b2282009-09-10 10:21:56 -0700699 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700700 default:
701 Log.w(TAG, "Requested invalid scan mode " + mode);
702 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700703 }
704 setPropertyBoolean("Pairable", pairable);
705 setPropertyBoolean("Discoverable", discoverable);
706
707 return true;
708 }
709
710 /*package*/ synchronized String getProperty (String name) {
Nick Pellybd022f42009-08-14 18:33:38 -0700711 if (!mAdapterProperties.isEmpty())
712 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700713 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -0700714 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700715 }
716
717 public synchronized String getAddress() {
718 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
719 return getProperty("Address");
720 }
721
722 public synchronized String getName() {
723 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
724 return getProperty("Name");
725 }
726
727 /**
728 * Returns the user-friendly name of a remote device. This value is
729 * returned from our local cache, which is updated when onPropertyChange
730 * event is received.
731 * Do not expect to retrieve the updated remote name immediately after
732 * changing the name on the remote device.
733 *
734 * @param address Bluetooth address of remote device.
735 *
736 * @return The user-friendly name of the specified remote device.
737 */
738 public synchronized String getRemoteName(String address) {
739 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700740 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700741 return null;
742 }
Nick Pellybd022f42009-08-14 18:33:38 -0700743 Map <String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700744 if (properties != null) return properties.get("Name");
745 return null;
746 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747
748 /**
749 * Get the discoverability window for the device. A timeout of zero
750 * means that the device is permanently discoverable (if the device is
751 * in the discoverable mode).
752 *
753 * @return The discoverability window of the device, in seconds. A negative
754 * value indicates an error.
755 */
756 public synchronized int getDiscoverableTimeout() {
757 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700758 String timeout = getProperty("DiscoverableTimeout");
759 if (timeout != null)
760 return Integer.valueOf(timeout);
761 else
762 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764
765 public synchronized int getScanMode() {
766 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700767 if (!isEnabled())
Nick Pellyde893f52009-09-08 13:15:33 -0700768 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700770 boolean pairable = getProperty("Pairable").equals("true");
771 boolean discoverable = getProperty("Discoverable").equals("true");
772 return bluezStringToScanMode (pairable, discoverable);
773 }
774
775 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
777 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700778 if (!isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 return false;
780 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700781 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700783
784 public synchronized boolean cancelDiscovery() {
785 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
786 "Need BLUETOOTH_ADMIN permission");
787 return stopDiscoveryNative();
788 }
789
790 public synchronized boolean isDiscovering() {
791 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
792 return mIsDiscovering;
793 }
794
795 /* package */ void setIsDiscovering(boolean isDiscovering) {
796 mIsDiscovering = isDiscovering;
797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798
799 public synchronized boolean createBond(String address) {
800 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
801 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700802 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 return false;
804 }
805 address = address.toUpperCase();
806
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700807 if (mBondState.getPendingOutgoingBonding() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 log("Ignoring createBond(): another device is bonding");
809 // a different device is currently bonding, fail
810 return false;
811 }
812
813 // Check for bond state only if we are not performing auto
814 // pairing exponential back-off attempts.
815 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -0700816 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 log("Ignoring createBond(): this device is already bonding or bonded");
818 return false;
819 }
820
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700821 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 return false;
823 }
824
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700825 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700827
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 return true;
829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
831 public synchronized boolean cancelBondProcess(String address) {
832 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
833 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700834 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 return false;
836 }
837 address = address.toUpperCase();
838 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
839 return false;
840 }
841
Nick Pelly005b2282009-09-10 10:21:56 -0700842 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700844 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 return true;
846 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847
848 public synchronized boolean removeBond(String address) {
849 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
850 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700851 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 return false;
853 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700854 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856
857 public synchronized String[] listBonds() {
858 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
859 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
860 }
861
862 public synchronized int getBondState(String address) {
863 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700864 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -0700865 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 }
867 return mBondState.getBondState(address.toUpperCase());
868 }
869
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700870 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -0700871 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700872 }
873
874 /*package*/ String[] getRemoteDeviceProperties(String address) {
875 String objectPath = getObjectPathFromAddress(address);
876 return (String [])getDevicePropertiesNative(objectPath);
877 }
878
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700879 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -0700880 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700881 if (properties != null) {
882 return properties.get(property);
883 } else {
884 // Query for remote device properties, again.
885 // We will need to reload the cache when we switch Bluetooth on / off
886 // or if we crash.
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700887 if (updateRemoteDevicePropertiesCache(address))
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700888 return getRemoteDeviceProperty(address, property);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700890 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
891 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700894 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
895 String[] propValues = getRemoteDeviceProperties(address);
896 if (propValues != null) {
897 addRemoteDeviceProperties(address, propValues);
898 return true;
899 }
900 return false;
901 }
902
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700903 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -0700904 /*
905 * We get a DeviceFound signal every time RSSI changes or name changes.
906 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -0700907 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700908 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -0700909 propertyValues = new HashMap<String, String>();
910 }
911
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700912 for (int i = 0; i < properties.length; i++) {
913 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700914 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700915 int len;
916 if (name == null) {
917 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
918 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700919 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700920 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700921 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700922 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700923 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700924 str.append(properties[++i]);
925 str.append(",");
926 }
927 if (len > 0) {
928 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700929 }
930 } else {
931 newValue = properties[++i];
932 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700933
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700934 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 }
Nick Pellybd022f42009-08-14 18:33:38 -0700936 mDeviceProperties.put(address, propertyValues);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700937
938 // We have added a new remote device or updated its properties.
939 // Also update the serviceChannel cache.
940 updateDeviceServiceChannelCache(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700943 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -0700944 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700945 }
946
947 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
948 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700949 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700950 if (propVal != null) {
951 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -0700952 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700953 } else {
954 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957
958 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +0800959 * Sets the remote device trust state.
960 *
961 * @return boolean to indicate operation success or fail
962 */
963 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -0700964 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +0800965 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
966 return false;
967 }
968
969 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
970 value ? 1 : 0);
971 }
972
973 /**
974 * Gets the remote device trust state as boolean.
975 * Note: this value may be
976 * retrieved from cache if we retrieved the data before *
977 *
978 * @return boolean to indicate trust or untrust state
979 */
980 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -0700981 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +0800982 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
983 return false;
984 }
985
986 String val = getRemoteDeviceProperty(address, "Trusted");
987 if (val == null) {
988 return false;
989 } else {
990 return val.equals("true") ? true : false;
991 }
992 }
993
994 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700995 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 * integer.
997 *
998 * Note: this value is retrieved from cache, because we get it during
999 * remote-device discovery.
1000 *
1001 * @return 32-bit integer encoding the remote major, minor, and service
1002 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 */
1004 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001005 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001006 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1007 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001009 String val = getRemoteDeviceProperty(address, "Class");
1010 if (val == null)
1011 return BluetoothClass.ERROR;
1012 else {
1013 return Integer.valueOf(val);
1014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001016
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017
1018 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001019 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001021 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001023 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001025 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 return null;
1027 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001028 return getUuidFromCache(address);
1029 }
1030
1031 private ParcelUuid[] getUuidFromCache(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001032 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001033 if (value == null) return null;
1034
1035 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001036 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001037 uuidStrings = value.split(",");
1038 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1039
1040 for (int i = 0; i < uuidStrings.length; i++) {
1041 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1042 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001043 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001046 public synchronized boolean fetchRemoteUuidsWithSdp(String address) {
1047 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1048 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1049 return false;
1050 }
1051
1052 if (mUuidIntentTracker.contains(address)) {
1053 // An SDP query for this address is already in progress
1054 return true;
1055 }
1056
1057 boolean ret;
1058 if (getBondState(address) == BluetoothDevice.BOND_BONDED) {
1059 String path = getObjectPathFromAddress(address);
1060 if (path == null) return false;
1061
1062 // Use an empty string for the UUID pattern
1063 ret = discoverServicesNative(path, "");
1064 } else {
1065 ret = createDeviceNative(address);
1066 }
1067
1068 mUuidIntentTracker.add(address);
1069
1070 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1071 message.obj = address;
1072 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1073 return ret;
1074 }
1075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001077 * Gets the rfcomm channel associated with the UUID.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001079 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001080 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001082 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001083 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001085 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001087 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001088 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001090 // Check if we are recovering from a crash.
1091 if (mDeviceProperties.isEmpty()) {
1092 if (!updateRemoteDevicePropertiesCache(address))
1093 return -1;
1094 }
1095
1096 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1097 if (value != null && value.containsKey(uuid))
1098 return value.get(uuid);
1099 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101
1102 public synchronized boolean setPin(String address, byte[] pin) {
1103 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1104 "Need BLUETOOTH_ADMIN permission");
1105 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001106 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 return false;
1108 }
1109 address = address.toUpperCase();
1110 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1111 if (data == null) {
1112 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1113 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1114 " or by bluez.\n");
1115 return false;
1116 }
1117 // bluez API wants pin as a string
1118 String pinString;
1119 try {
1120 pinString = new String(pin, "UTF8");
1121 } catch (UnsupportedEncodingException uee) {
1122 Log.e(TAG, "UTF8 not supported?!?");
1123 return false;
1124 }
1125 return setPinNative(address, pinString, data.intValue());
1126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001128 public synchronized boolean setPasskey(String address, int passkey) {
1129 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1130 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001131 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001132 return false;
1133 }
1134 address = address.toUpperCase();
1135 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1136 if (data == null) {
1137 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1138 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1139 " or by bluez.\n");
1140 return false;
1141 }
1142 return setPasskeyNative(address, passkey, data.intValue());
1143 }
1144
1145 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1146 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1147 "Need BLUETOOTH_ADMIN permission");
1148 address = address.toUpperCase();
1149 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1150 if (data == null) {
1151 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1152 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1153 " or by bluez.\n");
1154 return false;
1155 }
1156 return setPairingConfirmationNative(address, confirm, data.intValue());
1157 }
1158
1159 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1161 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001162 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 return false;
1164 }
Nick Pelly005b2282009-09-10 10:21:56 -07001165 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001166 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 address = address.toUpperCase();
1168 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1169 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001170 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1171 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1172 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 return false;
1174 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001175 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001178 public void updateDeviceServiceChannelCache(String address) {
1179 ParcelUuid[] deviceUuids = getRemoteUuids(address);
1180 // We are storing the rfcomm channel numbers only for the uuids
1181 // we are interested in.
1182 int channel;
1183 ParcelUuid[] interestedUuids = {BluetoothUuid.Handsfree,
1184 BluetoothUuid.HSP,
1185 BluetoothUuid.ObexObjectPush};
1186
1187 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
1188 for (ParcelUuid uuid: interestedUuids) {
1189 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1190 channel =
1191 getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(),
1192 0x0004);
1193 value.put(uuid, channel);
1194 }
1195 }
1196 mDeviceServiceChannelCache.put(address, value);
1197 }
1198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1200 @Override
1201 public void onReceive(Context context, Intent intent) {
1202 String action = intent.getAction();
1203 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1204 ContentResolver resolver = context.getContentResolver();
1205 // Query the airplane mode from Settings.System just to make sure that
1206 // some random app is not sending this intent and disabling bluetooth
1207 boolean enabled = !isAirplaneModeOn();
1208 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1209 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1210 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001211 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 } else {
1213 disable(false);
1214 }
1215 }
1216 }
1217 }
1218 };
1219
1220 private void registerForAirplaneMode() {
1221 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1222 Settings.System.AIRPLANE_MODE_RADIOS);
1223 mIsAirplaneSensitive = airplaneModeRadios == null
1224 ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1225 if (mIsAirplaneSensitive) {
1226 mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1227 mContext.registerReceiver(mReceiver, mIntentFilter);
1228 }
1229 }
1230
1231 /* Returns true if airplane mode is currently on */
1232 private final boolean isAirplaneModeOn() {
1233 return Settings.System.getInt(mContext.getContentResolver(),
1234 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1235 }
1236
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001237 /* Broadcast the Uuid intent */
1238 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001239 ParcelUuid[] uuid = getUuidFromCache(address);
1240 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07001241 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001242 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1243 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001244
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001245 if (mUuidIntentTracker.contains(address))
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001246 mUuidIntentTracker.remove(address);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001247 }
1248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 @Override
1250 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001251 pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252
The Android Open Source Project10592532009-03-18 17:39:46 -07001253 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07001254 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -07001255 pw.println("\nBluetooth OFF\n");
1256 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001257 case BluetoothAdapter.STATE_TURNING_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -07001258 pw.println("\nBluetooth TURNING ON\n");
1259 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001260 case BluetoothAdapter.STATE_TURNING_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -07001261 pw.println("\nBluetooth TURNING OFF\n");
1262 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001263 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -07001264 pw.println("\nBluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001266
1267 pw.println("\nLocal address = " + getAddress());
1268 pw.println("\nLocal name = " + getName());
1269 pw.println("\nisDiscovering() = " + isDiscovering());
1270
1271 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1272
The Android Open Source Project10592532009-03-18 17:39:46 -07001273 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07001274 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07001275 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07001276 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07001277 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07001278 mBondState.getAttempt(address),
1279 getRemoteName(address));
Nick Pelly1eada0d2009-08-26 10:57:33 -07001280 if (bondState == BluetoothDevice.BOND_BONDED) {
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001281 ParcelUuid[] uuids = getRemoteUuids(address);
Nick Pelly1eada0d2009-08-26 10:57:33 -07001282 if (uuids == null) {
1283 pw.printf("\tuuids = null\n");
1284 } else {
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001285 for (ParcelUuid uuid : uuids) {
Nick Pellyb015e192009-08-26 14:11:24 -07001286 pw.printf("\t" + uuid + "\n");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001287 }
1288 }
1289 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001290 }
1291
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001292 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001293 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001294 if (value != null) {
1295 devicesObjectPath = value.split(",");
1296 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001297 pw.println("\n--ACL connected devices--");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001298 for (String device : devicesObjectPath) {
1299 pw.println(getAddressFromObjectPath(device));
The Android Open Source Project10592532009-03-18 17:39:46 -07001300 }
1301
1302 // Rather not do this from here, but no-where else and I need this
1303 // dump
1304 pw.println("\n--Headset Service--");
1305 switch (headset.getState()) {
1306 case BluetoothHeadset.STATE_DISCONNECTED:
1307 pw.println("getState() = STATE_DISCONNECTED");
1308 break;
1309 case BluetoothHeadset.STATE_CONNECTING:
1310 pw.println("getState() = STATE_CONNECTING");
1311 break;
1312 case BluetoothHeadset.STATE_CONNECTED:
1313 pw.println("getState() = STATE_CONNECTED");
1314 break;
1315 case BluetoothHeadset.STATE_ERROR:
1316 pw.println("getState() = STATE_ERROR");
1317 break;
1318 }
Nick Pellybd022f42009-08-14 18:33:38 -07001319 pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset());
Nick Pelly6c901db2009-06-19 10:08:09 -07001320 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
1321
The Android Open Source Project10592532009-03-18 17:39:46 -07001322 headset.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 }
1324
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001325 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1326 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001327 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001328 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001329 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001330 else
Nick Pellybd022f42009-08-14 18:33:38 -07001331 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 }
1333
1334 /* package */ static String scanModeToBluezString(int mode) {
1335 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07001336 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07001338 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07001340 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341 return "discoverable";
1342 }
1343 return null;
1344 }
1345
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001346 /*package*/ String getAddressFromObjectPath(String objectPath) {
1347 String adapterObjectPath = getProperty("ObjectPath");
1348 if (adapterObjectPath == null || objectPath == null) {
1349 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1350 " or deviceObjectPath:" + objectPath + " is null");
1351 return null;
1352 }
1353 if (!objectPath.startsWith(adapterObjectPath)) {
1354 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1355 " is not a prefix of deviceObjectPath:" + objectPath +
1356 "bluetoothd crashed ?");
1357 return null;
1358 }
1359 String address = objectPath.substring(adapterObjectPath.length());
1360 if (address != null) return address.replace('_', ':');
1361
1362 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1363 return null;
1364 }
1365
1366 /*package*/ String getObjectPathFromAddress(String address) {
1367 String path = getProperty("ObjectPath");
1368 if (path == null) {
1369 Log.e(TAG, "Error: Object Path is null");
1370 return null;
1371 }
1372 path = path + address.replace(":", "_");
1373 return path;
1374 }
1375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 private static void log(String msg) {
1377 Log.d(TAG, msg);
1378 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001379
1380 private native static void classInitNative();
1381 private native void initializeNativeDataNative();
1382 private native boolean setupNativeDataNative();
1383 private native boolean tearDownNativeDataNative();
1384 private native void cleanupNativeDataNative();
1385 private native String getAdapterPathNative();
1386
1387 private native int isEnabledNative();
1388 private native int enableNative();
1389 private native int disableNative();
1390
1391 private native Object[] getAdapterPropertiesNative();
1392 private native Object[] getDevicePropertiesNative(String objectPath);
1393 private native boolean setAdapterPropertyStringNative(String key, String value);
1394 private native boolean setAdapterPropertyIntegerNative(String key, int value);
1395 private native boolean setAdapterPropertyBooleanNative(String key, int value);
1396
1397 private native boolean startDiscoveryNative();
1398 private native boolean stopDiscoveryNative();
1399
1400 private native boolean createPairedDeviceNative(String address, int timeout_ms);
1401 private native boolean cancelDeviceCreationNative(String address);
1402 private native boolean removeDeviceNative(String objectPath);
1403 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
1404 int attributeId);
1405
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001406 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001407 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001408 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
1409 private native boolean setPairingConfirmationNative(String address, boolean confirm,
1410 int nativeData);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001411 private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001412 private native boolean createDeviceNative(String address);
1413 private native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415}