blob: 1f19f9ec1de466bb469064af84e91942cd23552b [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;
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800100 private int[] mAdapterSdpUuids;
101 private int[] mAdapterSdpHandles;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
Nick Pellybd022f42009-08-14 18:33:38 -0700103 private BluetoothAdapter mAdapter; // constant after init()
104 private final BondState mBondState = new BondState(); // local cache of bondings
105 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private final Context mContext;
107
108 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
109 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
110
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800111 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
112 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
113
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800114 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
115 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
116
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800117 private static final int MESSAGE_FINISH_DISABLE = 1;
118 private static final int MESSAGE_UUID_INTENT = 2;
119 private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 3;
120 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 4;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700121
122 // The time (in millisecs) to delay the pairing attempt after the first
123 // auto pairing attempt fails. We use an exponential delay with
124 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
125 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
126 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
127 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700128
Danica Chang6fdd0c62010-08-11 14:54:43 -0700129 private ArrayList<String> mBluetoothIfaceAddresses;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700130 private int mMaxPanDevices;
131
132 private static final String BLUETOOTH_IFACE_ADDR_START= "192.168.44.1";
133 private static final int BLUETOOTH_MAX_PAN_CONNECTIONS = 5;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700134 private static final String BLUETOOTH_NETMASK = "255.255.255.0";
135
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700136 // The timeout used to sent the UUIDs Intent
137 // This timeout should be greater than the page timeout
138 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700139
Nick Pelly16fb88a2009-10-07 07:44:03 +0200140 /** Always retrieve RFCOMM channel for these SDP UUIDs */
141 private static final ParcelUuid[] RFCOMM_UUIDS = {
142 BluetoothUuid.Handsfree,
143 BluetoothUuid.HSP,
144 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700145
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700146 // TODO(): Optimize all these string handling
Nick Pelly16fb88a2009-10-07 07:44:03 +0200147 private final Map<String, String> mAdapterProperties;
148 private final HashMap<String, Map<String, String>> mDeviceProperties;
149
150 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
151 private final ArrayList<String> mUuidIntentTracker;
152 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700153
Nick Pelly24bb9b82009-10-02 20:34:18 -0700154 private final HashMap<Integer, Integer> mServiceRecordToPid;
155
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700156 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
157 private final BluetoothProfileState mA2dpProfileState;
158 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700159 private final BluetoothProfileState mHidProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700160
161 private BluetoothA2dpService mA2dpService;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700162 private final HashMap<BluetoothDevice, Integer> mInputDevices;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700163 private final HashMap<BluetoothDevice, Pair<Integer, String>> mPanDevices;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700164 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700165
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700166 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700167
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800168 private static String mDockAddress;
169 private String mDockPin;
170
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700171 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
172
Nick Pelly16fb88a2009-10-07 07:44:03 +0200173 private static class RemoteService {
174 public String address;
175 public ParcelUuid uuid;
176 public RemoteService(String address, ParcelUuid uuid) {
177 this.address = address;
178 this.uuid = uuid;
179 }
180 @Override
181 public boolean equals(Object o) {
182 if (o instanceof RemoteService) {
183 RemoteService service = (RemoteService)o;
184 return address.equals(service.address) && uuid.equals(service.uuid);
185 }
186 return false;
187 }
Kenny Root5f614162010-02-17 12:00:37 -0800188
189 @Override
190 public int hashCode() {
191 int hash = 1;
192 hash = hash * 31 + (address == null ? 0 : address.hashCode());
193 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
194 return hash;
195 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200196 }
197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 static {
199 classInitNative();
200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201
Nick Pellybd022f42009-08-14 18:33:38 -0700202 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700204
205 // Need to do this in place of:
206 // mBatteryStats = BatteryStatsService.getService();
207 // Since we can not import BatteryStatsService from here. This class really needs to be
208 // moved to java/services/com/android/server/
209 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700212
213 if (isEnabledNative() == 1) {
214 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
215 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700217
Nick Pellyde893f52009-09-08 13:15:33 -0700218 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 mIsDiscovering = false;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700220 mTetheringOn = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700221 mAdapterProperties = new HashMap<String, String>();
222 mDeviceProperties = new HashMap<String, Map<String,String>>();
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700223
224 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700225 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700226 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200227 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700228 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700229 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
230 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
231 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700232 mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700233
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700234 mBluetoothIfaceAddresses = new ArrayList<String>();
235 try {
236 mMaxPanDevices = context.getResources().getInteger(
237 com.android.internal.R.integer.config_max_pan_devices);
238 } catch (NotFoundException e) {
239 mMaxPanDevices = BLUETOOTH_MAX_PAN_CONNECTIONS;
Danica Chang6fdd0c62010-08-11 14:54:43 -0700240 }
241
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700242 mHfpProfileState.start();
243 mA2dpProfileState.start();
Jaikumar Ganeshde075032010-07-19 16:28:27 -0700244 mHidProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800245
246 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800247 registerForAirplaneMode(filter);
248
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800249 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800250 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700251 mInputDevices = new HashMap<BluetoothDevice, Integer>();
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700252 mPanDevices = new HashMap<BluetoothDevice, Pair<Integer, String>>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800253 }
254
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700255 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800256 if (mDockAddress != null) return mDockAddress;
257
258 BufferedInputStream file = null;
259 String dockAddress;
260 try {
261 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
262 byte[] address = new byte[17];
263 file.read(address);
264 dockAddress = new String(address);
265 dockAddress = dockAddress.toUpperCase();
266 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
267 mDockAddress = dockAddress;
268 return mDockAddress;
269 } else {
270 log("CheckBluetoothAddress failed for car dock address:" + dockAddress);
271 }
272 } catch (FileNotFoundException e) {
273 log("FileNotFoundException while trying to read dock address");
274 } catch (IOException e) {
275 log("IOException while trying to read dock address");
276 } finally {
277 if (file != null) {
278 try {
279 file.close();
280 } catch (IOException e) {
281 // Ignore
282 }
283 }
284 }
285 mDockAddress = null;
286 return null;
287 }
288
289 private synchronized boolean writeDockPin() {
290 BufferedWriter out = null;
291 try {
292 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
293
294 // Generate a random 4 digit pin between 0000 and 9999
295 // This is not truly random but good enough for our purposes.
296 int pin = (int) Math.floor(Math.random() * 10000);
297
298 mDockPin = String.format("%04d", pin);
299 out.write(mDockPin);
300 return true;
301 } catch (FileNotFoundException e) {
302 log("FileNotFoundException while trying to write dock pairing pin");
303 } catch (IOException e) {
304 log("IOException while while trying to write dock pairing pin");
305 } finally {
306 if (out != null) {
307 try {
308 out.close();
309 } catch (IOException e) {
310 // Ignore
311 }
312 }
313 }
314 mDockPin = null;
315 return false;
316 }
317
318 /*package*/ synchronized String getDockPin() {
319 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700320 }
321
322 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200323 mAdapter = BluetoothAdapter.getDefaultAdapter();
Nick Pellybd022f42009-08-14 18:33:38 -0700324 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326
Jaikumar Ganesh7d0548d2010-10-18 15:29:09 -0700327 public synchronized void initAfterA2dpRegistration() {
328 mEventLoop.getProfileProxy();
329 }
330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 @Override
332 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800333 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 try {
335 cleanupNativeDataNative();
336 } finally {
337 super.finalize();
338 }
339 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340
341 public boolean isEnabled() {
342 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800343 return isEnabledInternal();
344 }
345
346 private boolean isEnabledInternal() {
Nick Pellyde893f52009-09-08 13:15:33 -0700347 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349
The Android Open Source Project10592532009-03-18 17:39:46 -0700350 public int getBluetoothState() {
351 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
352 return mBluetoothState;
353 }
354
355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 /**
357 * Bring down bluetooth and disable BT in settings. Returns true on success.
358 */
359 public boolean disable() {
360 return disable(true);
361 }
362
363 /**
364 * Bring down bluetooth. Returns true on success.
365 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200366 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 */
368 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200369 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370
The Android Open Source Project10592532009-03-18 17:39:46 -0700371 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700372 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700373 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700374 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700375 break;
376 default:
377 return false;
378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379 if (mEnableThread != null && mEnableThread.isAlive()) {
380 return false;
381 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800382
Nick Pellyde893f52009-09-08 13:15:33 -0700383 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800384
385 if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles);
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700386 setBluetoothTetheringNative(false, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700387
388 // Allow 3 seconds for profiles to gracefully disconnect
389 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700390 // BluetoothService when it is done shutting down
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700391 disconnectDevices();
392
The Android Open Source Project10592532009-03-18 17:39:46 -0700393 mHandler.sendMessageDelayed(
394 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
395 return true;
396 }
397
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700398 private synchronized void disconnectDevices() {
399 // Disconnect devices handled by BluetoothService.
400 for (BluetoothDevice device: getConnectedInputDevices()) {
401 disconnectInputDevice(device);
402 }
403
404 for (BluetoothDevice device: getConnectedPanDevices()) {
405 disconnectPanDevice(device);
406 }
407 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700408
409 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700410 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700411 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 }
413 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700414 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 disableNative();
416
417 // mark in progress bondings as cancelled
418 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700419 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
421 }
422
Jaikumar Ganesh2d1fc4e2010-12-13 15:54:33 -0800423 // Stop the profile state machine for bonded devices.
424 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) {
425 removeProfileState(address);
426 }
427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700429 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
430 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
432
The Android Open Source Project10592532009-03-18 17:39:46 -0700433 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700434 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700435 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700436
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700437 mProfilesConnected = 0;
438 mProfilesConnecting = 0;
439 mProfilesDisconnecting = 0;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700440 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 if (saveSetting) {
443 persistBluetoothOnSetting(false);
444 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700445
Nick Pellyde893f52009-09-08 13:15:33 -0700446 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700447
448 // Log bluetooth off to battery stats.
449 long ident = Binder.clearCallingIdentity();
450 try {
451 mBatteryStats.noteBluetoothOff();
452 } catch (RemoteException e) {
453 } finally {
454 Binder.restoreCallingIdentity(ident);
455 }
Nick Pelly997c7612009-03-24 20:44:48 -0700456
457 if (mRestart) {
458 mRestart = false;
459 enable();
460 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 }
462
The Android Open Source Project10592532009-03-18 17:39:46 -0700463 /** Bring up BT and persist BT on in settings */
464 public boolean enable() {
465 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 }
467
468 /**
469 * Enable this Bluetooth device, asynchronously.
470 * This turns on/off the underlying hardware.
471 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700472 * @param saveSetting If true, persist the new state of BT in settings
473 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700475 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
477 "Need BLUETOOTH_ADMIN permission");
478
479 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800480 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 return false;
482 }
Nick Pellyde893f52009-09-08 13:15:33 -0700483 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 return false;
485 }
486 if (mEnableThread != null && mEnableThread.isAlive()) {
487 return false;
488 }
Nick Pellyde893f52009-09-08 13:15:33 -0700489 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700490 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 mEnableThread.start();
492 return true;
493 }
494
Nick Pelly997c7612009-03-24 20:44:48 -0700495 /** Forcibly restart Bluetooth if it is on */
496 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700497 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700498 return;
499 }
500 mRestart = true;
501 if (!disable(false)) {
502 mRestart = false;
503 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700504 }
Nick Pelly997c7612009-03-24 20:44:48 -0700505
The Android Open Source Project10592532009-03-18 17:39:46 -0700506 private synchronized void setBluetoothState(int state) {
507 if (state == mBluetoothState) {
508 return;
509 }
510
511 if (DBG) log("Bluetooth state " + mBluetoothState + " -> " + state);
512
Nick Pellyde893f52009-09-08 13:15:33 -0700513 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
514 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
515 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700516 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
517
518 mBluetoothState = state;
519
520 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
521 }
522
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 private final Handler mHandler = new Handler() {
524 @Override
525 public void handleMessage(Message msg) {
526 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700527 case MESSAGE_FINISH_DISABLE:
528 finishDisable(msg.arg1 != 0);
529 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700530 case MESSAGE_UUID_INTENT:
531 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200532 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700533 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200534 makeServiceChannelCallbacks(address);
535 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700536 break;
Nick Pelly12835472009-09-25 15:00:29 -0700537 case MESSAGE_DISCOVERABLE_TIMEOUT:
538 int mode = msg.arg1;
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800539 if (isEnabledInternal()) {
Nick Pelly12835472009-09-25 15:00:29 -0700540 // TODO: Switch back to the previous scan mode
541 // This is ok for now, because we only use
542 // CONNECTABLE and CONNECTABLE_DISCOVERABLE
543 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
544 }
545 break;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700546 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
547 address = (String)msg.obj;
548 if (address != null) {
549 createBond(address);
550 return;
551 }
552 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800553 }
554 }
555 };
556
557 private EnableThread mEnableThread;
558
559 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700561 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 mSaveSetting = saveSetting;
563 }
564 public void run() {
565 boolean res = (enableNative() == 0);
566 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700567 int retryCount = 2;
568 boolean running = false;
569 while ((retryCount-- > 0) && !running) {
570 mEventLoop.start();
The Android Open Source Project10592532009-03-18 17:39:46 -0700571 // it may take a momement for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700572 // thing. Check periodically for a while.
573 int pollCount = 5;
574 while ((pollCount-- > 0) && !running) {
575 if (mEventLoop.isEventLoopRunning()) {
576 running = true;
577 break;
578 }
579 try {
580 Thread.sleep(100);
581 } catch (InterruptedException e) {}
582 }
583 }
584 if (!running) {
585 log("bt EnableThread giving up");
586 res = false;
587 disableNative();
588 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 }
590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591
592 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700593 if (!setupNativeDataNative()) {
594 return;
595 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 if (mSaveSetting) {
597 persistBluetoothOnSetting(true);
598 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800599 //Register SDP records.
600 if (mContext.getResources().
601 getBoolean(com.android.internal.R.bool.config_voice_capable)) {
602 int[] uuids = {
603 BluetoothUuid.getServiceIdentifierFromParcelUuid(
604 BluetoothUuid.Handsfree_AG),
605 BluetoothUuid.getServiceIdentifierFromParcelUuid(
606 BluetoothUuid.HSP_AG),
607 BluetoothUuid.getServiceIdentifierFromParcelUuid(
608 BluetoothUuid.PBAP_PSE),
609 BluetoothUuid.getServiceIdentifierFromParcelUuid(
610 BluetoothUuid.ObexObjectPush),
611 };
612 mAdapterSdpUuids = uuids;
613
614 } else {
615 int[] uuids = {
616 BluetoothUuid.getServiceIdentifierFromParcelUuid(
617 BluetoothUuid.HSP_AG),
618 BluetoothUuid.getServiceIdentifierFromParcelUuid(
619 BluetoothUuid.PBAP_PSE),
620 BluetoothUuid.getServiceIdentifierFromParcelUuid(
621 BluetoothUuid.ObexObjectPush),
622 };
623 mAdapterSdpUuids = uuids;
624 }
625
626 mAdapterSdpHandles = addReservedServiceRecordsNative(mAdapterSdpUuids);
627 setBluetoothTetheringNative(true, BluetoothPan.NAP_ROLE, BluetoothPan.NAP_BRIDGE);
628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 mIsDiscovering = false;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800630 mBondState.readAutoPairingData();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 mBondState.loadBondState();
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700632 initProfileState();
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700633
The Android Open Source Project10592532009-03-18 17:39:46 -0700634 // Log bluetooth on to battery stats.
635 long ident = Binder.clearCallingIdentity();
636 try {
637 mBatteryStats.noteBluetoothOn();
638 } catch (RemoteException e) {
639 } finally {
640 Binder.restoreCallingIdentity(ident);
641 }
642 }
643
644 mEnableThread = null;
645
646 setBluetoothState(res ?
Nick Pellyde893f52009-09-08 13:15:33 -0700647 BluetoothAdapter.STATE_ON :
648 BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700649
650 if (res) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 // Update mode
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700652 String[] propVal = {"Pairable", getProperty("Pairable")};
653 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700655
Jeff Sharkey44303922009-12-01 18:25:02 -0800656 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900657 disable(false);
658 }
659
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 }
661 }
662
663 private void persistBluetoothOnSetting(boolean bluetoothOn) {
664 long origCallerIdentityToken = Binder.clearCallingIdentity();
665 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
666 bluetoothOn ? 1 : 0);
667 Binder.restoreCallingIdentity(origCallerIdentityToken);
668 }
669
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700670 /*package*/ synchronized boolean attemptAutoPair(String address) {
671 if (!mBondState.hasAutoPairingFailed(address) &&
672 !mBondState.isAutoPairingBlacklisted(address)) {
673 mBondState.attempt(address);
674 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
675 return true;
676 }
677 return false;
678 }
679
680 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
681 if (result == BluetoothDevice.BOND_SUCCESS) {
682 setBondState(address, BluetoothDevice.BOND_BONDED);
683 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
684 mBondState.clearPinAttempts(address);
685 }
686 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
687 mBondState.getAttempt(address) == 1) {
688 mBondState.addAutoPairingFailure(address);
689 pairingAttempt(address, result);
690 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
691 mBondState.isAutoPairingAttemptsInProgress(address)) {
692 pairingAttempt(address, result);
693 } else {
694 setBondState(address, BluetoothDevice.BOND_NONE, result);
695 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
696 mBondState.clearPinAttempts(address);
697 }
698 }
699 }
700
701 /*package*/ synchronized String getPendingOutgoingBonding() {
702 return mBondState.getPendingOutgoingBonding();
703 }
704
705 private void pairingAttempt(String address, int result) {
706 // This happens when our initial guess of "0000" as the pass key
707 // fails. Try to create the bond again and display the pin dialog
708 // to the user. Use back-off while posting the delayed
709 // message. The initial value is
710 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
711 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
712 // reached, display an error to the user.
713 int attempt = mBondState.getAttempt(address);
714 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
715 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
716 mBondState.clearPinAttempts(address);
717 setBondState(address, BluetoothDevice.BOND_NONE, result);
718 return;
719 }
720
721 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
722 message.obj = address;
723 boolean postResult = mHandler.sendMessageDelayed(message,
724 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
725 if (!postResult) {
726 mBondState.clearPinAttempts(address);
727 setBondState(address,
728 BluetoothDevice.BOND_NONE, result);
729 return;
730 }
731 mBondState.attempt(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 }
733
734 /** local cache of bonding state.
735 /* we keep our own state to track the intermediate state BONDING, which
736 /* bluez does not track.
Jake Hambyf51eada2010-09-21 13:39:53 -0700737 * All addresses must be passed in upper case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 */
739 public class BondState {
740 private final HashMap<String, Integer> mState = new HashMap<String, Integer>();
741 private final HashMap<String, Integer> mPinAttempt = new HashMap<String, Integer>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800743 private static final String AUTO_PAIRING_BLACKLIST =
744 "/etc/bluetooth/auto_pairing.conf";
745 private static final String DYNAMIC_AUTO_PAIRING_BLACKLIST =
746 "/data/misc/bluetooth/dynamic_auto_pairing.conf";
747 private ArrayList<String> mAutoPairingAddressBlacklist;
748 private ArrayList<String> mAutoPairingExactNameBlacklist;
749 private ArrayList<String> mAutoPairingPartialNameBlacklist;
750 // Addresses added to blacklist dynamically based on usage.
751 private ArrayList<String> mAutoPairingDynamicAddressBlacklist;
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800752
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700753
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700754 // If this is an outgoing connection, store the address.
755 // There can be only 1 pending outgoing connection at a time,
756 private String mPendingOutgoingBonding;
757
758 private synchronized void setPendingOutgoingBonding(String address) {
759 mPendingOutgoingBonding = address;
760 }
761
762 public synchronized String getPendingOutgoingBonding() {
763 return mPendingOutgoingBonding;
764 }
765
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 public synchronized void loadBondState() {
Nick Pellyde893f52009-09-08 13:15:33 -0700767 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_ON) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 return;
769 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700770 String []bonds = null;
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800771 String val = getPropertyInternal("Devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700772 if (val != null) {
773 bonds = val.split(",");
774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 if (bonds == null) {
776 return;
777 }
778 mState.clear();
779 if (DBG) log("found " + bonds.length + " bonded devices");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700780 for (String device : bonds) {
781 mState.put(getAddressFromObjectPath(device).toUpperCase(),
782 BluetoothDevice.BOND_BONDED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 }
784 }
785
786 public synchronized void setBondState(String address, int state) {
787 setBondState(address, state, 0);
788 }
789
790 /** reason is ignored unless state == BOND_NOT_BONDED */
791 public synchronized void setBondState(String address, int state, int reason) {
792 int oldState = getBondState(address);
793 if (oldState == state) {
794 return;
795 }
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700796
797 // Check if this was an pending outgoing bonding.
798 // If yes, reset the state.
799 if (oldState == BluetoothDevice.BOND_BONDING) {
800 if (address.equals(mPendingOutgoingBonding)) {
801 mPendingOutgoingBonding = null;
802 }
803 }
804
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700805 if (state == BluetoothDevice.BOND_BONDED) {
806 addProfileState(address);
807 } else if (state == BluetoothDevice.BOND_NONE) {
808 removeProfileState(address);
809 }
810
Jaikumar Ganeshc3ee99d2010-07-19 14:59:44 -0700811 // HID is handled by BluetoothService, other profiles
812 // will be handled by their respective services.
813 setInitialInputDevicePriority(mAdapter.getRemoteDevice(address), state);
814
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 if (DBG) log(address + " bond state " + oldState + " -> " + state + " (" +
816 reason + ")");
Nick Pelly005b2282009-09-10 10:21:56 -0700817 Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
818 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
819 intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, state);
820 intent.putExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, oldState);
821 if (state == BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 if (reason <= 0) {
823 Log.w(TAG, "setBondState() called to unbond device, but reason code is " +
824 "invalid. Overriding reason code with BOND_RESULT_REMOVED");
825 reason = BluetoothDevice.UNBOND_REASON_REMOVED;
826 }
Nick Pelly005b2282009-09-10 10:21:56 -0700827 intent.putExtra(BluetoothDevice.EXTRA_REASON, reason);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 mState.remove(address);
829 } else {
830 mState.put(address, state);
831 }
832
833 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
834 }
835
836 public boolean isAutoPairingBlacklisted(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800837 if (mAutoPairingAddressBlacklist != null) {
838 for (String blacklistAddress : mAutoPairingAddressBlacklist) {
839 if (address.startsWith(blacklistAddress)) return true;
840 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700842
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800843 if (mAutoPairingDynamicAddressBlacklist != null) {
844 for (String blacklistAddress: mAutoPairingDynamicAddressBlacklist) {
845 if (address.equals(blacklistAddress)) return true;
846 }
847 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700848 String name = getRemoteName(address);
849 if (name != null) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800850 if (mAutoPairingExactNameBlacklist != null) {
851 for (String blacklistName : mAutoPairingExactNameBlacklist) {
852 if (name.equals(blacklistName)) return true;
853 }
Jaikumar Ganesh738ed802009-11-11 23:12:04 -0800854 }
855
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800856 if (mAutoPairingPartialNameBlacklist != null) {
857 for (String blacklistName : mAutoPairingPartialNameBlacklist) {
858 if (name.startsWith(blacklistName)) return true;
859 }
Jaikumar Ganesh482d54b2009-09-14 13:43:09 -0700860 }
861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 return false;
863 }
864
865 public synchronized int getBondState(String address) {
866 Integer state = mState.get(address);
867 if (state == null) {
Nick Pelly005b2282009-09-10 10:21:56 -0700868 return BluetoothDevice.BOND_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 }
870 return state.intValue();
871 }
872
Jaikumar Ganeshe5d93b72009-10-08 02:27:52 -0700873 /*package*/ synchronized String[] listInState(int state) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 ArrayList<String> result = new ArrayList<String>(mState.size());
875 for (Map.Entry<String, Integer> e : mState.entrySet()) {
876 if (e.getValue().intValue() == state) {
877 result.add(e.getKey());
878 }
879 }
880 return result.toArray(new String[result.size()]);
881 }
882
883 public synchronized void addAutoPairingFailure(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800884 if (mAutoPairingDynamicAddressBlacklist == null) {
885 mAutoPairingDynamicAddressBlacklist = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 }
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800887
888 updateAutoPairingData(address);
889 mAutoPairingDynamicAddressBlacklist.add(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 }
891
892 public synchronized boolean isAutoPairingAttemptsInProgress(String address) {
893 return getAttempt(address) != 0;
894 }
895
896 public synchronized void clearPinAttempts(String address) {
897 mPinAttempt.remove(address);
898 }
899
900 public synchronized boolean hasAutoPairingFailed(String address) {
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800901 if (mAutoPairingDynamicAddressBlacklist == null) return false;
902
903 return mAutoPairingDynamicAddressBlacklist.contains(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 }
905
906 public synchronized int getAttempt(String address) {
907 Integer attempt = mPinAttempt.get(address);
908 if (attempt == null) {
909 return 0;
910 }
911 return attempt.intValue();
912 }
913
914 public synchronized void attempt(String address) {
915 Integer attempt = mPinAttempt.get(address);
916 int newAttempt;
917 if (attempt == null) {
918 newAttempt = 1;
919 } else {
920 newAttempt = attempt.intValue() + 1;
921 }
922 mPinAttempt.put(address, new Integer(newAttempt));
923 }
924
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800925 private void copyAutoPairingData() {
926 File file = null;
927 FileInputStream in = null;
928 FileOutputStream out = null;
929 try {
930 file = new File(DYNAMIC_AUTO_PAIRING_BLACKLIST);
931 if (file.exists()) return;
932
933 in = new FileInputStream(AUTO_PAIRING_BLACKLIST);
934 out= new FileOutputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
935
936 byte[] buf = new byte[1024];
937 int len;
938 while ((len = in.read(buf)) > 0) {
939 out.write(buf, 0, len);
940 }
941 } catch (FileNotFoundException e) {
942 log("FileNotFoundException: in copyAutoPairingData");
943 } catch (IOException e) {
944 log("IOException: in copyAutoPairingData");
945 } finally {
946 try {
947 if (in != null) in.close();
948 if (out != null) out.close();
949 } catch (IOException e) {}
950 }
951 }
952
953 public void readAutoPairingData() {
954 if (mAutoPairingAddressBlacklist != null) return;
955 copyAutoPairingData();
956 FileInputStream fstream = null;
957 try {
958 fstream = new FileInputStream(DYNAMIC_AUTO_PAIRING_BLACKLIST);
959 DataInputStream in = new DataInputStream(fstream);
960 BufferedReader file = new BufferedReader(new InputStreamReader(in));
961 String line;
962 while((line = file.readLine()) != null) {
963 line = line.trim();
964 if (line.length() == 0 || line.startsWith("//")) continue;
965 String[] value = line.split("=");
966 if (value != null && value.length == 2) {
967 String[] val = value[1].split(",");
968 if (value[0].equalsIgnoreCase("AddressBlacklist")) {
969 mAutoPairingAddressBlacklist =
970 new ArrayList<String>(Arrays.asList(val));
971 } else if (value[0].equalsIgnoreCase("ExactNameBlacklist")) {
972 mAutoPairingExactNameBlacklist =
973 new ArrayList<String>(Arrays.asList(val));
974 } else if (value[0].equalsIgnoreCase("PartialNameBlacklist")) {
975 mAutoPairingPartialNameBlacklist =
976 new ArrayList<String>(Arrays.asList(val));
977 } else if (value[0].equalsIgnoreCase("DynamicAddressBlacklist")) {
978 mAutoPairingDynamicAddressBlacklist =
979 new ArrayList<String>(Arrays.asList(val));
980 } else {
981 Log.e(TAG, "Error parsing Auto pairing blacklist file");
982 }
983 }
984 }
985 } catch (FileNotFoundException e) {
986 log("FileNotFoundException: readAutoPairingData" + e.toString());
987 } catch (IOException e) {
988 log("IOException: readAutoPairingData" + e.toString());
989 } finally {
990 if (fstream != null) {
991 try {
992 fstream.close();
993 } catch (IOException e) {
994 // Ignore
995 }
996 }
997 }
998 }
999
Jake Hambyf51eada2010-09-21 13:39:53 -07001000 // This function adds a bluetooth address to the auto pairing blacklist
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -08001001 // file. These addresses are added to DynamicAddressBlacklistSection
1002 private void updateAutoPairingData(String address) {
1003 BufferedWriter out = null;
1004 try {
1005 out = new BufferedWriter(new FileWriter(DYNAMIC_AUTO_PAIRING_BLACKLIST, true));
1006 StringBuilder str = new StringBuilder();
1007 if (mAutoPairingDynamicAddressBlacklist.size() == 0) {
1008 str.append("DynamicAddressBlacklist=");
1009 }
1010 str.append(address);
1011 str.append(",");
1012 out.write(str.toString());
1013 } catch (FileNotFoundException e) {
1014 log("FileNotFoundException: updateAutoPairingData" + e.toString());
1015 } catch (IOException e) {
1016 log("IOException: updateAutoPairingData" + e.toString());
1017 } finally {
1018 if (out != null) {
1019 try {
1020 out.close();
1021 } catch (IOException e) {
1022 // Ignore
1023 }
1024 }
1025 }
1026 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028
1029 private static String toBondStateString(int bondState) {
1030 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -07001031 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 return "not bonded";
1033 case BluetoothDevice.BOND_BONDING:
1034 return "bonding";
1035 case BluetoothDevice.BOND_BONDED:
1036 return "bonded";
1037 default:
1038 return "??????";
1039 }
1040 }
1041
Jaikumar Ganesh9519ce72009-09-08 21:37:32 -07001042 /*package*/ synchronized boolean isAdapterPropertiesEmpty() {
1043 return mAdapterProperties.isEmpty();
1044 }
1045
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001046 /*package*/synchronized void getAllProperties() {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001047
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pellybd022f42009-08-14 18:33:38 -07001049 mAdapterProperties.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001051 String properties[] = (String [])getAdapterPropertiesNative();
1052 // The String Array consists of key-value pairs.
1053 if (properties == null) {
1054 Log.e(TAG, "*Error*: GetAdapterProperties returned NULL");
1055 return;
1056 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001057
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001058 for (int i = 0; i < properties.length; i++) {
1059 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001060 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001061 int len;
1062 if (name == null) {
1063 Log.e(TAG, "Error:Adapter Property at index" + i + "is null");
1064 continue;
1065 }
Danica Chang7a9de8b2010-07-16 18:38:29 -07001066 if (name.equals("Devices") || name.equals("UUIDs")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001067 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001068 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001069 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001070 str.append(properties[++i]);
1071 str.append(",");
1072 }
1073 if (len > 0) {
1074 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001075 }
1076 } else {
1077 newValue = properties[++i];
1078 }
Nick Pellybd022f42009-08-14 18:33:38 -07001079 mAdapterProperties.put(name, newValue);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001080 }
1081
1082 // Add adapter object path property.
1083 String adapterPath = getAdapterPathNative();
1084 if (adapterPath != null)
Nick Pellybd022f42009-08-14 18:33:38 -07001085 mAdapterProperties.put("ObjectPath", adapterPath + "/dev_");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001087
1088 /* package */ synchronized void setProperty(String name, String value) {
Nick Pellybd022f42009-08-14 18:33:38 -07001089 mAdapterProperties.put(name, value);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001090 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091
1092 public synchronized boolean setName(String name) {
1093 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1094 "Need BLUETOOTH_ADMIN permission");
1095 if (name == null) {
1096 return false;
1097 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001098 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001101 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
1102 // Either have a single property function with Object as the parameter
1103 // or have a function for each property and then obfuscate in the JNI layer.
1104 // The following looks dirty.
1105 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001106 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001107 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001108 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001111 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001113 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001114 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 }
1116
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001117 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001119 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001120 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122
1123 /**
1124 * Set the discoverability window for the device. A timeout of zero
1125 * makes the device permanently discoverable (if the device is
1126 * discoverable). Setting the timeout to a nonzero value does not make
1127 * a device discoverable; you need to call setMode() to make the device
1128 * explicitly discoverable.
1129 *
Jake Hambyf51eada2010-09-21 13:39:53 -07001130 * @param timeout The discoverable timeout in seconds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 */
1132 public synchronized boolean setDiscoverableTimeout(int timeout) {
1133 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1134 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001135 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001137
Nick Pelly12835472009-09-25 15:00:29 -07001138 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -07001139 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
1140 "Need WRITE_SECURE_SETTINGS permission");
Nick Pellyde893f52009-09-08 13:15:33 -07001141 boolean pairable = false;
1142 boolean discoverable = false;
Nick Pelly12835472009-09-25 15:00:29 -07001143
Nick Pellyde893f52009-09-08 13:15:33 -07001144 switch (mode) {
1145 case BluetoothAdapter.SCAN_MODE_NONE:
Nick Pelly12835472009-09-25 15:00:29 -07001146 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001147 pairable = false;
1148 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -07001149 break;
1150 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001151 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001152 pairable = true;
1153 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -07001154 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001155 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Nick Pelly12835472009-09-25 15:00:29 -07001156 mHandler.removeMessages(MESSAGE_DISCOVERABLE_TIMEOUT);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001157 pairable = true;
1158 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -07001159 Message msg = mHandler.obtainMessage(MESSAGE_DISCOVERABLE_TIMEOUT);
1160 mHandler.sendMessageDelayed(msg, duration * 1000);
1161 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -07001162 break;
Nick Pellyde893f52009-09-08 13:15:33 -07001163 default:
1164 Log.w(TAG, "Requested invalid scan mode " + mode);
1165 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001166 }
1167 setPropertyBoolean("Pairable", pairable);
1168 setPropertyBoolean("Discoverable", discoverable);
1169
1170 return true;
1171 }
1172
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08001173 /*package*/ synchronized String getProperty(String name) {
1174 if (!isEnabledInternal()) return null;
1175 return getPropertyInternal(name);
1176 }
1177
1178 /*package*/ synchronized String getPropertyInternal(String name) {
Nick Pellybd022f42009-08-14 18:33:38 -07001179 if (!mAdapterProperties.isEmpty())
1180 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001181 getAllProperties();
Nick Pellybd022f42009-08-14 18:33:38 -07001182 return mAdapterProperties.get(name);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001183 }
1184
1185 public synchronized String getAddress() {
1186 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1187 return getProperty("Address");
1188 }
1189
1190 public synchronized String getName() {
1191 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1192 return getProperty("Name");
1193 }
1194
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -08001195 public synchronized ParcelUuid[] getUuids() {
1196 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1197 String value = getProperty("UUIDs");
1198 if (value == null) return null;
1199
1200 String[] uuidStrings = null;
1201 // The UUIDs are stored as a "," separated string.
1202 uuidStrings = value.split(",");
1203 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1204
1205 for (int i = 0; i < uuidStrings.length; i++) {
1206 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1207 }
1208 return uuids;
1209
1210 }
1211
1212
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001213 /**
1214 * Returns the user-friendly name of a remote device. This value is
1215 * returned from our local cache, which is updated when onPropertyChange
1216 * event is received.
1217 * Do not expect to retrieve the updated remote name immediately after
1218 * changing the name on the remote device.
1219 *
1220 * @param address Bluetooth address of remote device.
1221 *
1222 * @return The user-friendly name of the specified remote device.
1223 */
1224 public synchronized String getRemoteName(String address) {
1225 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001226 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001227 return null;
1228 }
Jaikumar Ganesh55929a92009-09-30 10:49:34 -07001229 return getRemoteDeviceProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231
1232 /**
1233 * Get the discoverability window for the device. A timeout of zero
1234 * means that the device is permanently discoverable (if the device is
1235 * in the discoverable mode).
1236 *
1237 * @return The discoverability window of the device, in seconds. A negative
1238 * value indicates an error.
1239 */
1240 public synchronized int getDiscoverableTimeout() {
1241 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001242 String timeout = getProperty("DiscoverableTimeout");
1243 if (timeout != null)
1244 return Integer.valueOf(timeout);
1245 else
1246 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248
1249 public synchronized int getScanMode() {
1250 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001251 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -07001252 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001254 boolean pairable = getProperty("Pairable").equals("true");
1255 boolean discoverable = getProperty("Discoverable").equals("true");
1256 return bluezStringToScanMode (pairable, discoverable);
1257 }
1258
1259 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1261 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001262 if (!isEnabledInternal()) return false;
1263
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001264 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001266
1267 public synchronized boolean cancelDiscovery() {
1268 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1269 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001270 if (!isEnabledInternal()) return false;
1271
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001272 return stopDiscoveryNative();
1273 }
1274
1275 public synchronized boolean isDiscovering() {
1276 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1277 return mIsDiscovering;
1278 }
1279
1280 /* package */ void setIsDiscovering(boolean isDiscovering) {
1281 mIsDiscovering = isDiscovering;
1282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001284 private boolean isBondingFeasible(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1286 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001287 if (!isEnabledInternal()) return false;
1288
Nick Pelly005b2282009-09-10 10:21:56 -07001289 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 return false;
1291 }
1292 address = address.toUpperCase();
1293
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001294 if (mBondState.getPendingOutgoingBonding() != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 log("Ignoring createBond(): another device is bonding");
1296 // a different device is currently bonding, fail
1297 return false;
1298 }
1299
1300 // Check for bond state only if we are not performing auto
1301 // pairing exponential back-off attempts.
1302 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -07001303 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 log("Ignoring createBond(): this device is already bonding or bonded");
1305 return false;
1306 }
1307
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001308 if (address.equals(mDockAddress)) {
1309 if (!writeDockPin()) {
1310 log("Error while writing Pin for the dock");
1311 return false;
1312 }
1313 }
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001314 return true;
1315 }
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001316
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001317 public synchronized boolean createBond(String address) {
1318 if (!isBondingFeasible(address)) return false;
1319
1320 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 return false;
1322 }
1323
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001324 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 return true;
1328 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001330 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
1331 byte[] randomizer) {
1332 if (!isBondingFeasible(address)) return false;
1333
1334 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1335 return false;
1336 }
1337
1338 setDeviceOutOfBandData(address, hash, randomizer);
1339 mBondState.setPendingOutgoingBonding(address);
1340 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1341
1342 return true;
1343 }
1344
1345 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1346 byte[] randomizer) {
1347 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1348 "Need BLUETOOTH_ADMIN permission");
1349 if (!isEnabledInternal()) return false;
1350
1351 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1352
1353 if (DBG) {
1354 log("Setting out of band data for:" + address + ":" +
1355 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
1356 }
1357
1358 mDeviceOobData.put(address, value);
1359 return true;
1360 }
1361
1362 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1363 return mDeviceOobData.get(device.getAddress());
1364 }
1365
1366
1367 public synchronized byte[] readOutOfBandData() {
1368 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1369 "Need BLUETOOTH permission");
1370 if (!isEnabledInternal()) return null;
1371
1372 return readAdapterOutOfBandDataNative();
1373 }
1374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 public synchronized boolean cancelBondProcess(String address) {
1376 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1377 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001378 if (!isEnabledInternal()) return false;
1379
Nick Pelly005b2282009-09-10 10:21:56 -07001380 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 return false;
1382 }
1383 address = address.toUpperCase();
1384 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1385 return false;
1386 }
1387
Nick Pelly005b2282009-09-10 10:21:56 -07001388 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001390 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 return true;
1392 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393
1394 public synchronized boolean removeBond(String address) {
1395 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1396 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001397 if (!isEnabledInternal()) return false;
1398
Nick Pelly005b2282009-09-10 10:21:56 -07001399 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 return false;
1401 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001402 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001403 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001404 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001405 return true;
1406 } else {
1407 return false;
1408 }
1409 }
1410
1411 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001412 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414
1415 public synchronized String[] listBonds() {
1416 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1417 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1418 }
1419
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001420 /*package*/ synchronized String[] listInState(int state) {
1421 return mBondState.listInState(state);
1422 }
1423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 public synchronized int getBondState(String address) {
1425 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001426 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001427 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 }
1429 return mBondState.getBondState(address.toUpperCase());
1430 }
1431
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001432 /*package*/ synchronized boolean setBondState(String address, int state) {
1433 return setBondState(address, state, 0);
1434 }
1435
1436 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
1437 mBondState.setBondState(address.toUpperCase(), state);
1438 return true;
1439 }
1440
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001441 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001442 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1443 mContext.MODE_PRIVATE);
1444
1445 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001446 }
1447
Danica Chang6fdd0c62010-08-11 14:54:43 -07001448 public synchronized boolean isTetheringOn() {
1449 return mTetheringOn;
1450 }
1451
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001452 /*package*/ synchronized boolean allowIncomingTethering() {
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001453 if (isTetheringOn() && getConnectedPanDevices().size() < mMaxPanDevices)
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001454 return true;
1455 return false;
1456 }
1457
Danica Chang8aac82a2010-08-20 14:22:07 -07001458 private BroadcastReceiver mTetheringReceiver = null;
1459
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001460 public synchronized void setBluetoothTethering(boolean value) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001461 if (!value) {
1462 disconnectPan();
1463 }
Danica Chang8aac82a2010-08-20 14:22:07 -07001464
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001465 if (getBluetoothState() != BluetoothAdapter.STATE_ON && value) {
Danica Chang8aac82a2010-08-20 14:22:07 -07001466 IntentFilter filter = new IntentFilter();
1467 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
1468 mTetheringReceiver = new BroadcastReceiver() {
1469 @Override
1470 public synchronized void onReceive(Context context, Intent intent) {
1471 if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
1472 == BluetoothAdapter.STATE_ON) {
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001473 mTetheringOn = true;
Danica Chang8aac82a2010-08-20 14:22:07 -07001474 mContext.unregisterReceiver(mTetheringReceiver);
1475 }
1476 }
1477 };
1478 mContext.registerReceiver(mTetheringReceiver, filter);
1479 } else {
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -07001480 mTetheringOn = value;
Danica Chang8aac82a2010-08-20 14:22:07 -07001481 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001482 }
1483
1484 public synchronized int getPanDeviceState(BluetoothDevice device) {
1485 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1486
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001487 Pair<Integer, String> panDevice = mPanDevices.get(device);
1488 if (panDevice == null) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001489 return BluetoothPan.STATE_DISCONNECTED;
1490 }
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001491 return panDevice.first;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001492 }
1493
1494 public synchronized boolean connectPanDevice(BluetoothDevice device) {
1495 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1496 "Need BLUETOOTH_ADMIN permission");
1497
1498 String objectPath = getObjectPathFromAddress(device.getAddress());
1499 if (DBG) log("connect PAN(" + objectPath + ")");
1500 if (getPanDeviceState(device) != BluetoothPan.STATE_DISCONNECTED) {
1501 log (device + " already connected to PAN");
1502 }
1503
1504 int connectedCount = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001505 for (BluetoothDevice panDevice: mPanDevices.keySet()) {
1506 if (getPanDeviceState(panDevice) == BluetoothPan.STATE_CONNECTED) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001507 connectedCount ++;
1508 }
1509 }
1510 if (connectedCount > 8) {
1511 log (device + " could not connect to PAN because 8 other devices are already connected");
1512 return false;
1513 }
1514
1515 handlePanDeviceStateChange(device, BluetoothPan.STATE_CONNECTING);
1516 if (connectPanDeviceNative(objectPath, "nap", "panu")) {
1517 log ("connecting to PAN");
1518 return true;
1519 } else {
1520 handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTED);
1521 log ("could not connect to PAN");
1522 return false;
1523 }
1524 }
1525
1526 private synchronized boolean disconnectPan() {
1527 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1528 if (DBG) log("disconnect all PAN devices");
1529
1530 for (BluetoothDevice device: mPanDevices.keySet()) {
1531 if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
1532 if (!disconnectPanDevice(device)) {
1533 log ("could not disconnect Pan Device "+device.getAddress());
1534 return false;
1535 }
1536 }
1537 }
1538 return true;
1539 }
1540
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001541 public synchronized List<BluetoothDevice> getConnectedPanDevices() {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001542 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1543
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001544 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
1545
Danica Chang6fdd0c62010-08-11 14:54:43 -07001546 for (BluetoothDevice device: mPanDevices.keySet()) {
1547 if (getPanDeviceState(device) == BluetoothPan.STATE_CONNECTED) {
1548 devices.add(device);
1549 }
1550 }
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001551 return devices;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001552 }
1553
1554 public synchronized boolean disconnectPanDevice(BluetoothDevice device) {
1555 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1556 "Need BLUETOOTH_ADMIN permission");
1557 String objectPath = getObjectPathFromAddress(device.getAddress());
1558 if (DBG) log("disconnect PAN(" + objectPath + ")");
1559 if (getPanDeviceState(device) != BluetoothPan.STATE_CONNECTED) {
1560 log (device + " already disconnected from PAN");
1561 }
1562 handlePanDeviceStateChange(device, BluetoothPan.STATE_DISCONNECTING);
1563 return disconnectPanDeviceNative(objectPath);
1564 }
1565
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001566 /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
1567 String iface,
1568 int state) {
Danica Chang6fdd0c62010-08-11 14:54:43 -07001569 int prevState;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001570 String ifaceAddr = null;
1571
Danica Chang6fdd0c62010-08-11 14:54:43 -07001572 if (mPanDevices.get(device) == null) {
1573 prevState = BluetoothPan.STATE_DISCONNECTED;
1574 } else {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001575 prevState = mPanDevices.get(device).first;
1576 ifaceAddr = mPanDevices.get(device).second;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001577 }
1578 if (prevState == state) return;
1579
Danica Chang6fdd0c62010-08-11 14:54:43 -07001580 if (state == BluetoothPan.STATE_CONNECTED) {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001581 ifaceAddr = enableTethering(iface);
1582 if (ifaceAddr == null) Log.e(TAG, "Error seting up tether interface");
1583 } else if (state == BluetoothPan.STATE_DISCONNECTED) {
1584 if (ifaceAddr != null) {
1585 mBluetoothIfaceAddresses.remove(ifaceAddr);
1586 ifaceAddr = null;
1587 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001588 }
1589
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001590 Pair<Integer, String> value = new Pair<Integer, String>(state, ifaceAddr);
1591 mPanDevices.put(device, value);
1592
Danica Chang6fdd0c62010-08-11 14:54:43 -07001593 Intent intent = new Intent(BluetoothPan.ACTION_PAN_STATE_CHANGED);
1594 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1595 intent.putExtra(BluetoothPan.EXTRA_PREVIOUS_PAN_STATE, prevState);
1596 intent.putExtra(BluetoothPan.EXTRA_PAN_STATE, state);
1597 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1598
1599 if (DBG) log("Pan Device state : device: " + device + " State:" + prevState + "->" + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07001600 sendConnectionStateChange(device, state, prevState);
Danica Chang6fdd0c62010-08-11 14:54:43 -07001601 }
1602
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001603 /*package*/ synchronized void handlePanDeviceStateChange(BluetoothDevice device,
1604 int state) {
1605 handlePanDeviceStateChange(device, null, state);
1606 }
1607
1608 private String createNewTetheringAddressLocked() {
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001609 if (getConnectedPanDevices().size() == mMaxPanDevices) {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001610 log("Max PAN device connections reached");
1611 return null;
1612 }
1613 String address = BLUETOOTH_IFACE_ADDR_START;
1614 while (true) {
1615 if (mBluetoothIfaceAddresses.contains(address)) {
1616 String[] addr = address.split("\\.");
1617 Integer newIp = Integer.parseInt(addr[2]) + 1;
1618 address = address.replace(addr[2], newIp.toString());
1619 } else {
1620 break;
1621 }
1622 }
1623 mBluetoothIfaceAddresses.add(address);
1624 return address;
1625 }
1626
1627 // configured when we start tethering
1628 private synchronized String enableTethering(String iface) {
1629 log("updateTetherState:" + iface);
Danica Chang6fdd0c62010-08-11 14:54:43 -07001630
1631 IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
1632 INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
1633 ConnectivityManager cm =
1634 (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
1635 String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
1636
1637 // bring toggle the interfaces
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001638 String[] currentIfaces = new String[0];
Danica Chang6fdd0c62010-08-11 14:54:43 -07001639 try {
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001640 currentIfaces = service.listInterfaces();
Danica Chang6fdd0c62010-08-11 14:54:43 -07001641 } catch (Exception e) {
1642 Log.e(TAG, "Error listing Interfaces :" + e);
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001643 return null;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001644 }
1645
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001646 boolean found = false;
1647 for (String currIface: currentIfaces) {
1648 if (currIface.equals(iface)) {
1649 found = true;
1650 break;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001651 }
1652 }
1653
Jaikumar Ganesh707952e2010-09-13 19:04:54 -07001654 if (!found) return null;
1655
1656 String address = createNewTetheringAddressLocked();
1657 if (address == null) return null;
1658
1659 InterfaceConfiguration ifcg = null;
1660 try {
1661 ifcg = service.getInterfaceConfig(iface);
1662 if (ifcg != null) {
1663 String[] addr = BLUETOOTH_NETMASK.split("\\.");
1664 ifcg.netmask = (Integer.parseInt(addr[0]) << 24) +
1665 (Integer.parseInt(addr[1]) << 16) +
1666 (Integer.parseInt(addr[2]) << 8) +
1667 (Integer.parseInt(addr[3]));
1668 if (ifcg.ipAddr == 0) {
1669 addr = address.split("\\.");
1670
1671 ifcg.ipAddr = (Integer.parseInt(addr[0]) << 24) +
1672 (Integer.parseInt(addr[1]) << 16) +
1673 (Integer.parseInt(addr[2]) << 8) +
1674 (Integer.parseInt(addr[3]));
1675 ifcg.interfaceFlags =
1676 ifcg.interfaceFlags.replace("down", "up");
1677 }
1678 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", "");
1679 ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," ");
1680 service.setInterfaceConfig(iface, ifcg);
1681 if (cm.tether(iface) != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
1682 Log.e(TAG, "Error tethering "+iface);
1683 }
1684 }
1685 } catch (Exception e) {
1686 Log.e(TAG, "Error configuring interface " + iface + ", :" + e);
1687 return null;
1688 }
1689 return address;
Danica Chang6fdd0c62010-08-11 14:54:43 -07001690 }
1691
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001692 public synchronized boolean connectInputDevice(BluetoothDevice device) {
1693 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1694 "Need BLUETOOTH_ADMIN permission");
1695
1696 String objectPath = getObjectPathFromAddress(device.getAddress());
Jaikumar Ganesh05a18632010-07-21 12:08:12 -07001697 if (objectPath == null ||
1698 getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED ||
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001699 getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) {
1700 return false;
1701 }
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001702 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
1703 if (state != null) {
1704 Message msg = new Message();
1705 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING;
1706 msg.obj = state;
1707 mHidProfileState.sendMessage(msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001708 return true;
1709 }
1710 return false;
1711 }
1712
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001713 public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) {
1714 String objectPath = getObjectPathFromAddress(device.getAddress());
1715 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING);
1716 if (!connectInputDeviceNative(objectPath)) {
1717 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED);
1718 return false;
1719 }
1720 return true;
1721 }
1722
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001723 public synchronized boolean disconnectInputDevice(BluetoothDevice device) {
1724 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1725 "Need BLUETOOTH_ADMIN permission");
1726
1727 String objectPath = getObjectPathFromAddress(device.getAddress());
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001728 if (objectPath == null || getConnectedInputDevices().size() == 0) {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001729 return false;
1730 }
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001731 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
1732 if (state != null) {
1733 Message msg = new Message();
1734 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING;
1735 msg.obj = state;
1736 mHidProfileState.sendMessage(msg);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001737 return true;
1738 }
1739 return false;
1740 }
1741
Jaikumar Ganeshde075032010-07-19 16:28:27 -07001742 public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) {
1743 String objectPath = getObjectPathFromAddress(device.getAddress());
1744 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING);
1745 if (!disconnectInputDeviceNative(objectPath)) {
1746 handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED);
1747 return false;
1748 }
1749 return true;
1750 }
1751
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001752 public synchronized int getInputDeviceState(BluetoothDevice device) {
1753 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1754
1755 if (mInputDevices.get(device) == null) {
1756 return BluetoothInputDevice.STATE_DISCONNECTED;
1757 }
Adam Powelldf7627d2010-06-21 16:23:42 -07001758 return mInputDevices.get(device);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001759 }
1760
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001761 public synchronized List<BluetoothDevice> getConnectedInputDevices() {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001762 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001763 List<BluetoothDevice> devices = lookupInputDevicesMatchingStates(
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001764 new int[] {BluetoothInputDevice.STATE_CONNECTED});
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001765 return devices;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001766 }
1767
1768 public synchronized int getInputDevicePriority(BluetoothDevice device) {
1769 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1770 return Settings.Secure.getInt(mContext.getContentResolver(),
1771 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1772 BluetoothInputDevice.PRIORITY_UNDEFINED);
1773 }
1774
1775 public synchronized boolean setInputDevicePriority(BluetoothDevice device, int priority) {
1776 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1777 "Need BLUETOOTH_ADMIN permission");
1778 if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
1779 return false;
1780 }
1781 return Settings.Secure.putInt(mContext.getContentResolver(),
1782 Settings.Secure.getBluetoothInputDevicePriorityKey(device.getAddress()),
1783 priority);
1784 }
1785
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001786 /*package*/synchronized List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
1787 List<BluetoothDevice> inputDevices = new ArrayList<BluetoothDevice>();
1788
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001789 for (BluetoothDevice device: mInputDevices.keySet()) {
1790 int inputDeviceState = getInputDeviceState(device);
1791 for (int state : states) {
1792 if (state == inputDeviceState) {
1793 inputDevices.add(device);
1794 break;
1795 }
1796 }
1797 }
1798 return inputDevices;
1799 }
1800
1801 private synchronized void handleInputDeviceStateChange(BluetoothDevice device, int state) {
1802 int prevState;
1803 if (mInputDevices.get(device) == null) {
1804 prevState = BluetoothInputDevice.STATE_DISCONNECTED;
1805 } else {
1806 prevState = mInputDevices.get(device);
1807 }
1808 if (prevState == state) return;
1809
1810 mInputDevices.put(device, state);
1811
1812 if (getInputDevicePriority(device) >
1813 BluetoothInputDevice.PRIORITY_OFF &&
1814 state == BluetoothInputDevice.STATE_CONNECTING ||
1815 state == BluetoothInputDevice.STATE_CONNECTED) {
1816 // We have connected or attempting to connect.
1817 // Bump priority
1818 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_AUTO_CONNECT);
1819 }
1820
1821 Intent intent = new Intent(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED);
1822 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
1823 intent.putExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, prevState);
1824 intent.putExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, state);
1825 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
1826
1827 if (DBG) log("InputDevice state : device: " + device + " State:" + prevState + "->" + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07001828 sendConnectionStateChange(device, state, prevState);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001829 }
1830
Jaikumar Ganesh56d26132010-07-15 15:56:04 -07001831 /*package*/ void handleInputDevicePropertyChange(String address, boolean connected) {
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07001832 int state = connected ? BluetoothInputDevice.STATE_CONNECTED :
1833 BluetoothInputDevice.STATE_DISCONNECTED;
1834 BluetoothDevice device = mAdapter.getRemoteDevice(address);
1835 handleInputDeviceStateChange(device, state);
1836 }
1837
Jaikumar Ganeshc3ee99d2010-07-19 14:59:44 -07001838 private void setInitialInputDevicePriority(BluetoothDevice device, int state) {
1839 switch (state) {
1840 case BluetoothDevice.BOND_BONDED:
1841 if (getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_UNDEFINED) {
1842 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_ON);
1843 }
1844 break;
1845 case BluetoothDevice.BOND_NONE:
1846 setInputDevicePriority(device, BluetoothInputDevice.PRIORITY_UNDEFINED);
1847 break;
1848 }
1849 }
1850
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001851 /*package*/ boolean isRemoteDeviceInCache(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001852 return (mDeviceProperties.get(address) != null);
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001853 }
1854
1855 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001856 if (!isEnabledInternal()) return null;
1857
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001858 String objectPath = getObjectPathFromAddress(address);
1859 return (String [])getDevicePropertiesNative(objectPath);
1860 }
1861
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001862 /*package*/ synchronized String getRemoteDeviceProperty(String address, String property) {
Nick Pellybd022f42009-08-14 18:33:38 -07001863 Map<String, String> properties = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001864 if (properties != null) {
1865 return properties.get(property);
1866 } else {
1867 // Query for remote device properties, again.
1868 // We will need to reload the cache when we switch Bluetooth on / off
1869 // or if we crash.
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001870 if (updateRemoteDevicePropertiesCache(address))
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001871 return getRemoteDeviceProperty(address, property);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 }
Danica Chang6fdd0c62010-08-11 14:54:43 -07001873 Log.e(TAG, "getRemoteDeviceProperty: " + property + " not present: " + address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001874 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001875 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001876
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001877 /* package */ synchronized boolean updateRemoteDevicePropertiesCache(String address) {
1878 String[] propValues = getRemoteDeviceProperties(address);
1879 if (propValues != null) {
1880 addRemoteDeviceProperties(address, propValues);
1881 return true;
1882 }
1883 return false;
1884 }
1885
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001886 /* package */ synchronized void addRemoteDeviceProperties(String address, String[] properties) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001887 /*
1888 * We get a DeviceFound signal every time RSSI changes or name changes.
1889 * Don't create a new Map object every time */
Nick Pellybd022f42009-08-14 18:33:38 -07001890 Map<String, String> propertyValues = mDeviceProperties.get(address);
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001891 if (propertyValues == null) {
Jaikumar Ganesh395d1022009-06-19 16:12:31 -07001892 propertyValues = new HashMap<String, String>();
1893 }
1894
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001895 for (int i = 0; i < properties.length; i++) {
1896 String name = properties[i];
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001897 String newValue = null;
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001898 int len;
1899 if (name == null) {
1900 Log.e(TAG, "Error: Remote Device Property at index" + i + "is null");
1901 continue;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001902 }
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001903 if (name.equals("UUIDs") || name.equals("Nodes")) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001904 StringBuilder str = new StringBuilder();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001905 len = Integer.valueOf(properties[++i]);
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001906 for (int j = 0; j < len; j++) {
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001907 str.append(properties[++i]);
1908 str.append(",");
1909 }
1910 if (len > 0) {
1911 newValue = str.toString();
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001912 }
1913 } else {
1914 newValue = properties[++i];
1915 }
Jaikumar Ganeshefa33672009-08-28 13:48:55 -07001916
Jaikumar Ganesh8bc8ce42009-06-24 16:42:51 -07001917 propertyValues.put(name, newValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001918 }
Nick Pellybd022f42009-08-14 18:33:38 -07001919 mDeviceProperties.put(address, propertyValues);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001920
1921 // We have added a new remote device or updated its properties.
1922 // Also update the serviceChannel cache.
1923 updateDeviceServiceChannelCache(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001926 /* package */ void removeRemoteDeviceProperties(String address) {
Nick Pellybd022f42009-08-14 18:33:38 -07001927 mDeviceProperties.remove(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001928 }
1929
1930 /* package */ synchronized void setRemoteDeviceProperty(String address, String name,
1931 String value) {
Nick Pellybd022f42009-08-14 18:33:38 -07001932 Map <String, String> propVal = mDeviceProperties.get(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001933 if (propVal != null) {
1934 propVal.put(name, value);
Nick Pellybd022f42009-08-14 18:33:38 -07001935 mDeviceProperties.put(address, propVal);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001936 } else {
1937 Log.e(TAG, "setRemoteDeviceProperty for a device not in cache:" + address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001938 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940
1941 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001942 * Sets the remote device trust state.
1943 *
1944 * @return boolean to indicate operation success or fail
1945 */
1946 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001947 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001948 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1949 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001950 return false;
1951 }
1952
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001953 if (!isEnabledInternal()) return false;
1954
Lixin Yueefa1dd72009-08-31 15:55:13 +08001955 return setDevicePropertyBooleanNative(getObjectPathFromAddress(address), "Trusted",
1956 value ? 1 : 0);
1957 }
1958
1959 /**
1960 * Gets the remote device trust state as boolean.
1961 * Note: this value may be
1962 * retrieved from cache if we retrieved the data before *
1963 *
1964 * @return boolean to indicate trust or untrust state
1965 */
1966 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001967 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001968 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1969 return false;
1970 }
1971
1972 String val = getRemoteDeviceProperty(address, "Trusted");
1973 if (val == null) {
1974 return false;
1975 } else {
1976 return val.equals("true") ? true : false;
1977 }
1978 }
1979
1980 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001981 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 * integer.
1983 *
1984 * Note: this value is retrieved from cache, because we get it during
1985 * remote-device discovery.
1986 *
1987 * @return 32-bit integer encoding the remote major, minor, and service
1988 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001989 */
1990 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001991 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001992 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1993 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001995 String val = getRemoteDeviceProperty(address, "Class");
1996 if (val == null)
1997 return BluetoothClass.ERROR;
1998 else {
1999 return Integer.valueOf(val);
2000 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002002
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002003
2004 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002005 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002007 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002008 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002009 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07002011 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002012 return null;
2013 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002014 return getUuidFromCache(address);
2015 }
2016
2017 private ParcelUuid[] getUuidFromCache(String address) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002018 String value = getRemoteDeviceProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002019 if (value == null) return null;
2020
2021 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002022 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002023 uuidStrings = value.split(",");
2024 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
2025
2026 for (int i = 0; i < uuidStrings.length; i++) {
2027 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
2028 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002029 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002031
Nick Pelly16fb88a2009-10-07 07:44:03 +02002032 /**
2033 * Connect and fetch new UUID's using SDP.
2034 * The UUID's found are broadcast as intents.
2035 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
2036 * a given uuid.
2037 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
2038 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
2039 * callback and broadcast intents.
2040 */
2041 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
2042 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002043 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002044 if (!isEnabledInternal()) return false;
2045
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002046 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2047 return false;
2048 }
2049
Nick Pelly16fb88a2009-10-07 07:44:03 +02002050 RemoteService service = new RemoteService(address, uuid);
2051 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
2052 // An SDP query for this address & uuid is already in progress
2053 // Do not add this callback for the uuid
2054 return false;
2055 }
2056
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002057 if (mUuidIntentTracker.contains(address)) {
2058 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02002059 // Add this uuid onto the in-progress SDP query
2060 if (uuid != null) {
2061 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
2062 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002063 return true;
2064 }
2065
2066 boolean ret;
Jaikumar Ganesh0e090302010-03-29 00:01:34 -07002067 // Just do the SDP if the device is already created and UUIDs are not
2068 // NULL, else create the device and then do SDP.
2069 if (isRemoteDeviceInCache(address) && getRemoteUuids(address) != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002070 String path = getObjectPathFromAddress(address);
2071 if (path == null) return false;
2072
2073 // Use an empty string for the UUID pattern
2074 ret = discoverServicesNative(path, "");
2075 } else {
2076 ret = createDeviceNative(address);
2077 }
2078
2079 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002080 if (uuid != null) {
2081 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
2082 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002083
2084 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
2085 message.obj = address;
2086 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
2087 return ret;
2088 }
2089
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002090 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002091 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02002092 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002094 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002095 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002096 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002097 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002098 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002099 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07002100 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002102 if (!isEnabledInternal()) return -1;
2103
Nick Pelly005b2282009-09-10 10:21:56 -07002104 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07002105 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002106 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002107 // Check if we are recovering from a crash.
2108 if (mDeviceProperties.isEmpty()) {
2109 if (!updateRemoteDevicePropertiesCache(address))
2110 return -1;
2111 }
2112
2113 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
2114 if (value != null && value.containsKey(uuid))
2115 return value.get(uuid);
2116 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002117 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118
2119 public synchronized boolean setPin(String address, byte[] pin) {
2120 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2121 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002122 if (!isEnabledInternal()) return false;
2123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002124 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07002125 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002126 return false;
2127 }
2128 address = address.toUpperCase();
2129 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2130 if (data == null) {
2131 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
2132 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2133 " or by bluez.\n");
2134 return false;
2135 }
2136 // bluez API wants pin as a string
2137 String pinString;
2138 try {
2139 pinString = new String(pin, "UTF8");
2140 } catch (UnsupportedEncodingException uee) {
2141 Log.e(TAG, "UTF8 not supported?!?");
2142 return false;
2143 }
2144 return setPinNative(address, pinString, data.intValue());
2145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002146
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002147 public synchronized boolean setPasskey(String address, int passkey) {
2148 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2149 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002150 if (!isEnabledInternal()) return false;
2151
Nick Pelly005b2282009-09-10 10:21:56 -07002152 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002153 return false;
2154 }
2155 address = address.toUpperCase();
2156 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2157 if (data == null) {
2158 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
2159 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2160 " or by bluez.\n");
2161 return false;
2162 }
2163 return setPasskeyNative(address, passkey, data.intValue());
2164 }
2165
2166 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
2167 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2168 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002169 if (!isEnabledInternal()) return false;
2170
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002171 address = address.toUpperCase();
2172 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2173 if (data == null) {
2174 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
2175 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2176 " or by bluez.\n");
2177 return false;
2178 }
2179 return setPairingConfirmationNative(address, confirm, data.intValue());
2180 }
2181
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002182 public synchronized boolean setRemoteOutOfBandData(String address) {
2183 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2184 "Need BLUETOOTH_ADMIN permission");
2185 if (!isEnabledInternal()) return false;
2186 address = address.toUpperCase();
2187 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2188 if (data == null) {
2189 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
2190 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
2191 " or by bluez.\n");
2192 return false;
2193 }
2194
2195 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
2196 byte[] hash, randomizer;
2197 if (val == null) {
2198 // TODO: check what should be passed in this case.
2199 hash = new byte[16];
2200 randomizer = new byte[16];
2201 } else {
2202 hash = val.first;
2203 randomizer = val.second;
2204 }
2205 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
2206 }
2207
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002208 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002209 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2210 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002211 if (!isEnabledInternal()) return false;
2212
Nick Pelly005b2282009-09-10 10:21:56 -07002213 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002214 return false;
2215 }
Nick Pelly005b2282009-09-10 10:21:56 -07002216 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07002217 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002218 address = address.toUpperCase();
2219 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
2220 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002221 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
2222 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
2223 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002224 return false;
2225 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002226 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002228
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002229 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002230 ParcelUuid[] deviceUuids = getRemoteUuids(address);
2231 // We are storing the rfcomm channel numbers only for the uuids
2232 // we are interested in.
2233 int channel;
Nick Pelly16fb88a2009-10-07 07:44:03 +02002234 if (DBG) log("updateDeviceServiceChannelCache(" + address + ")");
2235
2236 ArrayList<ParcelUuid> applicationUuids = new ArrayList();
2237
2238 synchronized (this) {
2239 for (RemoteService service : mUuidCallbackTracker.keySet()) {
2240 if (service.address.equals(address)) {
2241 applicationUuids.add(service.uuid);
2242 }
2243 }
2244 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002245
2246 Map <ParcelUuid, Integer> value = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02002247
2248 // Retrieve RFCOMM channel for default uuids
2249 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002250 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Nick Pelly16fb88a2009-10-07 07:44:03 +02002251 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
2252 uuid.toString(), 0x0004);
2253 if (DBG) log("\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002254 value.put(uuid, channel);
2255 }
2256 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02002257 // Retrieve RFCOMM channel for application requested uuids
2258 for (ParcelUuid uuid : applicationUuids) {
2259 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
2260 channel = getDeviceServiceChannelNative(getObjectPathFromAddress(address),
2261 uuid.toString(), 0x0004);
2262 if (DBG) log("\tuuid(application): " + uuid + " " + channel);
2263 value.put(uuid, channel);
2264 }
2265 }
2266
2267 synchronized (this) {
2268 // Make application callbacks
2269 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
2270 iter.hasNext();) {
2271 RemoteService service = iter.next();
2272 if (service.address.equals(address)) {
2273 channel = -1;
2274 if (value.get(service.uuid) != null) {
2275 channel = value.get(service.uuid);
2276 }
2277 if (channel != -1) {
2278 if (DBG) log("Making callback for " + service.uuid + " with result " +
2279 channel);
2280 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
2281 if (callback != null) {
2282 try {
2283 callback.onRfcommChannelFound(channel);
2284 } catch (RemoteException e) {Log.e(TAG, "", e);}
2285 }
2286
2287 iter.remove();
2288 }
2289 }
2290 }
2291
2292 // Update cache
2293 mDeviceServiceChannelCache.put(address, value);
2294 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002295 }
2296
Nick Pelly24bb9b82009-10-02 20:34:18 -07002297 /**
2298 * b is a handle to a Binder instance, so that this service can be notified
2299 * for Applications that terminate unexpectedly, to clean there service
2300 * records
2301 */
2302 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
2303 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08002304 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
2305 if (!isEnabledInternal()) return -1;
2306
Nick Pelly24bb9b82009-10-02 20:34:18 -07002307 if (serviceName == null || uuid == null || channel < 1 ||
2308 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
2309 return -1;
2310 }
2311 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
2312 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
2313 return -1;
2314 }
2315 int handle = addRfcommServiceRecordNative(serviceName,
2316 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
2317 (short)channel);
2318 if (DBG) log("new handle " + Integer.toHexString(handle));
2319 if (handle == -1) {
2320 return -1;
2321 }
2322
2323 int pid = Binder.getCallingPid();
2324 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
2325 try {
2326 b.linkToDeath(new Reaper(handle, pid), 0);
2327 } catch (RemoteException e) {}
2328 return handle;
2329 }
2330
2331 public void removeServiceRecord(int handle) {
2332 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2333 "Need BLUETOOTH permission");
2334 checkAndRemoveRecord(handle, Binder.getCallingPid());
2335 }
2336
2337 private synchronized void checkAndRemoveRecord(int handle, int pid) {
2338 Integer handleInt = new Integer(handle);
2339 Integer owner = mServiceRecordToPid.get(handleInt);
2340 if (owner != null && pid == owner.intValue()) {
2341 if (DBG) log("Removing service record " + Integer.toHexString(handle) + " for pid " +
2342 pid);
2343 mServiceRecordToPid.remove(handleInt);
2344 removeServiceRecordNative(handle);
2345 }
2346 }
2347
2348 private class Reaper implements IBinder.DeathRecipient {
2349 int pid;
2350 int handle;
2351 Reaper(int handle, int pid) {
2352 this.pid = pid;
2353 this.handle = handle;
2354 }
2355 public void binderDied() {
2356 synchronized (BluetoothService.this) {
2357 if (DBG) log("Tracked app " + pid + " died");
2358 checkAndRemoveRecord(handle, pid);
2359 }
2360 }
2361 }
2362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002363 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
2364 @Override
2365 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002366 if (intent == null) return;
2367
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002368 String action = intent.getAction();
2369 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
2370 ContentResolver resolver = context.getContentResolver();
2371 // Query the airplane mode from Settings.System just to make sure that
2372 // some random app is not sending this intent and disabling bluetooth
2373 boolean enabled = !isAirplaneModeOn();
2374 // If bluetooth is currently expected to be on, then enable or disable bluetooth
2375 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
2376 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002377 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002378 } else {
2379 disable(false);
2380 }
2381 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002382 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
2383 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2384 Intent.EXTRA_DOCK_STATE_UNDOCKED);
2385 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
2386 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
2387 mDockAddress = null;
2388 mDockPin = null;
2389 } else {
2390 SharedPreferences.Editor editor =
2391 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
2392 mContext.MODE_PRIVATE).edit();
2393 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07002394 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002395 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002396 }
2397 }
2398 };
2399
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002400 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08002401 final ContentResolver resolver = mContext.getContentResolver();
2402 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002403 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08002404 final String toggleableRadios = Settings.System.getString(resolver,
2405 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
2406
2407 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
2408 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
2409 mIsAirplaneToggleable = toggleableRadios == null ? false :
2410 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
2411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002412 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08002413 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002414 }
2415 }
2416
2417 /* Returns true if airplane mode is currently on */
2418 private final boolean isAirplaneModeOn() {
2419 return Settings.System.getInt(mContext.getContentResolver(),
2420 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
2421 }
2422
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002423 /* Broadcast the Uuid intent */
2424 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002425 ParcelUuid[] uuid = getUuidFromCache(address);
2426 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07002427 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002428 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
2429 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002430
Jaikumar Ganesh61799652009-09-20 16:01:21 -07002431 if (mUuidIntentTracker.contains(address))
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002432 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002433
2434 }
2435
2436 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
2437 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
2438 iter.hasNext();) {
2439 RemoteService service = iter.next();
2440 if (service.address.equals(address)) {
2441 if (DBG) log("Cleaning up failed UUID channel lookup: " + service.address +
2442 " " + service.uuid);
2443 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
2444 if (callback != null) {
2445 try {
2446 callback.onRfcommChannelFound(-1);
2447 } catch (RemoteException e) {Log.e(TAG, "", e);}
2448 }
2449
2450 iter.remove();
2451 }
2452 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002453 }
2454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002455 @Override
2456 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
The Android Open Source Project10592532009-03-18 17:39:46 -07002457 switch(mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -07002458 case BluetoothAdapter.STATE_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002459 pw.println("Bluetooth OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002460 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002461 case BluetoothAdapter.STATE_TURNING_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002462 pw.println("Bluetooth TURNING ON\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002463 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002464 case BluetoothAdapter.STATE_TURNING_OFF:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002465 pw.println("Bluetooth TURNING OFF\n");
The Android Open Source Project10592532009-03-18 17:39:46 -07002466 return;
Nick Pellyde893f52009-09-08 13:15:33 -07002467 case BluetoothAdapter.STATE_ON:
Nick Pelly24bb9b82009-10-02 20:34:18 -07002468 pw.println("Bluetooth ON\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002469 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002470
Nick Pelly24bb9b82009-10-02 20:34:18 -07002471 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08002472 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07002473
2474 pw.println("Local address = " + getAddress());
2475 pw.println("Local name = " + getName());
2476 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07002477
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002478 mAdapter.getProfileProxy(mContext,
2479 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
The Android Open Source Project10592532009-03-18 17:39:46 -07002480
The Android Open Source Project10592532009-03-18 17:39:46 -07002481 pw.println("\n--Known devices--");
Nick Pellybd022f42009-08-14 18:33:38 -07002482 for (String address : mDeviceProperties.keySet()) {
Nick Pelly1eada0d2009-08-26 10:57:33 -07002483 int bondState = mBondState.getBondState(address);
The Android Open Source Project10592532009-03-18 17:39:46 -07002484 pw.printf("%s %10s (%d) %s\n", address,
Nick Pelly1eada0d2009-08-26 10:57:33 -07002485 toBondStateString(bondState),
The Android Open Source Project10592532009-03-18 17:39:46 -07002486 mBondState.getAttempt(address),
2487 getRemoteName(address));
Nick Pelly24bb9b82009-10-02 20:34:18 -07002488
2489 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
2490 if (uuidChannels == null) {
2491 pw.println("\tuuids = null");
2492 } else {
2493 for (ParcelUuid uuid : uuidChannels.keySet()) {
2494 Integer channel = uuidChannels.get(uuid);
2495 if (channel == null) {
2496 pw.println("\t" + uuid);
2497 } else {
2498 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
Nick Pelly1eada0d2009-08-26 10:57:33 -07002499 }
2500 }
2501 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02002502 for (RemoteService service : mUuidCallbackTracker.keySet()) {
2503 if (service.address.equals(address)) {
2504 pw.println("\tPENDING CALLBACK: " + service.uuid);
2505 }
2506 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002507 }
2508
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002509 String value = getProperty("Devices");
Nick Pelly1eada0d2009-08-26 10:57:33 -07002510 String[] devicesObjectPath = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002511 if (value != null) {
2512 devicesObjectPath = value.split(",");
2513 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002514 pw.println("\n--ACL connected devices--");
Nick Pelly24bb9b82009-10-02 20:34:18 -07002515 if (devicesObjectPath != null) {
2516 for (String device : devicesObjectPath) {
2517 pw.println(getAddressFromObjectPath(device));
2518 }
The Android Open Source Project10592532009-03-18 17:39:46 -07002519 }
2520
2521 // Rather not do this from here, but no-where else and I need this
2522 // dump
2523 pw.println("\n--Headset Service--");
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002524 if (mBluetoothHeadset != null) {
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08002525 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
2526 if (deviceList.size() == 0) {
2527 pw.println("\n--No headsets connected--");
2528 } else {
2529 BluetoothDevice device = deviceList.get(0);
2530 pw.println("\ngetConnectedDevices[0] = " + device);
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002531
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08002532 switch (mBluetoothHeadset.getConnectionState(device)) {
2533 case BluetoothHeadset.STATE_CONNECTING:
2534 pw.println("getConnectionState() = STATE_CONNECTING");
2535 break;
2536 case BluetoothHeadset.STATE_CONNECTED:
2537 pw.println("getConnectionState() = STATE_CONNECTED");
2538 break;
2539 case BluetoothHeadset.STATE_DISCONNECTING:
2540 pw.println("getConnectionState() = STATE_DISCONNECTING");
2541 break;
2542 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
2543 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
2544 break;
2545 }
2546 pw.println("getBatteryUsageHint() = " +
2547 mBluetoothHeadset.getBatteryUsageHint(device));
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002548 }
2549
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07002550 deviceList.clear();
2551 deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002552 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
2553 pw.println("\n--Connected and Disconnected Headsets");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08002554 for (BluetoothDevice device: deviceList) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002555 pw.println(device);
2556 if (mBluetoothHeadset.isAudioConnected(device)) {
2557 pw.println("SCO audio connected to device:" + device);
2558 }
2559 }
2560
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002561 mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
The Android Open Source Project10592532009-03-18 17:39:46 -07002562 }
Nick Pelly6c901db2009-06-19 10:08:09 -07002563
Nick Pelly24bb9b82009-10-02 20:34:18 -07002564 pw.println("\n--Application Service Records--");
2565 for (Integer handle : mServiceRecordToPid.keySet()) {
2566 Integer pid = mServiceRecordToPid.get(handle);
2567 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
2568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002569 }
2570
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002571 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
2572 new BluetoothProfile.ServiceListener() {
2573 public void onServiceConnected(int profile, BluetoothProfile proxy) {
2574 mBluetoothHeadset = (BluetoothHeadset) proxy;
2575 }
2576 public void onServiceDisconnected(int profile) {
2577 mBluetoothHeadset = null;
2578 }
2579 };
2580
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002581 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2582 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002583 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002584 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002585 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002586 else
Nick Pellybd022f42009-08-14 18:33:38 -07002587 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002588 }
2589
2590 /* package */ static String scanModeToBluezString(int mode) {
2591 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07002592 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002593 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07002594 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002595 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07002596 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002597 return "discoverable";
2598 }
2599 return null;
2600 }
2601
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002602 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002603 String adapterObjectPath = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002604 if (adapterObjectPath == null || objectPath == null) {
Jake Hambyf51eada2010-09-21 13:39:53 -07002605 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002606 " or deviceObjectPath:" + objectPath + " is null");
2607 return null;
2608 }
2609 if (!objectPath.startsWith(adapterObjectPath)) {
Jake Hambyf51eada2010-09-21 13:39:53 -07002610 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002611 " is not a prefix of deviceObjectPath:" + objectPath +
2612 "bluetoothd crashed ?");
2613 return null;
2614 }
2615 String address = objectPath.substring(adapterObjectPath.length());
2616 if (address != null) return address.replace('_', ':');
2617
2618 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2619 return null;
2620 }
2621
2622 /*package*/ String getObjectPathFromAddress(String address) {
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -08002623 String path = getPropertyInternal("ObjectPath");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002624 if (path == null) {
2625 Log.e(TAG, "Error: Object Path is null");
2626 return null;
2627 }
2628 path = path + address.replace(":", "_");
2629 return path;
2630 }
2631
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002632 /*package */ void setLinkTimeout(String address, int num_slots) {
2633 String path = getObjectPathFromAddress(address);
2634 boolean result = setLinkTimeoutNative(path, num_slots);
2635
2636 if (!result) log("Set Link Timeout to:" + num_slots + " slots failed");
2637 }
2638
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002639 public boolean connectHeadset(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.CONNECT_HFP_OUTGOING;
2646 msg.obj = state;
2647 mHfpProfileState.sendMessage(msg);
2648 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002649 }
2650 return false;
2651 }
2652
2653 public boolean disconnectHeadset(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.DISCONNECT_HFP_OUTGOING;
2660 msg.obj = state;
2661 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002662 return true;
2663 }
2664 return false;
2665 }
2666
2667 public boolean connectSink(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);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002671 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002672 Message msg = new Message();
2673 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2674 msg.obj = state;
2675 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002676 return true;
2677 }
2678 return false;
2679 }
2680
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002681 public boolean disconnectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002682 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2683
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002684 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2685 if (state != null) {
2686 Message msg = new Message();
2687 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2688 msg.obj = state;
2689 mA2dpProfileState.sendMessage(msg);
2690 return true;
2691 }
2692 return false;
2693 }
2694
2695 private BluetoothDeviceProfileState addProfileState(String address) {
2696 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002697 if (state != null) return state;
2698
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002699 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2700 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002701 state.start();
2702 return state;
2703 }
2704
2705 private void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002706 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002707 }
2708
2709 private void initProfileState() {
2710 String []bonds = null;
2711 String val = getPropertyInternal("Devices");
2712 if (val != null) {
2713 bonds = val.split(",");
2714 }
2715 if (bonds == null) {
2716 return;
2717 }
2718
2719 for (String path : bonds) {
2720 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002721 BluetoothDeviceProfileState state = addProfileState(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002722 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002723 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
Jaikumar Ganesh2d1fc4e2010-12-13 15:54:33 -08002724 state.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002725 }
2726 }
2727
2728 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002729 BluetoothDeviceProfileState state =
2730 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002731 if (state != null) {
2732 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002733 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002734 state.sendMessage(msg);
2735 return true;
2736 }
2737 return false;
2738 }
2739
2740 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002741 BluetoothDeviceProfileState state =
2742 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002743 if (state != null) {
2744 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002745 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002746 state.sendMessage(msg);
2747 return true;
2748 }
2749 return false;
2750 }
2751
2752 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2753 mA2dpService = a2dpService;
2754 }
2755
Jaikumar Ganesh70a053b2010-10-13 15:54:30 -07002756 public void sendProfileStateMessage(int profile, int cmd) {
2757 Message msg = new Message();
2758 msg.what = cmd;
2759 if (profile == BluetoothProfileState.HFP) {
2760 mHfpProfileState.sendMessage(msg);
2761 } else if (profile == BluetoothProfileState.A2DP) {
2762 mA2dpProfileState.sendMessage(msg);
2763 }
2764 }
2765
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002766 public int getAdapterConnectionState() {
2767 return mAdapterConnectionState;
2768 }
2769
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002770 public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
2771 int prevState) {
2772 if (updateCountersAndCheckForConnectionStateChange(device, state, prevState)) {
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002773 state = translateToAdapterConnectionState(state);
2774 prevState = translateToAdapterConnectionState(prevState);
2775
2776 mAdapterConnectionState = state;
2777
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002778 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
2779 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
2780 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, state);
2781 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE, prevState);
2782 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
2783 log("CONNECTION_STATE_CHANGE: " + device + ": " + prevState + "-> " + state);
2784 }
2785 }
2786
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002787 private int translateToAdapterConnectionState(int state) {
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002788 switch (state) {
2789 case BluetoothProfile.STATE_CONNECTING:
2790 return BluetoothAdapter.STATE_CONNECTING;
2791 case BluetoothProfile.STATE_CONNECTED:
2792 return BluetoothAdapter.STATE_CONNECTED;
2793 case BluetoothProfile.STATE_DISCONNECTING:
2794 return BluetoothAdapter.STATE_DISCONNECTING;
2795 case BluetoothProfile.STATE_DISCONNECTED:
2796 return BluetoothAdapter.STATE_DISCONNECTED;
2797 default:
2798 Log.e(TAG, "Error in getAdapterConnectionState");
2799 return -1;
2800 }
2801 }
2802
2803 private boolean updateCountersAndCheckForConnectionStateChange(BluetoothDevice device,
2804 int state,
2805 int prevState) {
2806 switch (state) {
2807 case BluetoothProfile.STATE_CONNECTING:
2808 mProfilesConnecting++;
2809 if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
2810 if (mProfilesConnected > 0 || mProfilesConnecting > 1) return false;
2811
2812 break;
2813 case BluetoothProfile.STATE_CONNECTED:
2814 if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
2815 if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
2816
2817 mProfilesConnected++;
2818
2819 if (mProfilesConnected > 1) return false;
2820 break;
2821 case BluetoothProfile.STATE_DISCONNECTING:
2822 mProfilesDisconnecting++;
2823 mProfilesConnected--;
2824
2825 if (mProfilesConnected > 0 || mProfilesDisconnecting > 1) return false;
2826
2827 break;
2828 case BluetoothProfile.STATE_DISCONNECTED:
2829 if (prevState == BluetoothAdapter.STATE_CONNECTING) mProfilesConnecting--;
2830 if (prevState == BluetoothAdapter.STATE_DISCONNECTING) mProfilesDisconnecting--;
2831
2832 if (prevState == BluetoothAdapter.STATE_CONNECTED) {
2833 mProfilesConnected--;
2834 }
2835
2836 if (mProfilesConnected > 0 || mProfilesConnecting > 0) return false;
2837 break;
2838 }
2839 return true;
2840 }
2841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002842 private static void log(String msg) {
2843 Log.d(TAG, msg);
2844 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002845
2846 private native static void classInitNative();
2847 private native void initializeNativeDataNative();
2848 private native boolean setupNativeDataNative();
2849 private native boolean tearDownNativeDataNative();
2850 private native void cleanupNativeDataNative();
2851 private native String getAdapterPathNative();
2852
2853 private native int isEnabledNative();
2854 private native int enableNative();
2855 private native int disableNative();
2856
2857 private native Object[] getAdapterPropertiesNative();
2858 private native Object[] getDevicePropertiesNative(String objectPath);
2859 private native boolean setAdapterPropertyStringNative(String key, String value);
2860 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2861 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2862
2863 private native boolean startDiscoveryNative();
2864 private native boolean stopDiscoveryNative();
2865
2866 private native boolean createPairedDeviceNative(String address, int timeout_ms);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002867 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2868 private native byte[] readAdapterOutOfBandDataNative();
2869
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002870 private native boolean cancelDeviceCreationNative(String address);
2871 private native boolean removeDeviceNative(String objectPath);
2872 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2873 int attributeId);
2874
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002875 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002876 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002877 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2878 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2879 int nativeData);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002880 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2881 byte[] randomizer, int nativeData);
2882
Nick Pelly24bb9b82009-10-02 20:34:18 -07002883 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2884 int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002885 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002886 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002887
Nick Pelly24bb9b82009-10-02 20:34:18 -07002888 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2889 short channel);
2890 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002891 private native boolean setLinkTimeoutNative(String path, int num_slots);
Jaikumar Ganesh545e6702010-06-04 10:23:03 -07002892 private native boolean connectInputDeviceNative(String path);
2893 private native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002894
2895 private native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2896 private native boolean connectPanDeviceNative(String path, String srcRole, String dstRole);
2897 private native boolean disconnectPanDeviceNative(String path);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -08002898
2899 private native int[] addReservedServiceRecordsNative(int[] uuuids);
2900 private native boolean removeReservedServiceRecordsNative(int[] handles);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002901}