blob: 01a6b94729d59b12f58f257fe8e8887267d3e00b [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;
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -070030import android.bluetooth.BluetoothDeviceProfileState;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070031import android.bluetooth.BluetoothHeadset;
32import android.bluetooth.BluetoothInputDevice;
Danica Chang6fdd0c62010-08-11 14:54:43 -070033import android.bluetooth.BluetoothPan;
Jaikumar Ganesh96a79832010-09-27 17:02:01 -070034import android.bluetooth.BluetoothProfile;
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -070035import android.bluetooth.BluetoothProfileState;
Nick Pelly24bb9b82009-10-02 20:34:18 -070036import android.bluetooth.BluetoothSocket;
Jaikumar Ganesh10eac972009-09-21 12:48:51 -070037import android.bluetooth.BluetoothUuid;
Nick Pellybd022f42009-08-14 18:33:38 -070038import android.bluetooth.IBluetooth;
Nick Pelly16fb88a2009-10-07 07:44:03 +020039import android.bluetooth.IBluetoothCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.BroadcastReceiver;
41import android.content.ContentResolver;
42import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -080045import android.content.SharedPreferences;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -070046import android.content.res.Resources.NotFoundException;
Danica Chang6fdd0c62010-08-11 14:54:43 -070047import android.net.ConnectivityManager;
48import android.net.InterfaceConfiguration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Binder;
50import android.os.Handler;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080051import android.os.IBinder;
Danica Chang6fdd0c62010-08-11 14:54:43 -070052import android.os.INetworkManagementService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.Message;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080054import android.os.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070056import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.os.SystemService;
58import android.provider.Settings;
59import android.util.Log;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -070060import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070062import com.android.internal.app.IBatteryStats;
63
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080064import java.io.BufferedInputStream;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080065import java.io.BufferedReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080066import java.io.BufferedWriter;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080067import java.io.DataInputStream;
68import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import java.io.FileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080070import java.io.FileInputStream;
71import java.io.FileNotFoundException;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080072import java.io.FileOutputStream;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080073import java.io.FileWriter;
74import java.io.IOException;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -080075import java.io.InputStreamReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import java.io.PrintWriter;
77import java.io.UnsupportedEncodingException;
78import java.util.ArrayList;
79import java.util.Arrays;
80import java.util.HashMap;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070081import java.util.HashSet;
Nick Pelly16fb88a2009-10-07 07:44:03 +020082import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070083import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import java.util.Map;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070085import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Nick Pellybd022f42009-08-14 18:33:38 -070087public class BluetoothService extends IBluetooth.Stub {
88 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private static final boolean DBG = true;
90
91 private int mNativeData;
92 private BluetoothEventLoop mEventLoop;
Jaikumar Ganesh96a79832010-09-27 17:02:01 -070093 private BluetoothHeadset mBluetoothHeadset;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private boolean mIsAirplaneSensitive;
Jeff Sharkey44303922009-12-01 18:25:02 -080095 private boolean mIsAirplaneToggleable;
The Android Open Source Project10592532009-03-18 17:39:46 -070096 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070097 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 private boolean mIsDiscovering;
Danica Chang6fdd0c62010-08-11 14:54:43 -070099 private boolean mTetheringOn;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
Nick Pellybd022f42009-08-14 18:33:38 -0700101 private BluetoothAdapter mAdapter; // constant after init()
102 private final BondState mBondState = new BondState(); // local cache of bondings
103 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private final Context mContext;
105
106 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
107 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
108
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800109 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
110 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
111
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800112 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
113 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
114
The Android Open Source Project10592532009-03-18 17:39:46 -0700115 private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
116 private static final int MESSAGE_FINISH_DISABLE = 2;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700117 private static final int MESSAGE_UUID_INTENT = 3;
Nick Pelly12835472009-09-25 15:00:29 -0700118 private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700119 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 5;
120
121 // The time (in millisecs) to delay the pairing attempt after the first
122 // auto pairing attempt fails. We use an exponential delay with
123 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
124 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
125 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
126 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700127
Danica Chang6fdd0c62010-08-11 14:54:43 -0700128 private ArrayList<String> mBluetoothIfaceAddresses;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700129 private int mMaxPanDevices;
130
131 private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
132 private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700133 private static final String BLUETOOTH_NETMASK = "255.255.255.0";
134
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700135 // The timeout used to sent the UUIDs Intent
136 // This timeout should be greater than the page timeout
137 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700138
Nick Pelly16fb88a2009-10-07 07:44:03 +0200139 /** Always retrieve RFCOMM channel for these SDP UUIDs */
140 private static final ParcelUuid[] RFCOMM_UUIDS = {
141 BluetoothUuid.Handsfree,
142 BluetoothUuid.HSP,
143 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700144
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700145 // TODO(): Optimize all these string handling
Nick Pelly16fb88a2009-10-07 07:44:03 +0200146 private final Map<String, String> mAdapterProperties;
147 private final HashMap<String, Map<String, String>> mDeviceProperties;
148
149 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
150 private final ArrayList<String> mUuidIntentTracker;
151 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700152
Nick Pelly24bb9b82009-10-02 20:34:18 -0700153 private final HashMap<Integer, Integer> mServiceRecordToPid;
154
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700155 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
156 private final BluetoothProfileState mA2dpProfileState;
157 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700158 private final BluetoothProfileState mHidProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700159
160 private BluetoothA2dpService mA2dpService;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700161 private final HashMap<BluetoothDevice, Integer> mInputDevices;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700162 private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700163 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700164
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700165 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700166
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800167 private static String mDockAddress;
168 private String mDockPin;
169
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700170 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
171
Nick Pelly16fb88a2009-10-07 07:44:03 +0200172 private static class RemoteService {
173 public String address;
174 public ParcelUuid uuid;
175 public RemoteService(String address, ParcelUuid uuid) {
176 this.address = address;
177 this.uuid = uuid;
178 }
179 @Override
180 public boolean equals(Object o) {
181 if (o instanceof RemoteService) {
182 RemoteService service = (RemoteService)o;
183 return address.equals(service.address) && uuid.equals(service.uuid);
184 }
185 return false;
186 }
Kenny Root5f614162010-02-17 12:00:37 -0800187
188 @Override
189 public int hashCode() {
190 int hash = 1;
191 hash = hash * 31 + (address == null ? 0 : address.hashCode());
192 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
193 return hash;
194 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200195 }
196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 static {
198 classInitNative();
199 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200
Nick Pellybd022f42009-08-14 18:33:38 -0700201 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700203
204 // Need to do this in place of:
205 // mBatteryStats = BatteryStatsService.getService();
206 // Since we can not import BatteryStatsService from here. This class really needs to be
207 // moved to java/services/com/android/server/
208 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700211
212 if (isEnabledNative() == 1) {
213 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
214 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700216
Nick Pellyde893f52009-09-08 13:15:33 -0700217 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 mIsDiscovering = false;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700219 mTetheringOn = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700220 mAdapterProperties = new HashMap<String, String>();
221 mDeviceProperties = new HashMap<String, Map<String,String>>();
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700222
223 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700224 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700225 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200226 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700227 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700228 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
229 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
230 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700231 mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700232
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700233 mBluetoothIfaceAddresses = new ArrayList<String>();
234 try {
235 mMaxPanDevices = context.getResources().getInteger(
236 com.android.internal.R.integer.config_max_pan_devices);
237 } catch (NotFoundException e) {
238 mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700239 }
240
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700241 mHfpProfileState.start();
242 mA2dpProfileState.start();
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700243 mHidProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800244
245 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800246 registerForAirplaneMode(filter);
247
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800248 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800249 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700250 mInputDevices = new HashMap<BluetoothDevice, Integer>();
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700251 mPanDevices = new HashMap<BluetoothDevice, Pair<Integer, String>>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800252 }
253
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700254 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800255 if (mDockAddress != null) return mDockAddress;
256
257 BufferedInputStream file = null;
258 String dockAddress;
259 try {
260 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
261 byte[] address = new byte[17];
262 file.read(address);
263 dockAddress = new String(address);
264 dockAddress = dockAddress.toUpperCase();
265 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
266 mDockAddress = dockAddress;
267 return mDockAddress;
268 } else {
269 log("CheckBluetoothAddress failed for car dock address:" + dockAddress);
270 }
271 } catch (FileNotFoundException e) {
272 log("FileNotFoundException while trying to read dock address");
273 } catch (IOException e) {
274 log("IOException while trying to read dock address");
275 } finally {
276 if (file != null) {
277 try {
278 file.close();
279 } catch (IOException e) {
280 // Ignore
281 }
282 }
283 }
284 mDockAddress = null;
285 return null;
286 }
287
288 private synchronized boolean writeDockPin() {
289 BufferedWriter out = null;
290 try {
291 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
292
293 // Generate a random 4 digit pin between 0000 and 9999
294 // This is not truly random but good enough for our purposes.
295 int pin = (int) Math.floor(Math.random() * 10000);
296
297 mDockPin = String.format("%04d", pin);
298 out.write(mDockPin);
299 return true;
300 } catch (FileNotFoundException e) {
301 log("FileNotFoundException while trying to write dock pairing pin");
302 } catch (IOException e) {
303 log("IOException while while trying to write dock pairing pin");
304 } finally {
305 if (out != null) {
306 try {
307 out.close();
308 } catch (IOException e) {
309 // Ignore
310 }
311 }
312 }
313 mDockPin = null;
314 return false;
315 }
316
317 /*package*/ synchronized String getDockPin() {
318 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700319 }
320
321 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200322 mAdapter = BluetoothAdapter.getDefaultAdapter();
Nick Pellybd022f42009-08-14 18:33:38 -0700323 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
Jaikumar Ganesh7d0548d2010-10-18 15:29:09 -0700326 public synchronized void initAfterA2dpRegistration() {
327 mEventLoop.getProfileProxy();
328 }
329
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 @Override
331 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800332 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 try {
334 cleanupNativeDataNative();
335 } finally {
336 super.finalize();
337 }
338 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339
340 public boolean isEnabled() {
341 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800342 return isEnabledInternal();
343 }
344
345 private boolean isEnabledInternal() {
Nick Pellyde893f52009-09-08 13:15:33 -0700346 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348
The Android Open Source Project10592532009-03-18 17:39:46 -0700349 public int getBluetoothState() {
350 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
351 return mBluetoothState;
352 }
353
354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 /**
356 * Bring down bluetooth and disable BT in settings. Returns true on success.
357 */
358 public boolean disable() {
359 return disable(true);
360 }
361
362 /**
363 * Bring down bluetooth. Returns true on success.
364 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200365 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 */
367 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200368 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369
The Android Open Source Project10592532009-03-18 17:39:46 -0700370 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700371 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700372 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700373 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700374 break;
375 default:
376 return false;
377 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 if (mEnableThread != null && mEnableThread.isAlive()) {
379 return false;
380 }
Nick Pellyde893f52009-09-08 13:15:33 -0700381 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
Nick Pellybc1fc052009-10-12 09:54:39 -0700382 mHandler.removeMessages(MESSAGE_REGISTER_SDP_RECORDS);
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700383 setBluetoothTetheringNative(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700384
385 // Allow 3 seconds for profiles to gracefully disconnect
386 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700387 // BluetoothService when it is done shutting down
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700388 disconnectDevices();
389
The Android Open Source Project10592532009-03-18 17:39:46 -0700390 mHandler.sendMessageDelayed(
391 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
392 return true;
393 }
394
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700395 private synchronized void disconnectDevices() {
396 // Disconnect devices handled by BluetoothService.
397 for (BluetoothDevice device: getConnectedInputDevices()) {
398 disconnectInputDevice(device);
399 }
400
401 for (BluetoothDevice device: getConnectedPanDevices()) {
402 disconnectPanDevice(device);
403 }
404 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700405
406 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700407 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700408 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
410 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700411 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 disableNative();
413
414 // mark in progress bondings as cancelled
415 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700416 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
418 }
419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700421 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
422 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
424
The Android Open Source Project10592532009-03-18 17:39:46 -0700425 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700426 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700427 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700428
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700429 mProfilesConnected = 0;
430 mProfilesConnecting = 0;
431 mProfilesDisconnecting = 0;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700432 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700433
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 if (saveSetting) {
435 persistBluetoothOnSetting(false);
436 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700437
Nick Pellyde893f52009-09-08 13:15:33 -0700438 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700439
440 // Log bluetooth off to battery stats.
441 long ident = Binder.clearCallingIdentity();
442 try {
443 mBatteryStats.noteBluetoothOff();
444 } catch (RemoteException e) {
445 } finally {
446 Binder.restoreCallingIdentity(ident);
447 }
Nick Pelly997c7612009-03-24 20:44:48 -0700448
449 if (mRestart) {
450 mRestart = false;
451 enable();
452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
454
The Android Open Source Project10592532009-03-18 17:39:46 -0700455 /** Bring up BT and persist BT on in settings */
456 public boolean enable() {
457 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 }
459
460 /**
461 * Enable this Bluetooth device, asynchronously.
462 * This turns on/off the underlying hardware.
463 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700464 * @param saveSetting If true, persist the new state of BT in settings
465 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700467 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800468 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
469 "Need BLUETOOTH_ADMIN permission");
470
471 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800472 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 return false;
474 }
Nick Pellyde893f52009-09-08 13:15:33 -0700475 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 return false;
477 }
478 if (mEnableThread != null && mEnableThread.isAlive()) {
479 return false;
480 }
Nick Pellyde893f52009-09-08 13:15:33 -0700481 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700482 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 mEnableThread.start();
484 return true;
485 }
486
Nick Pelly997c7612009-03-24 20:44:48 -0700487 /** Forcibly restart Bluetooth if it is on */
488 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700489 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700490 return;
491 }
492 mRestart = true;
493 if (!disable(false)) {
494 mRestart = false;
495 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700496 }
Nick Pelly997c7612009-03-24 20:44:48 -0700497
The Android Open Source Project10592532009-03-18 17:39:46 -0700498 private synchronized void setBluetoothState(int state) {
499 if (state == mBluetoothState) {
500 return;
501 }
502
503 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
504
Nick Pellyde893f52009-09-08 13:15:33 -0700505 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
506 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
507 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700508 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
509
510 mBluetoothState = state;
511
512 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
513 }
514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 private final Handler mHandler = new Handler() {
516 @Override
517 public void handleMessage(Message msg) {
518 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700519 case MESSAGE_REGISTER_SDP_RECORDS:
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800520 if (!isEnabledInternal()) {
Nick Pellybc1fc052009-10-12 09:54:39 -0700521 return;
522 }
523 // SystemService.start() forks sdptool to register service
524 // records. It can fail to register some records if it is
525 // forked multiple times in a row, probably because there is
526 // some race in sdptool or bluez when operated in parallel.
527 // As a workaround, delay 500ms between each fork of sdptool.
Jake Hambyf51eada2010-09-21 13:39:53 -0700528 // TODO: Don't fork sdptool in order to register service
Nick Pellybc1fc052009-10-12 09:54:39 -0700529 // records, use a DBUS call instead.
530 switch (msg.arg1) {
531 case 1:
Jaikumar Ganesh77b4ad02009-11-30 14:17:30 -0800532 Log.d(TAG, "Registering hfag record");
533 SystemService.start("hfag");
Nick Pellybc1fc052009-10-12 09:54:39 -0700534 mHandler.sendMessageDelayed(
535 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 2, -1), 500);
536 break;
537 case 2:
Jaikumar Ganesh77b4ad02009-11-30 14:17:30 -0800538 Log.d(TAG, "Registering hsag record");
539 SystemService.start("hsag");
Nick Pellybc1fc052009-10-12 09:54:39 -0700540 mHandler.sendMessageDelayed(
541 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 3, -1), 500);
542 break;
543 case 3:
544 Log.d(TAG, "Registering opush record");
Nick Pelly03c707a2009-07-14 21:32:14 -0700545 SystemService.start("opush");
Nick Pellybc1fc052009-10-12 09:54:39 -0700546 mHandler.sendMessageDelayed(
547 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 4, -1), 500);
548 break;
549 case 4:
550 Log.d(TAG, "Registering pbap record");
Jaikumar Ganesh67542962009-07-23 21:32:45 -0700551 SystemService.start("pbap");
Nick Pellybc1fc052009-10-12 09:54:39 -0700552 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700554 break;
555 case MESSAGE_FINISH_DISABLE:
556 finishDisable(msg.arg1 != 0);
557 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700558 case MESSAGE_UUID_INTENT:
559 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200560 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700561 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200562 makeServiceChannelCallbacks(address);
563 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700564 break;
Nick Pelly12835472009-09-25 15:00:29 -0700565 case MESSAGE_DISCOVERABLE_TIMEOUT:
566 int mode = msg.arg1;
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800567 if (isEnabledInternal()) {
Nick Pelly12835472009-09-25 15:00:29 -0700568 // TODO: Switch back to the previous scan mode
569 // This is ok for now, because we only use
570 // CONNECTABLE and CONNECTABLE_DISCOVERABLE
571 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
572 }
573 break;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700574 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
575 address = (String)msg.obj;
576 if (address != null) {
577 createBond(address);
578 return;
579 }
580 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 }
582 }
583 };
584
585 private EnableThread mEnableThread;
586
587 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700589 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 mSaveSetting = saveSetting;
591 }
592 public void run() {
593 boolean res = (enableNative() == 0);
594 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700595 int retryCount = 2;
596 boolean running = false;
597 while ((retryCount-- > 0) && !running) {
598 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700599 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700600 // thing. Check periodically for a while.
601 int pollCount = 5;
602 while ((pollCount-- > 0) && !running) {
603 if (mEventLoop.isEventLoopRunning()) {
604 running = true;
605 break;
606 }
607 try {
608 Thread.sleep(100);
609 } catch (InterruptedException e) {}
610 }
611 }
612 if (!running) {
613 log("bt EnableThread giving up");
614 res = false;
615 disableNative();
616 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 }
618
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619
620 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700621 if (!setupNativeDataNative()) {
622 return;
623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 if (mSaveSetting) {
625 persistBluetoothOnSetting(true);
626 }
627 mIsDiscovering = false;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800628 mBondState.readAutoPairingData();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 mBondState.loadBondState();
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700630 initProfileState();
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700631
632 //Register SDP records.
Nick Pellybc1fc052009-10-12 09:54:39 -0700633 mHandler.sendMessageDelayed(
634 mHandler.obtainMessage(MESSAGE_REGISTER_SDP_RECORDS, 1, -1), 3000);
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700635 setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637
The Android Open Source Project10592532009-03-18 17:39:46 -0700638 // Log bluetooth on to battery stats.
639 long ident = Binder.clearCallingIdentity();
640 try {
641 mBatteryStats.noteBluetoothOn();
642 } catch (RemoteException e) {
643 } finally {
644 Binder.restoreCallingIdentity(ident);
645 }
646 }
647
648 mEnableThread = null;
649
650 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700651 BluetoothAdapter.STATE_ON :
652 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700653
654 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700656 String[] propVal = {"Pairable", getProperty("Pairable")};
657 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700659
Jeff Sharkey44303922009-12-01 18:25:02 -0800660 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900661 disable(false);
662 }
663
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 }
665 }
666
667 private void persistBluetoothOnSetting(boolean bluetoothOn) {
668 long origCallerIdentityToken = Binder.clearCallingIdentity();
669 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
670 bluetoothOn ? 1 : 0);
671 Binder.restoreCallingIdentity(origCallerIdentityToken);
672 }
673
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700674 /*package*/ synchronized boolean attemptAutoPair(String address) {
675 if (!mBondState.hasAutoPairingFailed(address) &&
676 !mBondState.isAutoPairingBlacklisted(address)) {
677 mBondState.attempt(address);
678 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
679 return true;
680 }
681 return false;
682 }
683
684 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
685 if (result == BluetoothDevice.BOND_SUCCESS) {
686 setBondState(address, BluetoothDevice.BOND_BONDED);
687 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
688 mBondState.clearPinAttempts(address);
689 }
690 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
691 mBondState.getAttempt(address) == 1) {
692 mBondState.addAutoPairingFailure(address);
693 pairingAttempt(address, result);
694 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
695 mBondState.isAutoPairingAttemptsInProgress(address)) {
696 pairingAttempt(address, result);
697 } else {
698 setBondState(address, BluetoothDevice.BOND_NONE, result);
699 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
700 mBondState.clearPinAttempts(address);
701 }
702 }
703 }
704
705 /*package*/ synchronized String getPendingOutgoingBonding() {
706 return mBondState.getPendingOutgoingBonding();
707 }
708
709 private void pairingAttempt(String address, int result) {
710 // This happens when our initial guess of "0000" as the pass key
711 // fails. Try to create the bond again and display the pin dialog
712 // to the user. Use back-off while posting the delayed
713 // message. The initial value is
714 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
715 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
716 // reached, display an error to the user.
717 int attempt = mBondState.getAttempt(address);
718 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
719 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
720 mBondState.clearPinAttempts(address);
721 setBondState(address, BluetoothDevice.BOND_NONE, result);
722 return;
723 }
724
725 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
726 message.obj = address;
727 boolean postResult = mHandler.sendMessageDelayed(message,
728 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
729 if (!postResult) {
730 mBondState.clearPinAttempts(address);
731 setBondState(address,
732 BluetoothDevice.BOND_NONE, result);
733 return;
734 }
735 mBondState.attempt(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 }
737
738 /** local cache of bonding state.
739 /* we keep our own state to track the intermediate state BONDING, which
740 /* bluez does not track.
Jake Hambyf51eada2010-09-21 13:39:53 -0700741 * All addresses must be passed in upper case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 */
743 public class BondState {
744 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
745 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800747 private static final String AUTO_PAIRING_BLACKLIST =
748 "/etc/bluetooth/auto_pairing.conf";
749 private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST =
750 "/data/misc/bluetooth/dynamic_auto_pairing.conf";
751 private ArrayList<String> mAutoPairingAddressBlacklist;
752 private ArrayList<String> mAutoPairingExactNameBlacklist;
753 private ArrayList<String> mAutoPairingPartialNameBlacklist;
754 // Addresses added to blacklist dynamically based on usage.
755 private ArrayList<String> mAutoPairingDynamicAddressBlacklist;
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800756
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700757
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700758 // If this is an outgoing connection, store the address.
759 // There can be only 1 pending outgoing connection at a time,
760 private String mPendingOutgoingBonding;
761
762 private synchronized void setPendingOutgoingBonding(String address) {
763 mPendingOutgoingBonding = address;
764 }
765
766 public synchronized String getPendingOutgoingBonding() {
767 return mPendingOutgoingBonding;
768 }
769
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700771 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 return;
773 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700774 String []bonds = null;
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800775 String val = getPropertyInternal("Devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700776 if (val != null) {
777 bonds = val.split(",");
778 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 if (bonds == null) {
780 return;
781 }
782 mState.clear();
783 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700784 for (String device : bonds) {
785 mState.put(getAddressFromObjectPath(device).toUpperCase(),
786 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
788 }
789
790 public synchronized void setBondState(String address, int state) {
791 setBondState(address, state, 0);
792 }
793
794 /** reason is ignored unless state == BOND_NOT_BONDED */
795 public synchronized void setBondState(String address, int state, int reason) {
796 int oldState = getBondState(address);
797 if (oldState == state) {
798 return;
799 }
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700800
801 // Check if this was an pending outgoing bonding.
802 // If yes, reset the state.
803 if (oldState == BluetoothDevice.BOND_BONDING) {
804 if (address.equals(mPendingOutgoingBonding)) {
805 mPendingOutgoingBonding = null;
806 }
807 }
808
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700809 if (state == BluetoothDevice.BOND_BONDED) {
810 addProfileState(address);
811 } else if (state == BluetoothDevice.BOND_NONE) {
812 removeProfileState(address);
813 }
814
Jaikumar Ganeshc3ee99d2010-07-19 14:59:44 -0700815 // HID is handled by BluetoothService, other profiles
816 // will be handled by their respective services.
817 setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state);
818
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
820 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700821 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
822 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
823 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
824 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
825 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 if (reason <= 0) {
827 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
828 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
829 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
830 }
Nick Pelly005b2282009-09-10 10:21:56 -0700831 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 mState.remove(address);
833 } else {
834 mState.put(address, state);
835 }
836
837 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
838 }
839
840 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800841 if (mAutoPairingAddressBlacklist != null) {
842 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
843 if (address.startsWith(blacklistAddress)) return true;
844 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700846
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800847 if (mAutoPairingDynamicAddressBlacklist != null) {
848 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) {
849 if (address.equals(blacklistAddress)) return true;
850 }
851 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700852 String name = getRemoteName(address);
853 if (name != null) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800854 if (mAutoPairingExactNameBlacklist != null) {
855 for (String blacklistName : mAutoPairingExactNameBlacklist) {
856 if (name.equals(blacklistName)) return true;
857 }
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800858 }
859
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800860 if (mAutoPairingPartialNameBlacklist != null) {
861 for (String blacklistName : mAutoPairingPartialNameBlacklist) {
862 if (name.startsWith(blacklistName)) return true;
863 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700864 }
865 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 return false;
867 }
868
869 public synchronized int getBondState(String address) {
870 Integer state = mState.get(address);
871 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700872 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 }
874 return state.intValue();
875 }
876
Jaikumar Ganeshe5d93b72009-10-08 02:27:52 -0700877 /*package*/ synchronized String[] listInState(int state) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 ArrayList<String> result = new ArrayList<String>(mState.size());
879 for (Map.Entry<String, Integer> e : mState.entrySet()) {
880 if (e.getValue().intValue() == state) {
881 result.add(e.getKey());
882 }
883 }
884 return result.toArray(new String[result.size()]);
885 }
886
887 public synchronized void addAutoPairingFailure(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800888 if (mAutoPairingDynamicAddressBlacklist == null) {
889 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 }
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800891
892 updateAutoPairingData(address);
893 mAutoPairingDynamicAddressBlacklist.add(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 }
895
896 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
897 return getAttempt(address) != 0;
898 }
899
900 public synchronized void clearPinAttempts(String address) {
901 mPinAttempt.remove(address);
902 }
903
904 public synchronized boolean hasAutoPairingFailed(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800905 if (mAutoPairingDynamicAddressBlacklist == null) return false;
906
907 return mAutoPairingDynamicAddressBlacklist.contains(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 }
909
910 public synchronized int getAttempt(String address) {
911 Integer attempt = mPinAttempt.get(address);
912 if (attempt == null) {
913 return 0;
914 }
915 return attempt.intValue();
916 }
917
918 public synchronized void attempt(String address) {
919 Integer attempt = mPinAttempt.get(address);
920 int newAttempt;
921 if (attempt == null) {
922 newAttempt = 1;
923 } else {
924 newAttempt = attempt.intValue() + 1;
925 }
926 mPinAttempt.put(address, new Integer(newAttempt));
927 }
928
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800929 private void copyAutoPairingData() {
930 File file = null;
931 FileInputStream in = null;
932 FileOutputStream out = null;
933 try {
934 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST);
935 if (file.exists()) return;
936
937 in = new FileInputStream(AUTO_PAIRING_BLACKLIST);
938 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
939
940 byte[] buf = new byte[1024];
941 int len;
942 while ((len = in.read(buf)) > 0) {
943 out.write(buf, 0, len);
944 }
945 } catch (FileNotFoundException e) {
946 log("FileNotFoundException: in copyAutoPairingData");
947 } catch (IOException e) {
948 log("IOException: in copyAutoPairingData");
949 } finally {
950 try {
951 if (in != null) in.close();
952 if (out != null) out.close();
953 } catch (IOException e) {}
954 }
955 }
956
957 public void readAutoPairingData() {
958 if (mAutoPairingAddressBlacklist != null) return;
959 copyAutoPairingData();
960 FileInputStream fstream = null;
961 try {
962 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
963 DataInputStream in = new DataInputStream(fstream);
964 BufferedReader file = new BufferedReader(new InputStreamReader(in));
965 String line;
966 while((line = file.readLine()) != null) {
967 line = line.trim();
968 if (line.length() == 0 || line.startsWith("//")) continue;
969 String[] value = line.split("=");
970 if (value != null && value.length == 2) {
971 String[] val = value[1].split(",");
972 if (value[0].equalsIgnoreCase("AddressBlacklist")) {
973 mAutoPairingAddressBlacklist =
974 new ArrayList<String>(Arrays.asList(val));
975 } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) {
976 mAutoPairingExactNameBlacklist =
977 new ArrayList<String>(Arrays.asList(val));
978 } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) {
979 mAutoPairingPartialNameBlacklist =
980 new ArrayList<String>(Arrays.asList(val));
981 } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) {
982 mAutoPairingDynamicAddressBlacklist =
983 new ArrayList<String>(Arrays.asList(val));
984 } else {
985 Log.e(TAG, "Error parsing Auto pairing blacklist file");
986 }
987 }
988 }
989 } catch (FileNotFoundException e) {
990 log("FileNotFoundException: readAutoPairingData" + e.toString());
991 } catch (IOException e) {
992 log("IOException: readAutoPairingData" + e.toString());
993 } finally {
994 if (fstream != null) {
995 try {
996 fstream.close();
997 } catch (IOException e) {
998 // Ignore
999 }
1000 }
1001 }
1002 }
1003
Jake Hambyf51eada2010-09-21 13:39:53 -07001004 // This function adds a bluetooth address to the auto pairing blacklist
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -08001005 // file. These addresses are added to DynamicAddressBlacklistSection
1006 private void updateAutoPairingData(String address) {
1007 BufferedWriter out = null;
1008 try {
1009 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true));
1010 StringBuilder str = new StringBuilder();
1011 if (mAutoPairingDynamicAddressBlacklist.size() == 0) {
1012 str.append("DynamicAddressBlacklist=");
1013 }
1014 str.append(address);
1015 str.append(",");
1016 out.write(str.toString());
1017 } catch (FileNotFoundException e) {
1018 log("FileNotFoundException: updateAutoPairingData" + e.toString());
1019 } catch (IOException e) {
1020 log("IOException: updateAutoPairingData" + e.toString());
1021 } finally {
1022 if (out != null) {
1023 try {
1024 out.close();
1025 } catch (IOException e) {
1026 // Ignore
1027 }
1028 }
1029 }
1030 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032
1033 private static String toBondStateString(int bondState) {
1034 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -07001035 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 return "not bonded";
1037 case BluetoothDevice.BOND_BONDING:
1038 return "bonding";
1039 case BluetoothDevice.BOND_BONDED:
1040 return "bonded";
1041 default:
1042 return "??????";
1043 }
1044 }
1045
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -07001046 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
1047 return mAdapterProperties.isEmpty();
1048 }
1049
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001050 /*package*/synchronized void getAllProperties() {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -07001053 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001055 String properties[] = (String [])getAdapterPropertiesNative();
1056 // The String Array consists of key-value pairs.
1057 if (properties == null) {
1058 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
1059 return;
1060 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001061
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001062 for (int i = 0; i < properties.length; i++) {
1063 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001064 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001065 int len;
1066 if (name == null) {
1067 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
1068 continue;
1069 }
Danica Chang7a9de8b2010-07-16 18:38:29 -07001070 if (name.equals("Devices") || name.equals("UUIDs")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001071 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001072 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001073 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001074 str.append(properties[++i]);
1075 str.append(",");
1076 }
1077 if (len > 0) {
1078 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001079 }
1080 } else {
1081 newValue = properties[++i];
1082 }
Nick Pellybd022f42009-08-14 18:33:38 -07001083 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001084 }
1085
1086 // Add adapter object path property.
1087 String adapterPath = getAdapterPathNative();
1088 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -07001089 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001091
1092 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -07001093 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001094 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001095
1096 public synchronized boolean setName(String name) {
1097 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1098 "Need BLUETOOTH_ADMIN permission");
1099 if (name == null) {
1100 return false;
1101 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001102 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001105 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
1106 // Either have a single property function with Object as the parameter
1107 // or have a function for each property and then obfuscate in the JNI layer.
1108 // The following looks dirty.
1109 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001111 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001112 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001115 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001117 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001118 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 }
1120
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001121 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001123 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001124 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126
1127 /**
1128 * Set the discoverability window for the device. A timeout of zero
1129 * makes the device permanently discoverable (if the device is
1130 * discoverable). Setting the timeout to a nonzero value does not make
1131 * a device discoverable; you need to call setMode() to make the device
1132 * explicitly discoverable.
1133 *
Jake Hambyf51eada2010-09-21 13:39:53 -07001134 * @param timeout The discoverable timeout in seconds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 */
1136 public synchronized boolean setDiscoverableTimeout(int timeout) {
1137 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1138 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001139 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001141
Nick Pelly12835472009-09-25 15:00:29 -07001142 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -07001143 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
1144 "Need WRITE_SECURE_SETTINGS permission");
Nick Pellyde893f52009-09-08 13:15:33 -07001145 boolean pairable = false;
1146 boolean discoverable = false;
Nick Pelly12835472009-09-25 15:00:29 -07001147
Nick Pellyde893f52009-09-08 13:15:33 -07001148 switch (mode) {
1149 case BluetoothAdapter.SCAN_MODE_NONE:
Nick Pelly12835472009-09-25 15:00:29 -07001150 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001151 pairable = false;
1152 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -07001153 break;
1154 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001155 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001156 pairable = true;
1157 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -07001158 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001159 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001160 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001161 pairable = true;
1162 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -07001163 Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
1164 mHandler.sendMessageDelayed(msg, duration * 1000);
1165 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -07001166 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001167 default:
1168 Log.w(TAG, "Requested invalid scan mode " + mode);
1169 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001170 }
1171 setPropertyBoolean("Pairable", pairable);
1172 setPropertyBoolean("Discoverable", discoverable);
1173
1174 return true;
1175 }
1176
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08001177 /*package*/ synchronized String getProperty(String name) {
1178 if (!isEnabledInternal()) return null;
1179 return getPropertyInternal(name);
1180 }
1181
1182 /*package*/ synchronized String getPropertyInternal(String name) {
Nick Pellybd022f42009-08-14 18:33:38 -07001183 if (!mAdapterProperties.isEmpty())
1184 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001185 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -07001186 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001187 }
1188
1189 public synchronized String getAddress() {
1190 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1191 return getProperty("Address");
1192 }
1193
1194 public synchronized String getName() {
1195 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1196 return getProperty("Name");
1197 }
1198
1199 /**
1200 * Returns the user-friendly name of a remote device. This value is
1201 * returned from our local cache, which is updated when onPropertyChange
1202 * event is received.
1203 * Do not expect to retrieve the updated remote name immediately after
1204 * changing the name on the remote device.
1205 *
1206 * @param address Bluetooth address of remote device.
1207 *
1208 * @return The user-friendly name of the specified remote device.
1209 */
1210 public synchronized String getRemoteName(String address) {
1211 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001212 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001213 return null;
1214 }
Jaikumar Ganesh55929a92009-09-30 10:49:34 -07001215 return getRemoteDeviceProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001216 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217
1218 /**
1219 * Get the discoverability window for the device. A timeout of zero
1220 * means that the device is permanently discoverable (if the device is
1221 * in the discoverable mode).
1222 *
1223 * @return The discoverability window of the device, in seconds. A negative
1224 * value indicates an error.
1225 */
1226 public synchronized int getDiscoverableTimeout() {
1227 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001228 String timeout = getProperty("DiscoverableTimeout");
1229 if (timeout != null)
1230 return Integer.valueOf(timeout);
1231 else
1232 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001234
1235 public synchronized int getScanMode() {
1236 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001237 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -07001238 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001240 boolean pairable = getProperty("Pairable").equals("true");
1241 boolean discoverable = getProperty("Discoverable").equals("true");
1242 return bluezStringToScanMode (pairable, discoverable);
1243 }
1244
1245 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1247 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001248 if (!isEnabledInternal()) return false;
1249
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001250 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001252
1253 public synchronized boolean cancelDiscovery() {
1254 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1255 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001256 if (!isEnabledInternal()) return false;
1257
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001258 return stopDiscoveryNative();
1259 }
1260
1261 public synchronized boolean isDiscovering() {
1262 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1263 return mIsDiscovering;
1264 }
1265
1266 /* package */ void setIsDiscovering(boolean isDiscovering) {
1267 mIsDiscovering = isDiscovering;
1268 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001270 private boolean isBondingFeasible(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1272 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001273 if (!isEnabledInternal()) return false;
1274
Nick Pelly005b2282009-09-10 10:21:56 -07001275 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 return false;
1277 }
1278 address = address.toUpperCase();
1279
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001280 if (mBondState.getPendingOutgoingBonding() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001281 log("Ignoring createBond(): another device is bonding");
1282 // a different device is currently bonding, fail
1283 return false;
1284 }
1285
1286 // Check for bond state only if we are not performing auto
1287 // pairing exponential back-off attempts.
1288 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -07001289 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 log("Ignoring createBond(): this device is already bonding or bonded");
1291 return false;
1292 }
1293
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001294 if (address.equals(mDockAddress)) {
1295 if (!writeDockPin()) {
1296 log("Error while writing Pin for the dock");
1297 return false;
1298 }
1299 }
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001300 return true;
1301 }
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001302
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001303 public synchronized boolean createBond(String address) {
1304 if (!isBondingFeasible(address)) return false;
1305
1306 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 return false;
1308 }
1309
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001310 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 return true;
1314 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001316 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
1317 byte[] randomizer) {
1318 if (!isBondingFeasible(address)) return false;
1319
1320 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1321 return false;
1322 }
1323
1324 setDeviceOutOfBandData(address, hash, randomizer);
1325 mBondState.setPendingOutgoingBonding(address);
1326 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1327
1328 return true;
1329 }
1330
1331 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1332 byte[] randomizer) {
1333 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1334 "Need BLUETOOTH_ADMIN permission");
1335 if (!isEnabledInternal()) return false;
1336
1337 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1338
1339 if (DBG) {
1340 log("Setting out of band data for:" + address + ":" +
1341 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
1342 }
1343
1344 mDeviceOobData.put(address, value);
1345 return true;
1346 }
1347
1348 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1349 return mDeviceOobData.get(device.getAddress());
1350 }
1351
1352
1353 public synchronized byte[] readOutOfBandData() {
1354 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1355 "Need BLUETOOTH permission");
1356 if (!isEnabledInternal()) return null;
1357
1358 return readAdapterOutOfBandDataNative();
1359 }
1360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 public synchronized boolean cancelBondProcess(String address) {
1362 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1363 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001364 if (!isEnabledInternal()) return false;
1365
Nick Pelly005b2282009-09-10 10:21:56 -07001366 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 return false;
1368 }
1369 address = address.toUpperCase();
1370 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1371 return false;
1372 }
1373
Nick Pelly005b2282009-09-10 10:21:56 -07001374 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001376 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 return true;
1378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379
1380 public synchronized boolean removeBond(String address) {
1381 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1382 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001383 if (!isEnabledInternal()) return false;
1384
Nick Pelly005b2282009-09-10 10:21:56 -07001385 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 return false;
1387 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001388 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001389 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001390 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001391 return true;
1392 } else {
1393 return false;
1394 }
1395 }
1396
1397 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001398 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400
1401 public synchronized String[] listBonds() {
1402 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1403 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1404 }
1405
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001406 /*package*/ synchronized String[] listInState(int state) {
1407 return mBondState.listInState(state);
1408 }
1409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 public synchronized int getBondState(String address) {
1411 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001412 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001413 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 }
1415 return mBondState.getBondState(address.toUpperCase());
1416 }
1417
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001418 /*package*/ synchronized boolean setBondState(String address, int state) {
1419 return setBondState(address, state, 0);
1420 }
1421
1422 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
1423 mBondState.setBondState(address.toUpperCase(), state);
1424 return true;
1425 }
1426
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001427 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001428 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1429 mContext.MODE_PRIVATE);
1430
1431 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001432 }
1433
Danica Chang6fdd0c62010-08-11 14:54:43 -07001434 public synchronized boolean isTetheringOn() {
1435 return mTetheringOn;
1436 }
1437
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001438 /*package*/ synchronized boolean allowIncomingTethering() {
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001439 if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001440 return true;
1441 return false;
1442 }
1443
Danica Chang8aac82a2010-08-20 14:22:07 -07001444 private BroadcastReceiver mTetheringReceiver = null;
1445
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001446 public synchronized void setBluetoothTethering(boolean value) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001447 if (!value) {
1448 disconnectPan();
1449 }
Danica Chang8aac82a2010-08-20 14:22:07 -07001450
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001451 if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
Danica Chang8aac82a2010-08-20 14:22:07 -07001452 IntentFilter filter = new IntentFilter();
1453 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
1454 mTetheringReceiver = new BroadcastReceiver() {
1455 @Override
1456 public synchronized void onReceive(Context context, Intent intent) {
1457 if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
1458 == BluetoothAdapter.STATE_ON) {
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001459 mTetheringOn = true;
Danica Chang8aac82a2010-08-20 14:22:07 -07001460 mContext.unregisterReceiver(mTetheringReceiver);
1461 }
1462 }
1463 };
1464 mContext.registerReceiver(mTetheringReceiver, filter);
1465 } else {
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001466 mTetheringOn = value;
Danica Chang8aac82a2010-08-20 14:22:07 -07001467 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001468 }
1469
1470 public synchronized int getPanDeviceState(BluetoothDevice device) {
1471 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1472
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001473 Pair<Integer, String> panDevice = mPanDevices.get(device);
1474 if (panDevice == null) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001475 return BluetoothPan.STATE_DISCONNECTED;
1476 }
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001477 return panDevice.first;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001478 }
1479
1480 public synchronized boolean connectPanDevice(BluetoothDevice device) {
1481 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1482 "Need BLUETOOTH_ADMIN permission");
1483
1484 String objectPath = getObjectPathFromAddress(device.getAddress());
1485 if (DBG) log("connect PAN(" + objectPath + ")");
1486 if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
1487 log (device + " already connected to PAN");
1488 }
1489
1490 int connectedCount = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001491 for (BluetoothDevice panDevice: mPanDevices.keySet()) {
1492 if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001493 connectedCount ++;
1494 }
1495 }
1496 if (connectedCount > 8) {
1497 log (device + " could not connect to PAN because 8 other devices are already connected");
1498 return false;
1499 }
1500
1501 handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
1502 if (connectPanDeviceNative(objectPath, "nap", "panu")) {
1503 log ("connecting to PAN");
1504 return true;
1505 } else {
1506 handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
1507 log ("could not connect to PAN");
1508 return false;
1509 }
1510 }
1511
1512 private synchronized boolean disconnectPan() {
1513 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1514 if (DBG) log("disconnect all PAN devices");
1515
1516 for (BluetoothDevice device: mPanDevices.keySet()) {
1517 if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
1518 if (!disconnectPanDevice(device)) {
1519 log ("could not disconnect Pan Device "+device.getAddress());
1520 return false;
1521 }
1522 }
1523 }
1524 return true;
1525 }
1526
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001527 public synchronized List<BluetoothDevice> getConnectedPanDevices() {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001528 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1529
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001530 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
1531
Danica Chang6fdd0c62010-08-11 14:54:43 -07001532 for (BluetoothDevice device: mPanDevices.keySet()) {
1533 if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
1534 devices.add(device);
1535 }
1536 }
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001537 return devices;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001538 }
1539
1540 public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
1541 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1542 "Need BLUETOOTH_ADMIN permission");
1543 String objectPath = getObjectPathFromAddress(device.getAddress());
1544 if (DBG) log("disconnect PAN(" + objectPath + ")");
1545 if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
1546 log (device + " already disconnected from PAN");
1547 }
1548 handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING);
1549 return disconnectPanDeviceNative(objectPath);
1550 }
1551
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001552 /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
1553 String iface,
1554 int state) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001555 int prevState;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001556 String ifaceAddr = null;
1557
Danica Chang6fdd0c62010-08-11 14:54:43 -07001558 if (mPanDevices.get(device) == null) {
1559 prevState = BluetoothPan.STATE_DISCONNECTED;
1560 } else {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001561 prevState = mPanDevices.get(device).first;
1562 ifaceAddr = mPanDevices.get(device).second;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001563 }
1564 if (prevState == state) return;
1565
Danica Chang6fdd0c62010-08-11 14:54:43 -07001566 if (state == BluetoothPan.STATE_CONNECTED) {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001567 ifaceAddr = enableTethering(iface);
1568 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
1569 } else if (state == BluetoothPan.STATE_DISCONNECTED) {
1570 if (ifaceAddr != null) {
1571 mBluetoothIfaceAddresses.remove(ifaceAddr);
1572 ifaceAddr = null;
1573 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001574 }
1575
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001576 Pair<Integer, String> value = new Pair<Integer, String>(state, ifaceAddr);
1577 mPanDevices.put(device, value);
1578
Danica Chang6fdd0c62010-08-11 14:54:43 -07001579 Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
1580 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1581 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
1582 intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
1583 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1584
1585 if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07001586 sendConnectionStateChange(device, state, prevState);
Danica Chang6fdd0c62010-08-11 14:54:43 -07001587 }
1588
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001589 /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
1590 int state) {
1591 handlePanDeviceStateChange(device, null, state);
1592 }
1593
1594 private String createNewTetheringAddressLocked() {
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001595 if (getConnectedPanDevices().size() == mMaxPanDevices) {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001596 log("Max PAN device connections reached");
1597 return null;
1598 }
1599 String address = BLUETOOTH_IFACE_ADDR_START;
1600 while (true) {
1601 if (mBluetoothIfaceAddresses.contains(address)) {
1602 String[] addr = address.split("\\.");
1603 Integer newIp = Integer.parseInt(addr[2]) + 1;
1604 address = address.replace(addr[2], newIp.toString());
1605 } else {
1606 break;
1607 }
1608 }
1609 mBluetoothIfaceAddresses.add(address);
1610 return address;
1611 }
1612
1613 // configured when we start tethering
1614 private synchronized String enableTethering(String iface) {
1615 log("updateTetherState:" + iface);
Danica Chang6fdd0c62010-08-11 14:54:43 -07001616
1617 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
1618 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
1619 ConnectivityManager cm =
1620 (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1621 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
1622
1623 // bring toggle the interfaces
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001624 String[] currentIfaces = new String[0];
Danica Chang6fdd0c62010-08-11 14:54:43 -07001625 try {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001626 currentIfaces = service.listInterfaces();
Danica Chang6fdd0c62010-08-11 14:54:43 -07001627 } catch (Exception e) {
1628 Log.e(TAG, "Error listing Interfaces :" + e);
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001629 return null;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001630 }
1631
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001632 boolean found = false;
1633 for (String currIface: currentIfaces) {
1634 if (currIface.equals(iface)) {
1635 found = true;
1636 break;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001637 }
1638 }
1639
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001640 if (!found) return null;
1641
1642 String address = createNewTetheringAddressLocked();
1643 if (address == null) return null;
1644
1645 InterfaceConfiguration ifcg = null;
1646 try {
1647 ifcg = service.getInterfaceConfig(iface);
1648 if (ifcg != null) {
1649 String[] addr = BLUETOOTH_NETMASK.split("\\.");
1650 ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
1651 (Integer.parseInt(addr[1]) << 16) +
1652 (Integer.parseInt(addr[2]) << 8) +
1653 (Integer.parseInt(addr[3]));
1654 if (ifcg.ipAddr == 0) {
1655 addr = address.split("\\.");
1656
1657 ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
1658 (Integer.parseInt(addr[1]) << 16) +
1659 (Integer.parseInt(addr[2]) << 8) +
1660 (Integer.parseInt(addr[3]));
1661 ifcg.interfaceFlags =
1662 ifcg.interfaceFlags.replace("down", "up");
1663 }
1664 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
1665 ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
1666 service.setInterfaceConfig(iface, ifcg);
1667 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1668 Log.e(TAG, "Error tethering "+iface);
1669 }
1670 }
1671 } catch (Exception e) {
1672 Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
1673 return null;
1674 }
1675 return address;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001676 }
1677
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001678 public synchronized boolean connectInputDevice(BluetoothDevice device) {
1679 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1680 "Need BLUETOOTH_ADMIN permission");
1681
1682 String objectPath = getObjectPathFromAddress(device.getAddress());
Jaikumar Ganesh05a18632010-07-21 12:08:12 -07001683 if (objectPath == null ||
1684 getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001685 getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
1686 return false;
1687 }
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001688 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
1689 if (state != null) {
1690 Message msg = new Message();
1691 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
1692 msg.obj = state;
1693 mHidProfileState.sendMessage(msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001694 return true;
1695 }
1696 return false;
1697 }
1698
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001699 public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
1700 String objectPath = getObjectPathFromAddress(device.getAddress());
1701 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
1702 if (!connectInputDeviceNative(objectPath)) {
1703 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
1704 return false;
1705 }
1706 return true;
1707 }
1708
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001709 public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
1710 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1711 "Need BLUETOOTH_ADMIN permission");
1712
1713 String objectPath = getObjectPathFromAddress(device.getAddress());
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001714 if (objectPath == null || getConnectedInputDevices().size() == 0) {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001715 return false;
1716 }
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001717 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
1718 if (state != null) {
1719 Message msg = new Message();
1720 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
1721 msg.obj = state;
1722 mHidProfileState.sendMessage(msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001723 return true;
1724 }
1725 return false;
1726 }
1727
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001728 public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
1729 String objectPath = getObjectPathFromAddress(device.getAddress());
1730 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
1731 if (!disconnectInputDeviceNative(objectPath)) {
1732 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
1733 return false;
1734 }
1735 return true;
1736 }
1737
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001738 public synchronized int getInputDeviceState(BluetoothDevice device) {
1739 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1740
1741 if (mInputDevices.get(device) == null) {
1742 return BluetoothInputDevice.STATE_DISCONNECTED;
1743 }
Adam Powelldf7627d2010-06-21 16:23:42 -07001744 return mInputDevices.get(device);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001745 }
1746
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001747 public synchronized List<BluetoothDevice> getConnectedInputDevices() {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001748 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001749 List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001750 new int[] {BluetoothInputDevice.STATE_CONNECTED});
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001751 return devices;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001752 }
1753
1754 public synchronized int getInputDevicePriority(BluetoothDevice device) {
1755 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1756 return Settings.Secure.getInt(mContext.getContentResolver(),
1757 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1758 BluetoothInputDevice.PRIORITY_UNDEFINED);
1759 }
1760
1761 public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
1762 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1763 "Need BLUETOOTH_ADMIN permission");
1764 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
1765 return false;
1766 }
1767 return Settings.Secure.putInt(mContext.getContentResolver(),
1768 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1769 priority);
1770 }
1771
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001772 /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
1773 List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
1774
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001775 for (BluetoothDevice device: mInputDevices.keySet()) {
1776 int inputDeviceState = getInputDeviceState(device);
1777 for (int state : states) {
1778 if (state == inputDeviceState) {
1779 inputDevices.add(device);
1780 break;
1781 }
1782 }
1783 }
1784 return inputDevices;
1785 }
1786
1787 private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
1788 int prevState;
1789 if (mInputDevices.get(device) == null) {
1790 prevState = BluetoothInputDevice.STATE_DISCONNECTED;
1791 } else {
1792 prevState = mInputDevices.get(device);
1793 }
1794 if (prevState == state) return;
1795
1796 mInputDevices.put(device, state);
1797
1798 if (getInputDevicePriority(device) >
1799 BluetoothInputDevice.PRIORITY_OFF &&
1800 state == BluetoothInputDevice.STATE_CONNECTING ||
1801 state == BluetoothInputDevice.STATE_CONNECTED) {
1802 // We have connected or attempting to connect.
1803 // Bump priority
1804 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
1805 }
1806
1807 Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
1808 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1809 intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
1810 intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
1811 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1812
1813 if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07001814 sendConnectionStateChange(device, state, prevState);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001815 }
1816
Jaikumar Ganesh56d26132010-07-15 15:56:04 -07001817 /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001818 int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
1819 BluetoothInputDevice.STATE_DISCONNECTED;
1820 BluetoothDevice device = mAdapter.getRemoteDevice(address);
1821 handleInputDeviceStateChange(device, state);
1822 }
1823
Jaikumar Ganeshc3ee99d2010-07-19 14:59:44 -07001824 private void setInitialInputDevicePriority(BluetoothDevice device, int state) {
1825 switch (state) {
1826 case BluetoothDevice.BOND_BONDED:
1827 if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
1828 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
1829 }
1830 break;
1831 case BluetoothDevice.BOND_NONE:
1832 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
1833 break;
1834 }
1835 }
1836
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001837 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001838 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001839 }
1840
1841 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001842 if (!isEnabledInternal()) return null;
1843
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001844 String objectPath = getObjectPathFromAddress(address);
1845 return (String [])getDevicePropertiesNative(objectPath);
1846 }
1847
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001848 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -07001849 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001850 if (properties != null) {
1851 return properties.get(property);
1852 } else {
1853 // Query for remote device properties, again.
1854 // We will need to reload the cache when we switch Bluetooth on / off
1855 // or if we crash.
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001856 if (updateRemoteDevicePropertiesCache(address))
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001857 return getRemoteDeviceProperty(address, property);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001858 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001859 Log.e(TAG, "getRemoteDeviceProperty: " + property + " not present: " + address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001860 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001863 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
1864 String[] propValues = getRemoteDeviceProperties(address);
1865 if (propValues != null) {
1866 addRemoteDeviceProperties(address, propValues);
1867 return true;
1868 }
1869 return false;
1870 }
1871
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001872 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001873 /*
1874 * We get a DeviceFound signal every time RSSI changes or name changes.
1875 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -07001876 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001877 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001878 propertyValues = new HashMap<String, String>();
1879 }
1880
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001881 for (int i = 0; i < properties.length; i++) {
1882 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001883 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001884 int len;
1885 if (name == null) {
1886 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1887 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001888 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001889 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001890 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001891 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001892 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001893 str.append(properties[++i]);
1894 str.append(",");
1895 }
1896 if (len > 0) {
1897 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001898 }
1899 } else {
1900 newValue = properties[++i];
1901 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001902
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001903 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001904 }
Nick Pellybd022f42009-08-14 18:33:38 -07001905 mDeviceProperties.put(address, propertyValues);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001906
1907 // We have added a new remote device or updated its properties.
1908 // Also update the serviceChannel cache.
1909 updateDeviceServiceChannelCache(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001910 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001912 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001913 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001914 }
1915
1916 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1917 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -07001918 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001919 if (propVal != null) {
1920 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -07001921 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001922 } else {
1923 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001926
1927 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001928 * Sets the remote device trust state.
1929 *
1930 * @return boolean to indicate operation success or fail
1931 */
1932 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001933 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001934 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1935 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001936 return false;
1937 }
1938
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001939 if (!isEnabledInternal()) return false;
1940
Lixin Yueefa1dd72009-08-31 15:55:13 +08001941 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1942 value ? 1 : 0);
1943 }
1944
1945 /**
1946 * Gets the remote device trust state as boolean.
1947 * Note: this value may be
1948 * retrieved from cache if we retrieved the data before *
1949 *
1950 * @return boolean to indicate trust or untrust state
1951 */
1952 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001953 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001954 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1955 return false;
1956 }
1957
1958 String val = getRemoteDeviceProperty(address, "Trusted");
1959 if (val == null) {
1960 return false;
1961 } else {
1962 return val.equals("true") ? true : false;
1963 }
1964 }
1965
1966 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001967 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 * integer.
1969 *
1970 * Note: this value is retrieved from cache, because we get it during
1971 * remote-device discovery.
1972 *
1973 * @return 32-bit integer encoding the remote major, minor, and service
1974 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 */
1976 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001977 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001978 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1979 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001981 String val = getRemoteDeviceProperty(address, "Class");
1982 if (val == null)
1983 return BluetoothClass.ERROR;
1984 else {
1985 return Integer.valueOf(val);
1986 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001987 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001988
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001989
1990 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001991 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001992 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001993 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001995 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001996 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001997 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001998 return null;
1999 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002000 return getUuidFromCache(address);
2001 }
2002
2003 private ParcelUuid[] getUuidFromCache(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002004 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002005 if (value == null) return null;
2006
2007 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002008 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002009 uuidStrings = value.split(",");
2010 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
2011
2012 for (int i = 0; i < uuidStrings.length; i++) {
2013 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
2014 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002015 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002016 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002017
Nick Pelly16fb88a2009-10-07 07:44:03 +02002018 /**
2019 * Connect and fetch new UUID's using SDP.
2020 * The UUID's found are broadcast as intents.
2021 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
2022 * a given uuid.
2023 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
2024 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
2025 * callback and broadcast intents.
2026 */
2027 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
2028 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002029 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002030 if (!isEnabledInternal()) return false;
2031
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002032 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2033 return false;
2034 }
2035
Nick Pelly16fb88a2009-10-07 07:44:03 +02002036 RemoteService service = new RemoteService(address, uuid);
2037 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
2038 // An SDP query for this address & uuid is already in progress
2039 // Do not add this callback for the uuid
2040 return false;
2041 }
2042
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002043 if (mUuidIntentTracker.contains(address)) {
2044 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02002045 // Add this uuid onto the in-progress SDP query
2046 if (uuid != null) {
2047 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
2048 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002049 return true;
2050 }
2051
2052 boolean ret;
Jaikumar Ganesh0e090302010-03-29 00:01:34 -07002053 // Just do the SDP if the device is already created and UUIDs are not
2054 // NULL, else create the device and then do SDP.
2055 if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002056 String path = getObjectPathFromAddress(address);
2057 if (path == null) return false;
2058
2059 // Use an empty string for the UUID pattern
2060 ret = discoverServicesNative(path, "");
2061 } else {
2062 ret = createDeviceNative(address);
2063 }
2064
2065 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002066 if (uuid != null) {
2067 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
2068 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002069
2070 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
2071 message.obj = address;
2072 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
2073 return ret;
2074 }
2075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002076 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002077 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02002078 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002079 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002080 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002081 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002082 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002083 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002084 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002085 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002086 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002087 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002088 if (!isEnabledInternal()) return -1;
2089
Nick Pelly005b2282009-09-10 10:21:56 -07002090 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07002091 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002093 // Check if we are recovering from a crash.
2094 if (mDeviceProperties.isEmpty()) {
2095 if (!updateRemoteDevicePropertiesCache(address))
2096 return -1;
2097 }
2098
2099 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
2100 if (value != null && value.containsKey(uuid))
2101 return value.get(uuid);
2102 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002103 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002104
2105 public synchronized boolean setPin(String address, byte[] pin) {
2106 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2107 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002108 if (!isEnabledInternal()) return false;
2109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002110 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07002111 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002112 return false;
2113 }
2114 address = address.toUpperCase();
2115 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2116 if (data == null) {
2117 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
2118 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2119 " or by bluez.\n");
2120 return false;
2121 }
2122 // bluez API wants pin as a string
2123 String pinString;
2124 try {
2125 pinString = new String(pin, "UTF8");
2126 } catch (UnsupportedEncodingException uee) {
2127 Log.e(TAG, "UTF8 not supported?!?");
2128 return false;
2129 }
2130 return setPinNative(address, pinString, data.intValue());
2131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002133 public synchronized boolean setPasskey(String address, int passkey) {
2134 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2135 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002136 if (!isEnabledInternal()) return false;
2137
Nick Pelly005b2282009-09-10 10:21:56 -07002138 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002139 return false;
2140 }
2141 address = address.toUpperCase();
2142 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2143 if (data == null) {
2144 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
2145 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2146 " or by bluez.\n");
2147 return false;
2148 }
2149 return setPasskeyNative(address, passkey, data.intValue());
2150 }
2151
2152 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
2153 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2154 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002155 if (!isEnabledInternal()) return false;
2156
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002157 address = address.toUpperCase();
2158 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2159 if (data == null) {
2160 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
2161 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2162 " or by bluez.\n");
2163 return false;
2164 }
2165 return setPairingConfirmationNative(address, confirm, data.intValue());
2166 }
2167
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002168 public synchronized boolean setRemoteOutOfBandData(String address) {
2169 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2170 "Need BLUETOOTH_ADMIN permission");
2171 if (!isEnabledInternal()) return false;
2172 address = address.toUpperCase();
2173 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2174 if (data == null) {
2175 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
2176 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2177 " or by bluez.\n");
2178 return false;
2179 }
2180
2181 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
2182 byte[] hash, randomizer;
2183 if (val == null) {
2184 // TODO: check what should be passed in this case.
2185 hash = new byte[16];
2186 randomizer = new byte[16];
2187 } else {
2188 hash = val.first;
2189 randomizer = val.second;
2190 }
2191 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
2192 }
2193
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002194 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002195 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2196 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002197 if (!isEnabledInternal()) return false;
2198
Nick Pelly005b2282009-09-10 10:21:56 -07002199 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 return false;
2201 }
Nick Pelly005b2282009-09-10 10:21:56 -07002202 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07002203 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002204 address = address.toUpperCase();
2205 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2206 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002207 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
2208 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
2209 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002210 return false;
2211 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002212 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002214
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002215 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002216 ParcelUuid[] deviceUuids = getRemoteUuids(address);
2217 // We are storing the rfcomm channel numbers only for the uuids
2218 // we are interested in.
2219 int channel;
Nick Pelly16fb88a2009-10-07 07:44:03 +02002220 if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
2221
2222 ArrayList<ParcelUuid> applicationUuids = new ArrayList();
2223
2224 synchronized (this) {
2225 for (RemoteService service : mUuidCallbackTracker.keySet()) {
2226 if (service.address.equals(address)) {
2227 applicationUuids.add(service.uuid);
2228 }
2229 }
2230 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002231
2232 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02002233
2234 // Retrieve RFCOMM channel for default uuids
2235 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002236 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Nick Pelly16fb88a2009-10-07 07:44:03 +02002237 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
2238 uuid.toString(), 0x0004);
2239 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002240 value.put(uuid, channel);
2241 }
2242 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02002243 // Retrieve RFCOMM channel for application requested uuids
2244 for (ParcelUuid uuid : applicationUuids) {
2245 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
2246 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
2247 uuid.toString(), 0x0004);
2248 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
2249 value.put(uuid, channel);
2250 }
2251 }
2252
2253 synchronized (this) {
2254 // Make application callbacks
2255 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
2256 iter.hasNext();) {
2257 RemoteService service = iter.next();
2258 if (service.address.equals(address)) {
2259 channel = -1;
2260 if (value.get(service.uuid) != null) {
2261 channel = value.get(service.uuid);
2262 }
2263 if (channel != -1) {
2264 if (DBG) log("Making callback for " + service.uuid + " with result " +
2265 channel);
2266 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
2267 if (callback != null) {
2268 try {
2269 callback.onRfcommChannelFound(channel);
2270 } catch (RemoteException e) {Log.e(TAG, "", e);}
2271 }
2272
2273 iter.remove();
2274 }
2275 }
2276 }
2277
2278 // Update cache
2279 mDeviceServiceChannelCache.put(address, value);
2280 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002281 }
2282
Nick Pelly24bb9b82009-10-02 20:34:18 -07002283 /**
2284 * b is a handle to a Binder instance, so that this service can be notified
2285 * for Applications that terminate unexpectedly, to clean there service
2286 * records
2287 */
2288 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
2289 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002290 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
2291 if (!isEnabledInternal()) return -1;
2292
Nick Pelly24bb9b82009-10-02 20:34:18 -07002293 if (serviceName == null || uuid == null || channel < 1 ||
2294 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
2295 return -1;
2296 }
2297 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
2298 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
2299 return -1;
2300 }
2301 int handle = addRfcommServiceRecordNative(serviceName,
2302 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
2303 (short)channel);
2304 if (DBG) log("new handle " + Integer.toHexString(handle));
2305 if (handle == -1) {
2306 return -1;
2307 }
2308
2309 int pid = Binder.getCallingPid();
2310 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
2311 try {
2312 b.linkToDeath(new Reaper(handle, pid), 0);
2313 } catch (RemoteException e) {}
2314 return handle;
2315 }
2316
2317 public void removeServiceRecord(int handle) {
2318 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2319 "Need BLUETOOTH permission");
2320 checkAndRemoveRecord(handle, Binder.getCallingPid());
2321 }
2322
2323 private synchronized void checkAndRemoveRecord(int handle, int pid) {
2324 Integer handleInt = new Integer(handle);
2325 Integer owner = mServiceRecordToPid.get(handleInt);
2326 if (owner != null && pid == owner.intValue()) {
2327 if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
2328 pid);
2329 mServiceRecordToPid.remove(handleInt);
2330 removeServiceRecordNative(handle);
2331 }
2332 }
2333
2334 private class Reaper implements IBinder.DeathRecipient {
2335 int pid;
2336 int handle;
2337 Reaper(int handle, int pid) {
2338 this.pid = pid;
2339 this.handle = handle;
2340 }
2341 public void binderDied() {
2342 synchronized (BluetoothService.this) {
2343 if (DBG) log("Tracked app " + pid + " died");
2344 checkAndRemoveRecord(handle, pid);
2345 }
2346 }
2347 }
2348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002349 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
2350 @Override
2351 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002352 if (intent == null) return;
2353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002354 String action = intent.getAction();
2355 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
2356 ContentResolver resolver = context.getContentResolver();
2357 // Query the airplane mode from Settings.System just to make sure that
2358 // some random app is not sending this intent and disabling bluetooth
2359 boolean enabled = !isAirplaneModeOn();
2360 // If bluetooth is currently expected to be on, then enable or disable bluetooth
2361 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
2362 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002363 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002364 } else {
2365 disable(false);
2366 }
2367 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002368 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
2369 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2370 Intent.EXTRA_DOCK_STATE_UNDOCKED);
2371 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
2372 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
2373 mDockAddress = null;
2374 mDockPin = null;
2375 } else {
2376 SharedPreferences.Editor editor =
2377 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
2378 mContext.MODE_PRIVATE).edit();
2379 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07002380 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002381 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002382 }
2383 }
2384 };
2385
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002386 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08002387 final ContentResolver resolver = mContext.getContentResolver();
2388 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08002390 final String toggleableRadios = Settings.System.getString(resolver,
2391 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
2392
2393 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
2394 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
2395 mIsAirplaneToggleable = toggleableRadios == null ? false :
2396 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
2397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002398 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002399 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 }
2401 }
2402
2403 /* Returns true if airplane mode is currently on */
2404 private final boolean isAirplaneModeOn() {
2405 return Settings.System.getInt(mContext.getContentResolver(),
2406 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
2407 }
2408
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002409 /* Broadcast the Uuid intent */
2410 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002411 ParcelUuid[] uuid = getUuidFromCache(address);
2412 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07002413 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002414 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
2415 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002416
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002417 if (mUuidIntentTracker.contains(address))
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002418 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002419
2420 }
2421
2422 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
2423 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
2424 iter.hasNext();) {
2425 RemoteService service = iter.next();
2426 if (service.address.equals(address)) {
2427 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
2428 " " + service.uuid);
2429 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
2430 if (callback != null) {
2431 try {
2432 callback.onRfcommChannelFound(-1);
2433 } catch (RemoteException e) {Log.e(TAG, "", e);}
2434 }
2435
2436 iter.remove();
2437 }
2438 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002439 }
2440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002441 @Override
2442 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002443 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07002444 case BluetoothAdapter.STATE_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002445 pw.println("Bluetooth OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002446 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002447 case BluetoothAdapter.STATE_TURNING_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002448 pw.println("Bluetooth TURNING ON\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002449 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002450 case BluetoothAdapter.STATE_TURNING_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002451 pw.println("Bluetooth TURNING OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002452 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002453 case BluetoothAdapter.STATE_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002454 pw.println("Bluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002456
Nick Pelly24bb9b82009-10-02 20:34:18 -07002457 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08002458 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07002459
2460 pw.println("Local address = " + getAddress());
2461 pw.println("Local name = " + getName());
2462 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07002463
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002464 mAdapter.getProfileProxy(mContext,
2465 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
The Android Open Source Project10592532009-03-18 17:39:46 -07002466
The Android Open Source Project10592532009-03-18 17:39:46 -07002467 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07002468 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07002469 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07002470 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07002471 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07002472 mBondState.getAttempt(address),
2473 getRemoteName(address));
Nick Pelly24bb9b82009-10-02 20:34:18 -07002474
2475 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
2476 if (uuidChannels == null) {
2477 pw.println("\tuuids = null");
2478 } else {
2479 for (ParcelUuid uuid : uuidChannels.keySet()) {
2480 Integer channel = uuidChannels.get(uuid);
2481 if (channel == null) {
2482 pw.println("\t" + uuid);
2483 } else {
2484 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
Nick Pelly1eada0d2009-08-26 10:57:33 -07002485 }
2486 }
2487 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02002488 for (RemoteService service : mUuidCallbackTracker.keySet()) {
2489 if (service.address.equals(address)) {
2490 pw.println("\tPENDING CALLBACK: " + service.uuid);
2491 }
2492 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002493 }
2494
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002495 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07002496 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002497 if (value != null) {
2498 devicesObjectPath = value.split(",");
2499 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002500 pw.println("\n--ACL connected devices--");
Nick Pelly24bb9b82009-10-02 20:34:18 -07002501 if (devicesObjectPath != null) {
2502 for (String device : devicesObjectPath) {
2503 pw.println(getAddressFromObjectPath(device));
2504 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002505 }
2506
2507 // Rather not do this from here, but no-where else and I need this
2508 // dump
2509 pw.println("\n--Headset Service--");
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002510 if (mBluetoothHeadset != null) {
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08002511 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
2512 if (deviceList.size() == 0) {
2513 pw.println("\n--No headsets connected--");
2514 } else {
2515 BluetoothDevice device = deviceList.get(0);
2516 pw.println("\ngetConnectedDevices[0] = " + device);
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002517
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08002518 switch (mBluetoothHeadset.getConnectionState(device)) {
2519 case BluetoothHeadset.STATE_CONNECTING:
2520 pw.println("getConnectionState() = STATE_CONNECTING");
2521 break;
2522 case BluetoothHeadset.STATE_CONNECTED:
2523 pw.println("getConnectionState() = STATE_CONNECTED");
2524 break;
2525 case BluetoothHeadset.STATE_DISCONNECTING:
2526 pw.println("getConnectionState() = STATE_DISCONNECTING");
2527 break;
2528 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
2529 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
2530 break;
2531 }
2532 pw.println("getBatteryUsageHint() = " +
2533 mBluetoothHeadset.getBatteryUsageHint(device));
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002534 }
2535
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07002536 deviceList.clear();
2537 deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002538 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
2539 pw.println("\n--Connected and Disconnected Headsets");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08002540 for (BluetoothDevice device: deviceList) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002541 pw.println(device);
2542 if (mBluetoothHeadset.isAudioConnected(device)) {
2543 pw.println("SCO audio connected to device:" + device);
2544 }
2545 }
2546
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002547 mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
The Android Open Source Project10592532009-03-18 17:39:46 -07002548 }
Nick Pelly6c901db2009-06-19 10:08:09 -07002549
Nick Pelly24bb9b82009-10-02 20:34:18 -07002550 pw.println("\n--Application Service Records--");
2551 for (Integer handle : mServiceRecordToPid.keySet()) {
2552 Integer pid = mServiceRecordToPid.get(handle);
2553 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
2554 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002555 }
2556
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002557 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
2558 new BluetoothProfile.ServiceListener() {
2559 public void onServiceConnected(int profile, BluetoothProfile proxy) {
2560 mBluetoothHeadset = (BluetoothHeadset) proxy;
2561 }
2562 public void onServiceDisconnected(int profile) {
2563 mBluetoothHeadset = null;
2564 }
2565 };
2566
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002567 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2568 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002569 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002570 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002571 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002572 else
Nick Pellybd022f42009-08-14 18:33:38 -07002573 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002574 }
2575
2576 /* package */ static String scanModeToBluezString(int mode) {
2577 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07002578 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002579 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07002580 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002581 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07002582 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002583 return "discoverable";
2584 }
2585 return null;
2586 }
2587
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002588 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002589 String adapterObjectPath = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002590 if (adapterObjectPath == null || objectPath == null) {
Jake Hambyf51eada2010-09-21 13:39:53 -07002591 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002592 " or deviceObjectPath:" + objectPath + " is null");
2593 return null;
2594 }
2595 if (!objectPath.startsWith(adapterObjectPath)) {
Jake Hambyf51eada2010-09-21 13:39:53 -07002596 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002597 " is not a prefix of deviceObjectPath:" + objectPath +
2598 "bluetoothd crashed ?");
2599 return null;
2600 }
2601 String address = objectPath.substring(adapterObjectPath.length());
2602 if (address != null) return address.replace('_', ':');
2603
2604 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2605 return null;
2606 }
2607
2608 /*package*/ String getObjectPathFromAddress(String address) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002609 String path = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002610 if (path == null) {
2611 Log.e(TAG, "Error: Object Path is null");
2612 return null;
2613 }
2614 path = path + address.replace(":", "_");
2615 return path;
2616 }
2617
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002618 /*package */ void setLinkTimeout(String address, int num_slots) {
2619 String path = getObjectPathFromAddress(address);
2620 boolean result = setLinkTimeoutNative(path, num_slots);
2621
2622 if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
2623 }
2624
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002625 public boolean connectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002626 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2627
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002628 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002629 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002630 Message msg = new Message();
2631 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2632 msg.obj = state;
2633 mHfpProfileState.sendMessage(msg);
2634 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002635 }
2636 return false;
2637 }
2638
2639 public boolean disconnectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002640 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2641
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002642 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002643 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002644 Message msg = new Message();
2645 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2646 msg.obj = state;
2647 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002648 return true;
2649 }
2650 return false;
2651 }
2652
2653 public boolean connectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002654 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2655
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002656 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002657 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002658 Message msg = new Message();
2659 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2660 msg.obj = state;
2661 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002662 return true;
2663 }
2664 return false;
2665 }
2666
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002667 public boolean disconnectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002668 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2669
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002670 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2671 if (state != null) {
2672 Message msg = new Message();
2673 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2674 msg.obj = state;
2675 mA2dpProfileState.sendMessage(msg);
2676 return true;
2677 }
2678 return false;
2679 }
2680
2681 private BluetoothDeviceProfileState addProfileState(String address) {
2682 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002683 if (state != null) return state;
2684
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002685 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2686 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002687 state.start();
2688 return state;
2689 }
2690
2691 private void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002692 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002693 }
2694
2695 private void initProfileState() {
2696 String []bonds = null;
2697 String val = getPropertyInternal("Devices");
2698 if (val != null) {
2699 bonds = val.split(",");
2700 }
2701 if (bonds == null) {
2702 return;
2703 }
2704
2705 for (String path : bonds) {
2706 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002707 BluetoothDeviceProfileState state = addProfileState(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002708 // Allow 8 secs for SDP records to get registered.
2709 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002710 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002711 state.sendMessageDelayed(msg, 8000);
2712 }
2713 }
2714
2715 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002716 BluetoothDeviceProfileState state =
2717 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002718 if (state != null) {
2719 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002720 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002721 state.sendMessage(msg);
2722 return true;
2723 }
2724 return false;
2725 }
2726
2727 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002728 BluetoothDeviceProfileState state =
2729 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002730 if (state != null) {
2731 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002732 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002733 state.sendMessage(msg);
2734 return true;
2735 }
2736 return false;
2737 }
2738
2739 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2740 mA2dpService = a2dpService;
2741 }
2742
Jaikumar Ganesh70a053b2010-10-13 15:54:30 -07002743 public void sendProfileStateMessage(int profile, int cmd) {
2744 Message msg = new Message();
2745 msg.what = cmd;
2746 if (profile == BluetoothProfileState.HFP) {
2747 mHfpProfileState.sendMessage(msg);
2748 } else if (profile == BluetoothProfileState.A2DP) {
2749 mA2dpProfileState.sendMessage(msg);
2750 }
2751 }
2752
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002753 public int getAdapterConnectionState() {
2754 return mAdapterConnectionState;
2755 }
2756
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002757 public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
2758 int prevState) {
2759 if (updateCountersAndCheckForConnectionStateChange(device, state, prevState)) {
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002760 state = translateToAdapterConnectionState(state);
2761 prevState = translateToAdapterConnectionState(prevState);
2762
2763 mAdapterConnectionState = state;
2764
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002765 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
2766 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2767 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, state);
2768 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, prevState);
2769 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
2770 log("CONNECTION_STATE_CHANGE: " + device + ": " + prevState + "-> " + state);
2771 }
2772 }
2773
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002774 private int translateToAdapterConnectionState(int state) {
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002775 switch (state) {
2776 case BluetoothProfile.STATE_CONNECTING:
2777 return BluetoothAdapter.STATE_CONNECTING;
2778 case BluetoothProfile.STATE_CONNECTED:
2779 return BluetoothAdapter.STATE_CONNECTED;
2780 case BluetoothProfile.STATE_DISCONNECTING:
2781 return BluetoothAdapter.STATE_DISCONNECTING;
2782 case BluetoothProfile.STATE_DISCONNECTED:
2783 return BluetoothAdapter.STATE_DISCONNECTED;
2784 default:
2785 Log.e(TAG, "Error in getAdapterConnectionState");
2786 return -1;
2787 }
2788 }
2789
2790 private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
2791 int state,
2792 int prevState) {
2793 switch (state) {
2794 case BluetoothProfile.STATE_CONNECTING:
2795 mProfilesConnecting++;
2796 if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
2797 if (mProfilesConnected > 0 || mProfilesConnecting > 1) return false;
2798
2799 break;
2800 case BluetoothProfile.STATE_CONNECTED:
2801 if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
2802 if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
2803
2804 mProfilesConnected++;
2805
2806 if (mProfilesConnected > 1) return false;
2807 break;
2808 case BluetoothProfile.STATE_DISCONNECTING:
2809 mProfilesDisconnecting++;
2810 mProfilesConnected--;
2811
2812 if (mProfilesConnected > 0 || mProfilesDisconnecting > 1) return false;
2813
2814 break;
2815 case BluetoothProfile.STATE_DISCONNECTED:
2816 if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
2817 if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
2818
2819 if (prevState == BluetoothAdapter.STATE_CONNECTED) {
2820 mProfilesConnected--;
2821 }
2822
2823 if (mProfilesConnected > 0 || mProfilesConnecting > 0) return false;
2824 break;
2825 }
2826 return true;
2827 }
2828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002829 private static void log(String msg) {
2830 Log.d(TAG, msg);
2831 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002832
2833 private native static void classInitNative();
2834 private native void initializeNativeDataNative();
2835 private native boolean setupNativeDataNative();
2836 private native boolean tearDownNativeDataNative();
2837 private native void cleanupNativeDataNative();
2838 private native String getAdapterPathNative();
2839
2840 private native int isEnabledNative();
2841 private native int enableNative();
2842 private native int disableNative();
2843
2844 private native Object[] getAdapterPropertiesNative();
2845 private native Object[] getDevicePropertiesNative(String objectPath);
2846 private native boolean setAdapterPropertyStringNative(String key, String value);
2847 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2848 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2849
2850 private native boolean startDiscoveryNative();
2851 private native boolean stopDiscoveryNative();
2852
2853 private native boolean createPairedDeviceNative(String address, int timeout_ms);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002854 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2855 private native byte[] readAdapterOutOfBandDataNative();
2856
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002857 private native boolean cancelDeviceCreationNative(String address);
2858 private native boolean removeDeviceNative(String objectPath);
2859 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2860 int attributeId);
2861
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002862 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002863 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002864 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2865 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2866 int nativeData);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002867 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2868 byte[] randomizer, int nativeData);
2869
Nick Pelly24bb9b82009-10-02 20:34:18 -07002870 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2871 int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002872 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002873 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002874
Nick Pelly24bb9b82009-10-02 20:34:18 -07002875 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2876 short channel);
2877 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002878 private native boolean setLinkTimeoutNative(String path, int num_slots);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07002879 private native boolean connectInputDeviceNative(String path);
2880 private native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002881
2882 private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2883 private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
2884 private native boolean disconnectPanDeviceNative(String path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002885}