blob: c0e4f341964e097d08a083900a8c8ecbb0e1a9b6 [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;
79
Nick Pellybd022f42009-08-14 18:33:38 -070080 private final Map<String, String> mAdapterProperties;
81 private final HashMap <String, Map<String, String>> mDeviceProperties;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070082
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 static {
84 classInitNative();
85 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Nick Pellybd022f42009-08-14 18:33:38 -070087 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -070089
90 // Need to do this in place of:
91 // mBatteryStats = BatteryStatsService.getService();
92 // Since we can not import BatteryStatsService from here. This class really needs to be
93 // moved to java/services/com/android/server/
94 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -070097
98 if (isEnabledNative() == 1) {
99 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
100 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700102
Nick Pellyde893f52009-09-08 13:15:33 -0700103 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700105 mAdapterProperties = new HashMap<String, String>();
106 mDeviceProperties = new HashMap<String, Map<String,String>>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 registerForAirplaneMode();
Nick Pellybd022f42009-08-14 18:33:38 -0700108 }
109
110 public synchronized void initAfterRegistration() {
111 mAdapter = (BluetoothAdapter) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
112 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114
115 @Override
116 protected void finalize() throws Throwable {
117 if (mIsAirplaneSensitive) {
118 mContext.unregisterReceiver(mReceiver);
119 }
120 try {
121 cleanupNativeDataNative();
122 } finally {
123 super.finalize();
124 }
125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
127 public boolean isEnabled() {
128 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellyde893f52009-09-08 13:15:33 -0700129 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131
The Android Open Source Project10592532009-03-18 17:39:46 -0700132 public int getBluetoothState() {
133 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
134 return mBluetoothState;
135 }
136
137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
139 * Bring down bluetooth and disable BT in settings. Returns true on success.
140 */
141 public boolean disable() {
142 return disable(true);
143 }
144
145 /**
146 * Bring down bluetooth. Returns true on success.
147 *
148 * @param saveSetting If true, disable BT in settings
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 */
150 public synchronized boolean disable(boolean saveSetting) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700151 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152
The Android Open Source Project10592532009-03-18 17:39:46 -0700153 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700154 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700155 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700156 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700157 break;
158 default:
159 return false;
160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 if (mEnableThread != null && mEnableThread.isAlive()) {
162 return false;
163 }
Nick Pellyde893f52009-09-08 13:15:33 -0700164 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700165
166 // Allow 3 seconds for profiles to gracefully disconnect
167 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700168 // BluetoothService when it is done shutting down
The Android Open Source Project10592532009-03-18 17:39:46 -0700169 mHandler.sendMessageDelayed(
170 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
171 return true;
172 }
173
174
175 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700176 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700177 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 }
179 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700180 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 disableNative();
182
183 // mark in progress bondings as cancelled
184 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700185 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
187 }
188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700190 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
191 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
193
The Android Open Source Project10592532009-03-18 17:39:46 -0700194 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700195 mAdapterProperties.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 if (saveSetting) {
198 persistBluetoothOnSetting(false);
199 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700200
Nick Pellyde893f52009-09-08 13:15:33 -0700201 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700202
203 // Log bluetooth off to battery stats.
204 long ident = Binder.clearCallingIdentity();
205 try {
206 mBatteryStats.noteBluetoothOff();
207 } catch (RemoteException e) {
208 } finally {
209 Binder.restoreCallingIdentity(ident);
210 }
Nick Pelly997c7612009-03-24 20:44:48 -0700211
212 if (mRestart) {
213 mRestart = false;
214 enable();
215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 }
217
The Android Open Source Project10592532009-03-18 17:39:46 -0700218 /** Bring up BT and persist BT on in settings */
219 public boolean enable() {
220 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 }
222
223 /**
224 * Enable this Bluetooth device, asynchronously.
225 * This turns on/off the underlying hardware.
226 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700227 * @param saveSetting If true, persist the new state of BT in settings
228 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700230 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
232 "Need BLUETOOTH_ADMIN permission");
233
234 // Airplane mode can prevent Bluetooth radio from being turned on.
235 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
236 return false;
237 }
Nick Pellyde893f52009-09-08 13:15:33 -0700238 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 return false;
240 }
241 if (mEnableThread != null && mEnableThread.isAlive()) {
242 return false;
243 }
Nick Pellyde893f52009-09-08 13:15:33 -0700244 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700245 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 mEnableThread.start();
247 return true;
248 }
249
Nick Pelly997c7612009-03-24 20:44:48 -0700250 /** Forcibly restart Bluetooth if it is on */
251 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700252 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700253 return;
254 }
255 mRestart = true;
256 if (!disable(false)) {
257 mRestart = false;
258 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700259 }
Nick Pelly997c7612009-03-24 20:44:48 -0700260
The Android Open Source Project10592532009-03-18 17:39:46 -0700261 private synchronized void setBluetoothState(int state) {
262 if (state == mBluetoothState) {
263 return;
264 }
265
266 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
267
Nick Pellyde893f52009-09-08 13:15:33 -0700268 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
269 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
270 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700271 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
272
273 mBluetoothState = state;
274
275 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
276 }
277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 private final Handler mHandler = new Handler() {
279 @Override
280 public void handleMessage(Message msg) {
281 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700282 case MESSAGE_REGISTER_SDP_RECORDS:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 //TODO: Don't assume HSP/HFP is running, don't use sdptool,
284 if (isEnabled()) {
285 SystemService.start("hsag");
286 SystemService.start("hfag");
Nick Pelly03c707a2009-07-14 21:32:14 -0700287 SystemService.start("opush");
Jaikumar Ganesh67542962009-07-23 21:32:45 -0700288 SystemService.start("pbap");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700290 break;
291 case MESSAGE_FINISH_DISABLE:
292 finishDisable(msg.arg1 != 0);
293 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 }
295 }
296 };
297
298 private EnableThread mEnableThread;
299
300 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700302 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 mSaveSetting = saveSetting;
304 }
305 public void run() {
306 boolean res = (enableNative() == 0);
307 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700308 int retryCount = 2;
309 boolean running = false;
310 while ((retryCount-- > 0) && !running) {
311 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700312 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700313 // thing. Check periodically for a while.
314 int pollCount = 5;
315 while ((pollCount-- > 0) && !running) {
316 if (mEventLoop.isEventLoopRunning()) {
317 running = true;
318 break;
319 }
320 try {
321 Thread.sleep(100);
322 } catch (InterruptedException e) {}
323 }
324 }
325 if (!running) {
326 log("bt EnableThread giving up");
327 res = false;
328 disableNative();
329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332
333 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700334 if (!setupNativeDataNative()) {
335 return;
336 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 if (mSaveSetting) {
338 persistBluetoothOnSetting(true);
339 }
340 mIsDiscovering = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 mBondState.loadBondState();
The Android Open Source Project10592532009-03-18 17:39:46 -0700342 mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS),
343 3000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344
The Android Open Source Project10592532009-03-18 17:39:46 -0700345 // Log bluetooth on to battery stats.
346 long ident = Binder.clearCallingIdentity();
347 try {
348 mBatteryStats.noteBluetoothOn();
349 } catch (RemoteException e) {
350 } finally {
351 Binder.restoreCallingIdentity(ident);
352 }
353 }
354
355 mEnableThread = null;
356
357 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700358 BluetoothAdapter.STATE_ON :
359 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700360
361 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700363 String[] propVal = {"Pairable", getProperty("Pairable")};
364 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700366
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900367 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
368 disable(false);
369 }
370
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 }
372 }
373
374 private void persistBluetoothOnSetting(boolean bluetoothOn) {
375 long origCallerIdentityToken = Binder.clearCallingIdentity();
376 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
377 bluetoothOn ? 1 : 0);
378 Binder.restoreCallingIdentity(origCallerIdentityToken);
379 }
380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 /* package */ BondState getBondState() {
382 return mBondState;
383 }
384
385 /** local cache of bonding state.
386 /* we keep our own state to track the intermediate state BONDING, which
387 /* bluez does not track.
388 * All addreses must be passed in upper case.
389 */
390 public class BondState {
391 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
392 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
393 private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
The Android Open Source Project10592532009-03-18 17:39:46 -0700394 // List of all the vendor_id prefix of Bluetooth addresses for
395 // which auto pairing is not attempted.
396 // The following companies are included in the list below:
397 // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
398 // Parrot, Zhongshan General K-mate Electronics, Great Well
399 // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
400 // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
Nick Pellyadbaef22009-09-14 19:04:47 -0700401 // Continental Automotive, Harman/Becker, Panasonic/Kyushu Ten
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700402 private final ArrayList<String> mAutoPairingAddressBlacklist =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 new ArrayList<String>(Arrays.asList(
The Android Open Source Project10592532009-03-18 17:39:46 -0700404 "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
405 "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
406 "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
407 "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 -0700408 "00:0A:30", "00:1E:AE", "00:1C:D7", "00:80:F0"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 ));
410
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700411 // List of names of Bluetooth devices for which auto pairing should be
412 // disabled.
413 private final ArrayList<String> mAutoPairingNameBlacklist =
414 new ArrayList<String>(Arrays.asList(
415 "Motorola IHF1000", "i.TechBlueBAND", "X5 Stereo v1.3"));
416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700418 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 return;
420 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700421 String []bonds = null;
422 String val = getProperty("Devices");
423 if (val != null) {
424 bonds = val.split(",");
425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 if (bonds == null) {
427 return;
428 }
429 mState.clear();
430 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700431 for (String device : bonds) {
432 mState.put(getAddressFromObjectPath(device).toUpperCase(),
433 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 }
435 }
436
437 public synchronized void setBondState(String address, int state) {
438 setBondState(address, state, 0);
439 }
440
441 /** reason is ignored unless state == BOND_NOT_BONDED */
442 public synchronized void setBondState(String address, int state, int reason) {
443 int oldState = getBondState(address);
444 if (oldState == state) {
445 return;
446 }
447 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
448 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700449 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
450 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
451 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
452 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
453 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800454 if (reason <= 0) {
455 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
456 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
457 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
458 }
Nick Pelly005b2282009-09-10 10:21:56 -0700459 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 mState.remove(address);
461 } else {
462 mState.put(address, state);
463 }
464
465 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
466 }
467
468 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700469 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 if (address.startsWith(blacklistAddress)) return true;
471 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700472
473 String name = getRemoteName(address);
474 if (name != null) {
475 for (String blacklistName : mAutoPairingNameBlacklist) {
476 if (name.equals(blacklistName)) return true;
477 }
478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 return false;
480 }
481
482 public synchronized int getBondState(String address) {
483 Integer state = mState.get(address);
484 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700485 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 return state.intValue();
488 }
489
490 private synchronized String[] listInState(int state) {
491 ArrayList<String> result = new ArrayList<String>(mState.size());
492 for (Map.Entry<String, Integer> e : mState.entrySet()) {
493 if (e.getValue().intValue() == state) {
494 result.add(e.getKey());
495 }
496 }
497 return result.toArray(new String[result.size()]);
498 }
499
500 public synchronized void addAutoPairingFailure(String address) {
501 if (!mAutoPairingFailures.contains(address)) {
502 mAutoPairingFailures.add(address);
503 }
504 }
505
506 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
507 return getAttempt(address) != 0;
508 }
509
510 public synchronized void clearPinAttempts(String address) {
511 mPinAttempt.remove(address);
512 }
513
514 public synchronized boolean hasAutoPairingFailed(String address) {
515 return mAutoPairingFailures.contains(address);
516 }
517
518 public synchronized int getAttempt(String address) {
519 Integer attempt = mPinAttempt.get(address);
520 if (attempt == null) {
521 return 0;
522 }
523 return attempt.intValue();
524 }
525
526 public synchronized void attempt(String address) {
527 Integer attempt = mPinAttempt.get(address);
528 int newAttempt;
529 if (attempt == null) {
530 newAttempt = 1;
531 } else {
532 newAttempt = attempt.intValue() + 1;
533 }
534 mPinAttempt.put(address, new Integer(newAttempt));
535 }
536
537 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538
539 private static String toBondStateString(int bondState) {
540 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700541 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 return "not bonded";
543 case BluetoothDevice.BOND_BONDING:
544 return "bonding";
545 case BluetoothDevice.BOND_BONDED:
546 return "bonded";
547 default:
548 return "??????";
549 }
550 }
551
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -0700552 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
553 return mAdapterProperties.isEmpty();
554 }
555
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700556 /*package*/synchronized void getAllProperties() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -0700558 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700560 String properties[] = (String [])getAdapterPropertiesNative();
561 // The String Array consists of key-value pairs.
562 if (properties == null) {
563 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
564 return;
565 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700566
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700567 for (int i = 0; i < properties.length; i++) {
568 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700569 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700570 int len;
571 if (name == null) {
572 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
573 continue;
574 }
575 if (name.equals("Devices")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700576 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700577 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700578 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700579 str.append(properties[++i]);
580 str.append(",");
581 }
582 if (len > 0) {
583 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700584 }
585 } else {
586 newValue = properties[++i];
587 }
Nick Pellybd022f42009-08-14 18:33:38 -0700588 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700589 }
590
591 // Add adapter object path property.
592 String adapterPath = getAdapterPathNative();
593 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -0700594 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700596
597 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700598 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700599 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600
601 public synchronized boolean setName(String name) {
602 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
603 "Need BLUETOOTH_ADMIN permission");
604 if (name == null) {
605 return false;
606 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700607 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700610 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
611 // Either have a single property function with Object as the parameter
612 // or have a function for each property and then obfuscate in the JNI layer.
613 // The following looks dirty.
614 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800615 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700616 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700619 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700621 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 }
623
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700624 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700626 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628
629 /**
630 * Set the discoverability window for the device. A timeout of zero
631 * makes the device permanently discoverable (if the device is
632 * discoverable). Setting the timeout to a nonzero value does not make
633 * a device discoverable; you need to call setMode() to make the device
634 * explicitly discoverable.
635 *
636 * @param timeout_s The discoverable timeout in seconds.
637 */
638 public synchronized boolean setDiscoverableTimeout(int timeout) {
639 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
640 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700641 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700643
644 public synchronized boolean setScanMode(int mode) {
645 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
646 "Need BLUETOOTH_ADMIN permission");
Nick Pellyde893f52009-09-08 13:15:33 -0700647 boolean pairable = false;
648 boolean discoverable = false;
649 switch (mode) {
650 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700651 pairable = false;
652 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700653 break;
654 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700655 pairable = true;
656 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700657 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700658 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700659 pairable = true;
660 discoverable = true;
Nick Pelly005b2282009-09-10 10:21:56 -0700661 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700662 default:
663 Log.w(TAG, "Requested invalid scan mode " + mode);
664 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700665 }
666 setPropertyBoolean("Pairable", pairable);
667 setPropertyBoolean("Discoverable", discoverable);
668
669 return true;
670 }
671
672 /*package*/ synchronized String getProperty (String name) {
Nick Pellybd022f42009-08-14 18:33:38 -0700673 if (!mAdapterProperties.isEmpty())
674 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700675 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -0700676 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700677 }
678
679 public synchronized String getAddress() {
680 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
681 return getProperty("Address");
682 }
683
684 public synchronized String getName() {
685 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
686 return getProperty("Name");
687 }
688
689 /**
690 * Returns the user-friendly name of a remote device. This value is
691 * returned from our local cache, which is updated when onPropertyChange
692 * event is received.
693 * Do not expect to retrieve the updated remote name immediately after
694 * changing the name on the remote device.
695 *
696 * @param address Bluetooth address of remote device.
697 *
698 * @return The user-friendly name of the specified remote device.
699 */
700 public synchronized String getRemoteName(String address) {
701 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700702 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700703 return null;
704 }
Nick Pellybd022f42009-08-14 18:33:38 -0700705 Map <String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700706 if (properties != null) return properties.get("Name");
707 return null;
708 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709
710 /**
711 * Get the discoverability window for the device. A timeout of zero
712 * means that the device is permanently discoverable (if the device is
713 * in the discoverable mode).
714 *
715 * @return The discoverability window of the device, in seconds. A negative
716 * value indicates an error.
717 */
718 public synchronized int getDiscoverableTimeout() {
719 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700720 String timeout = getProperty("DiscoverableTimeout");
721 if (timeout != null)
722 return Integer.valueOf(timeout);
723 else
724 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726
727 public synchronized int getScanMode() {
728 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700729 if (!isEnabled())
Nick Pellyde893f52009-09-08 13:15:33 -0700730 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700732 boolean pairable = getProperty("Pairable").equals("true");
733 boolean discoverable = getProperty("Discoverable").equals("true");
734 return bluezStringToScanMode (pairable, discoverable);
735 }
736
737 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
739 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700740 if (!isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 return false;
742 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700743 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700745
746 public synchronized boolean cancelDiscovery() {
747 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
748 "Need BLUETOOTH_ADMIN permission");
749 return stopDiscoveryNative();
750 }
751
752 public synchronized boolean isDiscovering() {
753 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
754 return mIsDiscovering;
755 }
756
757 /* package */ void setIsDiscovering(boolean isDiscovering) {
758 mIsDiscovering = isDiscovering;
759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760
761 public synchronized boolean createBond(String address) {
762 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
763 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700764 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 return false;
766 }
767 address = address.toUpperCase();
768
769 String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING);
770 if (bonding.length > 0 && !bonding[0].equals(address)) {
771 log("Ignoring createBond(): another device is bonding");
772 // a different device is currently bonding, fail
773 return false;
774 }
775
776 // Check for bond state only if we are not performing auto
777 // pairing exponential back-off attempts.
778 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -0700779 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 log("Ignoring createBond(): this device is already bonding or bonded");
781 return false;
782 }
783
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700784 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 return false;
786 }
787
788 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
789 return true;
790 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791
792 public synchronized boolean cancelBondProcess(String address) {
793 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
794 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700795 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 return false;
797 }
798 address = address.toUpperCase();
799 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
800 return false;
801 }
802
Nick Pelly005b2282009-09-10 10:21:56 -0700803 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700805 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 return true;
807 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808
809 public synchronized boolean removeBond(String address) {
810 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
811 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700812 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 return false;
814 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700815 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817
818 public synchronized String[] listBonds() {
819 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
820 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
821 }
822
823 public synchronized int getBondState(String address) {
824 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700825 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -0700826 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 }
828 return mBondState.getBondState(address.toUpperCase());
829 }
830
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700831 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -0700832 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700833 }
834
835 /*package*/ String[] getRemoteDeviceProperties(String address) {
836 String objectPath = getObjectPathFromAddress(address);
837 return (String [])getDevicePropertiesNative(objectPath);
838 }
839
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700840 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -0700841 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700842 if (properties != null) {
843 return properties.get(property);
844 } else {
845 // Query for remote device properties, again.
846 // We will need to reload the cache when we switch Bluetooth on / off
847 // or if we crash.
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -0700848 String[] propValues = getRemoteDeviceProperties(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700849 if (propValues != null) {
850 addRemoteDeviceProperties(address, propValues);
851 return getRemoteDeviceProperty(address, property);
852 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700854 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
855 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700858 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -0700859 /*
860 * We get a DeviceFound signal every time RSSI changes or name changes.
861 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -0700862 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700863 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -0700864 propertyValues = new HashMap<String, String>();
865 }
866
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700867 for (int i = 0; i < properties.length; i++) {
868 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700869 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700870 int len;
871 if (name == null) {
872 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
873 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700874 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700875 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700876 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700877 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700878 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700879 str.append(properties[++i]);
880 str.append(",");
881 }
882 if (len > 0) {
883 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700884 }
885 } else {
886 newValue = properties[++i];
887 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700888
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700889 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 }
Nick Pellybd022f42009-08-14 18:33:38 -0700891 mDeviceProperties.put(address, propertyValues);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700894 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -0700895 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700896 }
897
898 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
899 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700900 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700901 if (propVal != null) {
902 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -0700903 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700904 } else {
905 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908
909 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +0800910 * Sets the remote device trust state.
911 *
912 * @return boolean to indicate operation success or fail
913 */
914 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -0700915 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +0800916 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
917 return false;
918 }
919
920 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
921 value ? 1 : 0);
922 }
923
924 /**
925 * Gets the remote device trust state as boolean.
926 * Note: this value may be
927 * retrieved from cache if we retrieved the data before *
928 *
929 * @return boolean to indicate trust or untrust state
930 */
931 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -0700932 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +0800933 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
934 return false;
935 }
936
937 String val = getRemoteDeviceProperty(address, "Trusted");
938 if (val == null) {
939 return false;
940 } else {
941 return val.equals("true") ? true : false;
942 }
943 }
944
945 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700946 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 * integer.
948 *
949 * Note: this value is retrieved from cache, because we get it during
950 * remote-device discovery.
951 *
952 * @return 32-bit integer encoding the remote major, minor, and service
953 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 */
955 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -0700956 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -0700957 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
958 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700960 String val = getRemoteDeviceProperty(address, "Class");
961 if (val == null)
962 return BluetoothClass.ERROR;
963 else {
964 return Integer.valueOf(val);
965 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700967
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968
969 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700970 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700972 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700974 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700976 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 return null;
978 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700979 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700980 if (value == null) return null;
981
982 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700983 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700984 uuidStrings = value.split(",");
985 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
986
987 for (int i = 0; i < uuidStrings.length; i++) {
988 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
989 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700990 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992
993 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700994 * Gets the rfcomm channel associated with the UUID.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700996 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -0700997 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700999 * @return rfcomm channel associated with the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001001 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001003 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001004 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 }
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001006 return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid.toString(),
1007 0x0004);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009
1010 public synchronized boolean setPin(String address, byte[] pin) {
1011 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1012 "Need BLUETOOTH_ADMIN permission");
1013 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001014 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 return false;
1016 }
1017 address = address.toUpperCase();
1018 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1019 if (data == null) {
1020 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1021 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1022 " or by bluez.\n");
1023 return false;
1024 }
1025 // bluez API wants pin as a string
1026 String pinString;
1027 try {
1028 pinString = new String(pin, "UTF8");
1029 } catch (UnsupportedEncodingException uee) {
1030 Log.e(TAG, "UTF8 not supported?!?");
1031 return false;
1032 }
1033 return setPinNative(address, pinString, data.intValue());
1034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001036 public synchronized boolean setPasskey(String address, int passkey) {
1037 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1038 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001039 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001040 return false;
1041 }
1042 address = address.toUpperCase();
1043 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1044 if (data == null) {
1045 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1046 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1047 " or by bluez.\n");
1048 return false;
1049 }
1050 return setPasskeyNative(address, passkey, data.intValue());
1051 }
1052
1053 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1054 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1055 "Need BLUETOOTH_ADMIN permission");
1056 address = address.toUpperCase();
1057 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1058 if (data == null) {
1059 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1060 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1061 " or by bluez.\n");
1062 return false;
1063 }
1064 return setPairingConfirmationNative(address, confirm, data.intValue());
1065 }
1066
1067 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1069 "Need BLUETOOTH_ADMIN permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001070 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 return false;
1072 }
Nick Pelly005b2282009-09-10 10:21:56 -07001073 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001074 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 address = address.toUpperCase();
1076 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1077 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001078 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1079 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1080 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 return false;
1082 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001083 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085
1086 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1087 @Override
1088 public void onReceive(Context context, Intent intent) {
1089 String action = intent.getAction();
1090 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1091 ContentResolver resolver = context.getContentResolver();
1092 // Query the airplane mode from Settings.System just to make sure that
1093 // some random app is not sending this intent and disabling bluetooth
1094 boolean enabled = !isAirplaneModeOn();
1095 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1096 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1097 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001098 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 } else {
1100 disable(false);
1101 }
1102 }
1103 }
1104 }
1105 };
1106
1107 private void registerForAirplaneMode() {
1108 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
1109 Settings.System.AIRPLANE_MODE_RADIOS);
1110 mIsAirplaneSensitive = airplaneModeRadios == null
1111 ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1112 if (mIsAirplaneSensitive) {
1113 mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
1114 mContext.registerReceiver(mReceiver, mIntentFilter);
1115 }
1116 }
1117
1118 /* Returns true if airplane mode is currently on */
1119 private final boolean isAirplaneModeOn() {
1120 return Settings.System.getInt(mContext.getContentResolver(),
1121 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1122 }
1123
1124 @Override
1125 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001126 pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127
The Android Open Source Project10592532009-03-18 17:39:46 -07001128 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07001129 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -07001130 pw.println("\nBluetooth OFF\n");
1131 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001132 case BluetoothAdapter.STATE_TURNING_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -07001133 pw.println("\nBluetooth TURNING ON\n");
1134 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001135 case BluetoothAdapter.STATE_TURNING_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -07001136 pw.println("\nBluetooth TURNING OFF\n");
1137 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001138 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -07001139 pw.println("\nBluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001141
1142 pw.println("\nLocal address = " + getAddress());
1143 pw.println("\nLocal name = " + getName());
1144 pw.println("\nisDiscovering() = " + isDiscovering());
1145
1146 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1147
The Android Open Source Project10592532009-03-18 17:39:46 -07001148 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07001149 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07001150 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07001151 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07001152 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07001153 mBondState.getAttempt(address),
1154 getRemoteName(address));
Nick Pelly1eada0d2009-08-26 10:57:33 -07001155 if (bondState == BluetoothDevice.BOND_BONDED) {
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001156 ParcelUuid[] uuids = getRemoteUuids(address);
Nick Pelly1eada0d2009-08-26 10:57:33 -07001157 if (uuids == null) {
1158 pw.printf("\tuuids = null\n");
1159 } else {
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001160 for (ParcelUuid uuid : uuids) {
Nick Pellyb015e192009-08-26 14:11:24 -07001161 pw.printf("\t" + uuid + "\n");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001162 }
1163 }
1164 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001165 }
1166
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001167 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001168 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001169 if (value != null) {
1170 devicesObjectPath = value.split(",");
1171 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001172 pw.println("\n--ACL connected devices--");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001173 for (String device : devicesObjectPath) {
1174 pw.println(getAddressFromObjectPath(device));
The Android Open Source Project10592532009-03-18 17:39:46 -07001175 }
1176
1177 // Rather not do this from here, but no-where else and I need this
1178 // dump
1179 pw.println("\n--Headset Service--");
1180 switch (headset.getState()) {
1181 case BluetoothHeadset.STATE_DISCONNECTED:
1182 pw.println("getState() = STATE_DISCONNECTED");
1183 break;
1184 case BluetoothHeadset.STATE_CONNECTING:
1185 pw.println("getState() = STATE_CONNECTING");
1186 break;
1187 case BluetoothHeadset.STATE_CONNECTED:
1188 pw.println("getState() = STATE_CONNECTED");
1189 break;
1190 case BluetoothHeadset.STATE_ERROR:
1191 pw.println("getState() = STATE_ERROR");
1192 break;
1193 }
Nick Pellybd022f42009-08-14 18:33:38 -07001194 pw.println("getCurrentHeadset() = " + headset.getCurrentHeadset());
Nick Pelly6c901db2009-06-19 10:08:09 -07001195 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
1196
The Android Open Source Project10592532009-03-18 17:39:46 -07001197 headset.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198 }
1199
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001200 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1201 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001202 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001203 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001204 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001205 else
Nick Pellybd022f42009-08-14 18:33:38 -07001206 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208
1209 /* package */ static String scanModeToBluezString(int mode) {
1210 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07001211 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07001213 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07001215 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001216 return "discoverable";
1217 }
1218 return null;
1219 }
1220
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001221 /*package*/ String getAddressFromObjectPath(String objectPath) {
1222 String adapterObjectPath = getProperty("ObjectPath");
1223 if (adapterObjectPath == null || objectPath == null) {
1224 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1225 " or deviceObjectPath:" + objectPath + " is null");
1226 return null;
1227 }
1228 if (!objectPath.startsWith(adapterObjectPath)) {
1229 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1230 " is not a prefix of deviceObjectPath:" + objectPath +
1231 "bluetoothd crashed ?");
1232 return null;
1233 }
1234 String address = objectPath.substring(adapterObjectPath.length());
1235 if (address != null) return address.replace('_', ':');
1236
1237 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1238 return null;
1239 }
1240
1241 /*package*/ String getObjectPathFromAddress(String address) {
1242 String path = getProperty("ObjectPath");
1243 if (path == null) {
1244 Log.e(TAG, "Error: Object Path is null");
1245 return null;
1246 }
1247 path = path + address.replace(":", "_");
1248 return path;
1249 }
1250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 private static void log(String msg) {
1252 Log.d(TAG, msg);
1253 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001254
1255 private native static void classInitNative();
1256 private native void initializeNativeDataNative();
1257 private native boolean setupNativeDataNative();
1258 private native boolean tearDownNativeDataNative();
1259 private native void cleanupNativeDataNative();
1260 private native String getAdapterPathNative();
1261
1262 private native int isEnabledNative();
1263 private native int enableNative();
1264 private native int disableNative();
1265
1266 private native Object[] getAdapterPropertiesNative();
1267 private native Object[] getDevicePropertiesNative(String objectPath);
1268 private native boolean setAdapterPropertyStringNative(String key, String value);
1269 private native boolean setAdapterPropertyIntegerNative(String key, int value);
1270 private native boolean setAdapterPropertyBooleanNative(String key, int value);
1271
1272 private native boolean startDiscoveryNative();
1273 private native boolean stopDiscoveryNative();
1274
1275 private native boolean createPairedDeviceNative(String address, int timeout_ms);
1276 private native boolean cancelDeviceCreationNative(String address);
1277 private native boolean removeDeviceNative(String objectPath);
1278 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
1279 int attributeId);
1280
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001281 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001282 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001283 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
1284 private native boolean setPairingConfirmationNative(String address, boolean confirm,
1285 int nativeData);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001286 private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287}