blob: 36c432b4754b82a8e5ad3bbc46b0876ebf063417 [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
19 * java/services/com/android/server/BluetoothDeviceService.java
20 * and make the contructor package private again.
21 *
22 * @hide
23 */
24
25package android.server;
26
Nick Pellyea600cc2009-03-31 14:56:26 -070027import android.bluetooth.BluetoothClass;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.bluetooth.BluetoothDevice;
29import android.bluetooth.BluetoothError;
30import android.bluetooth.BluetoothHeadset;
31import android.bluetooth.BluetoothIntent;
32import android.bluetooth.IBluetoothDevice;
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;
Nick Pellyf9204e52009-03-24 21:15:00 -070055import java.util.Iterator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import java.util.Map;
57
58public class BluetoothDeviceService extends IBluetoothDevice.Stub {
59 private static final String TAG = "BluetoothDeviceService";
60 private static final boolean DBG = true;
61
62 private int mNativeData;
63 private BluetoothEventLoop mEventLoop;
64 private IntentFilter mIntentFilter;
65 private boolean mIsAirplaneSensitive;
The Android Open Source Project10592532009-03-18 17:39:46 -070066 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070067 private boolean mRestart = false; // need to call enable() after disable()
68
69 private final BondState mBondState = new BondState(); // local cache of bondings
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private boolean mIsDiscovering;
The Android Open Source Project10592532009-03-18 17:39:46 -070071 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 private final Context mContext;
74
75 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
76 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
77
The Android Open Source Project10592532009-03-18 17:39:46 -070078 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
79 private static final int MESSAGE_FINISH_DISABLE = 2;
80
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070081 private Map<String, String> mProperties;
82 private HashMap <String, Map<String, String>> mRemoteDeviceProperties;
83
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 static {
85 classInitNative();
86 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
88 public BluetoothDeviceService(Context context) {
89 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -070090
91 // Need to do this in place of:
92 // mBatteryStats = BatteryStatsService.getService();
93 // Since we can not import BatteryStatsService from here. This class really needs to be
94 // moved to java/services/com/android/server/
95 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 }
97
98 /** Must be called after construction, and before any other method.
99 */
100 public synchronized void init() {
101 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700102
103 if (isEnabledNative() == 1) {
104 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
105 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700107
The Android Open Source Project10592532009-03-18 17:39:46 -0700108 setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 mIsDiscovering = false;
110 mEventLoop = new BluetoothEventLoop(mContext, this);
111 registerForAirplaneMode();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700112 mProperties = new HashMap<String, String>();
113 mRemoteDeviceProperties = new HashMap<String, Map<String,String>>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115
116 @Override
117 protected void finalize() throws Throwable {
118 if (mIsAirplaneSensitive) {
119 mContext.unregisterReceiver(mReceiver);
120 }
121 try {
122 cleanupNativeDataNative();
123 } finally {
124 super.finalize();
125 }
126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127
128 public boolean isEnabled() {
129 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
The Android Open Source Project10592532009-03-18 17:39:46 -0700130 return mBluetoothState == BluetoothDevice.BLUETOOTH_STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132
The Android Open Source Project10592532009-03-18 17:39:46 -0700133 public int getBluetoothState() {
134 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
135 return mBluetoothState;
136 }
137
138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 /**
140 * Bring down bluetooth and disable BT in settings. Returns true on success.
141 */
142 public boolean disable() {
143 return disable(true);
144 }
145
146 /**
147 * Bring down bluetooth. Returns true on success.
148 *
149 * @param saveSetting If true, disable BT in settings
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 */
151 public synchronized boolean disable(boolean saveSetting) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700152 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153
The Android Open Source Project10592532009-03-18 17:39:46 -0700154 switch (mBluetoothState) {
155 case BluetoothDevice.BLUETOOTH_STATE_OFF:
156 return true;
157 case BluetoothDevice.BLUETOOTH_STATE_ON:
158 break;
159 default:
160 return false;
161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 if (mEnableThread != null && mEnableThread.isAlive()) {
163 return false;
164 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700165 setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF);
166
167 // Allow 3 seconds for profiles to gracefully disconnect
168 // TODO: Introduce a callback mechanism so that each profile can notify
169 // BluetoothDeviceService when it is done shutting down
170 mHandler.sendMessageDelayed(
171 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
172 return true;
173 }
174
175
176 private synchronized void finishDisable(boolean saveSetting) {
177 if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
178 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 }
180 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700181 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 disableNative();
183
184 // mark in progress bondings as cancelled
185 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
186 mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
187 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
188 }
189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 // update mode
191 Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
192 intent.putExtra(BluetoothIntent.SCAN_MODE, BluetoothDevice.SCAN_MODE_NONE);
193 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
194
The Android Open Source Project10592532009-03-18 17:39:46 -0700195 mIsDiscovering = false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700196 mProperties.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 if (saveSetting) {
199 persistBluetoothOnSetting(false);
200 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700201
202 setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_OFF);
203
204 // Log bluetooth off to battery stats.
205 long ident = Binder.clearCallingIdentity();
206 try {
207 mBatteryStats.noteBluetoothOff();
208 } catch (RemoteException e) {
209 } finally {
210 Binder.restoreCallingIdentity(ident);
211 }
Nick Pelly997c7612009-03-24 20:44:48 -0700212
213 if (mRestart) {
214 mRestart = false;
215 enable();
216 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 }
218
The Android Open Source Project10592532009-03-18 17:39:46 -0700219 /** Bring up BT and persist BT on in settings */
220 public boolean enable() {
221 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223
224 /**
225 * Enable this Bluetooth device, asynchronously.
226 * This turns on/off the underlying hardware.
227 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700228 * @param saveSetting If true, persist the new state of BT in settings
229 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800230 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700231 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
233 "Need BLUETOOTH_ADMIN permission");
234
235 // Airplane mode can prevent Bluetooth radio from being turned on.
236 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
237 return false;
238 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700239 if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 return false;
241 }
242 if (mEnableThread != null && mEnableThread.isAlive()) {
243 return false;
244 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700245 setBluetoothState(BluetoothDevice.BLUETOOTH_STATE_TURNING_ON);
246 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 mEnableThread.start();
248 return true;
249 }
250
Nick Pelly997c7612009-03-24 20:44:48 -0700251 /** Forcibly restart Bluetooth if it is on */
252 /* package */ synchronized void restart() {
253 if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_ON) {
254 return;
255 }
256 mRestart = true;
257 if (!disable(false)) {
258 mRestart = false;
259 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700260 }
Nick Pelly997c7612009-03-24 20:44:48 -0700261
The Android Open Source Project10592532009-03-18 17:39:46 -0700262 private synchronized void setBluetoothState(int state) {
263 if (state == mBluetoothState) {
264 return;
265 }
266
267 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
268
269 Intent intent = new Intent(BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION);
270 intent.putExtra(BluetoothIntent.BLUETOOTH_PREVIOUS_STATE, mBluetoothState);
271 intent.putExtra(BluetoothIntent.BLUETOOTH_STATE, state);
272 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
273
274 mBluetoothState = state;
275
276 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
277 }
278
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 private final Handler mHandler = new Handler() {
280 @Override
281 public void handleMessage(Message msg) {
282 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700283 case MESSAGE_REGISTER_SDP_RECORDS:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 //TODO: Don't assume HSP/HFP is running, don't use sdptool,
285 if (isEnabled()) {
286 SystemService.start("hsag");
287 SystemService.start("hfag");
288 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700289 break;
290 case MESSAGE_FINISH_DISABLE:
291 finishDisable(msg.arg1 != 0);
292 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 }
294 }
295 };
296
297 private EnableThread mEnableThread;
298
299 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700301 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 mSaveSetting = saveSetting;
303 }
304 public void run() {
305 boolean res = (enableNative() == 0);
306 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700307 int retryCount = 2;
308 boolean running = false;
309 while ((retryCount-- > 0) && !running) {
310 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700311 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700312 // thing. Check periodically for a while.
313 int pollCount = 5;
314 while ((pollCount-- > 0) && !running) {
315 if (mEventLoop.isEventLoopRunning()) {
316 running = true;
317 break;
318 }
319 try {
320 Thread.sleep(100);
321 } catch (InterruptedException e) {}
322 }
323 }
324 if (!running) {
325 log("bt EnableThread giving up");
326 res = false;
327 disableNative();
328 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331
332 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700333 if (!setupNativeDataNative()) {
334 return;
335 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 if (mSaveSetting) {
337 persistBluetoothOnSetting(true);
338 }
339 mIsDiscovering = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 mBondState.loadBondState();
The Android Open Source Project10592532009-03-18 17:39:46 -0700341 mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS),
342 3000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343
The Android Open Source Project10592532009-03-18 17:39:46 -0700344 // Log bluetooth on to battery stats.
345 long ident = Binder.clearCallingIdentity();
346 try {
347 mBatteryStats.noteBluetoothOn();
348 } catch (RemoteException e) {
349 } finally {
350 Binder.restoreCallingIdentity(ident);
351 }
352 }
353
354 mEnableThread = null;
355
356 setBluetoothState(res ?
357 BluetoothDevice.BLUETOOTH_STATE_ON :
358 BluetoothDevice.BLUETOOTH_STATE_OFF);
359
360 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700362 String[] propVal = {"Pairable", getProperty("Pairable")};
363 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700365
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900366 if (mIsAirplaneSensitive && isAirplaneModeOn()) {
367 disable(false);
368 }
369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 }
371 }
372
373 private void persistBluetoothOnSetting(boolean bluetoothOn) {
374 long origCallerIdentityToken = Binder.clearCallingIdentity();
375 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
376 bluetoothOn ? 1 : 0);
377 Binder.restoreCallingIdentity(origCallerIdentityToken);
378 }
379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 /* package */ BondState getBondState() {
381 return mBondState;
382 }
383
384 /** local cache of bonding state.
385 /* we keep our own state to track the intermediate state BONDING, which
386 /* bluez does not track.
387 * All addreses must be passed in upper case.
388 */
389 public class BondState {
390 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
391 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
392 private final ArrayList<String> mAutoPairingFailures = new ArrayList<String>();
The Android Open Source Project10592532009-03-18 17:39:46 -0700393 // List of all the vendor_id prefix of Bluetooth addresses for
394 // which auto pairing is not attempted.
395 // The following companies are included in the list below:
396 // ALPS (lexus), Murata (Prius 2007, Nokia 616), TEMIC SDS (Porsche, Audi),
397 // Parrot, Zhongshan General K-mate Electronics, Great Well
398 // Electronics, Flaircomm Electronics, Jatty Electronics, Delphi,
399 // Clarion, Novero, Denso (Lexus, Toyota), Johnson Controls (Acura),
Jaikumar Ganesh66359e32009-03-24 18:48:24 -0700400 // Continental Automotive, Harman/Becker
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 private final ArrayList<String> mAutoPairingBlacklisted =
402 new ArrayList<String>(Arrays.asList(
The Android Open Source Project10592532009-03-18 17:39:46 -0700403 "00:02:C7", "00:16:FE", "00:19:C1", "00:1B:FB", "00:1E:3D", "00:21:4F",
404 "00:23:06", "00:24:33", "00:A0:79", "00:0E:6D", "00:13:E0", "00:21:E8",
405 "00:60:57", "00:0E:9F", "00:12:1C", "00:18:91", "00:18:96", "00:13:04",
406 "00:16:FD", "00:22:A0", "00:0B:4C", "00:60:6F", "00:23:3D", "00:C0:59",
Jaikumar Ganesh66359e32009-03-24 18:48:24 -0700407 "00:0A:30", "00:1E:AE", "00:1C:D7"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 ));
409
410 public synchronized void loadBondState() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700411 if (mBluetoothState != BluetoothDevice.BLUETOOTH_STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 return;
413 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700414 String []bonds = null;
415 String val = getProperty("Devices");
416 if (val != null) {
417 bonds = val.split(",");
418 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 if (bonds == null) {
420 return;
421 }
422 mState.clear();
423 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700424 for (String device : bonds) {
425 mState.put(getAddressFromObjectPath(device).toUpperCase(),
426 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 }
428 }
429
430 public synchronized void setBondState(String address, int state) {
431 setBondState(address, state, 0);
432 }
433
434 /** reason is ignored unless state == BOND_NOT_BONDED */
435 public synchronized void setBondState(String address, int state, int reason) {
436 int oldState = getBondState(address);
437 if (oldState == state) {
438 return;
439 }
440 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
441 reason + ")");
442 Intent intent = new Intent(BluetoothIntent.BOND_STATE_CHANGED_ACTION);
443 intent.putExtra(BluetoothIntent.ADDRESS, address);
444 intent.putExtra(BluetoothIntent.BOND_STATE, state);
445 intent.putExtra(BluetoothIntent.BOND_PREVIOUS_STATE, oldState);
446 if (state == BluetoothDevice.BOND_NOT_BONDED) {
447 if (reason <= 0) {
448 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
449 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
450 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
451 }
452 intent.putExtra(BluetoothIntent.REASON, reason);
453 mState.remove(address);
454 } else {
455 mState.put(address, state);
456 }
457
458 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
459 }
460
461 public boolean isAutoPairingBlacklisted(String address) {
462 for (String blacklistAddress : mAutoPairingBlacklisted) {
463 if (address.startsWith(blacklistAddress)) return true;
464 }
465 return false;
466 }
467
468 public synchronized int getBondState(String address) {
469 Integer state = mState.get(address);
470 if (state == null) {
471 return BluetoothDevice.BOND_NOT_BONDED;
472 }
473 return state.intValue();
474 }
475
476 private synchronized String[] listInState(int state) {
477 ArrayList<String> result = new ArrayList<String>(mState.size());
478 for (Map.Entry<String, Integer> e : mState.entrySet()) {
479 if (e.getValue().intValue() == state) {
480 result.add(e.getKey());
481 }
482 }
483 return result.toArray(new String[result.size()]);
484 }
485
486 public synchronized void addAutoPairingFailure(String address) {
487 if (!mAutoPairingFailures.contains(address)) {
488 mAutoPairingFailures.add(address);
489 }
490 }
491
492 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
493 return getAttempt(address) != 0;
494 }
495
496 public synchronized void clearPinAttempts(String address) {
497 mPinAttempt.remove(address);
498 }
499
500 public synchronized boolean hasAutoPairingFailed(String address) {
501 return mAutoPairingFailures.contains(address);
502 }
503
504 public synchronized int getAttempt(String address) {
505 Integer attempt = mPinAttempt.get(address);
506 if (attempt == null) {
507 return 0;
508 }
509 return attempt.intValue();
510 }
511
512 public synchronized void attempt(String address) {
513 Integer attempt = mPinAttempt.get(address);
514 int newAttempt;
515 if (attempt == null) {
516 newAttempt = 1;
517 } else {
518 newAttempt = attempt.intValue() + 1;
519 }
520 mPinAttempt.put(address, new Integer(newAttempt));
521 }
522
523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524
525 private static String toBondStateString(int bondState) {
526 switch (bondState) {
527 case BluetoothDevice.BOND_NOT_BONDED:
528 return "not bonded";
529 case BluetoothDevice.BOND_BONDING:
530 return "bonding";
531 case BluetoothDevice.BOND_BONDED:
532 return "bonded";
533 default:
534 return "??????";
535 }
536 }
537
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700538 /*package*/synchronized void getAllProperties() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700540 mProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800541
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700542 String properties[] = (String [])getAdapterPropertiesNative();
543 // The String Array consists of key-value pairs.
544 if (properties == null) {
545 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
546 return;
547 }
548 for (int i = 0; i < properties.length; i+=2) {
549 String value = null;
550 if (mProperties.containsKey(properties[i])) {
551 value = mProperties.get(properties[i]);
552 value = value + ',' + properties[i+1];
553 } else
554 value = properties[i+1];
555
556 mProperties.put(properties[i], value);
557 }
558
559 // Add adapter object path property.
560 String adapterPath = getAdapterPathNative();
561 if (adapterPath != null)
562 mProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700564
565 /* package */ synchronized void setProperty(String name, String value) {
566 mProperties.put(name, value);
567 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568
569 public synchronized boolean setName(String name) {
570 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
571 "Need BLUETOOTH_ADMIN permission");
572 if (name == null) {
573 return false;
574 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700575 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700578 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
579 // Either have a single property function with Object as the parameter
580 // or have a function for each property and then obfuscate in the JNI layer.
581 // The following looks dirty.
582 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800583 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700584 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700587 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700589 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 }
591
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700592 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700594 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596
597 /**
598 * Set the discoverability window for the device. A timeout of zero
599 * makes the device permanently discoverable (if the device is
600 * discoverable). Setting the timeout to a nonzero value does not make
601 * a device discoverable; you need to call setMode() to make the device
602 * explicitly discoverable.
603 *
604 * @param timeout_s The discoverable timeout in seconds.
605 */
606 public synchronized boolean setDiscoverableTimeout(int timeout) {
607 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
608 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700609 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700611
612 public synchronized boolean setScanMode(int mode) {
613 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
614 "Need BLUETOOTH_ADMIN permission");
615 boolean pairable = false, discoverable = false;
616 String modeString = scanModeToBluezString(mode);
617 if (modeString.equals("off")) {
618 pairable = false;
619 discoverable = false;
620 } else if (modeString.equals("pariable")) {
621 pairable = true;
622 discoverable = false;
623 } else if (modeString.equals("discoverable")) {
624 pairable = true;
625 discoverable = true;
626 }
627 setPropertyBoolean("Pairable", pairable);
628 setPropertyBoolean("Discoverable", discoverable);
629
630 return true;
631 }
632
633 /*package*/ synchronized String getProperty (String name) {
634 if (!mProperties.isEmpty())
635 return mProperties.get(name);
636 getAllProperties();
637 return mProperties.get(name);
638 }
639
640 public synchronized String getAddress() {
641 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
642 return getProperty("Address");
643 }
644
645 public synchronized String getName() {
646 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
647 return getProperty("Name");
648 }
649
650 /**
651 * Returns the user-friendly name of a remote device. This value is
652 * returned from our local cache, which is updated when onPropertyChange
653 * event is received.
654 * Do not expect to retrieve the updated remote name immediately after
655 * changing the name on the remote device.
656 *
657 * @param address Bluetooth address of remote device.
658 *
659 * @return The user-friendly name of the specified remote device.
660 */
661 public synchronized String getRemoteName(String address) {
662 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
663 if (!BluetoothDevice.checkBluetoothAddress(address)) {
664 return null;
665 }
666 Map <String, String> properties = mRemoteDeviceProperties.get(address);
667 if (properties != null) return properties.get("Name");
668 return null;
669 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670
671 /**
672 * Get the discoverability window for the device. A timeout of zero
673 * means that the device is permanently discoverable (if the device is
674 * in the discoverable mode).
675 *
676 * @return The discoverability window of the device, in seconds. A negative
677 * value indicates an error.
678 */
679 public synchronized int getDiscoverableTimeout() {
680 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700681 String timeout = getProperty("DiscoverableTimeout");
682 if (timeout != null)
683 return Integer.valueOf(timeout);
684 else
685 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687
688 public synchronized int getScanMode() {
689 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700690 if (!isEnabled())
691 return BluetoothError.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700693 boolean pairable = getProperty("Pairable").equals("true");
694 boolean discoverable = getProperty("Discoverable").equals("true");
695 return bluezStringToScanMode (pairable, discoverable);
696 }
697
698 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
700 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700701 if (!isEnabled()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 return false;
703 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700704 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700706
707 public synchronized boolean cancelDiscovery() {
708 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
709 "Need BLUETOOTH_ADMIN permission");
710 return stopDiscoveryNative();
711 }
712
713 public synchronized boolean isDiscovering() {
714 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
715 return mIsDiscovering;
716 }
717
718 /* package */ void setIsDiscovering(boolean isDiscovering) {
719 mIsDiscovering = isDiscovering;
720 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721
722 public synchronized boolean createBond(String address) {
723 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
724 "Need BLUETOOTH_ADMIN permission");
725 if (!BluetoothDevice.checkBluetoothAddress(address)) {
726 return false;
727 }
728 address = address.toUpperCase();
729
730 String[] bonding = mBondState.listInState(BluetoothDevice.BOND_BONDING);
731 if (bonding.length > 0 && !bonding[0].equals(address)) {
732 log("Ignoring createBond(): another device is bonding");
733 // a different device is currently bonding, fail
734 return false;
735 }
736
737 // Check for bond state only if we are not performing auto
738 // pairing exponential back-off attempts.
739 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
740 mBondState.getBondState(address) != BluetoothDevice.BOND_NOT_BONDED) {
741 log("Ignoring createBond(): this device is already bonding or bonded");
742 return false;
743 }
744
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700745 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 return false;
747 }
748
749 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
750 return true;
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752
753 public synchronized boolean cancelBondProcess(String address) {
754 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
755 "Need BLUETOOTH_ADMIN permission");
756 if (!BluetoothDevice.checkBluetoothAddress(address)) {
757 return false;
758 }
759 address = address.toUpperCase();
760 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
761 return false;
762 }
763
764 mBondState.setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
765 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700766 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 return true;
768 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769
770 public synchronized boolean removeBond(String address) {
771 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
772 "Need BLUETOOTH_ADMIN permission");
773 if (!BluetoothDevice.checkBluetoothAddress(address)) {
774 return false;
775 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700776 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778
779 public synchronized String[] listBonds() {
780 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
781 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
782 }
783
784 public synchronized int getBondState(String address) {
785 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
786 if (!BluetoothDevice.checkBluetoothAddress(address)) {
787 return BluetoothError.ERROR;
788 }
789 return mBondState.getBondState(address.toUpperCase());
790 }
791
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700792 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
793 Map<String, String> properties = mRemoteDeviceProperties.get(address);
794 if (properties != null) {
795 return properties.get(property);
796 } else {
797 // Query for remote device properties, again.
798 // We will need to reload the cache when we switch Bluetooth on / off
799 // or if we crash.
800 String objectPath = getObjectPathFromAddress(address);
801 String propValues[] = (String [])getDevicePropertiesNative(objectPath);
802 if (propValues != null) {
803 addRemoteDeviceProperties(address, propValues);
804 return getRemoteDeviceProperty(address, property);
805 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700807 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
808 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700811 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
812 Map<String, String> propertyValues = new HashMap<String, String>();
813 for (int i = 0; i < properties.length; i+=2) {
814 String value = null;
815 if (propertyValues.containsKey(properties[i])) {
816 value = propertyValues.get(properties[i]);
817 value = value + ',' + properties[i+1];
818 } else {
819 value = properties[i+1];
820 }
821 propertyValues.put(properties[i], value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700823 mRemoteDeviceProperties.put(address, propertyValues);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700826 /* package */ void removeRemoteDeviceProperties(String address) {
827 mRemoteDeviceProperties.remove(address);
828 }
829
830 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
831 String value) {
832 Map <String, String> propVal = mRemoteDeviceProperties.get(address);
833 if (propVal != null) {
834 propVal.put(name, value);
835 mRemoteDeviceProperties.put(address, propVal);
836 } else {
837 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840
841 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700842 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 * integer.
844 *
845 * Note: this value is retrieved from cache, because we get it during
846 * remote-device discovery.
847 *
848 * @return 32-bit integer encoding the remote major, minor, and service
849 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 */
851 public synchronized int getRemoteClass(String address) {
852 if (!BluetoothDevice.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -0700853 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
854 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700856 String val = getRemoteDeviceProperty(address, "Class");
857 if (val == null)
858 return BluetoothClass.ERROR;
859 else {
860 return Integer.valueOf(val);
861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700863
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864
865 /**
866 * Gets the remote features encoded as bit mask.
867 *
868 * Note: This method may be obsoleted soon.
869 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700870 * @return String array of 128bit UUIDs
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 */
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700872 public synchronized String[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
874 if (!BluetoothDevice.checkBluetoothAddress(address)) {
875 return null;
876 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700877 String value = getRemoteDeviceProperty(address, "UUIDs");
878 String[] uuids = null;
879 // The UUIDs are stored as a "," separated string.
880 if (value != null)
881 uuids = value.split(",");
882 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884
885 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700886 * Gets the rfcomm channel associated with the UUID.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700888 * @param address Address of the remote device
889 * @param uuid UUID of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700891 * @return rfcomm channel associated with the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 */
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700893 public int getRemoteServiceChannel(String address, String uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
895 if (!BluetoothDevice.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700896 return BluetoothError.ERROR_IPC;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700898 return getDeviceServiceChannelNative(getObjectPathFromAddress(address), uuid, 0x0004);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900
901 public synchronized boolean setPin(String address, byte[] pin) {
902 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
903 "Need BLUETOOTH_ADMIN permission");
904 if (pin == null || pin.length <= 0 || pin.length > 16 ||
905 !BluetoothDevice.checkBluetoothAddress(address)) {
906 return false;
907 }
908 address = address.toUpperCase();
909 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
910 if (data == null) {
911 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
912 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
913 " or by bluez.\n");
914 return false;
915 }
916 // bluez API wants pin as a string
917 String pinString;
918 try {
919 pinString = new String(pin, "UTF8");
920 } catch (UnsupportedEncodingException uee) {
921 Log.e(TAG, "UTF8 not supported?!?");
922 return false;
923 }
924 return setPinNative(address, pinString, data.intValue());
925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926
927 public synchronized boolean cancelPin(String address) {
928 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
929 "Need BLUETOOTH_ADMIN permission");
930 if (!BluetoothDevice.checkBluetoothAddress(address)) {
931 return false;
932 }
933 address = address.toUpperCase();
934 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
935 if (data == null) {
936 Log.w(TAG, "cancelPin(" + address + ") called but no native data available, " +
937 "ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote " +
938 "or by bluez.\n");
939 return false;
940 }
941 return cancelPinNative(address, data.intValue());
942 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943
944 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
945 @Override
946 public void onReceive(Context context, Intent intent) {
947 String action = intent.getAction();
948 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
949 ContentResolver resolver = context.getContentResolver();
950 // Query the airplane mode from Settings.System just to make sure that
951 // some random app is not sending this intent and disabling bluetooth
952 boolean enabled = !isAirplaneModeOn();
953 // If bluetooth is currently expected to be on, then enable or disable bluetooth
954 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
955 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700956 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 } else {
958 disable(false);
959 }
960 }
961 }
962 }
963 };
964
965 private void registerForAirplaneMode() {
966 String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(),
967 Settings.System.AIRPLANE_MODE_RADIOS);
968 mIsAirplaneSensitive = airplaneModeRadios == null
969 ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
970 if (mIsAirplaneSensitive) {
971 mIntentFilter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
972 mContext.registerReceiver(mReceiver, mIntentFilter);
973 }
974 }
975
976 /* Returns true if airplane mode is currently on */
977 private final boolean isAirplaneModeOn() {
978 return Settings.System.getInt(mContext.getContentResolver(),
979 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
980 }
981
982 @Override
983 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700984 pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985
The Android Open Source Project10592532009-03-18 17:39:46 -0700986 switch(mBluetoothState) {
987 case BluetoothDevice.BLUETOOTH_STATE_OFF:
988 pw.println("\nBluetooth OFF\n");
989 return;
990 case BluetoothDevice.BLUETOOTH_STATE_TURNING_ON:
991 pw.println("\nBluetooth TURNING ON\n");
992 return;
993 case BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF:
994 pw.println("\nBluetooth TURNING OFF\n");
995 return;
996 case BluetoothDevice.BLUETOOTH_STATE_ON:
997 pw.println("\nBluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700999
1000 pw.println("\nLocal address = " + getAddress());
1001 pw.println("\nLocal name = " + getName());
1002 pw.println("\nisDiscovering() = " + isDiscovering());
1003
1004 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1005
The Android Open Source Project10592532009-03-18 17:39:46 -07001006 pw.println("\n--Known devices--");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001007 for (String address : mRemoteDeviceProperties.keySet()) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001008 pw.printf("%s %10s (%d) %s\n", address,
1009 toBondStateString(mBondState.getBondState(address)),
1010 mBondState.getAttempt(address),
1011 getRemoteName(address));
1012 }
1013
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001014 String value = getProperty("Devices");
1015 String []devicesObjectPath = null;
1016 if (value != null) {
1017 devicesObjectPath = value.split(",");
1018 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001019 pw.println("\n--ACL connected devices--");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001020 for (String device : devicesObjectPath) {
1021 pw.println(getAddressFromObjectPath(device));
The Android Open Source Project10592532009-03-18 17:39:46 -07001022 }
1023
1024 // Rather not do this from here, but no-where else and I need this
1025 // dump
1026 pw.println("\n--Headset Service--");
1027 switch (headset.getState()) {
1028 case BluetoothHeadset.STATE_DISCONNECTED:
1029 pw.println("getState() = STATE_DISCONNECTED");
1030 break;
1031 case BluetoothHeadset.STATE_CONNECTING:
1032 pw.println("getState() = STATE_CONNECTING");
1033 break;
1034 case BluetoothHeadset.STATE_CONNECTED:
1035 pw.println("getState() = STATE_CONNECTED");
1036 break;
1037 case BluetoothHeadset.STATE_ERROR:
1038 pw.println("getState() = STATE_ERROR");
1039 break;
1040 }
1041 pw.println("getHeadsetAddress() = " + headset.getHeadsetAddress());
1042 headset.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 }
1044
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001045 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1046 if (pairable && discoverable)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 return BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001048 else if (pairable && !discoverable)
1049 return BluetoothDevice.SCAN_MODE_CONNECTABLE;
1050 else
1051 return BluetoothDevice.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 }
1053
1054 /* package */ static String scanModeToBluezString(int mode) {
1055 switch (mode) {
1056 case BluetoothDevice.SCAN_MODE_NONE:
1057 return "off";
1058 case BluetoothDevice.SCAN_MODE_CONNECTABLE:
1059 return "connectable";
1060 case BluetoothDevice.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
1061 return "discoverable";
1062 }
1063 return null;
1064 }
1065
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001066 /*package*/ String getAddressFromObjectPath(String objectPath) {
1067 String adapterObjectPath = getProperty("ObjectPath");
1068 if (adapterObjectPath == null || objectPath == null) {
1069 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1070 " or deviceObjectPath:" + objectPath + " is null");
1071 return null;
1072 }
1073 if (!objectPath.startsWith(adapterObjectPath)) {
1074 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
1075 " is not a prefix of deviceObjectPath:" + objectPath +
1076 "bluetoothd crashed ?");
1077 return null;
1078 }
1079 String address = objectPath.substring(adapterObjectPath.length());
1080 if (address != null) return address.replace('_', ':');
1081
1082 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1083 return null;
1084 }
1085
1086 /*package*/ String getObjectPathFromAddress(String address) {
1087 String path = getProperty("ObjectPath");
1088 if (path == null) {
1089 Log.e(TAG, "Error: Object Path is null");
1090 return null;
1091 }
1092 path = path + address.replace(":", "_");
1093 return path;
1094 }
1095
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 private static void log(String msg) {
1097 Log.d(TAG, msg);
1098 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001099
1100 private native static void classInitNative();
1101 private native void initializeNativeDataNative();
1102 private native boolean setupNativeDataNative();
1103 private native boolean tearDownNativeDataNative();
1104 private native void cleanupNativeDataNative();
1105 private native String getAdapterPathNative();
1106
1107 private native int isEnabledNative();
1108 private native int enableNative();
1109 private native int disableNative();
1110
1111 private native Object[] getAdapterPropertiesNative();
1112 private native Object[] getDevicePropertiesNative(String objectPath);
1113 private native boolean setAdapterPropertyStringNative(String key, String value);
1114 private native boolean setAdapterPropertyIntegerNative(String key, int value);
1115 private native boolean setAdapterPropertyBooleanNative(String key, int value);
1116
1117 private native boolean startDiscoveryNative();
1118 private native boolean stopDiscoveryNative();
1119
1120 private native boolean createPairedDeviceNative(String address, int timeout_ms);
1121 private native boolean cancelDeviceCreationNative(String address);
1122 private native boolean removeDeviceNative(String objectPath);
1123 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
1124 int attributeId);
1125
1126 private native boolean cancelPinNative(String address, int nativeData);
1127 private native boolean setPinNative(String address, String pin, int nativeData);
1128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129}