blob: ce62f070073c4103f8be320ebabb6f352a6da898 [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;
Nick Pellybd022f42009-08-14 18:33:38 -070031import android.bluetooth.IBluetooth;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -070032import android.bluetooth.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.content.BroadcastReceiver;
34import android.content.ContentResolver;
35import android.content.Context;
36import android.content.Intent;
37import android.content.IntentFilter;
38import android.os.Binder;
39import android.os.Handler;
40import android.os.Message;
41import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070042import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.os.SystemService;
44import android.provider.Settings;
45import android.util.Log;
46
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070047import com.android.internal.app.IBatteryStats;
48
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.io.FileDescriptor;
50import java.io.PrintWriter;
51import java.io.UnsupportedEncodingException;
52import java.util.ArrayList;
53import java.util.Arrays;
54import java.util.HashMap;
55import java.util.Map;
56
Nick Pellybd022f42009-08-14 18:33:38 -070057public class BluetoothService extends IBluetooth.Stub {
58 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 private static final boolean DBG = true;
60
61 private int mNativeData;
62 private BluetoothEventLoop mEventLoop;
63 private IntentFilter mIntentFilter;
64 private boolean mIsAirplaneSensitive;
The Android Open Source Project10592532009-03-18 17:39:46 -070065 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070066 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 private boolean mIsDiscovering;
68
Nick Pellybd022f42009-08-14 18:33:38 -070069 private BluetoothAdapter mAdapter; // constant after init()
70 private final BondState mBondState = new BondState(); // local cache of bondings
71 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private final Context mContext;
73
74 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
75 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
76
The Android Open Source Project10592532009-03-18 17:39:46 -070077 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
78 private static final int MESSAGE_FINISH_DISABLE = 2;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -070079 private static final int MESSAGE_UUID_INTENT = 3;
80
81 // The timeout used to sent the UUIDs Intent
82 // This timeout should be greater than the page timeout
83 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -070084
Nick Pellybd022f42009-08-14 18:33:38 -070085 private final Map<String, String> mAdapterProperties;
86 private final HashMap <String, Map<String, String>> mDeviceProperties;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070087
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -070088 private final ArrayList <String> mUuidIntentTracker;
89
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 static {
91 classInitNative();
92 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
Nick Pellybd022f42009-08-14 18:33:38 -070094 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -070096
97 // Need to do this in place of:
98 // mBatteryStats = BatteryStatsService.getService();
99 // Since we can not import BatteryStatsService from here. This class really needs to be
100 // moved to java/services/com/android/server/
101 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700104
105 if (isEnabledNative() == 1) {
106 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
107 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700109
Nick Pellyde893f52009-09-08 13:15:33 -0700110 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700112 mAdapterProperties = new HashMap<String, String>();
113 mDeviceProperties = new HashMap<String, Map<String,String>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700114 mUuidIntentTracker = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 registerForAirplaneMode();
Nick Pellybd022f42009-08-14 18:33:38 -0700116 }
117
118 public synchronized void initAfterRegistration() {
119 mAdapter = (BluetoothAdapter) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
120 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122
123 @Override
124 protected void finalize() throws Throwable {
125 if (mIsAirplaneSensitive) {
126 mContext.unregisterReceiver(mReceiver);
127 }
128 try {
129 cleanupNativeDataNative();
130 } finally {
131 super.finalize();
132 }
133 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 public boolean isEnabled() {
136 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellyde893f52009-09-08 13:15:33 -0700137 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139
The Android Open Source Project10592532009-03-18 17:39:46 -0700140 public int getBluetoothState() {
141 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
142 return mBluetoothState;
143 }
144
145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 /**
147 * Bring down bluetooth and disable BT in settings. Returns true on success.
148 */
149 public boolean disable() {
150 return disable(true);
151 }
152
153 /**
154 * Bring down bluetooth. Returns true on success.
155 *
156 * @param saveSetting If true, disable BT in settings
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 */
158 public synchronized boolean disable(boolean saveSetting) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700159 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160
The Android Open Source Project10592532009-03-18 17:39:46 -0700161 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700162 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700163 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700164 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700165 break;
166 default:
167 return false;
168 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 if (mEnableThread != null && mEnableThread.isAlive()) {
170 return false;
171 }
Nick Pellyde893f52009-09-08 13:15:33 -0700172 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700173
174 // Allow 3 seconds for profiles to gracefully disconnect
175 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700176 // BluetoothService when it is done shutting down
The Android Open Source Project10592532009-03-18 17:39:46 -0700177 mHandler.sendMessageDelayed(
178 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
179 return true;
180 }
181
182
183 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700184 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700185 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 }
187 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700188 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 disableNative();
190
191 // mark in progress bondings as cancelled
192 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700193 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
195 }
196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700198 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
199 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
201
The Android Open Source Project10592532009-03-18 17:39:46 -0700202 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700203 mAdapterProperties.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 if (saveSetting) {
206 persistBluetoothOnSetting(false);
207 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700208
Nick Pellyde893f52009-09-08 13:15:33 -0700209 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700210
211 // Log bluetooth off to battery stats.
212 long ident = Binder.clearCallingIdentity();
213 try {
214 mBatteryStats.noteBluetoothOff();
215 } catch (RemoteException e) {
216 } finally {
217 Binder.restoreCallingIdentity(ident);
218 }
Nick Pelly997c7612009-03-24 20:44:48 -0700219
220 if (mRestart) {
221 mRestart = false;
222 enable();
223 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 }
225
The Android Open Source Project10592532009-03-18 17:39:46 -0700226 /** Bring up BT and persist BT on in settings */
227 public boolean enable() {
228 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 }
230
231 /**
232 * Enable this Bluetooth device, asynchronously.
233 * This turns on/off the underlying hardware.
234 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700235 * @param saveSetting If true, persist the new state of BT in settings
236 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700238 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
240 "Need BLUETOOTH_ADMIN permission");
241
242 // Airplane mode can prevent Bluetooth radio from being turned on.
243 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
244 return false;
245 }
Nick Pellyde893f52009-09-08 13:15:33 -0700246 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 return false;
248 }
249 if (mEnableThread != null && mEnableThread.isAlive()) {
250 return false;
251 }
Nick Pellyde893f52009-09-08 13:15:33 -0700252 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700253 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 mEnableThread.start();
255 return true;
256 }
257
Nick Pelly997c7612009-03-24 20:44:48 -0700258 /** Forcibly restart Bluetooth if it is on */
259 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700260 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700261 return;
262 }
263 mRestart = true;
264 if (!disable(false)) {
265 mRestart = false;
266 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700267 }
Nick Pelly997c7612009-03-24 20:44:48 -0700268
The Android Open Source Project10592532009-03-18 17:39:46 -0700269 private synchronized void setBluetoothState(int state) {
270 if (state == mBluetoothState) {
271 return;
272 }
273
274 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
275
Nick Pellyde893f52009-09-08 13:15:33 -0700276 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
277 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
278 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700279 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
280
281 mBluetoothState = state;
282
283 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
284 }
285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 private final Handler mHandler = new Handler() {
287 @Override
288 public void handleMessage(Message msg) {
289 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700290 case MESSAGE_REGISTER_SDP_RECORDS:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291 //TODO: Don't assume HSP/HFP is running, don't use sdptool,
292 if (isEnabled()) {
293 SystemService.start("hsag");
294 SystemService.start("hfag");
Nick Pelly03c707a2009-07-14 21:32:14 -0700295 SystemService.start("opush");
Jaikumar Ganesh67542962009-07-23 21:32:45 -0700296 SystemService.start("pbap");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700298 break;
299 case MESSAGE_FINISH_DISABLE:
300 finishDisable(msg.arg1 != 0);
301 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700302 case MESSAGE_UUID_INTENT:
303 String address = (String)msg.obj;
304 if (address != null)
305 sendUuidIntent(address);
306 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 }
308 }
309 };
310
311 private EnableThread mEnableThread;
312
313 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700315 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 mSaveSetting = saveSetting;
317 }
318 public void run() {
319 boolean res = (enableNative() == 0);
320 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700321 int retryCount = 2;
322 boolean running = false;
323 while ((retryCount-- > 0) && !running) {
324 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700325 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700326 // thing. Check periodically for a while.
327 int pollCount = 5;
328 while ((pollCount-- > 0) && !running) {
329 if (mEventLoop.isEventLoopRunning()) {
330 running = true;
331 break;
332 }
333 try {
334 Thread.sleep(100);
335 } catch (InterruptedException e) {}
336 }
337 }
338 if (!running) {
339 log("bt EnableThread giving up");
340 res = false;
341 disableNative();
342 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 }
344
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345
346 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700347 if (!setupNativeDataNative()) {
348 return;
349 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 if (mSaveSetting) {
351 persistBluetoothOnSetting(true);
352 }
353 mIsDiscovering = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mBondState.loadBondState();
The Android Open Source Project10592532009-03-18 17:39:46 -0700355 mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS),
356 3000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357
The Android Open Source Project10592532009-03-18 17:39:46 -0700358 // Log bluetooth on to battery stats.
359 long ident = Binder.clearCallingIdentity();
360 try {
361 mBatteryStats.noteBluetoothOn();
362 } catch (RemoteException e) {
363 } finally {
364 Binder.restoreCallingIdentity(ident);
365 }
366 }
367
368 mEnableThread = null;
369
370 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700371 BluetoothAdapter.STATE_ON :
372 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700373
374 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700376 String[] propVal = {"Pairable", getProperty("Pairable")};
377 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700379
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900380 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
381 disable(false);
382 }
383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 }
385 }
386
387 private void persistBluetoothOnSetting(boolean bluetoothOn) {
388 long origCallerIdentityToken = Binder.clearCallingIdentity();
389 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
390 bluetoothOn ? 1 : 0);
391 Binder.restoreCallingIdentity(origCallerIdentityToken);
392 }
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 /* package */ BondState getBondState() {
395 return mBondState;
396 }
397
398 /** local cache of bonding state.
399 /* we keep our own state to track the intermediate state BONDING, which
400 /* bluez does not track.
401 * All addreses must be passed in upper case.
402 */
403 public class BondState {
404 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
405 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
406 private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
The Android Open Source Project10592532009-03-18 17:39:46 -0700407 // List of all the vendor_id prefix of Bluetooth addresses for
408 // which auto pairing is not attempted.
409 // The following companies are included in the list below:
410 // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
411 // Parrot, Zhongshan General K-mate Electronics, Great Well
412 // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
413 // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
Nick Pellyadbaef22009-09-14 19:04:47 -0700414 // Continental Automotive, Harman/Becker, Panasonic/Kyushu Ten
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700415 private final ArrayList<String> mAutoPairingAddressBlacklist =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 new ArrayList<String>(Arrays.asList(
The Android Open Source Project10592532009-03-18 17:39:46 -0700417 "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
418 "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
419 "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
420 "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 -0700421 "00:0A:30", "00:1E:AE", "00:1C:D7", "00:80:F0"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 ));
423
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700424 // List of names of Bluetooth devices for which auto pairing should be
425 // disabled.
426 private final ArrayList<String> mAutoPairingNameBlacklist =
427 new ArrayList<String>(Arrays.asList(
428 "Motorola IHF1000", "i.TechBlueBAND", "X5 Stereo v1.3"));
429
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700431 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 return;
433 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700434 String []bonds = null;
435 String val = getProperty("Devices");
436 if (val != null) {
437 bonds = val.split(",");
438 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 if (bonds == null) {
440 return;
441 }
442 mState.clear();
443 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700444 for (String device : bonds) {
445 mState.put(getAddressFromObjectPath(device).toUpperCase(),
446 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 }
448 }
449
450 public synchronized void setBondState(String address, int state) {
451 setBondState(address, state, 0);
452 }
453
454 /** reason is ignored unless state == BOND_NOT_BONDED */
455 public synchronized void setBondState(String address, int state, int reason) {
456 int oldState = getBondState(address);
457 if (oldState == state) {
458 return;
459 }
460 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
461 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700462 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
463 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
464 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
465 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
466 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 if (reason <= 0) {
468 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
469 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
470 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
471 }
Nick Pelly005b2282009-09-10 10:21:56 -0700472 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 mState.remove(address);
474 } else {
475 mState.put(address, state);
476 }
477
478 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
479 }
480
481 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700482 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 if (address.startsWith(blacklistAddress)) return true;
484 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700485
486 String name = getRemoteName(address);
487 if (name != null) {
488 for (String blacklistName : mAutoPairingNameBlacklist) {
489 if (name.equals(blacklistName)) return true;
490 }
491 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 return false;
493 }
494
495 public synchronized int getBondState(String address) {
496 Integer state = mState.get(address);
497 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700498 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 }
500 return state.intValue();
501 }
502
503 private synchronized String[] listInState(int state) {
504 ArrayList<String> result = new ArrayList<String>(mState.size());
505 for (Map.Entry<String, Integer> e : mState.entrySet()) {
506 if (e.getValue().intValue() == state) {
507 result.add(e.getKey());
508 }
509 }
510 return result.toArray(new String[result.size()]);
511 }
512
513 public synchronized void addAutoPairingFailure(String address) {
514 if (!mAutoPairingFailures.contains(address)) {
515 mAutoPairingFailures.add(address);
516 }
517 }
518
519 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
520 return getAttempt(address) != 0;
521 }
522
523 public synchronized void clearPinAttempts(String address) {
524 mPinAttempt.remove(address);
525 }
526
527 public synchronized boolean hasAutoPairingFailed(String address) {
528 return mAutoPairingFailures.contains(address);
529 }
530
531 public synchronized int getAttempt(String address) {
532 Integer attempt = mPinAttempt.get(address);
533 if (attempt == null) {
534 return 0;
535 }
536 return attempt.intValue();
537 }
538
539 public synchronized void attempt(String address) {
540 Integer attempt = mPinAttempt.get(address);
541 int newAttempt;
542 if (attempt == null) {
543 newAttempt = 1;
544 } else {
545 newAttempt = attempt.intValue() + 1;
546 }
547 mPinAttempt.put(address, new Integer(newAttempt));
548 }
549
550 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551
552 private static String toBondStateString(int bondState) {
553 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700554 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 return "not bonded";
556 case BluetoothDevice.BOND_BONDING:
557 return "bonding";
558 case BluetoothDevice.BOND_BONDED:
559 return "bonded";
560 default:
561 return "??????";
562 }
563 }
564
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -0700565 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
566 return mAdapterProperties.isEmpty();
567 }
568
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700569 /*package*/synchronized void getAllProperties() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -0700571 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700573 String properties[] = (String [])getAdapterPropertiesNative();
574 // The String Array consists of key-value pairs.
575 if (properties == null) {
576 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
577 return;
578 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700579
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700580 for (int i = 0; i < properties.length; i++) {
581 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700582 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700583 int len;
584 if (name == null) {
585 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
586 continue;
587 }
588 if (name.equals("Devices")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700589 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700590 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700591 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700592 str.append(properties[++i]);
593 str.append(",");
594 }
595 if (len > 0) {
596 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700597 }
598 } else {
599 newValue = properties[++i];
600 }
Nick Pellybd022f42009-08-14 18:33:38 -0700601 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700602 }
603
604 // Add adapter object path property.
605 String adapterPath = getAdapterPathNative();
606 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -0700607 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700609
610 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700611 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700612 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613
614 public synchronized boolean setName(String name) {
615 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
616 "Need BLUETOOTH_ADMIN permission");
617 if (name == null) {
618 return false;
619 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700620 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700623 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
624 // Either have a single property function with Object as the parameter
625 // or have a function for each property and then obfuscate in the JNI layer.
626 // The following looks dirty.
627 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700629 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700632 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700634 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 }
636
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700637 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700639 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800640 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641
642 /**
643 * Set the discoverability window for the device. A timeout of zero
644 * makes the device permanently discoverable (if the device is
645 * discoverable). Setting the timeout to a nonzero value does not make
646 * a device discoverable; you need to call setMode() to make the device
647 * explicitly discoverable.
648 *
649 * @param timeout_s The discoverable timeout in seconds.
650 */
651 public synchronized boolean setDiscoverableTimeout(int timeout) {
652 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
653 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700654 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700656
657 public synchronized boolean setScanMode(int mode) {
658 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
659 "Need BLUETOOTH_ADMIN permission");
Nick Pellyde893f52009-09-08 13:15:33 -0700660 boolean pairable = false;
661 boolean discoverable = false;
662 switch (mode) {
663 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700664 pairable = false;
665 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700666 break;
667 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700668 pairable = true;
669 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700670 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700671 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700672 pairable = true;
673 discoverable = true;
Nick Pelly005b2282009-09-10 10:21:56 -0700674 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700675 default:
676 Log.w(TAG, "Requested invalid scan mode " + mode);
677 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700678 }
679 setPropertyBoolean("Pairable", pairable);
680 setPropertyBoolean("Discoverable", discoverable);
681
682 return true;
683 }
684
685 /*package*/ synchronized String getProperty (String name) {
Nick Pellybd022f42009-08-14 18:33:38 -0700686 if (!mAdapterProperties.isEmpty())
687 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700688 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -0700689 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700690 }
691
692 public synchronized String getAddress() {
693 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
694 return getProperty("Address");
695 }
696
697 public synchronized String getName() {
698 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
699 return getProperty("Name");
700 }
701
702 /**
703 * Returns the user-friendly name of a remote device. This value is
704 * returned from our local cache, which is updated when onPropertyChange
705 * event is received.
706 * Do not expect to retrieve the updated remote name immediately after
707 * changing the name on the remote device.
708 *
709 * @param address Bluetooth address of remote device.
710 *
711 * @return The user-friendly name of the specified remote device.
712 */
713 public synchronized String getRemoteName(String address) {
714 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700715 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700716 return null;
717 }
Nick Pellybd022f42009-08-14 18:33:38 -0700718 Map <String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700719 if (properties != null) return properties.get("Name");
720 return null;
721 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722
723 /**
724 * Get the discoverability window for the device. A timeout of zero
725 * means that the device is permanently discoverable (if the device is
726 * in the discoverable mode).
727 *
728 * @return The discoverability window of the device, in seconds. A negative
729 * value indicates an error.
730 */
731 public synchronized int getDiscoverableTimeout() {
732 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700733 String timeout = getProperty("DiscoverableTimeout");
734 if (timeout != null)
735 return Integer.valueOf(timeout);
736 else
737 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739
740 public synchronized int getScanMode() {
741 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700742 if (!isEnabled())
Nick Pellyde893f52009-09-08 13:15:33 -0700743 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700745 boolean pairable = getProperty("Pairable").equals("true");
746 boolean discoverable = getProperty("Discoverable").equals("true");
747 return bluezStringToScanMode (pairable, discoverable);
748 }
749
750 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
752 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700753 if (!isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 return false;
755 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700756 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700758
759 public synchronized boolean cancelDiscovery() {
760 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
761 "Need BLUETOOTH_ADMIN permission");
762 return stopDiscoveryNative();
763 }
764
765 public synchronized boolean isDiscovering() {
766 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
767 return mIsDiscovering;
768 }
769
770 /* package */ void setIsDiscovering(boolean isDiscovering) {
771 mIsDiscovering = isDiscovering;
772 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773
774 public synchronized boolean createBond(String address) {
775 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
776 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700777 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 return false;
779 }
780 address = address.toUpperCase();
781
782 String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING);
783 if (bonding.length > 0 && !bonding[0].equals(address)) {
784 log("Ignoring createBond(): another device is bonding");
785 // a different device is currently bonding, fail
786 return false;
787 }
788
789 // Check for bond state only if we are not performing auto
790 // pairing exponential back-off attempts.
791 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -0700792 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 log("Ignoring createBond(): this device is already bonding or bonded");
794 return false;
795 }
796
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700797 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 return false;
799 }
800
801 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
802 return true;
803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804
805 public synchronized boolean cancelBondProcess(String address) {
806 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
807 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700808 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 return false;
810 }
811 address = address.toUpperCase();
812 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
813 return false;
814 }
815
Nick Pelly005b2282009-09-10 10:21:56 -0700816 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700818 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 return true;
820 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821
822 public synchronized boolean removeBond(String address) {
823 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
824 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700825 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 return false;
827 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700828 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830
831 public synchronized String[] listBonds() {
832 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
833 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
834 }
835
836 public synchronized int getBondState(String address) {
837 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700838 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -0700839 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 }
841 return mBondState.getBondState(address.toUpperCase());
842 }
843
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700844 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -0700845 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700846 }
847
848 /*package*/ String[] getRemoteDeviceProperties(String address) {
849 String objectPath = getObjectPathFromAddress(address);
850 return (String [])getDevicePropertiesNative(objectPath);
851 }
852
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700853 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -0700854 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700855 if (properties != null) {
856 return properties.get(property);
857 } else {
858 // Query for remote device properties, again.
859 // We will need to reload the cache when we switch Bluetooth on / off
860 // or if we crash.
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700861 String[] propValues = getRemoteDeviceProperties(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700862 if (propValues != null) {
863 addRemoteDeviceProperties(address, propValues);
864 return getRemoteDeviceProperty(address, property);
865 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700867 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
868 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700871 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -0700872 /*
873 * We get a DeviceFound signal every time RSSI changes or name changes.
874 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -0700875 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700876 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -0700877 propertyValues = new HashMap<String, String>();
878 }
879
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700880 for (int i = 0; i < properties.length; i++) {
881 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700882 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700883 int len;
884 if (name == null) {
885 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
886 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700887 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700888 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700889 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700890 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700891 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700892 str.append(properties[++i]);
893 str.append(",");
894 }
895 if (len > 0) {
896 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700897 }
898 } else {
899 newValue = properties[++i];
900 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700901
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700902 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 }
Nick Pellybd022f42009-08-14 18:33:38 -0700904 mDeviceProperties.put(address, propertyValues);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700907 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -0700908 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700909 }
910
911 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
912 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700913 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700914 if (propVal != null) {
915 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -0700916 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700917 } else {
918 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921
922 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +0800923 * Sets the remote device trust state.
924 *
925 * @return boolean to indicate operation success or fail
926 */
927 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -0700928 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +0800929 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
930 return false;
931 }
932
933 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
934 value ? 1 : 0);
935 }
936
937 /**
938 * Gets the remote device trust state as boolean.
939 * Note: this value may be
940 * retrieved from cache if we retrieved the data before *
941 *
942 * @return boolean to indicate trust or untrust state
943 */
944 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -0700945 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +0800946 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
947 return false;
948 }
949
950 String val = getRemoteDeviceProperty(address, "Trusted");
951 if (val == null) {
952 return false;
953 } else {
954 return val.equals("true") ? true : false;
955 }
956 }
957
958 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700959 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 * integer.
961 *
962 * Note: this value is retrieved from cache, because we get it during
963 * remote-device discovery.
964 *
965 * @return 32-bit integer encoding the remote major, minor, and service
966 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 */
968 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -0700969 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -0700970 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
971 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700973 String val = getRemoteDeviceProperty(address, "Class");
974 if (val == null)
975 return BluetoothClass.ERROR;
976 else {
977 return Integer.valueOf(val);
978 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700980
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981
982 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700983 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700985 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700987 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700989 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 return null;
991 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700992 return getUuidFromCache(address);
993 }
994
995 private ParcelUuid[] getUuidFromCache(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700996 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700997 if (value == null) return null;
998
999 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001000 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001001 uuidStrings = value.split(",");
1002 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1003
1004 for (int i = 0; i < uuidStrings.length; i++) {
1005 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1006 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001007 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001010 public synchronized boolean fetchRemoteUuidsWithSdp(String address) {
1011 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1012 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1013 return false;
1014 }
1015
1016 if (mUuidIntentTracker.contains(address)) {
1017 // An SDP query for this address is already in progress
1018 return true;
1019 }
1020
1021 boolean ret;
1022 if (getBondState(address) == BluetoothDevice.BOND_BONDED) {
1023 String path = getObjectPathFromAddress(address);
1024 if (path == null) return false;
1025
1026 // Use an empty string for the UUID pattern
1027 ret = discoverServicesNative(path, "");
1028 } else {
1029 ret = createDeviceNative(address);
1030 }
1031
1032 mUuidIntentTracker.add(address);
1033
1034 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1035 message.obj = address;
1036 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1037 return ret;
1038 }
1039
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001041 * Gets the rfcomm channel associated with the UUID.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001043 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001044 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001046 * @return rfcomm channel associated with the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001048 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001050 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001051 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 }
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001053 return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(),
1054 0x0004);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056
1057 public synchronized boolean setPin(String address, byte[] pin) {
1058 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1059 "Need BLUETOOTH_ADMIN permission");
1060 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001061 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001062 return false;
1063 }
1064 address = address.toUpperCase();
1065 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1066 if (data == null) {
1067 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1068 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1069 " or by bluez.\n");
1070 return false;
1071 }
1072 // bluez API wants pin as a string
1073 String pinString;
1074 try {
1075 pinString = new String(pin, "UTF8");
1076 } catch (UnsupportedEncodingException uee) {
1077 Log.e(TAG, "UTF8 not supported?!?");
1078 return false;
1079 }
1080 return setPinNative(address, pinString, data.intValue());
1081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001083 public synchronized boolean setPasskey(String address, int passkey) {
1084 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1085 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001086 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001087 return false;
1088 }
1089 address = address.toUpperCase();
1090 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1091 if (data == null) {
1092 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1093 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1094 " or by bluez.\n");
1095 return false;
1096 }
1097 return setPasskeyNative(address, passkey, data.intValue());
1098 }
1099
1100 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1101 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1102 "Need BLUETOOTH_ADMIN permission");
1103 address = address.toUpperCase();
1104 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1105 if (data == null) {
1106 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1107 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1108 " or by bluez.\n");
1109 return false;
1110 }
1111 return setPairingConfirmationNative(address, confirm, data.intValue());
1112 }
1113
1114 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1116 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001117 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 return false;
1119 }
Nick Pelly005b2282009-09-10 10:21:56 -07001120 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001121 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 address = address.toUpperCase();
1123 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1124 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001125 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1126 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1127 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 return false;
1129 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001130 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132
1133 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1134 @Override
1135 public void onReceive(Context context, Intent intent) {
1136 String action = intent.getAction();
1137 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1138 ContentResolver resolver = context.getContentResolver();
1139 // Query the airplane mode from Settings.System just to make sure that
1140 // some random app is not sending this intent and disabling bluetooth
1141 boolean enabled = !isAirplaneModeOn();
1142 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1143 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1144 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001145 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 } else {
1147 disable(false);
1148 }
1149 }
1150 }
1151 }
1152 };
1153
1154 private void registerForAirplaneMode() {
1155 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1156 Settings.System.AIRPLANE_MODE_RADIOS);
1157 mIsAirplaneSensitive = airplaneModeRadios == null
1158 ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1159 if (mIsAirplaneSensitive) {
1160 mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1161 mContext.registerReceiver(mReceiver, mIntentFilter);
1162 }
1163 }
1164
1165 /* Returns true if airplane mode is currently on */
1166 private final boolean isAirplaneModeOn() {
1167 return Settings.System.getInt(mContext.getContentResolver(),
1168 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1169 }
1170
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001171 /* Broadcast the Uuid intent */
1172 /*package*/ synchronized void sendUuidIntent(String address) {
1173 if (mUuidIntentTracker.contains(address)) {
1174 ParcelUuid[] uuid = getUuidFromCache(address);
1175 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
1176 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1177 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
1178
1179 mUuidIntentTracker.remove(address);
1180 }
1181 }
1182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 @Override
1184 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001185 pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186
The Android Open Source Project10592532009-03-18 17:39:46 -07001187 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07001188 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -07001189 pw.println("\nBluetooth OFF\n");
1190 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001191 case BluetoothAdapter.STATE_TURNING_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -07001192 pw.println("\nBluetooth TURNING ON\n");
1193 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001194 case BluetoothAdapter.STATE_TURNING_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -07001195 pw.println("\nBluetooth TURNING OFF\n");
1196 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001197 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -07001198 pw.println("\nBluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001200
1201 pw.println("\nLocal address = " + getAddress());
1202 pw.println("\nLocal name = " + getName());
1203 pw.println("\nisDiscovering() = " + isDiscovering());
1204
1205 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1206
The Android Open Source Project10592532009-03-18 17:39:46 -07001207 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07001208 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07001209 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07001210 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07001211 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07001212 mBondState.getAttempt(address),
1213 getRemoteName(address));
Nick Pelly1eada0d2009-08-26 10:57:33 -07001214 if (bondState == BluetoothDevice.BOND_BONDED) {
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001215 ParcelUuid[] uuids = getRemoteUuids(address);
Nick Pelly1eada0d2009-08-26 10:57:33 -07001216 if (uuids == null) {
1217 pw.printf("\tuuids = null\n");
1218 } else {
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001219 for (ParcelUuid uuid : uuids) {
Nick Pellyb015e192009-08-26 14:11:24 -07001220 pw.printf("\t" + uuid + "\n");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001221 }
1222 }
1223 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001224 }
1225
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001226 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001227 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001228 if (value != null) {
1229 devicesObjectPath = value.split(",");
1230 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001231 pw.println("\n--ACL connected devices--");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001232 for (String device : devicesObjectPath) {
1233 pw.println(getAddressFromObjectPath(device));
The Android Open Source Project10592532009-03-18 17:39:46 -07001234 }
1235
1236 // Rather not do this from here, but no-where else and I need this
1237 // dump
1238 pw.println("\n--Headset Service--");
1239 switch (headset.getState()) {
1240 case BluetoothHeadset.STATE_DISCONNECTED:
1241 pw.println("getState() = STATE_DISCONNECTED");
1242 break;
1243 case BluetoothHeadset.STATE_CONNECTING:
1244 pw.println("getState() = STATE_CONNECTING");
1245 break;
1246 case BluetoothHeadset.STATE_CONNECTED:
1247 pw.println("getState() = STATE_CONNECTED");
1248 break;
1249 case BluetoothHeadset.STATE_ERROR:
1250 pw.println("getState() = STATE_ERROR");
1251 break;
1252 }
Nick Pellybd022f42009-08-14 18:33:38 -07001253 pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset());
Nick Pelly6c901db2009-06-19 10:08:09 -07001254 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
1255
The Android Open Source Project10592532009-03-18 17:39:46 -07001256 headset.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001257 }
1258
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001259 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1260 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001261 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001262 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001263 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001264 else
Nick Pellybd022f42009-08-14 18:33:38 -07001265 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 }
1267
1268 /* package */ static String scanModeToBluezString(int mode) {
1269 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07001270 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07001272 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07001274 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 return "discoverable";
1276 }
1277 return null;
1278 }
1279
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001280 /*package*/ String getAddressFromObjectPath(String objectPath) {
1281 String adapterObjectPath = getProperty("ObjectPath");
1282 if (adapterObjectPath == null || objectPath == null) {
1283 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1284 " or deviceObjectPath:" + objectPath + " is null");
1285 return null;
1286 }
1287 if (!objectPath.startsWith(adapterObjectPath)) {
1288 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1289 " is not a prefix of deviceObjectPath:" + objectPath +
1290 "bluetoothd crashed ?");
1291 return null;
1292 }
1293 String address = objectPath.substring(adapterObjectPath.length());
1294 if (address != null) return address.replace('_', ':');
1295
1296 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1297 return null;
1298 }
1299
1300 /*package*/ String getObjectPathFromAddress(String address) {
1301 String path = getProperty("ObjectPath");
1302 if (path == null) {
1303 Log.e(TAG, "Error: Object Path is null");
1304 return null;
1305 }
1306 path = path + address.replace(":", "_");
1307 return path;
1308 }
1309
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 private static void log(String msg) {
1311 Log.d(TAG, msg);
1312 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001313
1314 private native static void classInitNative();
1315 private native void initializeNativeDataNative();
1316 private native boolean setupNativeDataNative();
1317 private native boolean tearDownNativeDataNative();
1318 private native void cleanupNativeDataNative();
1319 private native String getAdapterPathNative();
1320
1321 private native int isEnabledNative();
1322 private native int enableNative();
1323 private native int disableNative();
1324
1325 private native Object[] getAdapterPropertiesNative();
1326 private native Object[] getDevicePropertiesNative(String objectPath);
1327 private native boolean setAdapterPropertyStringNative(String key, String value);
1328 private native boolean setAdapterPropertyIntegerNative(String key, int value);
1329 private native boolean setAdapterPropertyBooleanNative(String key, int value);
1330
1331 private native boolean startDiscoveryNative();
1332 private native boolean stopDiscoveryNative();
1333
1334 private native boolean createPairedDeviceNative(String address, int timeout_ms);
1335 private native boolean cancelDeviceCreationNative(String address);
1336 private native boolean removeDeviceNative(String objectPath);
1337 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
1338 int attributeId);
1339
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001340 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001341 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001342 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
1343 private native boolean setPairingConfirmationNative(String address, boolean confirm,
1344 int nativeData);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001345 private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001346 private native boolean createDeviceNative(String address);
1347 private native boolean discoverServicesNative(String objectPath, String pattern);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348}