blob: e68632d7ed6aafbe00038b6db094e380f0aedccc [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
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070027import android.bluetooth.BluetoothA2dp;
Nick Pellybd022f42009-08-14 18:33:38 -070028import android.bluetooth.BluetoothAdapter;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -070029import android.bluetooth.BluetoothClass;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.bluetooth.BluetoothDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.bluetooth.BluetoothHeadset;
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -070032import android.bluetooth.BluetoothDeviceProfileState;
33import android.bluetooth.BluetoothProfileState;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070034import android.bluetooth.BluetoothInputDevice;
Nick Pelly24bb9b82009-10-02 20:34:18 -070035import android.bluetooth.BluetoothSocket;
Jaikumar Ganesh10eac972009-09-21 12:48:51 -070036import android.bluetooth.BluetoothUuid;
Nick Pellybd022f42009-08-14 18:33:38 -070037import android.bluetooth.IBluetooth;
Nick Pelly16fb88a2009-10-07 07:44:03 +020038import android.bluetooth.IBluetoothCallback;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070039import android.bluetooth.IBluetoothHeadset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.BroadcastReceiver;
41import android.content.ContentResolver;
42import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -080045import android.content.SharedPreferences;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Binder;
47import android.os.Handler;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080048import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Message;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080050import android.os.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070052import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.SystemService;
54import android.provider.Settings;
55import android.util.Log;
56
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070057import com.android.internal.app.IBatteryStats;
58
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080059import java.io.BufferedInputStream;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080060import java.io.BufferedReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080061import java.io.BufferedWriter;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080062import java.io.DataInputStream;
63import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import java.io.FileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080065import java.io.FileInputStream;
66import java.io.FileNotFoundException;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080067import java.io.FileOutputStream;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080068import java.io.FileWriter;
69import java.io.IOException;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080070import java.io.InputStreamReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import java.io.PrintWriter;
72import java.io.UnsupportedEncodingException;
73import java.util.ArrayList;
74import java.util.Arrays;
75import java.util.HashMap;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070076import java.util.HashSet;
Nick Pelly16fb88a2009-10-07 07:44:03 +020077import java.util.Iterator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import java.util.Map;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070079import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
Nick Pellybd022f42009-08-14 18:33:38 -070081public class BluetoothService extends IBluetooth.Stub {
82 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private static final boolean DBG = true;
84
85 private int mNativeData;
86 private BluetoothEventLoop mEventLoop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 private boolean mIsAirplaneSensitive;
Jeff Sharkey44303922009-12-01 18:25:02 -080088 private boolean mIsAirplaneToggleable;
The Android Open Source Project10592532009-03-18 17:39:46 -070089 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070090 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 private boolean mIsDiscovering;
92
Nick Pellybd022f42009-08-14 18:33:38 -070093 private BluetoothAdapter mAdapter; // constant after init()
94 private final BondState mBondState = new BondState(); // local cache of bondings
95 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private final Context mContext;
97
98 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
99 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
100
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800101 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
102 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
103
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800104 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
105 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
106
The Android Open Source Project10592532009-03-18 17:39:46 -0700107 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
108 private static final int MESSAGE_FINISH_DISABLE = 2;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700109 private static final int MESSAGE_UUID_INTENT = 3;
Nick Pelly12835472009-09-25 15:00:29 -0700110 private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700111
112 // The timeout used to sent the UUIDs Intent
113 // This timeout should be greater than the page timeout
114 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700115
Nick Pelly16fb88a2009-10-07 07:44:03 +0200116 /** Always retrieve RFCOMM channel for these SDP UUIDs */
117 private static final ParcelUuid[] RFCOMM_UUIDS = {
118 BluetoothUuid.Handsfree,
119 BluetoothUuid.HSP,
120 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700121
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700122 // TODO(): Optimize all these string handling
Nick Pelly16fb88a2009-10-07 07:44:03 +0200123 private final Map<String, String> mAdapterProperties;
124 private final HashMap<String, Map<String, String>> mDeviceProperties;
125
126 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
127 private final ArrayList<String> mUuidIntentTracker;
128 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700129
Nick Pelly24bb9b82009-10-02 20:34:18 -0700130 private final HashMap<Integer, Integer> mServiceRecordToPid;
131
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700132 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
133 private final BluetoothProfileState mA2dpProfileState;
134 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700135
136 private BluetoothA2dpService mA2dpService;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700137 private final HashMap<BluetoothDevice, Integer> mInputDevices;
138
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800139 private static String mDockAddress;
140 private String mDockPin;
141
Nick Pelly16fb88a2009-10-07 07:44:03 +0200142 private static class RemoteService {
143 public String address;
144 public ParcelUuid uuid;
145 public RemoteService(String address, ParcelUuid uuid) {
146 this.address = address;
147 this.uuid = uuid;
148 }
149 @Override
150 public boolean equals(Object o) {
151 if (o instanceof RemoteService) {
152 RemoteService service = (RemoteService)o;
153 return address.equals(service.address) && uuid.equals(service.uuid);
154 }
155 return false;
156 }
Kenny Root5f614162010-02-17 12:00:37 -0800157
158 @Override
159 public int hashCode() {
160 int hash = 1;
161 hash = hash * 31 + (address == null ? 0 : address.hashCode());
162 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
163 return hash;
164 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200165 }
166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 static {
168 classInitNative();
169 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170
Nick Pellybd022f42009-08-14 18:33:38 -0700171 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700173
174 // Need to do this in place of:
175 // mBatteryStats = BatteryStatsService.getService();
176 // Since we can not import BatteryStatsService from here. This class really needs to be
177 // moved to java/services/com/android/server/
178 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700181
182 if (isEnabledNative() == 1) {
183 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
184 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700186
Nick Pellyde893f52009-09-08 13:15:33 -0700187 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700189 mAdapterProperties = new HashMap<String, String>();
190 mDeviceProperties = new HashMap<String, Map<String,String>>();
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700191
192 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700193 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200194 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700195 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700196 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
197 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
198 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
199
200 mHfpProfileState.start();
201 mA2dpProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800202
203 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800204 registerForAirplaneMode(filter);
205
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800206 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800207 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700208 mInputDevices = new HashMap<BluetoothDevice, Integer>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800209 }
210
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700211 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800212 if (mDockAddress != null) return mDockAddress;
213
214 BufferedInputStream file = null;
215 String dockAddress;
216 try {
217 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
218 byte[] address = new byte[17];
219 file.read(address);
220 dockAddress = new String(address);
221 dockAddress = dockAddress.toUpperCase();
222 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
223 mDockAddress = dockAddress;
224 return mDockAddress;
225 } else {
226 log("CheckBluetoothAddress failed for car dock address:" + dockAddress);
227 }
228 } catch (FileNotFoundException e) {
229 log("FileNotFoundException while trying to read dock address");
230 } catch (IOException e) {
231 log("IOException while trying to read dock address");
232 } finally {
233 if (file != null) {
234 try {
235 file.close();
236 } catch (IOException e) {
237 // Ignore
238 }
239 }
240 }
241 mDockAddress = null;
242 return null;
243 }
244
245 private synchronized boolean writeDockPin() {
246 BufferedWriter out = null;
247 try {
248 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
249
250 // Generate a random 4 digit pin between 0000 and 9999
251 // This is not truly random but good enough for our purposes.
252 int pin = (int) Math.floor(Math.random() * 10000);
253
254 mDockPin = String.format("%04d", pin);
255 out.write(mDockPin);
256 return true;
257 } catch (FileNotFoundException e) {
258 log("FileNotFoundException while trying to write dock pairing pin");
259 } catch (IOException e) {
260 log("IOException while while trying to write dock pairing pin");
261 } finally {
262 if (out != null) {
263 try {
264 out.close();
265 } catch (IOException e) {
266 // Ignore
267 }
268 }
269 }
270 mDockPin = null;
271 return false;
272 }
273
274 /*package*/ synchronized String getDockPin() {
275 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700276 }
277
278 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200279 mAdapter = BluetoothAdapter.getDefaultAdapter();
Nick Pellybd022f42009-08-14 18:33:38 -0700280 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282
283 @Override
284 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800285 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 try {
287 cleanupNativeDataNative();
288 } finally {
289 super.finalize();
290 }
291 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292
293 public boolean isEnabled() {
294 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800295 return isEnabledInternal();
296 }
297
298 private boolean isEnabledInternal() {
Nick Pellyde893f52009-09-08 13:15:33 -0700299 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301
The Android Open Source Project10592532009-03-18 17:39:46 -0700302 public int getBluetoothState() {
303 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
304 return mBluetoothState;
305 }
306
307
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 /**
309 * Bring down bluetooth and disable BT in settings. Returns true on success.
310 */
311 public boolean disable() {
312 return disable(true);
313 }
314
315 /**
316 * Bring down bluetooth. Returns true on success.
317 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200318 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 */
320 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200321 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322
The Android Open Source Project10592532009-03-18 17:39:46 -0700323 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700324 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700325 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700326 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700327 break;
328 default:
329 return false;
330 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 if (mEnableThread != null && mEnableThread.isAlive()) {
332 return false;
333 }
Nick Pellyde893f52009-09-08 13:15:33 -0700334 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
Nick Pellybc1fc052009-10-12 09:54:39 -0700335 mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
The Android Open Source Project10592532009-03-18 17:39:46 -0700336
337 // Allow 3 seconds for profiles to gracefully disconnect
338 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700339 // BluetoothService when it is done shutting down
The Android Open Source Project10592532009-03-18 17:39:46 -0700340 mHandler.sendMessageDelayed(
341 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
342 return true;
343 }
344
345
346 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700347 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700348 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 }
350 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700351 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 disableNative();
353
354 // mark in progress bondings as cancelled
355 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700356 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
358 }
359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700361 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
362 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
364
The Android Open Source Project10592532009-03-18 17:39:46 -0700365 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700366 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700367 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 if (saveSetting) {
370 persistBluetoothOnSetting(false);
371 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700372
Nick Pellyde893f52009-09-08 13:15:33 -0700373 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700374
375 // Log bluetooth off to battery stats.
376 long ident = Binder.clearCallingIdentity();
377 try {
378 mBatteryStats.noteBluetoothOff();
379 } catch (RemoteException e) {
380 } finally {
381 Binder.restoreCallingIdentity(ident);
382 }
Nick Pelly997c7612009-03-24 20:44:48 -0700383
384 if (mRestart) {
385 mRestart = false;
386 enable();
387 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
389
The Android Open Source Project10592532009-03-18 17:39:46 -0700390 /** Bring up BT and persist BT on in settings */
391 public boolean enable() {
392 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 }
394
395 /**
396 * Enable this Bluetooth device, asynchronously.
397 * This turns on/off the underlying hardware.
398 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700399 * @param saveSetting If true, persist the new state of BT in settings
400 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700402 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
404 "Need BLUETOOTH_ADMIN permission");
405
406 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800407 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 return false;
409 }
Nick Pellyde893f52009-09-08 13:15:33 -0700410 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 return false;
412 }
413 if (mEnableThread != null && mEnableThread.isAlive()) {
414 return false;
415 }
Nick Pellyde893f52009-09-08 13:15:33 -0700416 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700417 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 mEnableThread.start();
419 return true;
420 }
421
Nick Pelly997c7612009-03-24 20:44:48 -0700422 /** Forcibly restart Bluetooth if it is on */
423 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700424 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700425 return;
426 }
427 mRestart = true;
428 if (!disable(false)) {
429 mRestart = false;
430 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700431 }
Nick Pelly997c7612009-03-24 20:44:48 -0700432
The Android Open Source Project10592532009-03-18 17:39:46 -0700433 private synchronized void setBluetoothState(int state) {
434 if (state == mBluetoothState) {
435 return;
436 }
437
438 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
439
Nick Pellyde893f52009-09-08 13:15:33 -0700440 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
441 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
442 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700443 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
444
445 mBluetoothState = state;
446
447 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
448 }
449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800450 private final Handler mHandler = new Handler() {
451 @Override
452 public void handleMessage(Message msg) {
453 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700454 case MESSAGE_REGISTER_SDP_RECORDS:
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800455 if (!isEnabledInternal()) {
Nick Pellybc1fc052009-10-12 09:54:39 -0700456 return;
457 }
458 // SystemService.start() forks sdptool to register service
459 // records. It can fail to register some records if it is
460 // forked multiple times in a row, probably because there is
461 // some race in sdptool or bluez when operated in parallel.
462 // As a workaround, delay 500ms between each fork of sdptool.
463 // TODO: Don't fork sdptool in order to regsiter service
464 // records, use a DBUS call instead.
465 switch (msg.arg1) {
466 case 1:
Jaikumar Ganesh77b4ad02009-11-30 14:17:30 -0800467 Log.d(TAG, "Registering hfag record");
468 SystemService.start("hfag");
Nick Pellybc1fc052009-10-12 09:54:39 -0700469 mHandler.sendMessageDelayed(
470 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500);
471 break;
472 case 2:
Jaikumar Ganesh77b4ad02009-11-30 14:17:30 -0800473 Log.d(TAG, "Registering hsag record");
474 SystemService.start("hsag");
Nick Pellybc1fc052009-10-12 09:54:39 -0700475 mHandler.sendMessageDelayed(
476 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500);
477 break;
478 case 3:
479 Log.d(TAG, "Registering opush record");
Nick Pelly03c707a2009-07-14 21:32:14 -0700480 SystemService.start("opush");
Nick Pellybc1fc052009-10-12 09:54:39 -0700481 mHandler.sendMessageDelayed(
482 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500);
483 break;
484 case 4:
485 Log.d(TAG, "Registering pbap record");
Jaikumar Ganesh67542962009-07-23 21:32:45 -0700486 SystemService.start("pbap");
Nick Pellybc1fc052009-10-12 09:54:39 -0700487 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700489 break;
490 case MESSAGE_FINISH_DISABLE:
491 finishDisable(msg.arg1 != 0);
492 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700493 case MESSAGE_UUID_INTENT:
494 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200495 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700496 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200497 makeServiceChannelCallbacks(address);
498 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700499 break;
Nick Pelly12835472009-09-25 15:00:29 -0700500 case MESSAGE_DISCOVERABLE_TIMEOUT:
501 int mode = msg.arg1;
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800502 if (isEnabledInternal()) {
Nick Pelly12835472009-09-25 15:00:29 -0700503 // TODO: Switch back to the previous scan mode
504 // This is ok for now, because we only use
505 // CONNECTABLE and CONNECTABLE_DISCOVERABLE
506 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
507 }
508 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 }
510 }
511 };
512
513 private EnableThread mEnableThread;
514
515 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700517 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 mSaveSetting = saveSetting;
519 }
520 public void run() {
521 boolean res = (enableNative() == 0);
522 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700523 int retryCount = 2;
524 boolean running = false;
525 while ((retryCount-- > 0) && !running) {
526 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700527 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700528 // thing. Check periodically for a while.
529 int pollCount = 5;
530 while ((pollCount-- > 0) && !running) {
531 if (mEventLoop.isEventLoopRunning()) {
532 running = true;
533 break;
534 }
535 try {
536 Thread.sleep(100);
537 } catch (InterruptedException e) {}
538 }
539 }
540 if (!running) {
541 log("bt EnableThread giving up");
542 res = false;
543 disableNative();
544 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 }
546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547
548 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700549 if (!setupNativeDataNative()) {
550 return;
551 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 if (mSaveSetting) {
553 persistBluetoothOnSetting(true);
554 }
555 mIsDiscovering = false;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800556 mBondState.readAutoPairingData();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 mBondState.loadBondState();
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700558 initProfileState();
Nick Pellybc1fc052009-10-12 09:54:39 -0700559 mHandler.sendMessageDelayed(
560 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561
The Android Open Source Project10592532009-03-18 17:39:46 -0700562 // Log bluetooth on to battery stats.
563 long ident = Binder.clearCallingIdentity();
564 try {
565 mBatteryStats.noteBluetoothOn();
566 } catch (RemoteException e) {
567 } finally {
568 Binder.restoreCallingIdentity(ident);
569 }
570 }
571
572 mEnableThread = null;
573
574 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700575 BluetoothAdapter.STATE_ON :
576 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700577
578 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700580 String[] propVal = {"Pairable", getProperty("Pairable")};
581 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700583
Jeff Sharkey44303922009-12-01 18:25:02 -0800584 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900585 disable(false);
586 }
587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 }
589 }
590
591 private void persistBluetoothOnSetting(boolean bluetoothOn) {
592 long origCallerIdentityToken = Binder.clearCallingIdentity();
593 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
594 bluetoothOn ? 1 : 0);
595 Binder.restoreCallingIdentity(origCallerIdentityToken);
596 }
597
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 /* package */ BondState getBondState() {
599 return mBondState;
600 }
601
602 /** local cache of bonding state.
603 /* we keep our own state to track the intermediate state BONDING, which
604 /* bluez does not track.
605 * All addreses must be passed in upper case.
606 */
607 public class BondState {
608 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
609 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800611 private static final String AUTO_PAIRING_BLACKLIST =
612 "/etc/bluetooth/auto_pairing.conf";
613 private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST =
614 "/data/misc/bluetooth/dynamic_auto_pairing.conf";
615 private ArrayList<String> mAutoPairingAddressBlacklist;
616 private ArrayList<String> mAutoPairingExactNameBlacklist;
617 private ArrayList<String> mAutoPairingPartialNameBlacklist;
618 // Addresses added to blacklist dynamically based on usage.
619 private ArrayList<String> mAutoPairingDynamicAddressBlacklist;
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800620
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700621
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700622 // If this is an outgoing connection, store the address.
623 // There can be only 1 pending outgoing connection at a time,
624 private String mPendingOutgoingBonding;
625
626 private synchronized void setPendingOutgoingBonding(String address) {
627 mPendingOutgoingBonding = address;
628 }
629
630 public synchronized String getPendingOutgoingBonding() {
631 return mPendingOutgoingBonding;
632 }
633
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700635 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800636 return;
637 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700638 String []bonds = null;
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800639 String val = getPropertyInternal("Devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700640 if (val != null) {
641 bonds = val.split(",");
642 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 if (bonds == null) {
644 return;
645 }
646 mState.clear();
647 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700648 for (String device : bonds) {
649 mState.put(getAddressFromObjectPath(device).toUpperCase(),
650 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 }
652 }
653
654 public synchronized void setBondState(String address, int state) {
655 setBondState(address, state, 0);
656 }
657
658 /** reason is ignored unless state == BOND_NOT_BONDED */
659 public synchronized void setBondState(String address, int state, int reason) {
660 int oldState = getBondState(address);
661 if (oldState == state) {
662 return;
663 }
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700664
665 // Check if this was an pending outgoing bonding.
666 // If yes, reset the state.
667 if (oldState == BluetoothDevice.BOND_BONDING) {
668 if (address.equals(mPendingOutgoingBonding)) {
669 mPendingOutgoingBonding = null;
670 }
671 }
672
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700673 if (state == BluetoothDevice.BOND_BONDED) {
674 addProfileState(address);
675 } else if (state == BluetoothDevice.BOND_NONE) {
676 removeProfileState(address);
677 }
678
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
680 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700681 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
682 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
683 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
684 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
685 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 if (reason <= 0) {
687 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
688 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
689 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
690 }
Nick Pelly005b2282009-09-10 10:21:56 -0700691 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 mState.remove(address);
693 } else {
694 mState.put(address, state);
695 }
696
697 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
698 }
699
700 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800701 if (mAutoPairingAddressBlacklist != null) {
702 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
703 if (address.startsWith(blacklistAddress)) return true;
704 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700706
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800707 if (mAutoPairingDynamicAddressBlacklist != null) {
708 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) {
709 if (address.equals(blacklistAddress)) return true;
710 }
711 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700712 String name = getRemoteName(address);
713 if (name != null) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800714 if (mAutoPairingExactNameBlacklist != null) {
715 for (String blacklistName : mAutoPairingExactNameBlacklist) {
716 if (name.equals(blacklistName)) return true;
717 }
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800718 }
719
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800720 if (mAutoPairingPartialNameBlacklist != null) {
721 for (String blacklistName : mAutoPairingPartialNameBlacklist) {
722 if (name.startsWith(blacklistName)) return true;
723 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700724 }
725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 return false;
727 }
728
729 public synchronized int getBondState(String address) {
730 Integer state = mState.get(address);
731 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700732 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 }
734 return state.intValue();
735 }
736
Jaikumar Ganeshe5d93b72009-10-08 02:27:52 -0700737 /*package*/ synchronized String[] listInState(int state) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 ArrayList<String> result = new ArrayList<String>(mState.size());
739 for (Map.Entry<String, Integer> e : mState.entrySet()) {
740 if (e.getValue().intValue() == state) {
741 result.add(e.getKey());
742 }
743 }
744 return result.toArray(new String[result.size()]);
745 }
746
747 public synchronized void addAutoPairingFailure(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800748 if (mAutoPairingDynamicAddressBlacklist == null) {
749 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 }
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800751
752 updateAutoPairingData(address);
753 mAutoPairingDynamicAddressBlacklist.add(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 }
755
756 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
757 return getAttempt(address) != 0;
758 }
759
760 public synchronized void clearPinAttempts(String address) {
761 mPinAttempt.remove(address);
762 }
763
764 public synchronized boolean hasAutoPairingFailed(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800765 if (mAutoPairingDynamicAddressBlacklist == null) return false;
766
767 return mAutoPairingDynamicAddressBlacklist.contains(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
769
770 public synchronized int getAttempt(String address) {
771 Integer attempt = mPinAttempt.get(address);
772 if (attempt == null) {
773 return 0;
774 }
775 return attempt.intValue();
776 }
777
778 public synchronized void attempt(String address) {
779 Integer attempt = mPinAttempt.get(address);
780 int newAttempt;
781 if (attempt == null) {
782 newAttempt = 1;
783 } else {
784 newAttempt = attempt.intValue() + 1;
785 }
786 mPinAttempt.put(address, new Integer(newAttempt));
787 }
788
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800789 private void copyAutoPairingData() {
790 File file = null;
791 FileInputStream in = null;
792 FileOutputStream out = null;
793 try {
794 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST);
795 if (file.exists()) return;
796
797 in = new FileInputStream(AUTO_PAIRING_BLACKLIST);
798 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
799
800 byte[] buf = new byte[1024];
801 int len;
802 while ((len = in.read(buf)) > 0) {
803 out.write(buf, 0, len);
804 }
805 } catch (FileNotFoundException e) {
806 log("FileNotFoundException: in copyAutoPairingData");
807 } catch (IOException e) {
808 log("IOException: in copyAutoPairingData");
809 } finally {
810 try {
811 if (in != null) in.close();
812 if (out != null) out.close();
813 } catch (IOException e) {}
814 }
815 }
816
817 public void readAutoPairingData() {
818 if (mAutoPairingAddressBlacklist != null) return;
819 copyAutoPairingData();
820 FileInputStream fstream = null;
821 try {
822 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
823 DataInputStream in = new DataInputStream(fstream);
824 BufferedReader file = new BufferedReader(new InputStreamReader(in));
825 String line;
826 while((line = file.readLine()) != null) {
827 line = line.trim();
828 if (line.length() == 0 || line.startsWith("//")) continue;
829 String[] value = line.split("=");
830 if (value != null && value.length == 2) {
831 String[] val = value[1].split(",");
832 if (value[0].equalsIgnoreCase("AddressBlacklist")) {
833 mAutoPairingAddressBlacklist =
834 new ArrayList<String>(Arrays.asList(val));
835 } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) {
836 mAutoPairingExactNameBlacklist =
837 new ArrayList<String>(Arrays.asList(val));
838 } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) {
839 mAutoPairingPartialNameBlacklist =
840 new ArrayList<String>(Arrays.asList(val));
841 } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) {
842 mAutoPairingDynamicAddressBlacklist =
843 new ArrayList<String>(Arrays.asList(val));
844 } else {
845 Log.e(TAG, "Error parsing Auto pairing blacklist file");
846 }
847 }
848 }
849 } catch (FileNotFoundException e) {
850 log("FileNotFoundException: readAutoPairingData" + e.toString());
851 } catch (IOException e) {
852 log("IOException: readAutoPairingData" + e.toString());
853 } finally {
854 if (fstream != null) {
855 try {
856 fstream.close();
857 } catch (IOException e) {
858 // Ignore
859 }
860 }
861 }
862 }
863
864 // This function adds a bluetooth address to the auto pairing blacklis
865 // file. These addresses are added to DynamicAddressBlacklistSection
866 private void updateAutoPairingData(String address) {
867 BufferedWriter out = null;
868 try {
869 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true));
870 StringBuilder str = new StringBuilder();
871 if (mAutoPairingDynamicAddressBlacklist.size() == 0) {
872 str.append("DynamicAddressBlacklist=");
873 }
874 str.append(address);
875 str.append(",");
876 out.write(str.toString());
877 } catch (FileNotFoundException e) {
878 log("FileNotFoundException: updateAutoPairingData" + e.toString());
879 } catch (IOException e) {
880 log("IOException: updateAutoPairingData" + e.toString());
881 } finally {
882 if (out != null) {
883 try {
884 out.close();
885 } catch (IOException e) {
886 // Ignore
887 }
888 }
889 }
890 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892
893 private static String toBondStateString(int bondState) {
894 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700895 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 return "not bonded";
897 case BluetoothDevice.BOND_BONDING:
898 return "bonding";
899 case BluetoothDevice.BOND_BONDED:
900 return "bonded";
901 default:
902 return "??????";
903 }
904 }
905
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -0700906 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
907 return mAdapterProperties.isEmpty();
908 }
909
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700910 /*package*/synchronized void getAllProperties() {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800911
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -0700913 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700915 String properties[] = (String [])getAdapterPropertiesNative();
916 // The String Array consists of key-value pairs.
917 if (properties == null) {
918 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
919 return;
920 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700921
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700922 for (int i = 0; i < properties.length; i++) {
923 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700924 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700925 int len;
926 if (name == null) {
927 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
928 continue;
929 }
930 if (name.equals("Devices")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700931 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700932 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700933 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700934 str.append(properties[++i]);
935 str.append(",");
936 }
937 if (len > 0) {
938 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700939 }
940 } else {
941 newValue = properties[++i];
942 }
Nick Pellybd022f42009-08-14 18:33:38 -0700943 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700944 }
945
946 // Add adapter object path property.
947 String adapterPath = getAdapterPathNative();
948 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -0700949 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700951
952 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700953 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700954 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955
956 public synchronized boolean setName(String name) {
957 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
958 "Need BLUETOOTH_ADMIN permission");
959 if (name == null) {
960 return false;
961 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700962 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700965 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
966 // Either have a single property function with Object as the parameter
967 // or have a function for each property and then obfuscate in the JNI layer.
968 // The following looks dirty.
969 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800971 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700972 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700975 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800977 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700978 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 }
980
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700981 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800983 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700984 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986
987 /**
988 * Set the discoverability window for the device. A timeout of zero
989 * makes the device permanently discoverable (if the device is
990 * discoverable). Setting the timeout to a nonzero value does not make
991 * a device discoverable; you need to call setMode() to make the device
992 * explicitly discoverable.
993 *
994 * @param timeout_s The discoverable timeout in seconds.
995 */
996 public synchronized boolean setDiscoverableTimeout(int timeout) {
997 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
998 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700999 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001001
Nick Pelly12835472009-09-25 15:00:29 -07001002 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -07001003 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
1004 "Need WRITE_SECURE_SETTINGS permission");
Nick Pellyde893f52009-09-08 13:15:33 -07001005 boolean pairable = false;
1006 boolean discoverable = false;
Nick Pelly12835472009-09-25 15:00:29 -07001007
Nick Pellyde893f52009-09-08 13:15:33 -07001008 switch (mode) {
1009 case BluetoothAdapter.SCAN_MODE_NONE:
Nick Pelly12835472009-09-25 15:00:29 -07001010 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001011 pairable = false;
1012 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -07001013 break;
1014 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001015 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001016 pairable = true;
1017 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -07001018 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001019 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001020 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001021 pairable = true;
1022 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -07001023 Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
1024 mHandler.sendMessageDelayed(msg, duration * 1000);
1025 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -07001026 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001027 default:
1028 Log.w(TAG, "Requested invalid scan mode " + mode);
1029 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001030 }
1031 setPropertyBoolean("Pairable", pairable);
1032 setPropertyBoolean("Discoverable", discoverable);
1033
1034 return true;
1035 }
1036
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08001037 /*package*/ synchronized String getProperty(String name) {
1038 if (!isEnabledInternal()) return null;
1039 return getPropertyInternal(name);
1040 }
1041
1042 /*package*/ synchronized String getPropertyInternal(String name) {
Nick Pellybd022f42009-08-14 18:33:38 -07001043 if (!mAdapterProperties.isEmpty())
1044 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001045 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -07001046 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001047 }
1048
1049 public synchronized String getAddress() {
1050 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1051 return getProperty("Address");
1052 }
1053
1054 public synchronized String getName() {
1055 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1056 return getProperty("Name");
1057 }
1058
1059 /**
1060 * Returns the user-friendly name of a remote device. This value is
1061 * returned from our local cache, which is updated when onPropertyChange
1062 * event is received.
1063 * Do not expect to retrieve the updated remote name immediately after
1064 * changing the name on the remote device.
1065 *
1066 * @param address Bluetooth address of remote device.
1067 *
1068 * @return The user-friendly name of the specified remote device.
1069 */
1070 public synchronized String getRemoteName(String address) {
1071 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001072 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001073 return null;
1074 }
Jaikumar Ganesh55929a92009-09-30 10:49:34 -07001075 return getRemoteDeviceProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077
1078 /**
1079 * Get the discoverability window for the device. A timeout of zero
1080 * means that the device is permanently discoverable (if the device is
1081 * in the discoverable mode).
1082 *
1083 * @return The discoverability window of the device, in seconds. A negative
1084 * value indicates an error.
1085 */
1086 public synchronized int getDiscoverableTimeout() {
1087 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001088 String timeout = getProperty("DiscoverableTimeout");
1089 if (timeout != null)
1090 return Integer.valueOf(timeout);
1091 else
1092 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094
1095 public synchronized int getScanMode() {
1096 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001097 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -07001098 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001100 boolean pairable = getProperty("Pairable").equals("true");
1101 boolean discoverable = getProperty("Discoverable").equals("true");
1102 return bluezStringToScanMode (pairable, discoverable);
1103 }
1104
1105 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1107 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001108 if (!isEnabledInternal()) return false;
1109
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001110 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001112
1113 public synchronized boolean cancelDiscovery() {
1114 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1115 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001116 if (!isEnabledInternal()) return false;
1117
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001118 return stopDiscoveryNative();
1119 }
1120
1121 public synchronized boolean isDiscovering() {
1122 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1123 return mIsDiscovering;
1124 }
1125
1126 /* package */ void setIsDiscovering(boolean isDiscovering) {
1127 mIsDiscovering = isDiscovering;
1128 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129
1130 public synchronized boolean createBond(String address) {
1131 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1132 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001133 if (!isEnabledInternal()) return false;
1134
Nick Pelly005b2282009-09-10 10:21:56 -07001135 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 return false;
1137 }
1138 address = address.toUpperCase();
1139
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001140 if (mBondState.getPendingOutgoingBonding() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141 log("Ignoring createBond(): another device is bonding");
1142 // a different device is currently bonding, fail
1143 return false;
1144 }
1145
1146 // Check for bond state only if we are not performing auto
1147 // pairing exponential back-off attempts.
1148 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -07001149 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 log("Ignoring createBond(): this device is already bonding or bonded");
1151 return false;
1152 }
1153
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001154 if (address.equals(mDockAddress)) {
1155 if (!writeDockPin()) {
1156 log("Error while writing Pin for the dock");
1157 return false;
1158 }
1159 }
1160
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001161 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 return false;
1163 }
1164
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001165 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 return true;
1169 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170
1171 public synchronized boolean cancelBondProcess(String address) {
1172 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1173 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001174 if (!isEnabledInternal()) return false;
1175
Nick Pelly005b2282009-09-10 10:21:56 -07001176 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 return false;
1178 }
1179 address = address.toUpperCase();
1180 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1181 return false;
1182 }
1183
Nick Pelly005b2282009-09-10 10:21:56 -07001184 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001186 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 return true;
1188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189
1190 public synchronized boolean removeBond(String address) {
1191 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1192 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001193 if (!isEnabledInternal()) return false;
1194
Nick Pelly005b2282009-09-10 10:21:56 -07001195 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 return false;
1197 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001198 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001199 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001200 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001201 return true;
1202 } else {
1203 return false;
1204 }
1205 }
1206
1207 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001208 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001209 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210
1211 public synchronized String[] listBonds() {
1212 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1213 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1214 }
1215
1216 public synchronized int getBondState(String address) {
1217 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001218 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001219 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 }
1221 return mBondState.getBondState(address.toUpperCase());
1222 }
1223
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001224 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001225 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1226 mContext.MODE_PRIVATE);
1227
1228 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001229 }
1230
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001231 public synchronized boolean connectInputDevice(BluetoothDevice device) {
1232 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1233 "Need BLUETOOTH_ADMIN permission");
1234
1235 String objectPath = getObjectPathFromAddress(device.getAddress());
1236 if (objectPath == null || getConnectedInputDevices().length != 0 ||
1237 getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
1238 return false;
1239 }
1240 if(connectInputDeviceNative(objectPath)) {
1241 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
1242 return true;
1243 }
1244 return false;
1245 }
1246
1247 public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
1248 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1249 "Need BLUETOOTH_ADMIN permission");
1250
1251 String objectPath = getObjectPathFromAddress(device.getAddress());
1252 if (objectPath == null || getConnectedInputDevices().length == 0) {
1253 return false;
1254 }
1255 if(disconnectInputDeviceNative(objectPath)) {
1256 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
1257 return true;
1258 }
1259 return false;
1260 }
1261
1262 public synchronized int getInputDeviceState(BluetoothDevice device) {
1263 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1264
1265 if (mInputDevices.get(device) == null) {
1266 return BluetoothInputDevice.STATE_DISCONNECTED;
1267 }
1268 return mInputDevices.get(device.getAddress());
1269 }
1270
1271 public synchronized BluetoothDevice[] getConnectedInputDevices() {
1272 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1273 Set<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
1274 new int[] {BluetoothInputDevice.STATE_CONNECTED});
1275 return devices.toArray(new BluetoothDevice[devices.size()]);
1276 }
1277
1278 public synchronized int getInputDevicePriority(BluetoothDevice device) {
1279 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1280 return Settings.Secure.getInt(mContext.getContentResolver(),
1281 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1282 BluetoothInputDevice.PRIORITY_UNDEFINED);
1283 }
1284
1285 public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
1286 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1287 "Need BLUETOOTH_ADMIN permission");
1288 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
1289 return false;
1290 }
1291 return Settings.Secure.putInt(mContext.getContentResolver(),
1292 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1293 priority);
1294 }
1295
1296 /*package*/synchronized Set<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
1297 Set<BluetoothDevice> inputDevices = new HashSet<BluetoothDevice>();
1298 if (mInputDevices.isEmpty()) {
1299 return inputDevices;
1300 }
1301 for (BluetoothDevice device: mInputDevices.keySet()) {
1302 int inputDeviceState = getInputDeviceState(device);
1303 for (int state : states) {
1304 if (state == inputDeviceState) {
1305 inputDevices.add(device);
1306 break;
1307 }
1308 }
1309 }
1310 return inputDevices;
1311 }
1312
1313 private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
1314 int prevState;
1315 if (mInputDevices.get(device) == null) {
1316 prevState = BluetoothInputDevice.STATE_DISCONNECTED;
1317 } else {
1318 prevState = mInputDevices.get(device);
1319 }
1320 if (prevState == state) return;
1321
1322 mInputDevices.put(device, state);
1323
1324 if (getInputDevicePriority(device) >
1325 BluetoothInputDevice.PRIORITY_OFF &&
1326 state == BluetoothInputDevice.STATE_CONNECTING ||
1327 state == BluetoothInputDevice.STATE_CONNECTED) {
1328 // We have connected or attempting to connect.
1329 // Bump priority
1330 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
1331 }
1332
1333 Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
1334 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1335 intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
1336 intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
1337 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1338
1339 if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
1340
1341 }
1342
1343 /*package*/ void handleInputDevicePropertyChange(String path, boolean connected) {
1344 String address = getAddressFromObjectPath(path);
1345 if (address == null) return;
1346 int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
1347 BluetoothInputDevice.STATE_DISCONNECTED;
1348 BluetoothDevice device = mAdapter.getRemoteDevice(address);
1349 handleInputDeviceStateChange(device, state);
1350 }
1351
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001352 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001353 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001354 }
1355
1356 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001357 if (!isEnabledInternal()) return null;
1358
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001359 String objectPath = getObjectPathFromAddress(address);
1360 return (String [])getDevicePropertiesNative(objectPath);
1361 }
1362
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001363 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -07001364 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001365 if (properties != null) {
1366 return properties.get(property);
1367 } else {
1368 // Query for remote device properties, again.
1369 // We will need to reload the cache when we switch Bluetooth on / off
1370 // or if we crash.
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001371 if (updateRemoteDevicePropertiesCache(address))
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001372 return getRemoteDeviceProperty(address, property);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001374 Log.e(TAG, "getRemoteDeviceProperty: " + property + "not present:" + address);
1375 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001378 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
1379 String[] propValues = getRemoteDeviceProperties(address);
1380 if (propValues != null) {
1381 addRemoteDeviceProperties(address, propValues);
1382 return true;
1383 }
1384 return false;
1385 }
1386
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001387 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001388 /*
1389 * We get a DeviceFound signal every time RSSI changes or name changes.
1390 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -07001391 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001392 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001393 propertyValues = new HashMap<String, String>();
1394 }
1395
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001396 for (int i = 0; i < properties.length; i++) {
1397 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001398 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001399 int len;
1400 if (name == null) {
1401 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1402 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001403 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001404 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001405 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001406 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001407 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001408 str.append(properties[++i]);
1409 str.append(",");
1410 }
1411 if (len > 0) {
1412 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001413 }
1414 } else {
1415 newValue = properties[++i];
1416 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001417
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001418 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 }
Nick Pellybd022f42009-08-14 18:33:38 -07001420 mDeviceProperties.put(address, propertyValues);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001421
1422 // We have added a new remote device or updated its properties.
1423 // Also update the serviceChannel cache.
1424 updateDeviceServiceChannelCache(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001425 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001427 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001428 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001429 }
1430
1431 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1432 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -07001433 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001434 if (propVal != null) {
1435 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -07001436 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001437 } else {
1438 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441
1442 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001443 * Sets the remote device trust state.
1444 *
1445 * @return boolean to indicate operation success or fail
1446 */
1447 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001448 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001449 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1450 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001451 return false;
1452 }
1453
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001454 if (!isEnabledInternal()) return false;
1455
Lixin Yueefa1dd72009-08-31 15:55:13 +08001456 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1457 value ? 1 : 0);
1458 }
1459
1460 /**
1461 * Gets the remote device trust state as boolean.
1462 * Note: this value may be
1463 * retrieved from cache if we retrieved the data before *
1464 *
1465 * @return boolean to indicate trust or untrust state
1466 */
1467 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001468 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001469 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1470 return false;
1471 }
1472
1473 String val = getRemoteDeviceProperty(address, "Trusted");
1474 if (val == null) {
1475 return false;
1476 } else {
1477 return val.equals("true") ? true : false;
1478 }
1479 }
1480
1481 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001482 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 * integer.
1484 *
1485 * Note: this value is retrieved from cache, because we get it during
1486 * remote-device discovery.
1487 *
1488 * @return 32-bit integer encoding the remote major, minor, and service
1489 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 */
1491 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001492 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001493 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1494 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001496 String val = getRemoteDeviceProperty(address, "Class");
1497 if (val == null)
1498 return BluetoothClass.ERROR;
1499 else {
1500 return Integer.valueOf(val);
1501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001504
1505 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001506 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001507 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001508 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001510 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001512 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 return null;
1514 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001515 return getUuidFromCache(address);
1516 }
1517
1518 private ParcelUuid[] getUuidFromCache(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001519 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001520 if (value == null) return null;
1521
1522 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001523 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001524 uuidStrings = value.split(",");
1525 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1526
1527 for (int i = 0; i < uuidStrings.length; i++) {
1528 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1529 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001530 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001531 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532
Nick Pelly16fb88a2009-10-07 07:44:03 +02001533 /**
1534 * Connect and fetch new UUID's using SDP.
1535 * The UUID's found are broadcast as intents.
1536 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1537 * a given uuid.
1538 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1539 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1540 * callback and broadcast intents.
1541 */
1542 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1543 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001544 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001545 if (!isEnabledInternal()) return false;
1546
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001547 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1548 return false;
1549 }
1550
Nick Pelly16fb88a2009-10-07 07:44:03 +02001551 RemoteService service = new RemoteService(address, uuid);
1552 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1553 // An SDP query for this address & uuid is already in progress
1554 // Do not add this callback for the uuid
1555 return false;
1556 }
1557
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001558 if (mUuidIntentTracker.contains(address)) {
1559 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02001560 // Add this uuid onto the in-progress SDP query
1561 if (uuid != null) {
1562 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1563 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001564 return true;
1565 }
1566
1567 boolean ret;
Jaikumar Ganesh0e090302010-03-29 00:01:34 -07001568 // Just do the SDP if the device is already created and UUIDs are not
1569 // NULL, else create the device and then do SDP.
1570 if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001571 String path = getObjectPathFromAddress(address);
1572 if (path == null) return false;
1573
1574 // Use an empty string for the UUID pattern
1575 ret = discoverServicesNative(path, "");
1576 } else {
1577 ret = createDeviceNative(address);
1578 }
1579
1580 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001581 if (uuid != null) {
1582 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1583 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001584
1585 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1586 message.obj = address;
1587 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1588 return ret;
1589 }
1590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001592 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02001593 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001595 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001596 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001598 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001599 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001600 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001601 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001603 if (!isEnabledInternal()) return -1;
1604
Nick Pelly005b2282009-09-10 10:21:56 -07001605 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001606 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001608 // Check if we are recovering from a crash.
1609 if (mDeviceProperties.isEmpty()) {
1610 if (!updateRemoteDevicePropertiesCache(address))
1611 return -1;
1612 }
1613
1614 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1615 if (value != null && value.containsKey(uuid))
1616 return value.get(uuid);
1617 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619
1620 public synchronized boolean setPin(String address, byte[] pin) {
1621 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1622 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001623 if (!isEnabledInternal()) return false;
1624
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001626 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001627 return false;
1628 }
1629 address = address.toUpperCase();
1630 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1631 if (data == null) {
1632 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1633 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1634 " or by bluez.\n");
1635 return false;
1636 }
1637 // bluez API wants pin as a string
1638 String pinString;
1639 try {
1640 pinString = new String(pin, "UTF8");
1641 } catch (UnsupportedEncodingException uee) {
1642 Log.e(TAG, "UTF8 not supported?!?");
1643 return false;
1644 }
1645 return setPinNative(address, pinString, data.intValue());
1646 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001648 public synchronized boolean setPasskey(String address, int passkey) {
1649 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1650 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001651 if (!isEnabledInternal()) return false;
1652
Nick Pelly005b2282009-09-10 10:21:56 -07001653 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001654 return false;
1655 }
1656 address = address.toUpperCase();
1657 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1658 if (data == null) {
1659 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1660 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1661 " or by bluez.\n");
1662 return false;
1663 }
1664 return setPasskeyNative(address, passkey, data.intValue());
1665 }
1666
1667 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1668 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1669 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001670 if (!isEnabledInternal()) return false;
1671
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001672 address = address.toUpperCase();
1673 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1674 if (data == null) {
1675 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1676 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1677 " or by bluez.\n");
1678 return false;
1679 }
1680 return setPairingConfirmationNative(address, confirm, data.intValue());
1681 }
1682
1683 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1685 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001686 if (!isEnabledInternal()) return false;
1687
Nick Pelly005b2282009-09-10 10:21:56 -07001688 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 return false;
1690 }
Nick Pelly005b2282009-09-10 10:21:56 -07001691 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001692 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 address = address.toUpperCase();
1694 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1695 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001696 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1697 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1698 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 return false;
1700 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001701 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001702 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001704 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001705 ParcelUuid[] deviceUuids = getRemoteUuids(address);
1706 // We are storing the rfcomm channel numbers only for the uuids
1707 // we are interested in.
1708 int channel;
Nick Pelly16fb88a2009-10-07 07:44:03 +02001709 if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
1710
1711 ArrayList<ParcelUuid> applicationUuids = new ArrayList();
1712
1713 synchronized (this) {
1714 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1715 if (service.address.equals(address)) {
1716 applicationUuids.add(service.uuid);
1717 }
1718 }
1719 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001720
1721 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001722
1723 // Retrieve RFCOMM channel for default uuids
1724 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001725 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Nick Pelly16fb88a2009-10-07 07:44:03 +02001726 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1727 uuid.toString(), 0x0004);
1728 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001729 value.put(uuid, channel);
1730 }
1731 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02001732 // Retrieve RFCOMM channel for application requested uuids
1733 for (ParcelUuid uuid : applicationUuids) {
1734 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
1735 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1736 uuid.toString(), 0x0004);
1737 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
1738 value.put(uuid, channel);
1739 }
1740 }
1741
1742 synchronized (this) {
1743 // Make application callbacks
1744 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1745 iter.hasNext();) {
1746 RemoteService service = iter.next();
1747 if (service.address.equals(address)) {
1748 channel = -1;
1749 if (value.get(service.uuid) != null) {
1750 channel = value.get(service.uuid);
1751 }
1752 if (channel != -1) {
1753 if (DBG) log("Making callback for " + service.uuid + " with result " +
1754 channel);
1755 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1756 if (callback != null) {
1757 try {
1758 callback.onRfcommChannelFound(channel);
1759 } catch (RemoteException e) {Log.e(TAG, "", e);}
1760 }
1761
1762 iter.remove();
1763 }
1764 }
1765 }
1766
1767 // Update cache
1768 mDeviceServiceChannelCache.put(address, value);
1769 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001770 }
1771
Nick Pelly24bb9b82009-10-02 20:34:18 -07001772 /**
1773 * b is a handle to a Binder instance, so that this service can be notified
1774 * for Applications that terminate unexpectedly, to clean there service
1775 * records
1776 */
1777 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1778 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001779 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1780 if (!isEnabledInternal()) return -1;
1781
Nick Pelly24bb9b82009-10-02 20:34:18 -07001782 if (serviceName == null || uuid == null || channel < 1 ||
1783 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1784 return -1;
1785 }
1786 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1787 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1788 return -1;
1789 }
1790 int handle = addRfcommServiceRecordNative(serviceName,
1791 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1792 (short)channel);
1793 if (DBG) log("new handle " + Integer.toHexString(handle));
1794 if (handle == -1) {
1795 return -1;
1796 }
1797
1798 int pid = Binder.getCallingPid();
1799 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1800 try {
1801 b.linkToDeath(new Reaper(handle, pid), 0);
1802 } catch (RemoteException e) {}
1803 return handle;
1804 }
1805
1806 public void removeServiceRecord(int handle) {
1807 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1808 "Need BLUETOOTH permission");
1809 checkAndRemoveRecord(handle, Binder.getCallingPid());
1810 }
1811
1812 private synchronized void checkAndRemoveRecord(int handle, int pid) {
1813 Integer handleInt = new Integer(handle);
1814 Integer owner = mServiceRecordToPid.get(handleInt);
1815 if (owner != null && pid == owner.intValue()) {
1816 if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
1817 pid);
1818 mServiceRecordToPid.remove(handleInt);
1819 removeServiceRecordNative(handle);
1820 }
1821 }
1822
1823 private class Reaper implements IBinder.DeathRecipient {
1824 int pid;
1825 int handle;
1826 Reaper(int handle, int pid) {
1827 this.pid = pid;
1828 this.handle = handle;
1829 }
1830 public void binderDied() {
1831 synchronized (BluetoothService.this) {
1832 if (DBG) log("Tracked app " + pid + " died");
1833 checkAndRemoveRecord(handle, pid);
1834 }
1835 }
1836 }
1837
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1839 @Override
1840 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001841 if (intent == null) return;
1842
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 String action = intent.getAction();
1844 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1845 ContentResolver resolver = context.getContentResolver();
1846 // Query the airplane mode from Settings.System just to make sure that
1847 // some random app is not sending this intent and disabling bluetooth
1848 boolean enabled = !isAirplaneModeOn();
1849 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1850 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1851 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001852 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 } else {
1854 disable(false);
1855 }
1856 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001857 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1858 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1859 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1860 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1861 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1862 mDockAddress = null;
1863 mDockPin = null;
1864 } else {
1865 SharedPreferences.Editor editor =
1866 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1867 mContext.MODE_PRIVATE).edit();
1868 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
1869 editor.commit();
1870 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 }
1872 }
1873 };
1874
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001875 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08001876 final ContentResolver resolver = mContext.getContentResolver();
1877 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08001879 final String toggleableRadios = Settings.System.getString(resolver,
1880 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1881
1882 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1883 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1884 mIsAirplaneToggleable = toggleableRadios == null ? false :
1885 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1886
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001888 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 }
1890 }
1891
1892 /* Returns true if airplane mode is currently on */
1893 private final boolean isAirplaneModeOn() {
1894 return Settings.System.getInt(mContext.getContentResolver(),
1895 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1896 }
1897
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001898 /* Broadcast the Uuid intent */
1899 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001900 ParcelUuid[] uuid = getUuidFromCache(address);
1901 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07001902 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001903 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1904 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001905
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001906 if (mUuidIntentTracker.contains(address))
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001907 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001908
1909 }
1910
1911 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1912 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1913 iter.hasNext();) {
1914 RemoteService service = iter.next();
1915 if (service.address.equals(address)) {
1916 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
1917 " " + service.uuid);
1918 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1919 if (callback != null) {
1920 try {
1921 callback.onRfcommChannelFound(-1);
1922 } catch (RemoteException e) {Log.e(TAG, "", e);}
1923 }
1924
1925 iter.remove();
1926 }
1927 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001928 }
1929
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 @Override
1931 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001932 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07001933 case BluetoothAdapter.STATE_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07001934 pw.println("Bluetooth OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07001935 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001936 case BluetoothAdapter.STATE_TURNING_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07001937 pw.println("Bluetooth TURNING ON\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07001938 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001939 case BluetoothAdapter.STATE_TURNING_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07001940 pw.println("Bluetooth TURNING OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07001941 return;
Nick Pellyde893f52009-09-08 13:15:33 -07001942 case BluetoothAdapter.STATE_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07001943 pw.println("Bluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001945
Nick Pelly24bb9b82009-10-02 20:34:18 -07001946 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08001947 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001948
1949 pw.println("Local address = " + getAddress());
1950 pw.println("Local name = " + getName());
1951 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07001952
1953 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
1954
The Android Open Source Project10592532009-03-18 17:39:46 -07001955 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07001956 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07001957 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07001958 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07001959 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07001960 mBondState.getAttempt(address),
1961 getRemoteName(address));
Nick Pelly24bb9b82009-10-02 20:34:18 -07001962
1963 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
1964 if (uuidChannels == null) {
1965 pw.println("\tuuids = null");
1966 } else {
1967 for (ParcelUuid uuid : uuidChannels.keySet()) {
1968 Integer channel = uuidChannels.get(uuid);
1969 if (channel == null) {
1970 pw.println("\t" + uuid);
1971 } else {
1972 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
Nick Pelly1eada0d2009-08-26 10:57:33 -07001973 }
1974 }
1975 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02001976 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1977 if (service.address.equals(address)) {
1978 pw.println("\tPENDING CALLBACK: " + service.uuid);
1979 }
1980 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001981 }
1982
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001983 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07001984 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001985 if (value != null) {
1986 devicesObjectPath = value.split(",");
1987 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001988 pw.println("\n--ACL connected devices--");
Nick Pelly24bb9b82009-10-02 20:34:18 -07001989 if (devicesObjectPath != null) {
1990 for (String device : devicesObjectPath) {
1991 pw.println(getAddressFromObjectPath(device));
1992 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001993 }
1994
1995 // Rather not do this from here, but no-where else and I need this
1996 // dump
1997 pw.println("\n--Headset Service--");
Jaikumar Ganesh740e39b2010-06-02 12:33:53 -07001998 switch (headset.getState(headset.getCurrentHeadset())) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001999 case BluetoothHeadset.STATE_DISCONNECTED:
2000 pw.println("getState() = STATE_DISCONNECTED");
2001 break;
2002 case BluetoothHeadset.STATE_CONNECTING:
2003 pw.println("getState() = STATE_CONNECTING");
2004 break;
2005 case BluetoothHeadset.STATE_CONNECTED:
2006 pw.println("getState() = STATE_CONNECTED");
2007 break;
2008 case BluetoothHeadset.STATE_ERROR:
2009 pw.println("getState() = STATE_ERROR");
2010 break;
2011 }
Nick Pelly6c901db2009-06-19 10:08:09 -07002012
Nick Pelly24bb9b82009-10-02 20:34:18 -07002013 pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
2014 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
The Android Open Source Project10592532009-03-18 17:39:46 -07002015 headset.close();
Nick Pelly24bb9b82009-10-02 20:34:18 -07002016 pw.println("\n--Application Service Records--");
2017 for (Integer handle : mServiceRecordToPid.keySet()) {
2018 Integer pid = mServiceRecordToPid.get(handle);
2019 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
2020 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 }
2022
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002023 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2024 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002025 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002026 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002027 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002028 else
Nick Pellybd022f42009-08-14 18:33:38 -07002029 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 }
2031
2032 /* package */ static String scanModeToBluezString(int mode) {
2033 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07002034 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002035 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07002036 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002037 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07002038 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002039 return "discoverable";
2040 }
2041 return null;
2042 }
2043
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002044 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002045 String adapterObjectPath = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002046 if (adapterObjectPath == null || objectPath == null) {
2047 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
2048 " or deviceObjectPath:" + objectPath + " is null");
2049 return null;
2050 }
2051 if (!objectPath.startsWith(adapterObjectPath)) {
2052 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
2053 " is not a prefix of deviceObjectPath:" + objectPath +
2054 "bluetoothd crashed ?");
2055 return null;
2056 }
2057 String address = objectPath.substring(adapterObjectPath.length());
2058 if (address != null) return address.replace('_', ':');
2059
2060 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2061 return null;
2062 }
2063
2064 /*package*/ String getObjectPathFromAddress(String address) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002065 String path = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002066 if (path == null) {
2067 Log.e(TAG, "Error: Object Path is null");
2068 return null;
2069 }
2070 path = path + address.replace(":", "_");
2071 return path;
2072 }
2073
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002074 /*package */ void setLinkTimeout(String address, int num_slots) {
2075 String path = getObjectPathFromAddress(address);
2076 boolean result = setLinkTimeoutNative(path, num_slots);
2077
2078 if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
2079 }
2080
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002081 public boolean connectHeadset(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002082 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002083 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002084 Message msg = new Message();
2085 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2086 msg.obj = state;
2087 mHfpProfileState.sendMessage(msg);
2088 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002089 }
2090 return false;
2091 }
2092
2093 public boolean disconnectHeadset(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002094 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002095 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002096 Message msg = new Message();
2097 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2098 msg.obj = state;
2099 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002100 return true;
2101 }
2102 return false;
2103 }
2104
2105 public boolean connectSink(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002106 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002107 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002108 Message msg = new Message();
2109 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2110 msg.obj = state;
2111 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002112 return true;
2113 }
2114 return false;
2115 }
2116
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002117 public boolean disconnectSink(String address) {
2118 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2119 if (state != null) {
2120 Message msg = new Message();
2121 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2122 msg.obj = state;
2123 mA2dpProfileState.sendMessage(msg);
2124 return true;
2125 }
2126 return false;
2127 }
2128
2129 private BluetoothDeviceProfileState addProfileState(String address) {
2130 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002131 if (state != null) return state;
2132
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002133 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2134 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002135 state.start();
2136 return state;
2137 }
2138
2139 private void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002140 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002141 }
2142
2143 private void initProfileState() {
2144 String []bonds = null;
2145 String val = getPropertyInternal("Devices");
2146 if (val != null) {
2147 bonds = val.split(",");
2148 }
2149 if (bonds == null) {
2150 return;
2151 }
2152
2153 for (String path : bonds) {
2154 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002155 BluetoothDeviceProfileState state = addProfileState(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002156 // Allow 8 secs for SDP records to get registered.
2157 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002158 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002159 state.sendMessageDelayed(msg, 8000);
2160 }
2161 }
2162
2163 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002164 BluetoothDeviceProfileState state =
2165 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002166 if (state != null) {
2167 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002168 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002169 state.sendMessage(msg);
2170 return true;
2171 }
2172 return false;
2173 }
2174
2175 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002176 BluetoothDeviceProfileState state =
2177 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002178 if (state != null) {
2179 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002180 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002181 state.sendMessage(msg);
2182 return true;
2183 }
2184 return false;
2185 }
2186
2187 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2188 mA2dpService = a2dpService;
2189 }
2190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002191 private static void log(String msg) {
2192 Log.d(TAG, msg);
2193 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002194
2195 private native static void classInitNative();
2196 private native void initializeNativeDataNative();
2197 private native boolean setupNativeDataNative();
2198 private native boolean tearDownNativeDataNative();
2199 private native void cleanupNativeDataNative();
2200 private native String getAdapterPathNative();
2201
2202 private native int isEnabledNative();
2203 private native int enableNative();
2204 private native int disableNative();
2205
2206 private native Object[] getAdapterPropertiesNative();
2207 private native Object[] getDevicePropertiesNative(String objectPath);
2208 private native boolean setAdapterPropertyStringNative(String key, String value);
2209 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2210 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2211
2212 private native boolean startDiscoveryNative();
2213 private native boolean stopDiscoveryNative();
2214
2215 private native boolean createPairedDeviceNative(String address, int timeout_ms);
2216 private native boolean cancelDeviceCreationNative(String address);
2217 private native boolean removeDeviceNative(String objectPath);
2218 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2219 int attributeId);
2220
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002221 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002222 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002223 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2224 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2225 int nativeData);
Nick Pelly24bb9b82009-10-02 20:34:18 -07002226 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2227 int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002228 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002229 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002230
Nick Pelly24bb9b82009-10-02 20:34:18 -07002231 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2232 short channel);
2233 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002234 private native boolean setLinkTimeoutNative(String path, int num_slots);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07002235 private native boolean connectInputDeviceNative(String path);
2236 private native boolean disconnectInputDeviceNative(String path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002237}