blob: 7252736b10103a2b6c6cafb501e420f6a06e3645 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/**
18 * TODO: Move this to
Nick Pellybd022f42009-08-14 18:33:38 -070019 * java/services/com/android/server/BluetoothService.java
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020 * and make the contructor package private again.
21 *
22 * @hide
23 */
24
25package android.server;
26
Nick Pellybd022f42009-08-14 18:33:38 -070027import android.bluetooth.BluetoothAdapter;
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -070028import android.bluetooth.BluetoothClass;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.bluetooth.BluetoothDevice;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.bluetooth.BluetoothHeadset;
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -070031import android.bluetooth.BluetoothDeviceProfileState;
Danica Chang6fdd0c62010-08-11 14:54:43 -070032import android.bluetooth.BluetoothPan;
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -070033import 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.BroadcastReceiver;
40import android.content.ContentResolver;
41import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -080044import android.content.SharedPreferences;
Danica Chang6fdd0c62010-08-11 14:54:43 -070045import android.net.ConnectivityManager;
46import android.net.InterfaceConfiguration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.os.Binder;
48import android.os.Handler;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080049import android.os.IBinder;
Danica Chang6fdd0c62010-08-11 14:54:43 -070050import android.os.INetworkManagementService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Message;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080052import android.os.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070054import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.SystemService;
56import android.provider.Settings;
57import android.util.Log;
58
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070059import com.android.internal.app.IBatteryStats;
60
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080061import java.io.BufferedInputStream;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080062import java.io.BufferedReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080063import java.io.BufferedWriter;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080064import java.io.DataInputStream;
65import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import java.io.FileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080067import java.io.FileInputStream;
68import java.io.FileNotFoundException;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080069import java.io.FileOutputStream;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080070import java.io.FileWriter;
71import java.io.IOException;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080072import java.io.InputStreamReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import java.io.PrintWriter;
74import java.io.UnsupportedEncodingException;
75import java.util.ArrayList;
76import java.util.Arrays;
77import java.util.HashMap;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070078import java.util.HashSet;
Nick Pelly16fb88a2009-10-07 07:44:03 +020079import java.util.Iterator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080import java.util.Map;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070081import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
Nick Pellybd022f42009-08-14 18:33:38 -070083public class BluetoothService extends IBluetooth.Stub {
84 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private static final boolean DBG = true;
86
87 private int mNativeData;
88 private BluetoothEventLoop mEventLoop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private boolean mIsAirplaneSensitive;
Jeff Sharkey44303922009-12-01 18:25:02 -080090 private boolean mIsAirplaneToggleable;
The Android Open Source Project10592532009-03-18 17:39:46 -070091 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070092 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 private boolean mIsDiscovering;
Danica Chang6fdd0c62010-08-11 14:54:43 -070094 private boolean mTetheringOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095
Nick Pellybd022f42009-08-14 18:33:38 -070096 private BluetoothAdapter mAdapter; // constant after init()
97 private final BondState mBondState = new BondState(); // local cache of bondings
98 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private final Context mContext;
100
101 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
102 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
103
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800104 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
105 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
106
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800107 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
108 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
109
The Android Open Source Project10592532009-03-18 17:39:46 -0700110 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
111 private static final int MESSAGE_FINISH_DISABLE = 2;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700112 private static final int MESSAGE_UUID_INTENT = 3;
Nick Pelly12835472009-09-25 15:00:29 -0700113 private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700114
Danica Chang6fdd0c62010-08-11 14:54:43 -0700115 private ArrayList<String> mBluetoothIfaceAddresses;
116 private static final String BLUETOOTH_NEAR_IFACE_ADDR_PREFIX= "192.168.44.";
117 private static final String BLUETOOTH_NETMASK = "255.255.255.0";
118
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700119 // The timeout used to sent the UUIDs Intent
120 // This timeout should be greater than the page timeout
121 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700122
Nick Pelly16fb88a2009-10-07 07:44:03 +0200123 /** Always retrieve RFCOMM channel for these SDP UUIDs */
124 private static final ParcelUuid[] RFCOMM_UUIDS = {
125 BluetoothUuid.Handsfree,
126 BluetoothUuid.HSP,
127 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700128
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700129 // TODO(): Optimize all these string handling
Nick Pelly16fb88a2009-10-07 07:44:03 +0200130 private final Map<String, String> mAdapterProperties;
131 private final HashMap<String, Map<String, String>> mDeviceProperties;
132
133 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
134 private final ArrayList<String> mUuidIntentTracker;
135 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700136
Nick Pelly24bb9b82009-10-02 20:34:18 -0700137 private final HashMap<Integer, Integer> mServiceRecordToPid;
138
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700139 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
140 private final BluetoothProfileState mA2dpProfileState;
141 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700142 private final BluetoothProfileState mHidProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700143
144 private BluetoothA2dpService mA2dpService;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700145 private final HashMap<BluetoothDevice, Integer> mInputDevices;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700146 private final HashMap<BluetoothDevice, Integer> mPanDevices;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700147
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800148 private static String mDockAddress;
149 private String mDockPin;
150
Nick Pelly16fb88a2009-10-07 07:44:03 +0200151 private static class RemoteService {
152 public String address;
153 public ParcelUuid uuid;
154 public RemoteService(String address, ParcelUuid uuid) {
155 this.address = address;
156 this.uuid = uuid;
157 }
158 @Override
159 public boolean equals(Object o) {
160 if (o instanceof RemoteService) {
161 RemoteService service = (RemoteService)o;
162 return address.equals(service.address) && uuid.equals(service.uuid);
163 }
164 return false;
165 }
Kenny Root5f614162010-02-17 12:00:37 -0800166
167 @Override
168 public int hashCode() {
169 int hash = 1;
170 hash = hash * 31 + (address == null ? 0 : address.hashCode());
171 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
172 return hash;
173 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200174 }
175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 static {
177 classInitNative();
178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179
Nick Pellybd022f42009-08-14 18:33:38 -0700180 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700182
183 // Need to do this in place of:
184 // mBatteryStats = BatteryStatsService.getService();
185 // Since we can not import BatteryStatsService from here. This class really needs to be
186 // moved to java/services/com/android/server/
187 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700190
191 if (isEnabledNative() == 1) {
192 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
193 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700195
Nick Pellyde893f52009-09-08 13:15:33 -0700196 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 mIsDiscovering = false;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700198 mTetheringOn = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700199 mAdapterProperties = new HashMap<String, String>();
200 mDeviceProperties = new HashMap<String, Map<String,String>>();
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700201
202 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700203 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200204 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700205 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700206 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
207 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
208 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700209 mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700210
Danica Chang6fdd0c62010-08-11 14:54:43 -0700211 // Can tether to up to 7 devices
212 mBluetoothIfaceAddresses = new ArrayList<String>(BluetoothPan.MAX_CONNECTIONS);
213 for (int i=1; i <= BluetoothPan.MAX_CONNECTIONS; i++) {
214 mBluetoothIfaceAddresses.add(BLUETOOTH_NEAR_IFACE_ADDR_PREFIX + i);
215 }
216
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700217 mHfpProfileState.start();
218 mA2dpProfileState.start();
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700219 mHidProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800220
221 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800222 registerForAirplaneMode(filter);
223
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800224 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800225 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700226 mInputDevices = new HashMap<BluetoothDevice, Integer>();
Danica Chang6fdd0c62010-08-11 14:54:43 -0700227 mPanDevices = new HashMap<BluetoothDevice, Integer>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800228 }
229
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700230 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800231 if (mDockAddress != null) return mDockAddress;
232
233 BufferedInputStream file = null;
234 String dockAddress;
235 try {
236 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
237 byte[] address = new byte[17];
238 file.read(address);
239 dockAddress = new String(address);
240 dockAddress = dockAddress.toUpperCase();
241 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
242 mDockAddress = dockAddress;
243 return mDockAddress;
244 } else {
245 log("CheckBluetoothAddress failed for car dock address:" + dockAddress);
246 }
247 } catch (FileNotFoundException e) {
248 log("FileNotFoundException while trying to read dock address");
249 } catch (IOException e) {
250 log("IOException while trying to read dock address");
251 } finally {
252 if (file != null) {
253 try {
254 file.close();
255 } catch (IOException e) {
256 // Ignore
257 }
258 }
259 }
260 mDockAddress = null;
261 return null;
262 }
263
264 private synchronized boolean writeDockPin() {
265 BufferedWriter out = null;
266 try {
267 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
268
269 // Generate a random 4 digit pin between 0000 and 9999
270 // This is not truly random but good enough for our purposes.
271 int pin = (int) Math.floor(Math.random() * 10000);
272
273 mDockPin = String.format("%04d", pin);
274 out.write(mDockPin);
275 return true;
276 } catch (FileNotFoundException e) {
277 log("FileNotFoundException while trying to write dock pairing pin");
278 } catch (IOException e) {
279 log("IOException while while trying to write dock pairing pin");
280 } finally {
281 if (out != null) {
282 try {
283 out.close();
284 } catch (IOException e) {
285 // Ignore
286 }
287 }
288 }
289 mDockPin = null;
290 return false;
291 }
292
293 /*package*/ synchronized String getDockPin() {
294 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700295 }
296
297 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200298 mAdapter = BluetoothAdapter.getDefaultAdapter();
Nick Pellybd022f42009-08-14 18:33:38 -0700299 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301
302 @Override
303 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800304 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 try {
306 cleanupNativeDataNative();
307 } finally {
308 super.finalize();
309 }
310 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311
312 public boolean isEnabled() {
313 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800314 return isEnabledInternal();
315 }
316
317 private boolean isEnabledInternal() {
Nick Pellyde893f52009-09-08 13:15:33 -0700318 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320
The Android Open Source Project10592532009-03-18 17:39:46 -0700321 public int getBluetoothState() {
322 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
323 return mBluetoothState;
324 }
325
326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 /**
328 * Bring down bluetooth and disable BT in settings. Returns true on success.
329 */
330 public boolean disable() {
331 return disable(true);
332 }
333
334 /**
335 * Bring down bluetooth. Returns true on success.
336 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200337 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 */
339 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200340 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341
The Android Open Source Project10592532009-03-18 17:39:46 -0700342 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700343 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700344 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700345 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700346 break;
347 default:
348 return false;
349 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 if (mEnableThread != null && mEnableThread.isAlive()) {
351 return false;
352 }
Nick Pellyde893f52009-09-08 13:15:33 -0700353 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
Nick Pellybc1fc052009-10-12 09:54:39 -0700354 mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700355 setBluetoothTetheringNative(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700356
357 // Allow 3 seconds for profiles to gracefully disconnect
358 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700359 // BluetoothService when it is done shutting down
The Android Open Source Project10592532009-03-18 17:39:46 -0700360 mHandler.sendMessageDelayed(
361 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
362 return true;
363 }
364
365
366 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700367 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700368 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 }
370 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700371 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 disableNative();
373
374 // mark in progress bondings as cancelled
375 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700376 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
378 }
379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700381 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
382 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
384
The Android Open Source Project10592532009-03-18 17:39:46 -0700385 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700386 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700387 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389 if (saveSetting) {
390 persistBluetoothOnSetting(false);
391 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700392
Nick Pellyde893f52009-09-08 13:15:33 -0700393 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700394
395 // Log bluetooth off to battery stats.
396 long ident = Binder.clearCallingIdentity();
397 try {
398 mBatteryStats.noteBluetoothOff();
399 } catch (RemoteException e) {
400 } finally {
401 Binder.restoreCallingIdentity(ident);
402 }
Nick Pelly997c7612009-03-24 20:44:48 -0700403
404 if (mRestart) {
405 mRestart = false;
406 enable();
407 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 }
409
The Android Open Source Project10592532009-03-18 17:39:46 -0700410 /** Bring up BT and persist BT on in settings */
411 public boolean enable() {
412 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 }
414
415 /**
416 * Enable this Bluetooth device, asynchronously.
417 * This turns on/off the underlying hardware.
418 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700419 * @param saveSetting If true, persist the new state of BT in settings
420 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700422 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
424 "Need BLUETOOTH_ADMIN permission");
425
426 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800427 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 return false;
429 }
Nick Pellyde893f52009-09-08 13:15:33 -0700430 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 return false;
432 }
433 if (mEnableThread != null && mEnableThread.isAlive()) {
434 return false;
435 }
Nick Pellyde893f52009-09-08 13:15:33 -0700436 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700437 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 mEnableThread.start();
439 return true;
440 }
441
Nick Pelly997c7612009-03-24 20:44:48 -0700442 /** Forcibly restart Bluetooth if it is on */
443 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700444 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700445 return;
446 }
447 mRestart = true;
448 if (!disable(false)) {
449 mRestart = false;
450 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700451 }
Nick Pelly997c7612009-03-24 20:44:48 -0700452
The Android Open Source Project10592532009-03-18 17:39:46 -0700453 private synchronized void setBluetoothState(int state) {
454 if (state == mBluetoothState) {
455 return;
456 }
457
458 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
459
Nick Pellyde893f52009-09-08 13:15:33 -0700460 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
461 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
462 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700463 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
464
465 mBluetoothState = state;
466
467 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
468 }
469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 private final Handler mHandler = new Handler() {
471 @Override
472 public void handleMessage(Message msg) {
473 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700474 case MESSAGE_REGISTER_SDP_RECORDS:
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800475 if (!isEnabledInternal()) {
Nick Pellybc1fc052009-10-12 09:54:39 -0700476 return;
477 }
478 // SystemService.start() forks sdptool to register service
479 // records. It can fail to register some records if it is
480 // forked multiple times in a row, probably because there is
481 // some race in sdptool or bluez when operated in parallel.
482 // As a workaround, delay 500ms between each fork of sdptool.
483 // TODO: Don't fork sdptool in order to regsiter service
484 // records, use a DBUS call instead.
485 switch (msg.arg1) {
486 case 1:
Jaikumar Ganesh77b4ad02009-11-30 14:17:30 -0800487 Log.d(TAG, "Registering hfag record");
488 SystemService.start("hfag");
Nick Pellybc1fc052009-10-12 09:54:39 -0700489 mHandler.sendMessageDelayed(
490 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500);
491 break;
492 case 2:
Jaikumar Ganesh77b4ad02009-11-30 14:17:30 -0800493 Log.d(TAG, "Registering hsag record");
494 SystemService.start("hsag");
Nick Pellybc1fc052009-10-12 09:54:39 -0700495 mHandler.sendMessageDelayed(
496 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500);
497 break;
498 case 3:
499 Log.d(TAG, "Registering opush record");
Nick Pelly03c707a2009-07-14 21:32:14 -0700500 SystemService.start("opush");
Nick Pellybc1fc052009-10-12 09:54:39 -0700501 mHandler.sendMessageDelayed(
502 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500);
503 break;
504 case 4:
505 Log.d(TAG, "Registering pbap record");
Jaikumar Ganesh67542962009-07-23 21:32:45 -0700506 SystemService.start("pbap");
Nick Pellybc1fc052009-10-12 09:54:39 -0700507 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700509 break;
510 case MESSAGE_FINISH_DISABLE:
511 finishDisable(msg.arg1 != 0);
512 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700513 case MESSAGE_UUID_INTENT:
514 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200515 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700516 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200517 makeServiceChannelCallbacks(address);
518 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700519 break;
Nick Pelly12835472009-09-25 15:00:29 -0700520 case MESSAGE_DISCOVERABLE_TIMEOUT:
521 int mode = msg.arg1;
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800522 if (isEnabledInternal()) {
Nick Pelly12835472009-09-25 15:00:29 -0700523 // TODO: Switch back to the previous scan mode
524 // This is ok for now, because we only use
525 // CONNECTABLE and CONNECTABLE_DISCOVERABLE
526 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
527 }
528 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530 }
531 };
532
533 private EnableThread mEnableThread;
534
535 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700537 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 mSaveSetting = saveSetting;
539 }
540 public void run() {
541 boolean res = (enableNative() == 0);
542 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700543 int retryCount = 2;
544 boolean running = false;
545 while ((retryCount-- > 0) && !running) {
546 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700547 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700548 // thing. Check periodically for a while.
549 int pollCount = 5;
550 while ((pollCount-- > 0) && !running) {
551 if (mEventLoop.isEventLoopRunning()) {
552 running = true;
553 break;
554 }
555 try {
556 Thread.sleep(100);
557 } catch (InterruptedException e) {}
558 }
559 }
560 if (!running) {
561 log("bt EnableThread giving up");
562 res = false;
563 disableNative();
564 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
566
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800567
568 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700569 if (!setupNativeDataNative()) {
570 return;
571 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 if (mSaveSetting) {
573 persistBluetoothOnSetting(true);
574 }
575 mIsDiscovering = false;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800576 mBondState.readAutoPairingData();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 mBondState.loadBondState();
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700578 initProfileState();
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700579
580 //Register SDP records.
Nick Pellybc1fc052009-10-12 09:54:39 -0700581 mHandler.sendMessageDelayed(
582 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700583 setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
584
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585
The Android Open Source Project10592532009-03-18 17:39:46 -0700586 // Log bluetooth on to battery stats.
587 long ident = Binder.clearCallingIdentity();
588 try {
589 mBatteryStats.noteBluetoothOn();
590 } catch (RemoteException e) {
591 } finally {
592 Binder.restoreCallingIdentity(ident);
593 }
594 }
595
596 mEnableThread = null;
597
598 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700599 BluetoothAdapter.STATE_ON :
600 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700601
602 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700604 String[] propVal = {"Pairable", getProperty("Pairable")};
605 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700607
Jeff Sharkey44303922009-12-01 18:25:02 -0800608 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900609 disable(false);
610 }
611
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 }
613 }
614
615 private void persistBluetoothOnSetting(boolean bluetoothOn) {
616 long origCallerIdentityToken = Binder.clearCallingIdentity();
617 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
618 bluetoothOn ? 1 : 0);
619 Binder.restoreCallingIdentity(origCallerIdentityToken);
620 }
621
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 /* package */ BondState getBondState() {
623 return mBondState;
624 }
625
626 /** local cache of bonding state.
627 /* we keep our own state to track the intermediate state BONDING, which
628 /* bluez does not track.
629 * All addreses must be passed in upper case.
630 */
631 public class BondState {
632 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
633 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800635 private static final String AUTO_PAIRING_BLACKLIST =
636 "/etc/bluetooth/auto_pairing.conf";
637 private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST =
638 "/data/misc/bluetooth/dynamic_auto_pairing.conf";
639 private ArrayList<String> mAutoPairingAddressBlacklist;
640 private ArrayList<String> mAutoPairingExactNameBlacklist;
641 private ArrayList<String> mAutoPairingPartialNameBlacklist;
642 // Addresses added to blacklist dynamically based on usage.
643 private ArrayList<String> mAutoPairingDynamicAddressBlacklist;
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800644
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700645
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700646 // If this is an outgoing connection, store the address.
647 // There can be only 1 pending outgoing connection at a time,
648 private String mPendingOutgoingBonding;
649
650 private synchronized void setPendingOutgoingBonding(String address) {
651 mPendingOutgoingBonding = address;
652 }
653
654 public synchronized String getPendingOutgoingBonding() {
655 return mPendingOutgoingBonding;
656 }
657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700659 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 return;
661 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700662 String []bonds = null;
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800663 String val = getPropertyInternal("Devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700664 if (val != null) {
665 bonds = val.split(",");
666 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 if (bonds == null) {
668 return;
669 }
670 mState.clear();
671 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700672 for (String device : bonds) {
673 mState.put(getAddressFromObjectPath(device).toUpperCase(),
674 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 }
676 }
677
678 public synchronized void setBondState(String address, int state) {
679 setBondState(address, state, 0);
680 }
681
682 /** reason is ignored unless state == BOND_NOT_BONDED */
683 public synchronized void setBondState(String address, int state, int reason) {
684 int oldState = getBondState(address);
685 if (oldState == state) {
686 return;
687 }
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700688
689 // Check if this was an pending outgoing bonding.
690 // If yes, reset the state.
691 if (oldState == BluetoothDevice.BOND_BONDING) {
692 if (address.equals(mPendingOutgoingBonding)) {
693 mPendingOutgoingBonding = null;
694 }
695 }
696
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700697 if (state == BluetoothDevice.BOND_BONDED) {
698 addProfileState(address);
699 } else if (state == BluetoothDevice.BOND_NONE) {
700 removeProfileState(address);
701 }
702
Jaikumar Ganeshc3ee99d2010-07-19 14:59:44 -0700703 // HID is handled by BluetoothService, other profiles
704 // will be handled by their respective services.
705 setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state);
706
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
708 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700709 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
710 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
711 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
712 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
713 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 if (reason <= 0) {
715 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
716 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
717 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
718 }
Nick Pelly005b2282009-09-10 10:21:56 -0700719 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 mState.remove(address);
721 } else {
722 mState.put(address, state);
723 }
724
725 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
726 }
727
728 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800729 if (mAutoPairingAddressBlacklist != null) {
730 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
731 if (address.startsWith(blacklistAddress)) return true;
732 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700734
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800735 if (mAutoPairingDynamicAddressBlacklist != null) {
736 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) {
737 if (address.equals(blacklistAddress)) return true;
738 }
739 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700740 String name = getRemoteName(address);
741 if (name != null) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800742 if (mAutoPairingExactNameBlacklist != null) {
743 for (String blacklistName : mAutoPairingExactNameBlacklist) {
744 if (name.equals(blacklistName)) return true;
745 }
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800746 }
747
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800748 if (mAutoPairingPartialNameBlacklist != null) {
749 for (String blacklistName : mAutoPairingPartialNameBlacklist) {
750 if (name.startsWith(blacklistName)) return true;
751 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700752 }
753 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 return false;
755 }
756
757 public synchronized int getBondState(String address) {
758 Integer state = mState.get(address);
759 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700760 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 }
762 return state.intValue();
763 }
764
Jaikumar Ganeshe5d93b72009-10-08 02:27:52 -0700765 /*package*/ synchronized String[] listInState(int state) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 ArrayList<String> result = new ArrayList<String>(mState.size());
767 for (Map.Entry<String, Integer> e : mState.entrySet()) {
768 if (e.getValue().intValue() == state) {
769 result.add(e.getKey());
770 }
771 }
772 return result.toArray(new String[result.size()]);
773 }
774
775 public synchronized void addAutoPairingFailure(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800776 if (mAutoPairingDynamicAddressBlacklist == null) {
777 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 }
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800779
780 updateAutoPairingData(address);
781 mAutoPairingDynamicAddressBlacklist.add(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
783
784 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
785 return getAttempt(address) != 0;
786 }
787
788 public synchronized void clearPinAttempts(String address) {
789 mPinAttempt.remove(address);
790 }
791
792 public synchronized boolean hasAutoPairingFailed(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800793 if (mAutoPairingDynamicAddressBlacklist == null) return false;
794
795 return mAutoPairingDynamicAddressBlacklist.contains(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 }
797
798 public synchronized int getAttempt(String address) {
799 Integer attempt = mPinAttempt.get(address);
800 if (attempt == null) {
801 return 0;
802 }
803 return attempt.intValue();
804 }
805
806 public synchronized void attempt(String address) {
807 Integer attempt = mPinAttempt.get(address);
808 int newAttempt;
809 if (attempt == null) {
810 newAttempt = 1;
811 } else {
812 newAttempt = attempt.intValue() + 1;
813 }
814 mPinAttempt.put(address, new Integer(newAttempt));
815 }
816
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800817 private void copyAutoPairingData() {
818 File file = null;
819 FileInputStream in = null;
820 FileOutputStream out = null;
821 try {
822 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST);
823 if (file.exists()) return;
824
825 in = new FileInputStream(AUTO_PAIRING_BLACKLIST);
826 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
827
828 byte[] buf = new byte[1024];
829 int len;
830 while ((len = in.read(buf)) > 0) {
831 out.write(buf, 0, len);
832 }
833 } catch (FileNotFoundException e) {
834 log("FileNotFoundException: in copyAutoPairingData");
835 } catch (IOException e) {
836 log("IOException: in copyAutoPairingData");
837 } finally {
838 try {
839 if (in != null) in.close();
840 if (out != null) out.close();
841 } catch (IOException e) {}
842 }
843 }
844
845 public void readAutoPairingData() {
846 if (mAutoPairingAddressBlacklist != null) return;
847 copyAutoPairingData();
848 FileInputStream fstream = null;
849 try {
850 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
851 DataInputStream in = new DataInputStream(fstream);
852 BufferedReader file = new BufferedReader(new InputStreamReader(in));
853 String line;
854 while((line = file.readLine()) != null) {
855 line = line.trim();
856 if (line.length() == 0 || line.startsWith("//")) continue;
857 String[] value = line.split("=");
858 if (value != null && value.length == 2) {
859 String[] val = value[1].split(",");
860 if (value[0].equalsIgnoreCase("AddressBlacklist")) {
861 mAutoPairingAddressBlacklist =
862 new ArrayList<String>(Arrays.asList(val));
863 } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) {
864 mAutoPairingExactNameBlacklist =
865 new ArrayList<String>(Arrays.asList(val));
866 } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) {
867 mAutoPairingPartialNameBlacklist =
868 new ArrayList<String>(Arrays.asList(val));
869 } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) {
870 mAutoPairingDynamicAddressBlacklist =
871 new ArrayList<String>(Arrays.asList(val));
872 } else {
873 Log.e(TAG, "Error parsing Auto pairing blacklist file");
874 }
875 }
876 }
877 } catch (FileNotFoundException e) {
878 log("FileNotFoundException: readAutoPairingData" + e.toString());
879 } catch (IOException e) {
880 log("IOException: readAutoPairingData" + e.toString());
881 } finally {
882 if (fstream != null) {
883 try {
884 fstream.close();
885 } catch (IOException e) {
886 // Ignore
887 }
888 }
889 }
890 }
891
892 // This function adds a bluetooth address to the auto pairing blacklis
893 // file. These addresses are added to DynamicAddressBlacklistSection
894 private void updateAutoPairingData(String address) {
895 BufferedWriter out = null;
896 try {
897 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true));
898 StringBuilder str = new StringBuilder();
899 if (mAutoPairingDynamicAddressBlacklist.size() == 0) {
900 str.append("DynamicAddressBlacklist=");
901 }
902 str.append(address);
903 str.append(",");
904 out.write(str.toString());
905 } catch (FileNotFoundException e) {
906 log("FileNotFoundException: updateAutoPairingData" + e.toString());
907 } catch (IOException e) {
908 log("IOException: updateAutoPairingData" + e.toString());
909 } finally {
910 if (out != null) {
911 try {
912 out.close();
913 } catch (IOException e) {
914 // Ignore
915 }
916 }
917 }
918 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920
921 private static String toBondStateString(int bondState) {
922 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700923 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 return "not bonded";
925 case BluetoothDevice.BOND_BONDING:
926 return "bonding";
927 case BluetoothDevice.BOND_BONDED:
928 return "bonded";
929 default:
930 return "??????";
931 }
932 }
933
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -0700934 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
935 return mAdapterProperties.isEmpty();
936 }
937
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700938 /*package*/synchronized void getAllProperties() {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800939
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -0700941 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700943 String properties[] = (String [])getAdapterPropertiesNative();
944 // The String Array consists of key-value pairs.
945 if (properties == null) {
946 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
947 return;
948 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700949
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700950 for (int i = 0; i < properties.length; i++) {
951 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700952 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700953 int len;
954 if (name == null) {
955 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
956 continue;
957 }
Danica Chang7a9de8b2010-07-16 18:38:29 -0700958 if (name.equals("Devices") || name.equals("UUIDs")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700959 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700960 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700961 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -0700962 str.append(properties[++i]);
963 str.append(",");
964 }
965 if (len > 0) {
966 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -0700967 }
968 } else {
969 newValue = properties[++i];
970 }
Nick Pellybd022f42009-08-14 18:33:38 -0700971 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700972 }
973
974 // Add adapter object path property.
975 String adapterPath = getAdapterPathNative();
976 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -0700977 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700979
980 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -0700981 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700982 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983
984 public synchronized boolean setName(String name) {
985 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
986 "Need BLUETOOTH_ADMIN permission");
987 if (name == null) {
988 return false;
989 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700990 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800991 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700993 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
994 // Either have a single property function with Object as the parameter
995 // or have a function for each property and then obfuscate in the JNI layer.
996 // The following looks dirty.
997 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800999 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001000 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001003 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001005 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001006 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 }
1008
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001009 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001011 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001012 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014
1015 /**
1016 * Set the discoverability window for the device. A timeout of zero
1017 * makes the device permanently discoverable (if the device is
1018 * discoverable). Setting the timeout to a nonzero value does not make
1019 * a device discoverable; you need to call setMode() to make the device
1020 * explicitly discoverable.
1021 *
1022 * @param timeout_s The discoverable timeout in seconds.
1023 */
1024 public synchronized boolean setDiscoverableTimeout(int timeout) {
1025 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1026 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001027 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001029
Nick Pelly12835472009-09-25 15:00:29 -07001030 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -07001031 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
1032 "Need WRITE_SECURE_SETTINGS permission");
Nick Pellyde893f52009-09-08 13:15:33 -07001033 boolean pairable = false;
1034 boolean discoverable = false;
Nick Pelly12835472009-09-25 15:00:29 -07001035
Nick Pellyde893f52009-09-08 13:15:33 -07001036 switch (mode) {
1037 case BluetoothAdapter.SCAN_MODE_NONE:
Nick Pelly12835472009-09-25 15:00:29 -07001038 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001039 pairable = false;
1040 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -07001041 break;
1042 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001043 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001044 pairable = true;
1045 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -07001046 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001047 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001048 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001049 pairable = true;
1050 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -07001051 Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
1052 mHandler.sendMessageDelayed(msg, duration * 1000);
1053 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -07001054 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001055 default:
1056 Log.w(TAG, "Requested invalid scan mode " + mode);
1057 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001058 }
1059 setPropertyBoolean("Pairable", pairable);
1060 setPropertyBoolean("Discoverable", discoverable);
1061
1062 return true;
1063 }
1064
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08001065 /*package*/ synchronized String getProperty(String name) {
1066 if (!isEnabledInternal()) return null;
1067 return getPropertyInternal(name);
1068 }
1069
1070 /*package*/ synchronized String getPropertyInternal(String name) {
Nick Pellybd022f42009-08-14 18:33:38 -07001071 if (!mAdapterProperties.isEmpty())
1072 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001073 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -07001074 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001075 }
1076
1077 public synchronized String getAddress() {
1078 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1079 return getProperty("Address");
1080 }
1081
1082 public synchronized String getName() {
1083 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1084 return getProperty("Name");
1085 }
1086
1087 /**
1088 * Returns the user-friendly name of a remote device. This value is
1089 * returned from our local cache, which is updated when onPropertyChange
1090 * event is received.
1091 * Do not expect to retrieve the updated remote name immediately after
1092 * changing the name on the remote device.
1093 *
1094 * @param address Bluetooth address of remote device.
1095 *
1096 * @return The user-friendly name of the specified remote device.
1097 */
1098 public synchronized String getRemoteName(String address) {
1099 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001100 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001101 return null;
1102 }
Jaikumar Ganesh55929a92009-09-30 10:49:34 -07001103 return getRemoteDeviceProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001104 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105
1106 /**
1107 * Get the discoverability window for the device. A timeout of zero
1108 * means that the device is permanently discoverable (if the device is
1109 * in the discoverable mode).
1110 *
1111 * @return The discoverability window of the device, in seconds. A negative
1112 * value indicates an error.
1113 */
1114 public synchronized int getDiscoverableTimeout() {
1115 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001116 String timeout = getProperty("DiscoverableTimeout");
1117 if (timeout != null)
1118 return Integer.valueOf(timeout);
1119 else
1120 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122
1123 public synchronized int getScanMode() {
1124 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001125 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -07001126 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001128 boolean pairable = getProperty("Pairable").equals("true");
1129 boolean discoverable = getProperty("Discoverable").equals("true");
1130 return bluezStringToScanMode (pairable, discoverable);
1131 }
1132
1133 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1135 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001136 if (!isEnabledInternal()) return false;
1137
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001138 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001140
1141 public synchronized boolean cancelDiscovery() {
1142 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1143 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001144 if (!isEnabledInternal()) return false;
1145
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001146 return stopDiscoveryNative();
1147 }
1148
1149 public synchronized boolean isDiscovering() {
1150 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1151 return mIsDiscovering;
1152 }
1153
1154 /* package */ void setIsDiscovering(boolean isDiscovering) {
1155 mIsDiscovering = isDiscovering;
1156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157
1158 public synchronized boolean createBond(String address) {
1159 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1160 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001161 if (!isEnabledInternal()) return false;
1162
Nick Pelly005b2282009-09-10 10:21:56 -07001163 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 return false;
1165 }
1166 address = address.toUpperCase();
1167
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001168 if (mBondState.getPendingOutgoingBonding() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169 log("Ignoring createBond(): another device is bonding");
1170 // a different device is currently bonding, fail
1171 return false;
1172 }
1173
1174 // Check for bond state only if we are not performing auto
1175 // pairing exponential back-off attempts.
1176 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -07001177 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 log("Ignoring createBond(): this device is already bonding or bonded");
1179 return false;
1180 }
1181
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001182 if (address.equals(mDockAddress)) {
1183 if (!writeDockPin()) {
1184 log("Error while writing Pin for the dock");
1185 return false;
1186 }
1187 }
1188
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001189 if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 return false;
1191 }
1192
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001193 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 return true;
1197 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001198
1199 public synchronized boolean cancelBondProcess(String address) {
1200 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1201 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001202 if (!isEnabledInternal()) return false;
1203
Nick Pelly005b2282009-09-10 10:21:56 -07001204 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 return false;
1206 }
1207 address = address.toUpperCase();
1208 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1209 return false;
1210 }
1211
Nick Pelly005b2282009-09-10 10:21:56 -07001212 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001214 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215 return true;
1216 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217
1218 public synchronized boolean removeBond(String address) {
1219 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1220 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001221 if (!isEnabledInternal()) return false;
1222
Nick Pelly005b2282009-09-10 10:21:56 -07001223 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001224 return false;
1225 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001226 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001227 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001228 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001229 return true;
1230 } else {
1231 return false;
1232 }
1233 }
1234
1235 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001236 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238
1239 public synchronized String[] listBonds() {
1240 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1241 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1242 }
1243
1244 public synchronized int getBondState(String address) {
1245 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001246 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001247 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 }
1249 return mBondState.getBondState(address.toUpperCase());
1250 }
1251
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001252 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001253 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1254 mContext.MODE_PRIVATE);
1255
1256 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001257 }
1258
Danica Chang6fdd0c62010-08-11 14:54:43 -07001259 public synchronized boolean isTetheringOn() {
1260 return mTetheringOn;
1261 }
1262
Danica Chang8aac82a2010-08-20 14:22:07 -07001263 private BroadcastReceiver mTetheringReceiver = null;
1264
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001265 public synchronized void setBluetoothTethering(boolean value) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001266 if (!value) {
1267 disconnectPan();
1268 }
Danica Chang8aac82a2010-08-20 14:22:07 -07001269
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001270 if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
Danica Chang8aac82a2010-08-20 14:22:07 -07001271 IntentFilter filter = new IntentFilter();
1272 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
1273 mTetheringReceiver = new BroadcastReceiver() {
1274 @Override
1275 public synchronized void onReceive(Context context, Intent intent) {
1276 if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
1277 == BluetoothAdapter.STATE_ON) {
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001278 mTetheringOn = true;
Danica Chang8aac82a2010-08-20 14:22:07 -07001279 mContext.unregisterReceiver(mTetheringReceiver);
1280 }
1281 }
1282 };
1283 mContext.registerReceiver(mTetheringReceiver, filter);
1284 } else {
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001285 mTetheringOn = value;
Danica Chang8aac82a2010-08-20 14:22:07 -07001286 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001287 }
1288
1289 public synchronized int getPanDeviceState(BluetoothDevice device) {
1290 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1291
1292 if (mPanDevices.get(device) == null) {
1293 return BluetoothPan.STATE_DISCONNECTED;
1294 }
1295 return mPanDevices.get(device);
1296 }
1297
1298 public synchronized boolean connectPanDevice(BluetoothDevice device) {
1299 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1300 "Need BLUETOOTH_ADMIN permission");
1301
1302 String objectPath = getObjectPathFromAddress(device.getAddress());
1303 if (DBG) log("connect PAN(" + objectPath + ")");
1304 if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
1305 log (device + " already connected to PAN");
1306 }
1307
1308 int connectedCount = 0;
1309 for (BluetoothDevice BTdevice: mPanDevices.keySet()) {
1310 if (getPanDeviceState(BTdevice) == BluetoothPan.STATE_CONNECTED) {
1311 connectedCount ++;
1312 }
1313 }
1314 if (connectedCount > 8) {
1315 log (device + " could not connect to PAN because 8 other devices are already connected");
1316 return false;
1317 }
1318
1319 handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
1320 if (connectPanDeviceNative(objectPath, "nap", "panu")) {
1321 log ("connecting to PAN");
1322 return true;
1323 } else {
1324 handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
1325 log ("could not connect to PAN");
1326 return false;
1327 }
1328 }
1329
1330 private synchronized boolean disconnectPan() {
1331 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1332 if (DBG) log("disconnect all PAN devices");
1333
1334 for (BluetoothDevice device: mPanDevices.keySet()) {
1335 if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
1336 if (!disconnectPanDevice(device)) {
1337 log ("could not disconnect Pan Device "+device.getAddress());
1338 return false;
1339 }
1340 }
1341 }
1342 return true;
1343 }
1344
1345 public synchronized BluetoothDevice[] getConnectedPanDevices() {
1346 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1347
1348 Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>();
1349 for (BluetoothDevice device: mPanDevices.keySet()) {
1350 if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
1351 devices.add(device);
1352 }
1353 }
1354 return devices.toArray(new BluetoothDevice[devices.size()]);
1355 }
1356
1357 public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
1358 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1359 "Need BLUETOOTH_ADMIN permission");
1360 String objectPath = getObjectPathFromAddress(device.getAddress());
1361 if (DBG) log("disconnect PAN(" + objectPath + ")");
1362 if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
1363 log (device + " already disconnected from PAN");
1364 }
1365 handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING);
1366 return disconnectPanDeviceNative(objectPath);
1367 }
1368
1369 /*package*/ void handlePanDeviceStateChange(BluetoothDevice device, int state) {
1370 int prevState;
1371 if (mPanDevices.get(device) == null) {
1372 prevState = BluetoothPan.STATE_DISCONNECTED;
1373 } else {
1374 prevState = mPanDevices.get(device);
1375 }
1376 if (prevState == state) return;
1377
1378 mPanDevices.put(device, state);
1379
1380 if (state == BluetoothPan.STATE_CONNECTED) {
1381 updateTetherState(true);
1382 }
1383
1384 Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
1385 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1386 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
1387 intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
1388 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1389
1390 if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
1391
1392 }
1393
1394 // configured when we start tethering and unconfig'd on error or conclusion
1395 private boolean updateTetherState(boolean enabled) {
1396 Log.d(TAG, "configureBluetoothIface(" + enabled + ")");
1397
1398 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
1399 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
1400 ConnectivityManager cm =
1401 (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1402 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
1403
1404 // bring toggle the interfaces
1405 String[] ifaces = new String[0];
1406 try {
1407 ifaces = service.listInterfaces();
1408 } catch (Exception e) {
1409 Log.e(TAG, "Error listing Interfaces :" + e);
1410 return false;
1411 }
1412
1413 ArrayList<String> ifaceAddresses = (ArrayList<String>) mBluetoothIfaceAddresses.clone();
1414 for (String iface : ifaces) {
1415 for (String regex : bluetoothRegexs) {
1416 if (iface.matches(regex)) {
1417 InterfaceConfiguration ifcg = null;
1418 try {
1419 ifcg = service.getInterfaceConfig(iface);
1420 if (ifcg != null) {
1421 if (enabled) {
1422 String[] addr = BLUETOOTH_NETMASK.split("\\.");
1423 ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
1424 (Integer.parseInt(addr[1]) << 16) +
1425 (Integer.parseInt(addr[2]) << 8) +
1426 (Integer.parseInt(addr[3]));
1427 if (ifcg.ipAddr == 0 && !ifaceAddresses.isEmpty()) {
1428 addr = ifaceAddresses.remove(0).split("\\.");
1429 ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
1430 (Integer.parseInt(addr[1]) << 16) +
1431 (Integer.parseInt(addr[2]) << 8) +
1432 (Integer.parseInt(addr[3]));
1433 } else {
1434 String IfaceAddress =
1435 String.valueOf(ifcg.ipAddr >>> 24) + "." +
1436 String.valueOf((ifcg.ipAddr & 0x00FF0000) >>> 16) + "." +
1437 String.valueOf((ifcg.ipAddr & 0x0000FF00) >>> 8) + "." +
1438 String.valueOf(ifcg.ipAddr & 0x000000FF);
1439 ifaceAddresses.remove(IfaceAddress);
1440 }
1441 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
1442 } else {
1443 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down");
1444 }
1445 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
1446 ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
1447 service.setInterfaceConfig(iface, ifcg);
1448 if (enabled) {
1449 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1450 Log.e(TAG, "Error tethering "+ifaces);
1451 }
1452 }
1453 }
1454 } catch (Exception e) {
1455 Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
1456 return false;
1457 }
1458 }
1459 }
1460 }
1461
1462 return true;
1463 }
1464
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001465 public synchronized boolean connectInputDevice(BluetoothDevice device) {
1466 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1467 "Need BLUETOOTH_ADMIN permission");
1468
1469 String objectPath = getObjectPathFromAddress(device.getAddress());
Jaikumar Ganesh05a18632010-07-21 12:08:12 -07001470 if (objectPath == null ||
1471 getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001472 getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
1473 return false;
1474 }
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001475 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
1476 if (state != null) {
1477 Message msg = new Message();
1478 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
1479 msg.obj = state;
1480 mHidProfileState.sendMessage(msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001481 return true;
1482 }
1483 return false;
1484 }
1485
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001486 public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
1487 String objectPath = getObjectPathFromAddress(device.getAddress());
1488 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
1489 if (!connectInputDeviceNative(objectPath)) {
1490 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
1491 return false;
1492 }
1493 return true;
1494 }
1495
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001496 public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
1497 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1498 "Need BLUETOOTH_ADMIN permission");
1499
1500 String objectPath = getObjectPathFromAddress(device.getAddress());
1501 if (objectPath == null || getConnectedInputDevices().length == 0) {
1502 return false;
1503 }
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001504 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
1505 if (state != null) {
1506 Message msg = new Message();
1507 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
1508 msg.obj = state;
1509 mHidProfileState.sendMessage(msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001510 return true;
1511 }
1512 return false;
1513 }
1514
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001515 public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
1516 String objectPath = getObjectPathFromAddress(device.getAddress());
1517 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
1518 if (!disconnectInputDeviceNative(objectPath)) {
1519 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
1520 return false;
1521 }
1522 return true;
1523 }
1524
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001525 public synchronized int getInputDeviceState(BluetoothDevice device) {
1526 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1527
1528 if (mInputDevices.get(device) == null) {
1529 return BluetoothInputDevice.STATE_DISCONNECTED;
1530 }
Adam Powelldf7627d2010-06-21 16:23:42 -07001531 return mInputDevices.get(device);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001532 }
1533
1534 public synchronized BluetoothDevice[] getConnectedInputDevices() {
1535 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1536 Set<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
1537 new int[] {BluetoothInputDevice.STATE_CONNECTED});
1538 return devices.toArray(new BluetoothDevice[devices.size()]);
1539 }
1540
1541 public synchronized int getInputDevicePriority(BluetoothDevice device) {
1542 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1543 return Settings.Secure.getInt(mContext.getContentResolver(),
1544 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1545 BluetoothInputDevice.PRIORITY_UNDEFINED);
1546 }
1547
1548 public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
1549 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1550 "Need BLUETOOTH_ADMIN permission");
1551 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
1552 return false;
1553 }
1554 return Settings.Secure.putInt(mContext.getContentResolver(),
1555 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1556 priority);
1557 }
1558
1559 /*package*/synchronized Set<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
1560 Set<BluetoothDevice> inputDevices = new HashSet<BluetoothDevice>();
1561 if (mInputDevices.isEmpty()) {
1562 return inputDevices;
1563 }
1564 for (BluetoothDevice device: mInputDevices.keySet()) {
1565 int inputDeviceState = getInputDeviceState(device);
1566 for (int state : states) {
1567 if (state == inputDeviceState) {
1568 inputDevices.add(device);
1569 break;
1570 }
1571 }
1572 }
1573 return inputDevices;
1574 }
1575
1576 private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
1577 int prevState;
1578 if (mInputDevices.get(device) == null) {
1579 prevState = BluetoothInputDevice.STATE_DISCONNECTED;
1580 } else {
1581 prevState = mInputDevices.get(device);
1582 }
1583 if (prevState == state) return;
1584
1585 mInputDevices.put(device, state);
1586
1587 if (getInputDevicePriority(device) >
1588 BluetoothInputDevice.PRIORITY_OFF &&
1589 state == BluetoothInputDevice.STATE_CONNECTING ||
1590 state == BluetoothInputDevice.STATE_CONNECTED) {
1591 // We have connected or attempting to connect.
1592 // Bump priority
1593 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
1594 }
1595
1596 Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
1597 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1598 intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
1599 intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
1600 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1601
1602 if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
1603
1604 }
1605
Jaikumar Ganesh56d26132010-07-15 15:56:04 -07001606 /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001607 int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
1608 BluetoothInputDevice.STATE_DISCONNECTED;
1609 BluetoothDevice device = mAdapter.getRemoteDevice(address);
1610 handleInputDeviceStateChange(device, state);
1611 }
1612
Jaikumar Ganeshc3ee99d2010-07-19 14:59:44 -07001613 private void setInitialInputDevicePriority(BluetoothDevice device, int state) {
1614 switch (state) {
1615 case BluetoothDevice.BOND_BONDED:
1616 if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
1617 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
1618 }
1619 break;
1620 case BluetoothDevice.BOND_NONE:
1621 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
1622 break;
1623 }
1624 }
1625
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001626 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001627 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001628 }
1629
1630 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001631 if (!isEnabledInternal()) return null;
1632
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001633 String objectPath = getObjectPathFromAddress(address);
1634 return (String [])getDevicePropertiesNative(objectPath);
1635 }
1636
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001637 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -07001638 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001639 if (properties != null) {
1640 return properties.get(property);
1641 } else {
1642 // Query for remote device properties, again.
1643 // We will need to reload the cache when we switch Bluetooth on / off
1644 // or if we crash.
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001645 if (updateRemoteDevicePropertiesCache(address))
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001646 return getRemoteDeviceProperty(address, property);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001648 Log.e(TAG, "getRemoteDeviceProperty: " + property + " not present: " + address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001649 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001651
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001652 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
1653 String[] propValues = getRemoteDeviceProperties(address);
1654 if (propValues != null) {
1655 addRemoteDeviceProperties(address, propValues);
1656 return true;
1657 }
1658 return false;
1659 }
1660
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001661 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001662 /*
1663 * We get a DeviceFound signal every time RSSI changes or name changes.
1664 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -07001665 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001666 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001667 propertyValues = new HashMap<String, String>();
1668 }
1669
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001670 for (int i = 0; i < properties.length; i++) {
1671 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001672 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001673 int len;
1674 if (name == null) {
1675 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1676 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001677 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001678 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001679 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001680 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001681 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001682 str.append(properties[++i]);
1683 str.append(",");
1684 }
1685 if (len > 0) {
1686 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001687 }
1688 } else {
1689 newValue = properties[++i];
1690 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001691
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001692 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 }
Nick Pellybd022f42009-08-14 18:33:38 -07001694 mDeviceProperties.put(address, propertyValues);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001695
1696 // We have added a new remote device or updated its properties.
1697 // Also update the serviceChannel cache.
1698 updateDeviceServiceChannelCache(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001699 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001701 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001702 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001703 }
1704
1705 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1706 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -07001707 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001708 if (propVal != null) {
1709 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -07001710 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001711 } else {
1712 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001714 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001715
1716 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001717 * Sets the remote device trust state.
1718 *
1719 * @return boolean to indicate operation success or fail
1720 */
1721 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001722 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001723 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1724 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001725 return false;
1726 }
1727
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001728 if (!isEnabledInternal()) return false;
1729
Lixin Yueefa1dd72009-08-31 15:55:13 +08001730 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1731 value ? 1 : 0);
1732 }
1733
1734 /**
1735 * Gets the remote device trust state as boolean.
1736 * Note: this value may be
1737 * retrieved from cache if we retrieved the data before *
1738 *
1739 * @return boolean to indicate trust or untrust state
1740 */
1741 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001742 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001743 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1744 return false;
1745 }
1746
1747 String val = getRemoteDeviceProperty(address, "Trusted");
1748 if (val == null) {
1749 return false;
1750 } else {
1751 return val.equals("true") ? true : false;
1752 }
1753 }
1754
1755 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001756 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 * integer.
1758 *
1759 * Note: this value is retrieved from cache, because we get it during
1760 * remote-device discovery.
1761 *
1762 * @return 32-bit integer encoding the remote major, minor, and service
1763 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 */
1765 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001766 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001767 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1768 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001770 String val = getRemoteDeviceProperty(address, "Class");
1771 if (val == null)
1772 return BluetoothClass.ERROR;
1773 else {
1774 return Integer.valueOf(val);
1775 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001776 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001777
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001778
1779 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001780 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001782 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001784 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001786 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 return null;
1788 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001789 return getUuidFromCache(address);
1790 }
1791
1792 private ParcelUuid[] getUuidFromCache(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001793 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001794 if (value == null) return null;
1795
1796 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001797 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001798 uuidStrings = value.split(",");
1799 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1800
1801 for (int i = 0; i < uuidStrings.length; i++) {
1802 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1803 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001804 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001805 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001806
Nick Pelly16fb88a2009-10-07 07:44:03 +02001807 /**
1808 * Connect and fetch new UUID's using SDP.
1809 * The UUID's found are broadcast as intents.
1810 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1811 * a given uuid.
1812 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1813 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1814 * callback and broadcast intents.
1815 */
1816 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1817 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001818 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001819 if (!isEnabledInternal()) return false;
1820
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001821 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1822 return false;
1823 }
1824
Nick Pelly16fb88a2009-10-07 07:44:03 +02001825 RemoteService service = new RemoteService(address, uuid);
1826 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1827 // An SDP query for this address & uuid is already in progress
1828 // Do not add this callback for the uuid
1829 return false;
1830 }
1831
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001832 if (mUuidIntentTracker.contains(address)) {
1833 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02001834 // Add this uuid onto the in-progress SDP query
1835 if (uuid != null) {
1836 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1837 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001838 return true;
1839 }
1840
1841 boolean ret;
Jaikumar Ganesh0e090302010-03-29 00:01:34 -07001842 // Just do the SDP if the device is already created and UUIDs are not
1843 // NULL, else create the device and then do SDP.
1844 if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001845 String path = getObjectPathFromAddress(address);
1846 if (path == null) return false;
1847
1848 // Use an empty string for the UUID pattern
1849 ret = discoverServicesNative(path, "");
1850 } else {
1851 ret = createDeviceNative(address);
1852 }
1853
1854 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001855 if (uuid != null) {
1856 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1857 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001858
1859 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1860 message.obj = address;
1861 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1862 return ret;
1863 }
1864
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001865 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001866 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02001867 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001869 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001870 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001872 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001873 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001875 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001877 if (!isEnabledInternal()) return -1;
1878
Nick Pelly005b2282009-09-10 10:21:56 -07001879 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001880 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001881 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001882 // Check if we are recovering from a crash.
1883 if (mDeviceProperties.isEmpty()) {
1884 if (!updateRemoteDevicePropertiesCache(address))
1885 return -1;
1886 }
1887
1888 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1889 if (value != null && value.containsKey(uuid))
1890 return value.get(uuid);
1891 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001893
1894 public synchronized boolean setPin(String address, byte[] pin) {
1895 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1896 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001897 if (!isEnabledInternal()) return false;
1898
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001899 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001900 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001901 return false;
1902 }
1903 address = address.toUpperCase();
1904 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1905 if (data == null) {
1906 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1907 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1908 " or by bluez.\n");
1909 return false;
1910 }
1911 // bluez API wants pin as a string
1912 String pinString;
1913 try {
1914 pinString = new String(pin, "UTF8");
1915 } catch (UnsupportedEncodingException uee) {
1916 Log.e(TAG, "UTF8 not supported?!?");
1917 return false;
1918 }
1919 return setPinNative(address, pinString, data.intValue());
1920 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001922 public synchronized boolean setPasskey(String address, int passkey) {
1923 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1924 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001925 if (!isEnabledInternal()) return false;
1926
Nick Pelly005b2282009-09-10 10:21:56 -07001927 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001928 return false;
1929 }
1930 address = address.toUpperCase();
1931 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1932 if (data == null) {
1933 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1934 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1935 " or by bluez.\n");
1936 return false;
1937 }
1938 return setPasskeyNative(address, passkey, data.intValue());
1939 }
1940
1941 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1942 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1943 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001944 if (!isEnabledInternal()) return false;
1945
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001946 address = address.toUpperCase();
1947 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1948 if (data == null) {
1949 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1950 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1951 " or by bluez.\n");
1952 return false;
1953 }
1954 return setPairingConfirmationNative(address, confirm, data.intValue());
1955 }
1956
1957 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1959 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001960 if (!isEnabledInternal()) return false;
1961
Nick Pelly005b2282009-09-10 10:21:56 -07001962 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 return false;
1964 }
Nick Pelly005b2282009-09-10 10:21:56 -07001965 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001966 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 address = address.toUpperCase();
1968 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1969 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001970 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1971 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1972 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 return false;
1974 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001975 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001976 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001977
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001978 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001979 ParcelUuid[] deviceUuids = getRemoteUuids(address);
1980 // We are storing the rfcomm channel numbers only for the uuids
1981 // we are interested in.
1982 int channel;
Nick Pelly16fb88a2009-10-07 07:44:03 +02001983 if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
1984
1985 ArrayList<ParcelUuid> applicationUuids = new ArrayList();
1986
1987 synchronized (this) {
1988 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1989 if (service.address.equals(address)) {
1990 applicationUuids.add(service.uuid);
1991 }
1992 }
1993 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001994
1995 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001996
1997 // Retrieve RFCOMM channel for default uuids
1998 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001999 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Nick Pelly16fb88a2009-10-07 07:44:03 +02002000 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
2001 uuid.toString(), 0x0004);
2002 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002003 value.put(uuid, channel);
2004 }
2005 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02002006 // Retrieve RFCOMM channel for application requested uuids
2007 for (ParcelUuid uuid : applicationUuids) {
2008 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
2009 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
2010 uuid.toString(), 0x0004);
2011 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
2012 value.put(uuid, channel);
2013 }
2014 }
2015
2016 synchronized (this) {
2017 // Make application callbacks
2018 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
2019 iter.hasNext();) {
2020 RemoteService service = iter.next();
2021 if (service.address.equals(address)) {
2022 channel = -1;
2023 if (value.get(service.uuid) != null) {
2024 channel = value.get(service.uuid);
2025 }
2026 if (channel != -1) {
2027 if (DBG) log("Making callback for " + service.uuid + " with result " +
2028 channel);
2029 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
2030 if (callback != null) {
2031 try {
2032 callback.onRfcommChannelFound(channel);
2033 } catch (RemoteException e) {Log.e(TAG, "", e);}
2034 }
2035
2036 iter.remove();
2037 }
2038 }
2039 }
2040
2041 // Update cache
2042 mDeviceServiceChannelCache.put(address, value);
2043 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002044 }
2045
Nick Pelly24bb9b82009-10-02 20:34:18 -07002046 /**
2047 * b is a handle to a Binder instance, so that this service can be notified
2048 * for Applications that terminate unexpectedly, to clean there service
2049 * records
2050 */
2051 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
2052 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002053 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
2054 if (!isEnabledInternal()) return -1;
2055
Nick Pelly24bb9b82009-10-02 20:34:18 -07002056 if (serviceName == null || uuid == null || channel < 1 ||
2057 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
2058 return -1;
2059 }
2060 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
2061 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
2062 return -1;
2063 }
2064 int handle = addRfcommServiceRecordNative(serviceName,
2065 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
2066 (short)channel);
2067 if (DBG) log("new handle " + Integer.toHexString(handle));
2068 if (handle == -1) {
2069 return -1;
2070 }
2071
2072 int pid = Binder.getCallingPid();
2073 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
2074 try {
2075 b.linkToDeath(new Reaper(handle, pid), 0);
2076 } catch (RemoteException e) {}
2077 return handle;
2078 }
2079
2080 public void removeServiceRecord(int handle) {
2081 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2082 "Need BLUETOOTH permission");
2083 checkAndRemoveRecord(handle, Binder.getCallingPid());
2084 }
2085
2086 private synchronized void checkAndRemoveRecord(int handle, int pid) {
2087 Integer handleInt = new Integer(handle);
2088 Integer owner = mServiceRecordToPid.get(handleInt);
2089 if (owner != null && pid == owner.intValue()) {
2090 if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
2091 pid);
2092 mServiceRecordToPid.remove(handleInt);
2093 removeServiceRecordNative(handle);
2094 }
2095 }
2096
2097 private class Reaper implements IBinder.DeathRecipient {
2098 int pid;
2099 int handle;
2100 Reaper(int handle, int pid) {
2101 this.pid = pid;
2102 this.handle = handle;
2103 }
2104 public void binderDied() {
2105 synchronized (BluetoothService.this) {
2106 if (DBG) log("Tracked app " + pid + " died");
2107 checkAndRemoveRecord(handle, pid);
2108 }
2109 }
2110 }
2111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002112 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
2113 @Override
2114 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002115 if (intent == null) return;
2116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 String action = intent.getAction();
2118 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
2119 ContentResolver resolver = context.getContentResolver();
2120 // Query the airplane mode from Settings.System just to make sure that
2121 // some random app is not sending this intent and disabling bluetooth
2122 boolean enabled = !isAirplaneModeOn();
2123 // If bluetooth is currently expected to be on, then enable or disable bluetooth
2124 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
2125 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002126 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002127 } else {
2128 disable(false);
2129 }
2130 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002131 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
2132 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2133 Intent.EXTRA_DOCK_STATE_UNDOCKED);
2134 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
2135 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
2136 mDockAddress = null;
2137 mDockPin = null;
2138 } else {
2139 SharedPreferences.Editor editor =
2140 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
2141 mContext.MODE_PRIVATE).edit();
2142 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07002143 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002145 }
2146 }
2147 };
2148
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002149 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08002150 final ContentResolver resolver = mContext.getContentResolver();
2151 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002152 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08002153 final String toggleableRadios = Settings.System.getString(resolver,
2154 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
2155
2156 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
2157 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
2158 mIsAirplaneToggleable = toggleableRadios == null ? false :
2159 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
2160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002162 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 }
2164 }
2165
2166 /* Returns true if airplane mode is currently on */
2167 private final boolean isAirplaneModeOn() {
2168 return Settings.System.getInt(mContext.getContentResolver(),
2169 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
2170 }
2171
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002172 /* Broadcast the Uuid intent */
2173 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002174 ParcelUuid[] uuid = getUuidFromCache(address);
2175 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07002176 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002177 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
2178 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002179
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002180 if (mUuidIntentTracker.contains(address))
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002181 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002182
2183 }
2184
2185 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
2186 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
2187 iter.hasNext();) {
2188 RemoteService service = iter.next();
2189 if (service.address.equals(address)) {
2190 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
2191 " " + service.uuid);
2192 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
2193 if (callback != null) {
2194 try {
2195 callback.onRfcommChannelFound(-1);
2196 } catch (RemoteException e) {Log.e(TAG, "", e);}
2197 }
2198
2199 iter.remove();
2200 }
2201 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002202 }
2203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002204 @Override
2205 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002206 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07002207 case BluetoothAdapter.STATE_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002208 pw.println("Bluetooth OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002209 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002210 case BluetoothAdapter.STATE_TURNING_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002211 pw.println("Bluetooth TURNING ON\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002212 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002213 case BluetoothAdapter.STATE_TURNING_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002214 pw.println("Bluetooth TURNING OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002215 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002216 case BluetoothAdapter.STATE_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002217 pw.println("Bluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002218 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002219
Nick Pelly24bb9b82009-10-02 20:34:18 -07002220 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08002221 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07002222
2223 pw.println("Local address = " + getAddress());
2224 pw.println("Local name = " + getName());
2225 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07002226
2227 BluetoothHeadset headset = new BluetoothHeadset(mContext, null);
2228
The Android Open Source Project10592532009-03-18 17:39:46 -07002229 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07002230 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07002231 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07002232 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07002233 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07002234 mBondState.getAttempt(address),
2235 getRemoteName(address));
Nick Pelly24bb9b82009-10-02 20:34:18 -07002236
2237 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
2238 if (uuidChannels == null) {
2239 pw.println("\tuuids = null");
2240 } else {
2241 for (ParcelUuid uuid : uuidChannels.keySet()) {
2242 Integer channel = uuidChannels.get(uuid);
2243 if (channel == null) {
2244 pw.println("\t" + uuid);
2245 } else {
2246 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
Nick Pelly1eada0d2009-08-26 10:57:33 -07002247 }
2248 }
2249 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02002250 for (RemoteService service : mUuidCallbackTracker.keySet()) {
2251 if (service.address.equals(address)) {
2252 pw.println("\tPENDING CALLBACK: " + service.uuid);
2253 }
2254 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002255 }
2256
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002257 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07002258 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002259 if (value != null) {
2260 devicesObjectPath = value.split(",");
2261 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002262 pw.println("\n--ACL connected devices--");
Nick Pelly24bb9b82009-10-02 20:34:18 -07002263 if (devicesObjectPath != null) {
2264 for (String device : devicesObjectPath) {
2265 pw.println(getAddressFromObjectPath(device));
2266 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002267 }
2268
2269 // Rather not do this from here, but no-where else and I need this
2270 // dump
2271 pw.println("\n--Headset Service--");
Jaikumar Ganesh740e39b2010-06-02 12:33:53 -07002272 switch (headset.getState(headset.getCurrentHeadset())) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002273 case BluetoothHeadset.STATE_DISCONNECTED:
2274 pw.println("getState() = STATE_DISCONNECTED");
2275 break;
2276 case BluetoothHeadset.STATE_CONNECTING:
2277 pw.println("getState() = STATE_CONNECTING");
2278 break;
2279 case BluetoothHeadset.STATE_CONNECTED:
2280 pw.println("getState() = STATE_CONNECTED");
2281 break;
2282 case BluetoothHeadset.STATE_ERROR:
2283 pw.println("getState() = STATE_ERROR");
2284 break;
2285 }
Nick Pelly6c901db2009-06-19 10:08:09 -07002286
Nick Pelly24bb9b82009-10-02 20:34:18 -07002287 pw.println("\ngetCurrentHeadset() = " + headset.getCurrentHeadset());
2288 pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint());
The Android Open Source Project10592532009-03-18 17:39:46 -07002289 headset.close();
Nick Pelly24bb9b82009-10-02 20:34:18 -07002290 pw.println("\n--Application Service Records--");
2291 for (Integer handle : mServiceRecordToPid.keySet()) {
2292 Integer pid = mServiceRecordToPid.get(handle);
2293 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
2294 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002295 }
2296
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002297 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2298 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002299 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002300 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002301 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002302 else
Nick Pellybd022f42009-08-14 18:33:38 -07002303 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 }
2305
2306 /* package */ static String scanModeToBluezString(int mode) {
2307 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07002308 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002309 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07002310 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002311 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07002312 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002313 return "discoverable";
2314 }
2315 return null;
2316 }
2317
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002318 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002319 String adapterObjectPath = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002320 if (adapterObjectPath == null || objectPath == null) {
2321 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
2322 " or deviceObjectPath:" + objectPath + " is null");
2323 return null;
2324 }
2325 if (!objectPath.startsWith(adapterObjectPath)) {
2326 Log.e(TAG, "getAddressFromObjectPath: AdpaterObjectPath:" + adapterObjectPath +
2327 " is not a prefix of deviceObjectPath:" + objectPath +
2328 "bluetoothd crashed ?");
2329 return null;
2330 }
2331 String address = objectPath.substring(adapterObjectPath.length());
2332 if (address != null) return address.replace('_', ':');
2333
2334 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2335 return null;
2336 }
2337
2338 /*package*/ String getObjectPathFromAddress(String address) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002339 String path = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002340 if (path == null) {
2341 Log.e(TAG, "Error: Object Path is null");
2342 return null;
2343 }
2344 path = path + address.replace(":", "_");
2345 return path;
2346 }
2347
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002348 /*package */ void setLinkTimeout(String address, int num_slots) {
2349 String path = getObjectPathFromAddress(address);
2350 boolean result = setLinkTimeoutNative(path, num_slots);
2351
2352 if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
2353 }
2354
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002355 public boolean connectHeadset(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002356 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002357 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002358 Message msg = new Message();
2359 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2360 msg.obj = state;
2361 mHfpProfileState.sendMessage(msg);
2362 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002363 }
2364 return false;
2365 }
2366
2367 public boolean disconnectHeadset(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002368 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002369 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002370 Message msg = new Message();
2371 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2372 msg.obj = state;
2373 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002374 return true;
2375 }
2376 return false;
2377 }
2378
2379 public boolean connectSink(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002380 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002381 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002382 Message msg = new Message();
2383 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2384 msg.obj = state;
2385 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002386 return true;
2387 }
2388 return false;
2389 }
2390
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002391 public boolean disconnectSink(String address) {
2392 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2393 if (state != null) {
2394 Message msg = new Message();
2395 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2396 msg.obj = state;
2397 mA2dpProfileState.sendMessage(msg);
2398 return true;
2399 }
2400 return false;
2401 }
2402
2403 private BluetoothDeviceProfileState addProfileState(String address) {
2404 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002405 if (state != null) return state;
2406
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002407 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2408 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002409 state.start();
2410 return state;
2411 }
2412
2413 private void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002414 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002415 }
2416
2417 private void initProfileState() {
2418 String []bonds = null;
2419 String val = getPropertyInternal("Devices");
2420 if (val != null) {
2421 bonds = val.split(",");
2422 }
2423 if (bonds == null) {
2424 return;
2425 }
2426
2427 for (String path : bonds) {
2428 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002429 BluetoothDeviceProfileState state = addProfileState(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002430 // Allow 8 secs for SDP records to get registered.
2431 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002432 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002433 state.sendMessageDelayed(msg, 8000);
2434 }
2435 }
2436
2437 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002438 BluetoothDeviceProfileState state =
2439 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002440 if (state != null) {
2441 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002442 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002443 state.sendMessage(msg);
2444 return true;
2445 }
2446 return false;
2447 }
2448
2449 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002450 BluetoothDeviceProfileState state =
2451 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002452 if (state != null) {
2453 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002454 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002455 state.sendMessage(msg);
2456 return true;
2457 }
2458 return false;
2459 }
2460
2461 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2462 mA2dpService = a2dpService;
2463 }
2464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002465 private static void log(String msg) {
2466 Log.d(TAG, msg);
2467 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002468
2469 private native static void classInitNative();
2470 private native void initializeNativeDataNative();
2471 private native boolean setupNativeDataNative();
2472 private native boolean tearDownNativeDataNative();
2473 private native void cleanupNativeDataNative();
2474 private native String getAdapterPathNative();
2475
2476 private native int isEnabledNative();
2477 private native int enableNative();
2478 private native int disableNative();
2479
2480 private native Object[] getAdapterPropertiesNative();
2481 private native Object[] getDevicePropertiesNative(String objectPath);
2482 private native boolean setAdapterPropertyStringNative(String key, String value);
2483 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2484 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2485
2486 private native boolean startDiscoveryNative();
2487 private native boolean stopDiscoveryNative();
2488
2489 private native boolean createPairedDeviceNative(String address, int timeout_ms);
2490 private native boolean cancelDeviceCreationNative(String address);
2491 private native boolean removeDeviceNative(String objectPath);
2492 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2493 int attributeId);
2494
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002495 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002496 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002497 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2498 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2499 int nativeData);
Nick Pelly24bb9b82009-10-02 20:34:18 -07002500 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2501 int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002502 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002503 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002504
Nick Pelly24bb9b82009-10-02 20:34:18 -07002505 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2506 short channel);
2507 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002508 private native boolean setLinkTimeoutNative(String path, int num_slots);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07002509 private native boolean connectInputDeviceNative(String path);
2510 private native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002511
2512 private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2513 private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
2514 private native boolean disconnectPanDeviceNative(String path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002515}