blob: ff16c1883984335734f84072c07da9438095dd02 [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;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070032import android.bluetooth.BluetoothHealthAppConfiguration;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080033import android.bluetooth.BluetoothInputDevice;
Danica Chang6fdd0c62010-08-11 14:54:43 -070034import android.bluetooth.BluetoothPan;
Jaikumar Ganesh96a79832010-09-27 17:02:01 -070035import android.bluetooth.BluetoothProfile;
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -070036import android.bluetooth.BluetoothProfileState;
Nick Pelly24bb9b82009-10-02 20:34:18 -070037import android.bluetooth.BluetoothSocket;
Jaikumar Ganesh10eac972009-09-21 12:48:51 -070038import android.bluetooth.BluetoothUuid;
Nick Pellybd022f42009-08-14 18:33:38 -070039import android.bluetooth.IBluetooth;
Nick Pelly16fb88a2009-10-07 07:44:03 +020040import android.bluetooth.IBluetoothCallback;
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -070041import android.bluetooth.IBluetoothHealthCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.content.BroadcastReceiver;
43import android.content.ContentResolver;
44import android.content.Context;
45import android.content.Intent;
46import android.content.IntentFilter;
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -080047import android.content.SharedPreferences;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.os.Binder;
49import android.os.Handler;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080050import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Message;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070052import android.os.ParcelFileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080053import android.os.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070055import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.provider.Settings;
57import android.util.Log;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -070058import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -070060import com.android.internal.app.IBatteryStats;
61
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080062import java.io.BufferedInputStream;
Matthew Xiea0c68032011-06-25 21:47:07 -070063import java.io.BufferedReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080064import java.io.BufferedWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070065import java.io.DataInputStream;
66import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import java.io.FileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080068import java.io.FileInputStream;
69import java.io.FileNotFoundException;
70import java.io.FileWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070071import java.io.InputStreamReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080072import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import java.io.PrintWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070074import java.io.RandomAccessFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import java.io.UnsupportedEncodingException;
76import java.util.ArrayList;
77import java.util.Arrays;
78import java.util.HashMap;
Nick Pelly16fb88a2009-10-07 07:44:03 +020079import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070080import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081import java.util.Map;
82
Nick Pellybd022f42009-08-14 18:33:38 -070083public class BluetoothService extends IBluetooth.Stub {
84 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private static final boolean DBG = true;
86
87 private int mNativeData;
88 private BluetoothEventLoop mEventLoop;
Jaikumar Ganesh96a79832010-09-27 17:02:01 -070089 private BluetoothHeadset mBluetoothHeadset;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080090 private BluetoothInputDevice mInputDevice;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -080091 private BluetoothPan mPan;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 private boolean mIsAirplaneSensitive;
Jeff Sharkey44303922009-12-01 18:25:02 -080093 private boolean mIsAirplaneToggleable;
The Android Open Source Project10592532009-03-18 17:39:46 -070094 private int mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070095 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private boolean mIsDiscovering;
Jaikumar Ganesh84690c82010-12-10 12:48:58 -080097 private int[] mAdapterSdpHandles;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -080098 private ParcelUuid[] mAdapterUuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099
Nick Pellybd022f42009-08-14 18:33:38 -0700100 private BluetoothAdapter mAdapter; // constant after init()
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800101 private final BluetoothBondState mBondState; // local cache of bondings
Nick Pellybd022f42009-08-14 18:33:38 -0700102 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private final Context mContext;
104
105 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800106 static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800108 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
109 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
110
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800111 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
112 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
113
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800114 private static final int MESSAGE_FINISH_DISABLE = 1;
115 private static final int MESSAGE_UUID_INTENT = 2;
Jaikumar Ganeshfbb238d2011-02-01 14:12:24 -0800116 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700117
118 // The time (in millisecs) to delay the pairing attempt after the first
119 // auto pairing attempt fails. We use an exponential delay with
120 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
121 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
122 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
123 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700124
125 // The timeout used to sent the UUIDs Intent
126 // This timeout should be greater than the page timeout
127 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700128
Nick Pelly16fb88a2009-10-07 07:44:03 +0200129 /** Always retrieve RFCOMM channel for these SDP UUIDs */
130 private static final ParcelUuid[] RFCOMM_UUIDS = {
131 BluetoothUuid.Handsfree,
132 BluetoothUuid.HSP,
133 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700134
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800135 private final BluetoothAdapterProperties mAdapterProperties;
136 private final BluetoothDeviceProperties mDeviceProperties;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200137
138 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
139 private final ArrayList<String> mUuidIntentTracker;
140 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700141
Nick Pelly24bb9b82009-10-02 20:34:18 -0700142 private final HashMap<Integer, Integer> mServiceRecordToPid;
143
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700144 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
145 private final BluetoothProfileState mA2dpProfileState;
146 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700147
148 private BluetoothA2dpService mA2dpService;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700149 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700150
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700151 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700152
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800153 private static String mDockAddress;
154 private String mDockPin;
155
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700156 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800157 private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800158 private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700159 private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler;
Matthew Xiea0c68032011-06-25 21:47:07 -0700160 private static final String INCOMING_CONNECTION_FILE =
161 "/data/misc/bluetooth/incoming_connection.conf";
162 private HashMap<String, Pair<Integer, String>> mIncomingConnections;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700163
Nick Pelly16fb88a2009-10-07 07:44:03 +0200164 private static class RemoteService {
165 public String address;
166 public ParcelUuid uuid;
167 public RemoteService(String address, ParcelUuid uuid) {
168 this.address = address;
169 this.uuid = uuid;
170 }
171 @Override
172 public boolean equals(Object o) {
173 if (o instanceof RemoteService) {
174 RemoteService service = (RemoteService)o;
175 return address.equals(service.address) && uuid.equals(service.uuid);
176 }
177 return false;
178 }
Kenny Root5f614162010-02-17 12:00:37 -0800179
180 @Override
181 public int hashCode() {
182 int hash = 1;
183 hash = hash * 31 + (address == null ? 0 : address.hashCode());
184 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
185 return hash;
186 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200187 }
188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 static {
190 classInitNative();
191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192
Nick Pellybd022f42009-08-14 18:33:38 -0700193 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700195
196 // Need to do this in place of:
197 // mBatteryStats = BatteryStatsService.getService();
198 // Since we can not import BatteryStatsService from here. This class really needs to be
199 // moved to java/services/com/android/server/
200 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700203
204 if (isEnabledNative() == 1) {
205 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
206 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700208
Nick Pellyde893f52009-09-08 13:15:33 -0700209 mBluetoothState = BluetoothAdapter.STATE_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 mIsDiscovering = false;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800211
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800212 mBondState = new BluetoothBondState(context, this);
213 mAdapterProperties = new BluetoothAdapterProperties(context, this);
214 mDeviceProperties = new BluetoothDeviceProperties(this);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700215
216 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700217 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700218 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200219 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700220 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700221 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
222 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
223 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
224
225 mHfpProfileState.start();
226 mA2dpProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800227
228 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800229 registerForAirplaneMode(filter);
230
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800231 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800232 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800233 mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800234 mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700235 mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
Matthew Xiea0c68032011-06-25 21:47:07 -0700236 mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800237 }
238
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700239 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800240 if (mDockAddress != null) return mDockAddress;
241
242 BufferedInputStream file = null;
243 String dockAddress;
244 try {
245 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
246 byte[] address = new byte[17];
247 file.read(address);
248 dockAddress = new String(address);
249 dockAddress = dockAddress.toUpperCase();
250 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
251 mDockAddress = dockAddress;
252 return mDockAddress;
253 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800254 Log.e(TAG, "CheckBluetoothAddress failed for car dock address: "
255 + dockAddress);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800256 }
257 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800258 Log.e(TAG, "FileNotFoundException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800259 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800260 Log.e(TAG, "IOException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800261 } finally {
262 if (file != null) {
263 try {
264 file.close();
265 } catch (IOException e) {
266 // Ignore
267 }
268 }
269 }
270 mDockAddress = null;
271 return null;
272 }
273
274 private synchronized boolean writeDockPin() {
275 BufferedWriter out = null;
276 try {
277 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
278
279 // Generate a random 4 digit pin between 0000 and 9999
280 // This is not truly random but good enough for our purposes.
281 int pin = (int) Math.floor(Math.random() * 10000);
282
283 mDockPin = String.format("%04d", pin);
284 out.write(mDockPin);
285 return true;
286 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800287 Log.e(TAG, "FileNotFoundException while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800288 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800289 Log.e(TAG, "IOException while while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800290 } finally {
291 if (out != null) {
292 try {
293 out.close();
294 } catch (IOException e) {
295 // Ignore
296 }
297 }
298 }
299 mDockPin = null;
300 return false;
301 }
302
303 /*package*/ synchronized String getDockPin() {
304 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700305 }
306
307 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200308 mAdapter = BluetoothAdapter.getDefaultAdapter();
Nick Pellybd022f42009-08-14 18:33:38 -0700309 mEventLoop = new BluetoothEventLoop(mContext, mAdapter, this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800310 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311
Jaikumar Ganesh7d0548d2010-10-18 15:29:09 -0700312 public synchronized void initAfterA2dpRegistration() {
313 mEventLoop.getProfileProxy();
314 }
315
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 @Override
317 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800318 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 try {
320 cleanupNativeDataNative();
321 } finally {
322 super.finalize();
323 }
324 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325
326 public boolean isEnabled() {
327 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800328 return isEnabledInternal();
329 }
330
331 private boolean isEnabledInternal() {
Nick Pellyde893f52009-09-08 13:15:33 -0700332 return mBluetoothState == BluetoothAdapter.STATE_ON;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
The Android Open Source Project10592532009-03-18 17:39:46 -0700335 public int getBluetoothState() {
336 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
337 return mBluetoothState;
338 }
339
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800340 int getBluetoothStateInternal() {
341 return mBluetoothState;
342 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700343
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 /**
345 * Bring down bluetooth and disable BT in settings. Returns true on success.
346 */
347 public boolean disable() {
348 return disable(true);
349 }
350
351 /**
352 * Bring down bluetooth. Returns true on success.
353 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200354 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 */
356 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200357 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358
The Android Open Source Project10592532009-03-18 17:39:46 -0700359 switch (mBluetoothState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700360 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700361 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700362 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700363 break;
364 default:
365 return false;
366 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 if (mEnableThread != null && mEnableThread.isAlive()) {
368 return false;
369 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800370
Nick Pellyde893f52009-09-08 13:15:33 -0700371 setBluetoothState(BluetoothAdapter.STATE_TURNING_OFF);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800372
373 if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800374 setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE,
375 BluetoothPanProfileHandler.NAP_BRIDGE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700376
377 // Allow 3 seconds for profiles to gracefully disconnect
378 // TODO: Introduce a callback mechanism so that each profile can notify
Nick Pellybd022f42009-08-14 18:33:38 -0700379 // BluetoothService when it is done shutting down
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700380 disconnectDevices();
381
The Android Open Source Project10592532009-03-18 17:39:46 -0700382 mHandler.sendMessageDelayed(
383 mHandler.obtainMessage(MESSAGE_FINISH_DISABLE, saveSetting ? 1 : 0, 0), 3000);
384 return true;
385 }
386
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700387 private synchronized void disconnectDevices() {
388 // Disconnect devices handled by BluetoothService.
389 for (BluetoothDevice device: getConnectedInputDevices()) {
390 disconnectInputDevice(device);
391 }
392
393 for (BluetoothDevice device: getConnectedPanDevices()) {
394 disconnectPanDevice(device);
395 }
396 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700397
398 private synchronized void finishDisable(boolean saveSetting) {
Nick Pellyde893f52009-09-08 13:15:33 -0700399 if (mBluetoothState != BluetoothAdapter.STATE_TURNING_OFF) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700400 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 }
402 mEventLoop.stop();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700403 tearDownNativeDataNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 disableNative();
405
406 // mark in progress bondings as cancelled
407 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700408 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
410 }
411
Jaikumar Ganesh2d1fc4e2010-12-13 15:54:33 -0800412 // Stop the profile state machine for bonded devices.
413 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) {
414 removeProfileState(address);
415 }
416
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700418 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
419 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
421
The Android Open Source Project10592532009-03-18 17:39:46 -0700422 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700423 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700424 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700425
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700426 mProfilesConnected = 0;
427 mProfilesConnecting = 0;
428 mProfilesDisconnecting = 0;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700429 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800430 mAdapterUuids = null;
431 mAdapterSdpHandles = null;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 if (saveSetting) {
434 persistBluetoothOnSetting(false);
435 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700436
Nick Pellyde893f52009-09-08 13:15:33 -0700437 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700438
439 // Log bluetooth off to battery stats.
440 long ident = Binder.clearCallingIdentity();
441 try {
442 mBatteryStats.noteBluetoothOff();
443 } catch (RemoteException e) {
444 } finally {
445 Binder.restoreCallingIdentity(ident);
446 }
Nick Pelly997c7612009-03-24 20:44:48 -0700447
448 if (mRestart) {
449 mRestart = false;
450 enable();
451 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 }
453
The Android Open Source Project10592532009-03-18 17:39:46 -0700454 /** Bring up BT and persist BT on in settings */
455 public boolean enable() {
456 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 }
458
459 /**
460 * Enable this Bluetooth device, asynchronously.
461 * This turns on/off the underlying hardware.
462 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700463 * @param saveSetting If true, persist the new state of BT in settings
464 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700466 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
468 "Need BLUETOOTH_ADMIN permission");
469
470 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800471 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 return false;
473 }
Nick Pellyde893f52009-09-08 13:15:33 -0700474 if (mBluetoothState != BluetoothAdapter.STATE_OFF) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 return false;
476 }
477 if (mEnableThread != null && mEnableThread.isAlive()) {
478 return false;
479 }
Nick Pellyde893f52009-09-08 13:15:33 -0700480 setBluetoothState(BluetoothAdapter.STATE_TURNING_ON);
The Android Open Source Project10592532009-03-18 17:39:46 -0700481 mEnableThread = new EnableThread(saveSetting);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 mEnableThread.start();
483 return true;
484 }
485
Nick Pelly997c7612009-03-24 20:44:48 -0700486 /** Forcibly restart Bluetooth if it is on */
487 /* package */ synchronized void restart() {
Nick Pellyde893f52009-09-08 13:15:33 -0700488 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700489 return;
490 }
491 mRestart = true;
492 if (!disable(false)) {
493 mRestart = false;
494 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700495 }
Nick Pelly997c7612009-03-24 20:44:48 -0700496
The Android Open Source Project10592532009-03-18 17:39:46 -0700497 private synchronized void setBluetoothState(int state) {
498 if (state == mBluetoothState) {
499 return;
500 }
501
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800502 if (DBG) Log.d(TAG, "Bluetooth state " + mBluetoothState + " -> " + state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700503
Nick Pellyde893f52009-09-08 13:15:33 -0700504 Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
505 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, mBluetoothState);
506 intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);
The Android Open Source Project10592532009-03-18 17:39:46 -0700507 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
508
509 mBluetoothState = state;
510
511 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
512 }
513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 private final Handler mHandler = new Handler() {
515 @Override
516 public void handleMessage(Message msg) {
517 switch (msg.what) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700518 case MESSAGE_FINISH_DISABLE:
519 finishDisable(msg.arg1 != 0);
520 break;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700521 case MESSAGE_UUID_INTENT:
522 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200523 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700524 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200525 makeServiceChannelCallbacks(address);
526 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700527 break;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700528 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
529 address = (String)msg.obj;
530 if (address != null) {
531 createBond(address);
532 return;
533 }
534 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 }
536 }
537 };
538
539 private EnableThread mEnableThread;
540
541 private class EnableThread extends Thread {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 private final boolean mSaveSetting;
The Android Open Source Project10592532009-03-18 17:39:46 -0700543 public EnableThread(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 mSaveSetting = saveSetting;
545 }
546 public void run() {
547 boolean res = (enableNative() == 0);
548 if (res) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700549 int retryCount = 2;
550 boolean running = false;
551 while ((retryCount-- > 0) && !running) {
552 mEventLoop.start();
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800553 // it may take a moment for the other thread to do its
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700554 // thing. Check periodically for a while.
555 int pollCount = 5;
556 while ((pollCount-- > 0) && !running) {
557 if (mEventLoop.isEventLoopRunning()) {
558 running = true;
559 break;
560 }
561 try {
562 Thread.sleep(100);
563 } catch (InterruptedException e) {}
564 }
565 }
566 if (!running) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800567 Log.e(TAG, "bt EnableThread giving up");
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700568 res = false;
569 disableNative();
570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 }
572
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 if (res) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700574 if (!setupNativeDataNative()) {
575 return;
576 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 if (mSaveSetting) {
578 persistBluetoothOnSetting(true);
579 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 mIsDiscovering = false;
Jaikumar Ganeshc06fe592010-01-07 20:22:44 -0800582 mBondState.readAutoPairingData();
Jaikumar Ganeshc73dd732011-06-27 17:50:15 -0700583 mBondState.initBondState();
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700584 initProfileState();
Jaikumar Ganeshb70765c2010-09-02 12:17:05 -0700585
Jaikumar Ganesh9efb1342011-02-11 18:10:31 -0800586 // This should be the last step of the the enable thread.
587 // Because this adds SDP records which asynchronously
588 // broadcasts the Bluetooth On State in updateBluetoothState.
589 // So we want all internal state setup before this.
590 updateSdpRecords();
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800591 } else {
592 setBluetoothState(BluetoothAdapter.STATE_OFF);
The Android Open Source Project10592532009-03-18 17:39:46 -0700593 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700594 mEnableThread = null;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800595 }
596 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700597
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800598 private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
599 //Register SDP records.
600 int[] svcIdentifiers = new int[uuids.size()];
601 for (int i = 0; i < uuids.size(); i++) {
602 svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
603 }
604 mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
605 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700606
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800607 private synchronized void updateSdpRecords() {
608 ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
609
610 // Add the default records
611 uuids.add(BluetoothUuid.HSP_AG);
612 uuids.add(BluetoothUuid.ObexObjectPush);
613
614 if (mContext.getResources().
615 getBoolean(com.android.internal.R.bool.config_voice_capable)) {
616 uuids.add(BluetoothUuid.Handsfree_AG);
617 uuids.add(BluetoothUuid.PBAP_PSE);
618 }
619
620 // Add SDP records for profiles maintained by Android userspace
621 addReservedSdpRecords(uuids);
622
623 // Enable profiles maintained by Bluez userspace.
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800624 setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
625 BluetoothPanProfileHandler.NAP_BRIDGE);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800626
627 // Add SDP records for profiles maintained by Bluez userspace
628 uuids.add(BluetoothUuid.AudioSource);
629 uuids.add(BluetoothUuid.AvrcpTarget);
630 uuids.add(BluetoothUuid.NAP);
631
632 // Cannot cast uuids.toArray directly since ParcelUuid is parcelable
633 mAdapterUuids = new ParcelUuid[uuids.size()];
634 for (int i = 0; i < uuids.size(); i++) {
635 mAdapterUuids[i] = uuids.get(i);
636 }
637 }
638
Jaikumar Ganesh9efb1342011-02-11 18:10:31 -0800639 /**
640 * This function is called from Bluetooth Event Loop when onPropertyChanged
641 * for adapter comes in with UUID property.
642 * @param uuidsThe uuids of adapter as reported by Bluez.
643 */
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800644 synchronized void updateBluetoothState(String uuids) {
645 if (mBluetoothState == BluetoothAdapter.STATE_TURNING_ON) {
646 ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
647
648 if (mAdapterUuids != null &&
649 BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
650 setBluetoothState(BluetoothAdapter.STATE_ON);
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800651 autoConnect();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700652 String[] propVal = {"Pairable", getProperty("Pairable")};
653 mEventLoop.onPropertyChanged(propVal);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700654
Jaikumar Ganesh9efb1342011-02-11 18:10:31 -0800655 // Log bluetooth on to battery stats.
656 long ident = Binder.clearCallingIdentity();
657 try {
658 mBatteryStats.noteBluetoothOn();
659 } catch (RemoteException e) {
660 } finally {
661 Binder.restoreCallingIdentity(ident);
662 }
663
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800664 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
665 disable(false);
666 }
Daisuke Miyakawa5c43f732009-06-08 12:55:56 +0900667 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 }
669 }
670
671 private void persistBluetoothOnSetting(boolean bluetoothOn) {
672 long origCallerIdentityToken = Binder.clearCallingIdentity();
673 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.BLUETOOTH_ON,
674 bluetoothOn ? 1 : 0);
675 Binder.restoreCallingIdentity(origCallerIdentityToken);
676 }
677
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700678 /*package*/ synchronized boolean attemptAutoPair(String address) {
679 if (!mBondState.hasAutoPairingFailed(address) &&
680 !mBondState.isAutoPairingBlacklisted(address)) {
681 mBondState.attempt(address);
682 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
683 return true;
684 }
685 return false;
686 }
687
Jaikumar Ganeshf487d722011-01-12 10:12:01 -0800688 /*package*/ synchronized boolean isFixedPinZerosAutoPairKeyboard(String address) {
689 // Check for keyboards which have fixed PIN 0000 as the pairing pin
690 return mBondState.isFixedPinZerosAutoPairKeyboard(address);
691 }
692
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700693 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
694 if (result == BluetoothDevice.BOND_SUCCESS) {
695 setBondState(address, BluetoothDevice.BOND_BONDED);
696 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
697 mBondState.clearPinAttempts(address);
698 }
699 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
700 mBondState.getAttempt(address) == 1) {
701 mBondState.addAutoPairingFailure(address);
702 pairingAttempt(address, result);
703 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
704 mBondState.isAutoPairingAttemptsInProgress(address)) {
705 pairingAttempt(address, result);
706 } else {
707 setBondState(address, BluetoothDevice.BOND_NONE, result);
708 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
709 mBondState.clearPinAttempts(address);
710 }
711 }
712 }
713
714 /*package*/ synchronized String getPendingOutgoingBonding() {
715 return mBondState.getPendingOutgoingBonding();
716 }
717
718 private void pairingAttempt(String address, int result) {
719 // This happens when our initial guess of "0000" as the pass key
720 // fails. Try to create the bond again and display the pin dialog
721 // to the user. Use back-off while posting the delayed
722 // message. The initial value is
723 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
724 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
725 // reached, display an error to the user.
726 int attempt = mBondState.getAttempt(address);
727 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
728 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
729 mBondState.clearPinAttempts(address);
730 setBondState(address, BluetoothDevice.BOND_NONE, result);
731 return;
732 }
733
734 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
735 message.obj = address;
736 boolean postResult = mHandler.sendMessageDelayed(message,
737 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
738 if (!postResult) {
739 mBondState.clearPinAttempts(address);
740 setBondState(address,
741 BluetoothDevice.BOND_NONE, result);
742 return;
743 }
744 mBondState.attempt(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 }
746
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800747 /*package*/ BluetoothDevice getRemoteDevice(String address) {
748 return mAdapter.getRemoteDevice(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750
751 private static String toBondStateString(int bondState) {
752 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700753 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 return "not bonded";
755 case BluetoothDevice.BOND_BONDING:
756 return "bonding";
757 case BluetoothDevice.BOND_BONDED:
758 return "bonded";
759 default:
760 return "??????";
761 }
762 }
763
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 public synchronized boolean setName(String name) {
765 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
766 "Need BLUETOOTH_ADMIN permission");
767 if (name == null) {
768 return false;
769 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700770 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700773 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
774 // Either have a single property function with Object as the parameter
775 // or have a function for each property and then obfuscate in the JNI layer.
776 // The following looks dirty.
777 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800779 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700780 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700783 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800785 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700786 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 }
788
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700789 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800791 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700792 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794
795 /**
796 * Set the discoverability window for the device. A timeout of zero
797 * makes the device permanently discoverable (if the device is
798 * discoverable). Setting the timeout to a nonzero value does not make
799 * a device discoverable; you need to call setMode() to make the device
800 * explicitly discoverable.
801 *
Jake Hambyf51eada2010-09-21 13:39:53 -0700802 * @param timeout The discoverable timeout in seconds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 */
804 public synchronized boolean setDiscoverableTimeout(int timeout) {
805 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
806 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700807 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700809
Nick Pelly12835472009-09-25 15:00:29 -0700810 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -0700811 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
812 "Need WRITE_SECURE_SETTINGS permission");
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800813 boolean pairable;
814 boolean discoverable;
Nick Pelly12835472009-09-25 15:00:29 -0700815
Nick Pellyde893f52009-09-08 13:15:33 -0700816 switch (mode) {
817 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700818 pairable = false;
819 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700820 break;
821 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700822 pairable = true;
823 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700824 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700825 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700826 pairable = true;
827 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -0700828 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -0700829 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700830 default:
831 Log.w(TAG, "Requested invalid scan mode " + mode);
832 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700833 }
834 setPropertyBoolean("Pairable", pairable);
835 setPropertyBoolean("Discoverable", discoverable);
836
837 return true;
838 }
839
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800840 /*package*/ synchronized String getProperty(String name) {
841 if (!isEnabledInternal()) return null;
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800842 return mAdapterProperties.getProperty(name);
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800843 }
844
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800845 BluetoothAdapterProperties getAdapterProperties() {
846 return mAdapterProperties;
847 }
848
849 BluetoothDeviceProperties getDeviceProperties() {
850 return mDeviceProperties;
851 }
852
853 boolean isRemoteDeviceInCache(String address) {
854 return mDeviceProperties.isInCache(address);
855 }
856
857 void setRemoteDeviceProperty(String address, String name, String value) {
858 mDeviceProperties.setProperty(address, name, value);
859 }
860
861 void updateRemoteDevicePropertiesCache(String address) {
862 mDeviceProperties.updateCache(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700863 }
864
865 public synchronized String getAddress() {
866 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
867 return getProperty("Address");
868 }
869
870 public synchronized String getName() {
871 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
872 return getProperty("Name");
873 }
874
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800875 public synchronized ParcelUuid[] getUuids() {
876 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
877 String value = getProperty("UUIDs");
878 if (value == null) return null;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800879 return convertStringToParcelUuid(value);
880 }
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800881
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800882 private synchronized ParcelUuid[] convertStringToParcelUuid(String value) {
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800883 String[] uuidStrings = null;
884 // The UUIDs are stored as a "," separated string.
885 uuidStrings = value.split(",");
886 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
887
888 for (int i = 0; i < uuidStrings.length; i++) {
889 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
890 }
891 return uuids;
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800892 }
893
894
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700895 /**
896 * Returns the user-friendly name of a remote device. This value is
897 * returned from our local cache, which is updated when onPropertyChange
898 * event is received.
899 * Do not expect to retrieve the updated remote name immediately after
900 * changing the name on the remote device.
901 *
902 * @param address Bluetooth address of remote device.
903 *
904 * @return The user-friendly name of the specified remote device.
905 */
906 public synchronized String getRemoteName(String address) {
907 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700908 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700909 return null;
910 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800911 return mDeviceProperties.getProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700912 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913
914 /**
915 * Get the discoverability window for the device. A timeout of zero
916 * means that the device is permanently discoverable (if the device is
917 * in the discoverable mode).
918 *
919 * @return The discoverability window of the device, in seconds. A negative
920 * value indicates an error.
921 */
922 public synchronized int getDiscoverableTimeout() {
923 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700924 String timeout = getProperty("DiscoverableTimeout");
925 if (timeout != null)
926 return Integer.valueOf(timeout);
927 else
928 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930
931 public synchronized int getScanMode() {
932 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800933 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -0700934 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700936 boolean pairable = getProperty("Pairable").equals("true");
937 boolean discoverable = getProperty("Discoverable").equals("true");
938 return bluezStringToScanMode (pairable, discoverable);
939 }
940
941 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
943 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800944 if (!isEnabledInternal()) return false;
945
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700946 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700948
949 public synchronized boolean cancelDiscovery() {
950 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
951 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800952 if (!isEnabledInternal()) return false;
953
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700954 return stopDiscoveryNative();
955 }
956
957 public synchronized boolean isDiscovering() {
958 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
959 return mIsDiscovering;
960 }
961
962 /* package */ void setIsDiscovering(boolean isDiscovering) {
963 mIsDiscovering = isDiscovering;
964 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700966 private boolean isBondingFeasible(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
968 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800969 if (!isEnabledInternal()) return false;
970
Nick Pelly005b2282009-09-10 10:21:56 -0700971 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 return false;
973 }
974 address = address.toUpperCase();
975
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700976 if (mBondState.getPendingOutgoingBonding() != null) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800977 Log.d(TAG, "Ignoring createBond(): another device is bonding");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 // a different device is currently bonding, fail
979 return false;
980 }
981
982 // Check for bond state only if we are not performing auto
983 // pairing exponential back-off attempts.
984 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -0700985 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800986 Log.d(TAG, "Ignoring createBond(): this device is already bonding or bonded");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 return false;
988 }
989
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800990 if (address.equals(mDockAddress)) {
991 if (!writeDockPin()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800992 Log.e(TAG, "Error while writing Pin for the dock");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800993 return false;
994 }
995 }
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700996 return true;
997 }
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800998
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700999 public synchronized boolean createBond(String address) {
1000 if (!isBondingFeasible(address)) return false;
1001
1002 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 return false;
1004 }
1005
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001006 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001008
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 return true;
1010 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001012 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
1013 byte[] randomizer) {
1014 if (!isBondingFeasible(address)) return false;
1015
1016 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1017 return false;
1018 }
1019
1020 setDeviceOutOfBandData(address, hash, randomizer);
1021 mBondState.setPendingOutgoingBonding(address);
1022 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1023
1024 return true;
1025 }
1026
1027 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1028 byte[] randomizer) {
1029 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1030 "Need BLUETOOTH_ADMIN permission");
1031 if (!isEnabledInternal()) return false;
1032
1033 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1034
1035 if (DBG) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001036 Log.d(TAG, "Setting out of band data for: " + address + ":" +
1037 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001038 }
1039
1040 mDeviceOobData.put(address, value);
1041 return true;
1042 }
1043
1044 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1045 return mDeviceOobData.get(device.getAddress());
1046 }
1047
1048
1049 public synchronized byte[] readOutOfBandData() {
1050 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1051 "Need BLUETOOTH permission");
1052 if (!isEnabledInternal()) return null;
1053
1054 return readAdapterOutOfBandDataNative();
1055 }
1056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 public synchronized boolean cancelBondProcess(String address) {
1058 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1059 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001060 if (!isEnabledInternal()) return false;
1061
Nick Pelly005b2282009-09-10 10:21:56 -07001062 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 return false;
1064 }
1065 address = address.toUpperCase();
1066 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1067 return false;
1068 }
1069
Nick Pelly005b2282009-09-10 10:21:56 -07001070 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001072 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 return true;
1074 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075
1076 public synchronized boolean removeBond(String address) {
1077 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1078 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001079 if (!isEnabledInternal()) return false;
1080
Nick Pelly005b2282009-09-10 10:21:56 -07001081 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 return false;
1083 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001084 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001085 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001086 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001087 return true;
1088 } else {
1089 return false;
1090 }
1091 }
1092
1093 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshcc2c0662011-02-25 12:27:48 -08001094 // Unset the trusted device state and then unpair
1095 setTrust(address, false);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001096 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098
1099 public synchronized String[] listBonds() {
1100 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1101 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1102 }
1103
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001104 /*package*/ synchronized String[] listInState(int state) {
1105 return mBondState.listInState(state);
1106 }
1107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 public synchronized int getBondState(String address) {
1109 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001110 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001111 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 }
1113 return mBondState.getBondState(address.toUpperCase());
1114 }
1115
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001116 /*package*/ synchronized boolean setBondState(String address, int state) {
1117 return setBondState(address, state, 0);
1118 }
1119
1120 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
Arek Lichwaae5fbb02011-02-23 11:52:34 +01001121 mBondState.setBondState(address.toUpperCase(), state, reason);
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001122 return true;
1123 }
1124
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001125 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001126 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001127 Context.MODE_PRIVATE);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001128
1129 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001130 }
1131
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001132 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001133 if (!isEnabledInternal()) return null;
1134
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001135 String objectPath = getObjectPathFromAddress(address);
1136 return (String [])getDevicePropertiesNative(objectPath);
1137 }
1138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001140 * Sets the remote device trust state.
1141 *
1142 * @return boolean to indicate operation success or fail
1143 */
1144 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001145 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001146 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1147 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001148 return false;
1149 }
1150
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001151 if (!isEnabledInternal()) return false;
1152
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001153 return setDevicePropertyBooleanNative(
1154 getObjectPathFromAddress(address), "Trusted", value ? 1 : 0);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001155 }
1156
1157 /**
1158 * Gets the remote device trust state as boolean.
1159 * Note: this value may be
1160 * retrieved from cache if we retrieved the data before *
1161 *
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001162 * @return boolean to indicate trusted or untrusted state
Lixin Yueefa1dd72009-08-31 15:55:13 +08001163 */
1164 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001165 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001166 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1167 return false;
1168 }
1169
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001170 String val = mDeviceProperties.getProperty(address, "Trusted");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001171 if (val == null) {
1172 return false;
1173 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001174 return val.equals("true");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001175 }
1176 }
1177
1178 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001179 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 * integer.
1181 *
1182 * Note: this value is retrieved from cache, because we get it during
1183 * remote-device discovery.
1184 *
1185 * @return 32-bit integer encoding the remote major, minor, and service
1186 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 */
1188 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001189 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001190 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1191 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001193 String val = mDeviceProperties.getProperty(address, "Class");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001194 if (val == null)
1195 return BluetoothClass.ERROR;
1196 else {
1197 return Integer.valueOf(val);
1198 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201
1202 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001203 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001205 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001207 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001209 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 return null;
1211 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001212 return getUuidFromCache(address);
1213 }
1214
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001215 ParcelUuid[] getUuidFromCache(String address) {
1216 String value = mDeviceProperties.getProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001217 if (value == null) return null;
1218
1219 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001220 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001221 uuidStrings = value.split(",");
1222 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1223
1224 for (int i = 0; i < uuidStrings.length; i++) {
1225 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1226 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001227 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229
Nick Pelly16fb88a2009-10-07 07:44:03 +02001230 /**
1231 * Connect and fetch new UUID's using SDP.
1232 * The UUID's found are broadcast as intents.
1233 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1234 * a given uuid.
1235 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1236 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1237 * callback and broadcast intents.
1238 */
1239 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1240 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001241 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001242 if (!isEnabledInternal()) return false;
1243
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001244 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1245 return false;
1246 }
1247
Nick Pelly16fb88a2009-10-07 07:44:03 +02001248 RemoteService service = new RemoteService(address, uuid);
1249 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1250 // An SDP query for this address & uuid is already in progress
1251 // Do not add this callback for the uuid
1252 return false;
1253 }
1254
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001255 if (mUuidIntentTracker.contains(address)) {
1256 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02001257 // Add this uuid onto the in-progress SDP query
1258 if (uuid != null) {
1259 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1260 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001261 return true;
1262 }
1263
Jaikumar Ganesh421f0102011-06-06 17:31:58 +09001264 // If the device is already created, we will
1265 // do the SDP on the callback of createDeviceNative.
1266 boolean ret= createDeviceNative(address);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001267
1268 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001269 if (uuid != null) {
1270 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1271 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001272
1273 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1274 message.obj = address;
1275 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1276 return ret;
1277 }
1278
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001280 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02001281 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001283 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001284 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001285 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001286 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001287 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001289 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001290 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001291 if (!isEnabledInternal()) return -1;
1292
Nick Pelly005b2282009-09-10 10:21:56 -07001293 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001294 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001296 // Check if we are recovering from a crash.
1297 if (mDeviceProperties.isEmpty()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001298 if (mDeviceProperties.updateCache(address) == null)
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001299 return -1;
1300 }
1301
1302 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1303 if (value != null && value.containsKey(uuid))
1304 return value.get(uuid);
1305 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307
1308 public synchronized boolean setPin(String address, byte[] pin) {
1309 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1310 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001311 if (!isEnabledInternal()) return false;
1312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001314 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 return false;
1316 }
1317 address = address.toUpperCase();
1318 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1319 if (data == null) {
1320 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1321 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1322 " or by bluez.\n");
1323 return false;
1324 }
1325 // bluez API wants pin as a string
1326 String pinString;
1327 try {
1328 pinString = new String(pin, "UTF8");
1329 } catch (UnsupportedEncodingException uee) {
1330 Log.e(TAG, "UTF8 not supported?!?");
1331 return false;
1332 }
1333 return setPinNative(address, pinString, data.intValue());
1334 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001335
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001336 public synchronized boolean setPasskey(String address, int passkey) {
1337 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1338 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001339 if (!isEnabledInternal()) return false;
1340
Nick Pelly005b2282009-09-10 10:21:56 -07001341 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001342 return false;
1343 }
1344 address = address.toUpperCase();
1345 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1346 if (data == null) {
1347 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1348 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1349 " or by bluez.\n");
1350 return false;
1351 }
1352 return setPasskeyNative(address, passkey, data.intValue());
1353 }
1354
1355 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1356 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1357 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001358 if (!isEnabledInternal()) return false;
1359
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001360 address = address.toUpperCase();
1361 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1362 if (data == null) {
1363 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1364 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1365 " or by bluez.\n");
1366 return false;
1367 }
1368 return setPairingConfirmationNative(address, confirm, data.intValue());
1369 }
1370
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001371 public synchronized boolean setRemoteOutOfBandData(String address) {
1372 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1373 "Need BLUETOOTH_ADMIN permission");
1374 if (!isEnabledInternal()) return false;
1375 address = address.toUpperCase();
1376 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1377 if (data == null) {
1378 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
1379 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1380 " or by bluez.\n");
1381 return false;
1382 }
1383
1384 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
1385 byte[] hash, randomizer;
1386 if (val == null) {
1387 // TODO: check what should be passed in this case.
1388 hash = new byte[16];
1389 randomizer = new byte[16];
1390 } else {
1391 hash = val.first;
1392 randomizer = val.second;
1393 }
1394 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
1395 }
1396
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001397 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1399 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001400 if (!isEnabledInternal()) return false;
1401
Nick Pelly005b2282009-09-10 10:21:56 -07001402 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 return false;
1404 }
Nick Pelly005b2282009-09-10 10:21:56 -07001405 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001406 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407 address = address.toUpperCase();
1408 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1409 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001410 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1411 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1412 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001413 return false;
1414 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001415 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001416 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001418 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001419 if (DBG) Log.d(TAG, "updateDeviceServiceChannelCache(" + address + ")");
1420
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001421 // We are storing the rfcomm channel numbers only for the uuids
1422 // we are interested in.
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001423 ParcelUuid[] deviceUuids = getRemoteUuids(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001424
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001425 ArrayList<ParcelUuid> applicationUuids = new ArrayList<ParcelUuid>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001426
1427 synchronized (this) {
1428 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1429 if (service.address.equals(address)) {
1430 applicationUuids.add(service.uuid);
1431 }
1432 }
1433 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001434
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001435 Map <ParcelUuid, Integer> uuidToChannelMap = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001436
1437 // Retrieve RFCOMM channel for default uuids
1438 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001439 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001440 int channel = getDeviceServiceChannelForUuid(address, uuid);
1441 uuidToChannelMap.put(uuid, channel);
1442 if (DBG) Log.d(TAG, "\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001443 }
1444 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02001445 // Retrieve RFCOMM channel for application requested uuids
1446 for (ParcelUuid uuid : applicationUuids) {
1447 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001448 int channel = getDeviceServiceChannelForUuid(address, uuid);
1449 uuidToChannelMap.put(uuid, channel);
1450 if (DBG) Log.d(TAG, "\tuuid(application): " + uuid + " " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001451 }
1452 }
1453
1454 synchronized (this) {
1455 // Make application callbacks
1456 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1457 iter.hasNext();) {
1458 RemoteService service = iter.next();
1459 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001460 if (uuidToChannelMap.containsKey(service.uuid)) {
1461 int channel = uuidToChannelMap.get(service.uuid);
1462
1463 if (DBG) Log.d(TAG, "Making callback for " + service.uuid +
1464 " with result " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001465 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1466 if (callback != null) {
1467 try {
1468 callback.onRfcommChannelFound(channel);
1469 } catch (RemoteException e) {Log.e(TAG, "", e);}
1470 }
1471
1472 iter.remove();
1473 }
1474 }
1475 }
1476
1477 // Update cache
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001478 mDeviceServiceChannelCache.put(address, uuidToChannelMap);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001479 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001480 }
1481
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001482 private int getDeviceServiceChannelForUuid(String address,
1483 ParcelUuid uuid) {
1484 return getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1485 uuid.toString(), 0x0004);
1486 }
1487
Nick Pelly24bb9b82009-10-02 20:34:18 -07001488 /**
1489 * b is a handle to a Binder instance, so that this service can be notified
1490 * for Applications that terminate unexpectedly, to clean there service
1491 * records
1492 */
1493 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1494 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001495 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1496 if (!isEnabledInternal()) return -1;
1497
Nick Pelly24bb9b82009-10-02 20:34:18 -07001498 if (serviceName == null || uuid == null || channel < 1 ||
1499 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1500 return -1;
1501 }
1502 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1503 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1504 return -1;
1505 }
1506 int handle = addRfcommServiceRecordNative(serviceName,
1507 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1508 (short)channel);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001509 if (DBG) Log.d(TAG, "new handle " + Integer.toHexString(handle));
Nick Pelly24bb9b82009-10-02 20:34:18 -07001510 if (handle == -1) {
1511 return -1;
1512 }
1513
1514 int pid = Binder.getCallingPid();
1515 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1516 try {
1517 b.linkToDeath(new Reaper(handle, pid), 0);
1518 } catch (RemoteException e) {}
1519 return handle;
1520 }
1521
1522 public void removeServiceRecord(int handle) {
1523 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1524 "Need BLUETOOTH permission");
1525 checkAndRemoveRecord(handle, Binder.getCallingPid());
1526 }
1527
1528 private synchronized void checkAndRemoveRecord(int handle, int pid) {
1529 Integer handleInt = new Integer(handle);
1530 Integer owner = mServiceRecordToPid.get(handleInt);
1531 if (owner != null && pid == owner.intValue()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001532 if (DBG) Log.d(TAG, "Removing service record " +
1533 Integer.toHexString(handle) + " for pid " + pid);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001534 mServiceRecordToPid.remove(handleInt);
1535 removeServiceRecordNative(handle);
1536 }
1537 }
1538
1539 private class Reaper implements IBinder.DeathRecipient {
1540 int pid;
1541 int handle;
1542 Reaper(int handle, int pid) {
1543 this.pid = pid;
1544 this.handle = handle;
1545 }
1546 public void binderDied() {
1547 synchronized (BluetoothService.this) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001548 if (DBG) Log.d(TAG, "Tracked app " + pid + " died");
Nick Pelly24bb9b82009-10-02 20:34:18 -07001549 checkAndRemoveRecord(handle, pid);
1550 }
1551 }
1552 }
1553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1555 @Override
1556 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001557 if (intent == null) return;
1558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001559 String action = intent.getAction();
1560 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1561 ContentResolver resolver = context.getContentResolver();
1562 // Query the airplane mode from Settings.System just to make sure that
1563 // some random app is not sending this intent and disabling bluetooth
1564 boolean enabled = !isAirplaneModeOn();
1565 // If bluetooth is currently expected to be on, then enable or disable bluetooth
1566 if (Settings.Secure.getInt(resolver, Settings.Secure.BLUETOOTH_ON, 0) > 0) {
1567 if (enabled) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001568 enable(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 } else {
1570 disable(false);
1571 }
1572 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001573 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1574 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1575 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1576 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1577 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1578 mDockAddress = null;
1579 mDockPin = null;
1580 } else {
1581 SharedPreferences.Editor editor =
1582 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1583 mContext.MODE_PRIVATE).edit();
1584 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07001585 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001586 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 }
1588 }
1589 };
1590
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001591 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08001592 final ContentResolver resolver = mContext.getContentResolver();
1593 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08001595 final String toggleableRadios = Settings.System.getString(resolver,
1596 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1597
1598 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1599 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1600 mIsAirplaneToggleable = toggleableRadios == null ? false :
1601 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1602
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001604 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 }
1606 }
1607
1608 /* Returns true if airplane mode is currently on */
1609 private final boolean isAirplaneModeOn() {
1610 return Settings.System.getInt(mContext.getContentResolver(),
1611 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1612 }
1613
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001614 /* Broadcast the Uuid intent */
1615 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001616 ParcelUuid[] uuid = getUuidFromCache(address);
1617 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07001618 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001619 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1620 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001621 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001622 }
1623
1624 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1625 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1626 iter.hasNext();) {
1627 RemoteService service = iter.next();
1628 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001629 if (DBG) Log.d(TAG, "Cleaning up failed UUID channel lookup: "
1630 + service.address + " " + service.uuid);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001631 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1632 if (callback != null) {
1633 try {
1634 callback.onRfcommChannelFound(-1);
1635 } catch (RemoteException e) {Log.e(TAG, "", e);}
1636 }
1637
1638 iter.remove();
1639 }
1640 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001641 }
1642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 @Override
1644 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001645 dumpBluetoothState(pw);
1646 if (mBluetoothState != BluetoothAdapter.STATE_ON) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001647 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001649
Nick Pelly24bb9b82009-10-02 20:34:18 -07001650 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08001651 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001652
1653 pw.println("Local address = " + getAddress());
1654 pw.println("Local name = " + getName());
1655 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07001656
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001657 mAdapter.getProfileProxy(mContext,
1658 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001659 mAdapter.getProfileProxy(mContext,
1660 mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001661 mAdapter.getProfileProxy(mContext,
1662 mBluetoothProfileServiceListener, BluetoothProfile.PAN);
The Android Open Source Project10592532009-03-18 17:39:46 -07001663
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001664 dumpKnownDevices(pw);
1665 dumpAclConnectedDevices(pw);
1666 dumpHeadsetService(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001667 dumpInputDeviceProfile(pw);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001668 dumpPanProfile(pw);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001669 dumpApplicationServiceRecords(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001670 }
1671
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001672 private void dumpHeadsetService(PrintWriter pw) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001673 pw.println("\n--Headset Service--");
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001674 if (mBluetoothHeadset != null) {
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001675 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
1676 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001677 pw.println("No headsets connected");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001678 } else {
1679 BluetoothDevice device = deviceList.get(0);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001680 pw.println("\ngetConnectedDevices[0] = " + device);
1681 dumpHeadsetConnectionState(pw, device);
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001682 pw.println("getBatteryUsageHint() = " +
1683 mBluetoothHeadset.getBatteryUsageHint(device));
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001684 }
1685
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001686 deviceList.clear();
1687 deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001688 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001689 pw.println("--Connected and Disconnected Headsets");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001690 for (BluetoothDevice device: deviceList) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001691 pw.println(device);
1692 if (mBluetoothHeadset.isAudioConnected(device)) {
1693 pw.println("SCO audio connected to device:" + device);
1694 }
1695 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001696 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001697 mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
1698 }
Nick Pelly6c901db2009-06-19 10:08:09 -07001699
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001700 private void dumpInputDeviceProfile(PrintWriter pw) {
1701 pw.println("\n--Bluetooth Service- Input Device Profile");
1702 if (mInputDevice != null) {
1703 List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices();
1704 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001705 pw.println("No input devices connected");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001706 } else {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001707 pw.println("Number of connected devices:" + deviceList.size());
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001708 BluetoothDevice device = deviceList.get(0);
1709 pw.println("getConnectedDevices[0] = " + device);
1710 pw.println("Priority of Connected device = " + mInputDevice.getPriority(device));
1711
1712 switch (mInputDevice.getConnectionState(device)) {
1713 case BluetoothInputDevice.STATE_CONNECTING:
1714 pw.println("getConnectionState() = STATE_CONNECTING");
1715 break;
1716 case BluetoothInputDevice.STATE_CONNECTED:
1717 pw.println("getConnectionState() = STATE_CONNECTED");
1718 break;
1719 case BluetoothInputDevice.STATE_DISCONNECTING:
1720 pw.println("getConnectionState() = STATE_DISCONNECTING");
1721 break;
1722 }
1723 }
1724 deviceList.clear();
1725 deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] {
1726 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1727 pw.println("--Connected and Disconnected input devices");
1728 for (BluetoothDevice device: deviceList) {
1729 pw.println(device);
1730 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001731 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001732 mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 }
1734
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001735 private void dumpPanProfile(PrintWriter pw) {
1736 pw.println("\n--Bluetooth Service- Pan Profile");
1737 if (mPan != null) {
1738 List<BluetoothDevice> deviceList = mPan.getConnectedDevices();
1739 if (deviceList.size() == 0) {
1740 pw.println("No Pan devices connected");
1741 } else {
1742 pw.println("Number of connected devices:" + deviceList.size());
1743 BluetoothDevice device = deviceList.get(0);
1744 pw.println("getConnectedDevices[0] = " + device);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001745
1746 switch (mPan.getConnectionState(device)) {
1747 case BluetoothInputDevice.STATE_CONNECTING:
1748 pw.println("getConnectionState() = STATE_CONNECTING");
1749 break;
1750 case BluetoothInputDevice.STATE_CONNECTED:
1751 pw.println("getConnectionState() = STATE_CONNECTED");
1752 break;
1753 case BluetoothInputDevice.STATE_DISCONNECTING:
1754 pw.println("getConnectionState() = STATE_DISCONNECTING");
1755 break;
1756 }
1757 }
1758 deviceList.clear();
1759 deviceList = mPan.getDevicesMatchingConnectionStates(new int[] {
1760 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1761 pw.println("--Connected and Disconnected Pan devices");
1762 for (BluetoothDevice device: deviceList) {
1763 pw.println(device);
1764 }
1765 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001766 }
1767
1768 private void dumpHeadsetConnectionState(PrintWriter pw,
1769 BluetoothDevice device) {
1770 switch (mBluetoothHeadset.getConnectionState(device)) {
1771 case BluetoothHeadset.STATE_CONNECTING:
1772 pw.println("getConnectionState() = STATE_CONNECTING");
1773 break;
1774 case BluetoothHeadset.STATE_CONNECTED:
1775 pw.println("getConnectionState() = STATE_CONNECTED");
1776 break;
1777 case BluetoothHeadset.STATE_DISCONNECTING:
1778 pw.println("getConnectionState() = STATE_DISCONNECTING");
1779 break;
1780 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
1781 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
1782 break;
1783 }
1784 }
1785
1786 private void dumpApplicationServiceRecords(PrintWriter pw) {
1787 pw.println("\n--Application Service Records--");
1788 for (Integer handle : mServiceRecordToPid.keySet()) {
1789 Integer pid = mServiceRecordToPid.get(handle);
1790 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
1791 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001792 mAdapter.closeProfileProxy(BluetoothProfile.PAN, mBluetoothHeadset);
1793 }
1794
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001795 private void dumpAclConnectedDevices(PrintWriter pw) {
1796 String[] devicesObjectPath = getKnownDevices();
1797 pw.println("\n--ACL connected devices--");
1798 if (devicesObjectPath != null) {
1799 for (String device : devicesObjectPath) {
1800 pw.println(getAddressFromObjectPath(device));
1801 }
1802 }
1803 }
1804
1805 private void dumpKnownDevices(PrintWriter pw) {
1806 pw.println("\n--Known devices--");
1807 for (String address : mDeviceProperties.keySet()) {
1808 int bondState = mBondState.getBondState(address);
1809 pw.printf("%s %10s (%d) %s\n", address,
1810 toBondStateString(bondState),
1811 mBondState.getAttempt(address),
1812 getRemoteName(address));
1813
1814 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
1815 if (uuidChannels == null) {
1816 pw.println("\tuuids = null");
1817 } else {
1818 for (ParcelUuid uuid : uuidChannels.keySet()) {
1819 Integer channel = uuidChannels.get(uuid);
1820 if (channel == null) {
1821 pw.println("\t" + uuid);
1822 } else {
1823 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
1824 }
1825 }
1826 }
1827 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1828 if (service.address.equals(address)) {
1829 pw.println("\tPENDING CALLBACK: " + service.uuid);
1830 }
1831 }
1832 }
1833 }
1834
1835 private void dumpBluetoothState(PrintWriter pw) {
1836 switch(mBluetoothState) {
1837 case BluetoothAdapter.STATE_OFF:
1838 pw.println("Bluetooth OFF\n");
1839 break;
1840 case BluetoothAdapter.STATE_TURNING_ON:
1841 pw.println("Bluetooth TURNING ON\n");
1842 break;
1843 case BluetoothAdapter.STATE_TURNING_OFF:
1844 pw.println("Bluetooth TURNING OFF\n");
1845 break;
1846 case BluetoothAdapter.STATE_ON:
1847 pw.println("Bluetooth ON\n");
1848 break;
1849 default:
1850 pw.println("Bluetooth UNKNOWN STATE " + mBluetoothState);
1851 }
1852 }
1853
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001854 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1855 new BluetoothProfile.ServiceListener() {
1856 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001857 if (profile == BluetoothProfile.HEADSET) {
1858 mBluetoothHeadset = (BluetoothHeadset) proxy;
1859 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1860 mInputDevice = (BluetoothInputDevice) proxy;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001861 } else if (profile == BluetoothProfile.PAN) {
1862 mPan = (BluetoothPan) proxy;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001863 }
1864 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001865 public void onServiceDisconnected(int profile) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001866 if (profile == BluetoothProfile.HEADSET) {
1867 mBluetoothHeadset = null;
1868 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1869 mInputDevice = null;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001870 } else if (profile == BluetoothProfile.PAN) {
1871 mPan = null;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001872 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001873 }
1874 };
1875
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001876 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1877 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001878 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001879 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001880 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001881 else
Nick Pellybd022f42009-08-14 18:33:38 -07001882 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001883 }
1884
1885 /* package */ static String scanModeToBluezString(int mode) {
1886 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07001887 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07001889 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07001891 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001892 return "discoverable";
1893 }
1894 return null;
1895 }
1896
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001897 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001898 String adapterObjectPath = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001899 if (adapterObjectPath == null || objectPath == null) {
Jake Hambyf51eada2010-09-21 13:39:53 -07001900 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001901 " or deviceObjectPath:" + objectPath + " is null");
1902 return null;
1903 }
1904 if (!objectPath.startsWith(adapterObjectPath)) {
Jake Hambyf51eada2010-09-21 13:39:53 -07001905 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001906 " is not a prefix of deviceObjectPath:" + objectPath +
1907 "bluetoothd crashed ?");
1908 return null;
1909 }
1910 String address = objectPath.substring(adapterObjectPath.length());
1911 if (address != null) return address.replace('_', ':');
1912
1913 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1914 return null;
1915 }
1916
1917 /*package*/ String getObjectPathFromAddress(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001918 String path = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001919 if (path == null) {
1920 Log.e(TAG, "Error: Object Path is null");
1921 return null;
1922 }
1923 path = path + address.replace(":", "_");
1924 return path;
1925 }
1926
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08001927 /*package */ void setLinkTimeout(String address, int num_slots) {
1928 String path = getObjectPathFromAddress(address);
1929 boolean result = setLinkTimeoutNative(path, num_slots);
1930
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001931 if (!result) Log.d(TAG, "Set Link Timeout to " + num_slots + " slots failed");
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08001932 }
1933
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001934 /**** Handlers for PAN Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001935 // TODO: This needs to be converted to a state machine.
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001936
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001937 public boolean isTetheringOn() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001938 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001939 synchronized (mBluetoothPanProfileHandler) {
1940 return mBluetoothPanProfileHandler.isTetheringOn();
1941 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001942 }
1943
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001944 /*package*/boolean allowIncomingTethering() {
1945 synchronized (mBluetoothPanProfileHandler) {
1946 return mBluetoothPanProfileHandler.allowIncomingTethering();
1947 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001948 }
1949
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001950 public void setBluetoothTethering(boolean value) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001951 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001952 synchronized (mBluetoothPanProfileHandler) {
1953 mBluetoothPanProfileHandler.setBluetoothTethering(value);
1954 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001955 }
1956
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001957 public int getPanDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001958 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001959 synchronized (mBluetoothPanProfileHandler) {
1960 return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
1961 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001962 }
1963
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001964 public boolean connectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001965 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1966 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001967 synchronized (mBluetoothPanProfileHandler) {
1968 return mBluetoothPanProfileHandler.connectPanDevice(device);
1969 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001970 }
1971
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001972 public List<BluetoothDevice> getConnectedPanDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001973 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001974 synchronized (mBluetoothPanProfileHandler) {
1975 return mBluetoothPanProfileHandler.getConnectedPanDevices();
1976 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001977 }
1978
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001979 public List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001980 int[] states) {
1981 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001982 synchronized (mBluetoothPanProfileHandler) {
1983 return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
1984 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001985 }
1986
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001987 public boolean disconnectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001988 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1989 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001990 synchronized (mBluetoothPanProfileHandler) {
1991 return mBluetoothPanProfileHandler.disconnectPanDevice(device);
1992 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001993 }
1994
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001995 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001996 String iface,
1997 int state,
1998 int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001999 synchronized (mBluetoothPanProfileHandler) {
2000 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
2001 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002002 }
2003
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002004 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002005 int state, int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002006 synchronized (mBluetoothPanProfileHandler) {
2007 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
2008 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002009 }
2010
2011 /**** Handlers for Input Device Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002012 // This needs to be converted to state machine
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002013
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002014 public boolean connectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002015 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2016 "Need BLUETOOTH_ADMIN permission");
2017 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002018 synchronized (mBluetoothInputProfileHandler) {
2019 return mBluetoothInputProfileHandler.connectInputDevice(device, state);
2020 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002021 }
2022
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002023 public boolean connectInputDeviceInternal(BluetoothDevice device) {
2024 synchronized (mBluetoothInputProfileHandler) {
2025 return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
2026 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002027 }
2028
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002029 public boolean disconnectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002030 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2031 "Need BLUETOOTH_ADMIN permission");
2032 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002033 synchronized (mBluetoothInputProfileHandler) {
2034 return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
2035 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002036 }
2037
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002038 public boolean disconnectInputDeviceInternal(BluetoothDevice device) {
2039 synchronized (mBluetoothInputProfileHandler) {
2040 return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
2041 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002042 }
2043
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002044 public int getInputDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002045 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002046 synchronized (mBluetoothInputProfileHandler) {
2047 return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
2048 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002049 }
2050
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002051 public List<BluetoothDevice> getConnectedInputDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002052 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002053 synchronized (mBluetoothInputProfileHandler) {
2054 return mBluetoothInputProfileHandler.getConnectedInputDevices();
2055 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002056 }
2057
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002058 public List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002059 int[] states) {
2060 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002061 synchronized (mBluetoothInputProfileHandler) {
2062 return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
2063 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002064 }
2065
2066
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002067 public int getInputDevicePriority(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002068 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002069 synchronized (mBluetoothInputProfileHandler) {
2070 return mBluetoothInputProfileHandler.getInputDevicePriority(device);
2071 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002072 }
2073
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002074 public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002075 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2076 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002077 synchronized (mBluetoothInputProfileHandler) {
2078 return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
2079 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002080 }
2081
Matthew Xiea0c68032011-06-25 21:47:07 -07002082 public boolean allowIncomingHidConnect(BluetoothDevice device, boolean allow) {
2083 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2084 "Need BLUETOOTH_ADMIN permission");
2085 String address = device.getAddress();
2086 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2087 return false;
2088 }
2089
2090 Integer data = getAuthorizationAgentRequestData(address);
2091 if (data == null) {
2092 Log.w(TAG, "allowIncomingHidConnect(" + device +
2093 ") called but no native data available");
2094 return false;
2095 }
2096 if (DBG) log("allowIncomingHidConnect: " + device + " : " + allow + " : " + data);
2097 return setAuthorizationNative(address, allow, data.intValue());
2098 }
2099
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002100 /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
2101 synchronized (mBluetoothInputProfileHandler) {
2102 return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
2103 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002104 }
2105
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002106 /*package*/void handleInputDevicePropertyChange(String address, boolean connected) {
2107 synchronized (mBluetoothInputProfileHandler) {
2108 mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
2109 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002110 }
2111
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002112 /**** Handlers for Health Device Profile ****/
2113 // TODO: All these need to be converted to a state machine.
2114
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002115 public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
2116 IBluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002117 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2118 "Need BLUETOOTH permission");
2119 synchronized (mBluetoothHealthProfileHandler) {
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002120 return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002121 }
2122 }
2123
2124 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
2125 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2126 "Need BLUETOOTH permission");
2127 synchronized (mBluetoothHealthProfileHandler) {
2128 return mBluetoothHealthProfileHandler.unregisterAppConfiguration(config);
2129 }
2130 }
2131
2132
2133 public boolean connectChannelToSource(BluetoothDevice device,
2134 BluetoothHealthAppConfiguration config) {
2135 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2136 "Need BLUETOOTH permission");
2137 synchronized (mBluetoothHealthProfileHandler) {
2138 return mBluetoothHealthProfileHandler.connectChannelToSource(device,
2139 config);
2140 }
2141 }
2142
2143 public boolean connectChannelToSink(BluetoothDevice device,
2144 BluetoothHealthAppConfiguration config, int channelType) {
2145 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2146 "Need BLUETOOTH permission");
2147 synchronized (mBluetoothHealthProfileHandler) {
2148 return mBluetoothHealthProfileHandler.connectChannel(device, config,
2149 channelType);
2150 }
2151 }
2152
2153 public boolean disconnectChannel(BluetoothDevice device,
2154 BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
2155 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2156 "Need BLUETOOTH permission");
2157 synchronized (mBluetoothHealthProfileHandler) {
2158 return mBluetoothHealthProfileHandler.disconnectChannel(device, config, fd);
2159 }
2160 }
2161
2162 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
2163 BluetoothHealthAppConfiguration config) {
2164 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2165 "Need BLUETOOTH permission");
2166 synchronized (mBluetoothHealthProfileHandler) {
2167 return mBluetoothHealthProfileHandler.getMainChannelFd(device, config);
2168 }
2169 }
2170
2171 /*package*/ void onHealthDevicePropertyChanged(String devicePath,
2172 String channelPath) {
2173 synchronized (mBluetoothHealthProfileHandler) {
2174 mBluetoothHealthProfileHandler.onHealthDevicePropertyChanged(devicePath,
2175 channelPath);
2176 }
2177 }
2178
2179 /*package*/ void onHealthDeviceChannelChanged(String devicePath,
2180 String channelPath, boolean exists) {
2181 synchronized(mBluetoothHealthProfileHandler) {
2182 mBluetoothHealthProfileHandler.onHealthDeviceChannelChanged(devicePath,
2183 channelPath, exists);
2184 }
2185 }
2186
2187 public int getHealthDeviceConnectionState(BluetoothDevice device) {
2188 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2189 "Need BLUETOOTH permission");
2190 synchronized (mBluetoothHealthProfileHandler) {
2191 return mBluetoothHealthProfileHandler.getHealthDeviceConnectionState(device);
2192 }
2193 }
2194
2195 public List<BluetoothDevice> getConnectedHealthDevices() {
2196 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2197 "Need BLUETOOTH permission");
2198 synchronized (mBluetoothHealthProfileHandler) {
2199 return mBluetoothHealthProfileHandler.getConnectedHealthDevices();
2200 }
2201 }
2202
2203 public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(
2204 int[] states) {
2205 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2206 "Need BLUETOOTH permission");
2207 synchronized (mBluetoothHealthProfileHandler) {
2208 return mBluetoothHealthProfileHandler.
2209 getHealthDevicesMatchingConnectionStates(states);
2210 }
2211 }
2212
Matthew Xiea0c68032011-06-25 21:47:07 -07002213 /*package*/boolean notifyIncomingHidConnection(String address) {
2214 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2215 if (state == null) {
2216 return false;
2217 }
2218 Message msg = new Message();
2219 msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING;
2220 state.sendMessage(msg);
2221 return true;
2222 }
2223
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002224 public boolean connectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002225 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2226
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002227 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002228 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002229 Message msg = new Message();
2230 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2231 msg.obj = state;
2232 mHfpProfileState.sendMessage(msg);
2233 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002234 }
2235 return false;
2236 }
2237
2238 public boolean disconnectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002239 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2240
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002241 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002242 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002243 Message msg = new Message();
2244 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2245 msg.obj = state;
2246 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002247 return true;
2248 }
2249 return false;
2250 }
2251
2252 public boolean connectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002253 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2254
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002255 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002256 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002257 Message msg = new Message();
2258 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2259 msg.obj = state;
2260 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002261 return true;
2262 }
2263 return false;
2264 }
2265
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002266 public boolean disconnectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002267 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2268
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002269 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2270 if (state != null) {
2271 Message msg = new Message();
2272 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2273 msg.obj = state;
2274 mA2dpProfileState.sendMessage(msg);
2275 return true;
2276 }
2277 return false;
2278 }
2279
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002280 BluetoothDeviceProfileState addProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002281 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002282 if (state != null) return state;
2283
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002284 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService);
2285 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002286 state.start();
2287 return state;
2288 }
2289
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002290 void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002291 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002292 }
2293
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002294 synchronized String[] getKnownDevices() {
2295 String[] bonds = null;
2296 String val = getProperty("Devices");
2297 if (val != null) {
2298 bonds = val.split(",");
2299 }
2300 return bonds;
2301 }
2302
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002303 private void initProfileState() {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002304 String[] bonds = null;
2305 String val = mAdapterProperties.getProperty("Devices");
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002306 if (val != null) {
2307 bonds = val.split(",");
2308 }
2309 if (bonds == null) {
2310 return;
2311 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002312 for (String path : bonds) {
2313 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002314 BluetoothDeviceProfileState state = addProfileState(address);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002315 }
2316 }
2317
2318 private void autoConnect() {
2319 String[] bonds = getKnownDevices();
2320 if (bonds == null) {
2321 return;
2322 }
2323 for (String path : bonds) {
2324 String address = getAddressFromObjectPath(path);
2325 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2326 if (state != null) {
2327 Message msg = new Message();
2328 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
2329 state.sendMessage(msg);
2330 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002331 }
2332 }
2333
2334 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002335 BluetoothDeviceProfileState state =
2336 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002337 if (state != null) {
2338 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002339 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002340 state.sendMessage(msg);
2341 return true;
2342 }
2343 return false;
2344 }
2345
2346 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002347 BluetoothDeviceProfileState state =
2348 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002349 if (state != null) {
2350 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002351 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002352 state.sendMessage(msg);
2353 return true;
2354 }
2355 return false;
2356 }
2357
2358 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2359 mA2dpService = a2dpService;
2360 }
2361
Matthew Xiea0c68032011-06-25 21:47:07 -07002362 /*package*/ Integer getAuthorizationAgentRequestData(String address) {
2363 Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
2364 return data;
2365 }
2366
Jaikumar Ganesh70a053b2010-10-13 15:54:30 -07002367 public void sendProfileStateMessage(int profile, int cmd) {
2368 Message msg = new Message();
2369 msg.what = cmd;
2370 if (profile == BluetoothProfileState.HFP) {
2371 mHfpProfileState.sendMessage(msg);
2372 } else if (profile == BluetoothProfileState.A2DP) {
2373 mA2dpProfileState.sendMessage(msg);
2374 }
2375 }
2376
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002377 public int getAdapterConnectionState() {
2378 return mAdapterConnectionState;
2379 }
2380
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002381 public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
2382 int prevState) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002383 // Since this is a binder call check if Bluetooth is on still
2384 if (mBluetoothState == BluetoothAdapter.STATE_OFF) return;
2385
2386 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
2387 if (!validateProfileConnectionState(state) ||
2388 !validateProfileConnectionState(prevState)) {
2389 // Previously, an invalid state was broadcast anyway,
2390 // with the invalid state converted to -1 in the intent.
2391 // Better to log an error and not send an intent with
2392 // invalid contents or set mAdapterConnectionState to -1.
2393 Log.e(TAG, "Error in sendConnectionStateChange: "
2394 + "prevState " + prevState + " state " + state);
2395 return;
2396 }
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002397
2398 mAdapterConnectionState = state;
2399
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002400 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
2401 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002402 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
2403 convertToAdapterState(state));
2404 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
2405 convertToAdapterState(prevState));
Jaikumar Ganesh60b4d2a2011-07-12 14:14:49 -07002406 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002407 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002408 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
2409 + prevState + " -> " + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002410 }
2411 }
2412
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002413 private boolean validateProfileConnectionState(int state) {
2414 return (state == BluetoothProfile.STATE_DISCONNECTED ||
2415 state == BluetoothProfile.STATE_CONNECTING ||
2416 state == BluetoothProfile.STATE_CONNECTED ||
2417 state == BluetoothProfile.STATE_DISCONNECTING);
2418 }
2419
2420 private int convertToAdapterState(int state) {
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002421 switch (state) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002422 case BluetoothProfile.STATE_DISCONNECTED:
2423 return BluetoothAdapter.STATE_DISCONNECTED;
2424 case BluetoothProfile.STATE_DISCONNECTING:
2425 return BluetoothAdapter.STATE_DISCONNECTING;
2426 case BluetoothProfile.STATE_CONNECTED:
2427 return BluetoothAdapter.STATE_CONNECTED;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002428 case BluetoothProfile.STATE_CONNECTING:
2429 return BluetoothAdapter.STATE_CONNECTING;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002430 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002431 Log.e(TAG, "Error in convertToAdapterState");
2432 return -1;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002433 }
2434
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002435 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
2436 switch (prevState) {
2437 case BluetoothProfile.STATE_CONNECTING:
2438 mProfilesConnecting--;
2439 break;
2440
2441 case BluetoothProfile.STATE_CONNECTED:
2442 mProfilesConnected--;
2443 break;
2444
2445 case BluetoothProfile.STATE_DISCONNECTING:
2446 mProfilesDisconnecting--;
2447 break;
2448 }
2449
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002450 switch (state) {
2451 case BluetoothProfile.STATE_CONNECTING:
2452 mProfilesConnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002453 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002454
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002455 case BluetoothProfile.STATE_CONNECTED:
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002456 mProfilesConnected++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002457 return (mProfilesConnected == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002458
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002459 case BluetoothProfile.STATE_DISCONNECTING:
2460 mProfilesDisconnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002461 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002462
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002463 case BluetoothProfile.STATE_DISCONNECTED:
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002464 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002465
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002466 default:
2467 return true;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002468 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002469 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002470
Matthew Xiea0c68032011-06-25 21:47:07 -07002471 private void createIncomingConnectionStateFile() {
2472 File f = new File(INCOMING_CONNECTION_FILE);
2473 if (!f.exists()) {
2474 try {
2475 f.createNewFile();
2476 } catch (IOException e) {
2477 Log.e(TAG, "IOException: cannot create file");
2478 }
2479 }
2480 }
2481
2482 /** @hide */
2483 public Pair<Integer, String> getIncomingState(String address) {
2484 if (mIncomingConnections.isEmpty()) {
2485 createIncomingConnectionStateFile();
2486 readIncomingConnectionState();
2487 }
2488 return mIncomingConnections.get(address);
2489 }
2490
2491 private void readIncomingConnectionState() {
2492 synchronized(mIncomingConnections) {
2493 FileInputStream fstream = null;
2494 try {
2495 fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
2496 DataInputStream in = new DataInputStream(fstream);
2497 BufferedReader file = new BufferedReader(new InputStreamReader(in));
2498 String line;
2499 while((line = file.readLine()) != null) {
2500 line = line.trim();
2501 if (line.length() == 0) continue;
2502 String[] value = line.split(",");
2503 if (value != null && value.length == 3) {
2504 Integer val1 = Integer.parseInt(value[1]);
2505 Pair<Integer, String> val = new Pair(val1, value[2]);
2506 mIncomingConnections.put(value[0], val);
2507 }
2508 }
2509 } catch (FileNotFoundException e) {
2510 log("FileNotFoundException: readIncomingConnectionState" + e.toString());
2511 } catch (IOException e) {
2512 log("IOException: readIncomingConnectionState" + e.toString());
2513 } finally {
2514 if (fstream != null) {
2515 try {
2516 fstream.close();
2517 } catch (IOException e) {
2518 // Ignore
2519 }
2520 }
2521 }
2522 }
2523 }
2524
2525 private void truncateIncomingConnectionFile() {
2526 RandomAccessFile r = null;
2527 try {
2528 r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
2529 r.setLength(0);
2530 } catch (FileNotFoundException e) {
2531 log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
2532 } catch (IOException e) {
2533 log("IOException: truncateIncomingConnectionState" + e.toString());
2534 } finally {
2535 if (r != null) {
2536 try {
2537 r.close();
2538 } catch (IOException e) {
2539 // ignore
2540 }
2541 }
2542 }
2543 }
2544
2545 /** @hide */
2546 public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
2547 synchronized(mIncomingConnections) {
2548 mIncomingConnections.put(address, data);
2549
2550 truncateIncomingConnectionFile();
2551 BufferedWriter out = null;
2552 StringBuilder value = new StringBuilder();
2553 try {
2554 out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
2555 for (String devAddress: mIncomingConnections.keySet()) {
2556 Pair<Integer, String> val = mIncomingConnections.get(devAddress);
2557 value.append(devAddress);
2558 value.append(",");
2559 value.append(val.first.toString());
2560 value.append(",");
2561 value.append(val.second);
2562 value.append("\n");
2563 }
2564 out.write(value.toString());
2565 } catch (FileNotFoundException e) {
2566 log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
2567 } catch (IOException e) {
2568 log("IOException: writeIncomingConnectionState" + e.toString());
2569 } finally {
2570 if (out != null) {
2571 try {
2572 out.close();
2573 } catch (IOException e) {
2574 // Ignore
2575 }
2576 }
2577 }
2578 }
2579 }
2580
2581 private static void log(String msg) {
2582 Log.d(TAG, msg);
2583 }
2584
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002585 private native static void classInitNative();
2586 private native void initializeNativeDataNative();
2587 private native boolean setupNativeDataNative();
2588 private native boolean tearDownNativeDataNative();
2589 private native void cleanupNativeDataNative();
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002590 /*package*/ native String getAdapterPathNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002591
2592 private native int isEnabledNative();
2593 private native int enableNative();
2594 private native int disableNative();
2595
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002596 /*package*/ native Object[] getAdapterPropertiesNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002597 private native Object[] getDevicePropertiesNative(String objectPath);
2598 private native boolean setAdapterPropertyStringNative(String key, String value);
2599 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2600 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2601
2602 private native boolean startDiscoveryNative();
2603 private native boolean stopDiscoveryNative();
2604
2605 private native boolean createPairedDeviceNative(String address, int timeout_ms);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002606 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2607 private native byte[] readAdapterOutOfBandDataNative();
2608
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002609 private native boolean cancelDeviceCreationNative(String address);
2610 private native boolean removeDeviceNative(String objectPath);
2611 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2612 int attributeId);
2613
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002614 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002615 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002616 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2617 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2618 int nativeData);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002619 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2620 byte[] randomizer, int nativeData);
2621
Nick Pelly24bb9b82009-10-02 20:34:18 -07002622 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2623 int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002624 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002625 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002626
Nick Pelly24bb9b82009-10-02 20:34:18 -07002627 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2628 short channel);
2629 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002630 private native boolean setLinkTimeoutNative(String path, int num_slots);
Matthew Xiea0c68032011-06-25 21:47:07 -07002631
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002632 native boolean connectInputDeviceNative(String path);
2633 native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002634
Jaikumar Ganesha8571f12011-02-11 15:46:54 -08002635 native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2636 native boolean connectPanDeviceNative(String path, String dstRole);
2637 native boolean disconnectPanDeviceNative(String path);
2638 native boolean disconnectPanServerDeviceNative(String path,
Jaikumar Ganesha44a1e72011-02-11 12:40:44 -08002639 String address, String iface);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -08002640
2641 private native int[] addReservedServiceRecordsNative(int[] uuuids);
2642 private native boolean removeReservedServiceRecordsNative(int[] handles);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002643
2644 // Health API
2645 native String registerHealthApplicationNative(int dataType, String role, String name,
2646 String channelType);
2647 native String registerHealthApplicationNative(int dataType, String role, String name);
2648 native boolean unregisterHealthApplicationNative(String path);
2649 native boolean createChannelNative(String devicePath, String appPath, String channelType);
2650 native boolean destroyChannelNative(String devicePath, String channelpath);
2651 native String getMainChannelNative(String path);
2652 native String getChannelApplicationNative(String channelPath);
2653 native ParcelFileDescriptor getChannelFdNative(String channelPath);
2654 native boolean releaseChannelFdNative(String channelPath);
Matthew Xiea0c68032011-06-25 21:47:07 -07002655 native boolean setAuthorizationNative(String address, boolean value, int data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002656}