blob: 3a563e6f3f10f55176b13ffdb78ca6c18e098523 [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;
Matthew Xie7f9ecca82011-07-15 13:03:58 -070094 private BluetoothAdapterStateMachine 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
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700114 private static final int MESSAGE_UUID_INTENT = 1;
115 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700116
117 // The time (in millisecs) to delay the pairing attempt after the first
118 // auto pairing attempt fails. We use an exponential delay with
119 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
120 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
121 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
122 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700123
124 // The timeout used to sent the UUIDs Intent
125 // This timeout should be greater than the page timeout
126 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700127
Nick Pelly16fb88a2009-10-07 07:44:03 +0200128 /** Always retrieve RFCOMM channel for these SDP UUIDs */
129 private static final ParcelUuid[] RFCOMM_UUIDS = {
130 BluetoothUuid.Handsfree,
131 BluetoothUuid.HSP,
132 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700133
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800134 private final BluetoothAdapterProperties mAdapterProperties;
135 private final BluetoothDeviceProperties mDeviceProperties;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200136
137 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
138 private final ArrayList<String> mUuidIntentTracker;
139 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700140
Nick Pelly24bb9b82009-10-02 20:34:18 -0700141 private final HashMap<Integer, Integer> mServiceRecordToPid;
142
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700143 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
144 private final BluetoothProfileState mA2dpProfileState;
145 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700146
147 private BluetoothA2dpService mA2dpService;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700148 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700149
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700150 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700151
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800152 private static String mDockAddress;
153 private String mDockPin;
154
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700155 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800156 private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800157 private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700158 private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler;
Matthew Xiea0c68032011-06-25 21:47:07 -0700159 private static final String INCOMING_CONNECTION_FILE =
160 "/data/misc/bluetooth/incoming_connection.conf";
161 private HashMap<String, Pair<Integer, String>> mIncomingConnections;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700162
Nick Pelly16fb88a2009-10-07 07:44:03 +0200163 private static class RemoteService {
164 public String address;
165 public ParcelUuid uuid;
166 public RemoteService(String address, ParcelUuid uuid) {
167 this.address = address;
168 this.uuid = uuid;
169 }
170 @Override
171 public boolean equals(Object o) {
172 if (o instanceof RemoteService) {
173 RemoteService service = (RemoteService)o;
174 return address.equals(service.address) && uuid.equals(service.uuid);
175 }
176 return false;
177 }
Kenny Root5f614162010-02-17 12:00:37 -0800178
179 @Override
180 public int hashCode() {
181 int hash = 1;
182 hash = hash * 31 + (address == null ? 0 : address.hashCode());
183 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
184 return hash;
185 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200186 }
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 static {
189 classInitNative();
190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191
Nick Pellybd022f42009-08-14 18:33:38 -0700192 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700194
195 // Need to do this in place of:
196 // mBatteryStats = BatteryStatsService.getService();
197 // Since we can not import BatteryStatsService from here. This class really needs to be
198 // moved to java/services/com/android/server/
199 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700202
203 if (isEnabledNative() == 1) {
204 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
205 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 mIsDiscovering = false;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800209
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800210 mBondState = new BluetoothBondState(context, this);
211 mAdapterProperties = new BluetoothAdapterProperties(context, this);
212 mDeviceProperties = new BluetoothDeviceProperties(this);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700213
214 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700215 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700216 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200217 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700218 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700219 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
220 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
221 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
222
223 mHfpProfileState.start();
224 mA2dpProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800225
226 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800227 registerForAirplaneMode(filter);
228
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800229 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800230 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800231 mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800232 mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700233 mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
Matthew Xiea0c68032011-06-25 21:47:07 -0700234 mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800235 }
236
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700237 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800238 if (mDockAddress != null) return mDockAddress;
239
240 BufferedInputStream file = null;
241 String dockAddress;
242 try {
243 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
244 byte[] address = new byte[17];
245 file.read(address);
246 dockAddress = new String(address);
247 dockAddress = dockAddress.toUpperCase();
248 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
249 mDockAddress = dockAddress;
250 return mDockAddress;
251 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800252 Log.e(TAG, "CheckBluetoothAddress failed for car dock address: "
253 + dockAddress);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800254 }
255 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800256 Log.e(TAG, "FileNotFoundException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800257 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800258 Log.e(TAG, "IOException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800259 } finally {
260 if (file != null) {
261 try {
262 file.close();
263 } catch (IOException e) {
264 // Ignore
265 }
266 }
267 }
268 mDockAddress = null;
269 return null;
270 }
271
272 private synchronized boolean writeDockPin() {
273 BufferedWriter out = null;
274 try {
275 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
276
277 // Generate a random 4 digit pin between 0000 and 9999
278 // This is not truly random but good enough for our purposes.
279 int pin = (int) Math.floor(Math.random() * 10000);
280
281 mDockPin = String.format("%04d", pin);
282 out.write(mDockPin);
283 return true;
284 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800285 Log.e(TAG, "FileNotFoundException while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800286 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800287 Log.e(TAG, "IOException while while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800288 } finally {
289 if (out != null) {
290 try {
291 out.close();
292 } catch (IOException e) {
293 // Ignore
294 }
295 }
296 }
297 mDockPin = null;
298 return false;
299 }
300
301 /*package*/ synchronized String getDockPin() {
302 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700303 }
304
305 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200306 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700307 mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter);
308 mBluetoothState.start();
309 mEventLoop = mBluetoothState.getBluetoothEventLoop();
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() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700332 return (getBluetoothStateInternal() == 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");
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700337 return getBluetoothStateInternal();
The Android Open Source Project10592532009-03-18 17:39:46 -0700338 }
339
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800340 int getBluetoothStateInternal() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700341 return mBluetoothState.getBluetoothAdapterState();
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800342 }
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
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700359 int adapterState = getBluetoothStateInternal();
360
361 switch (adapterState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700362 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700363 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700364 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700365 break;
366 default:
367 return false;
368 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800369
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700370 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_OFF, saveSetting);
The Android Open Source Project10592532009-03-18 17:39:46 -0700371 return true;
372 }
373
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700374 synchronized void disconnectDevices() {
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700375 // Disconnect devices handled by BluetoothService.
376 for (BluetoothDevice device: getConnectedInputDevices()) {
377 disconnectInputDevice(device);
378 }
379
380 for (BluetoothDevice device: getConnectedPanDevices()) {
381 disconnectPanDevice(device);
382 }
383 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700384
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700385 /**
386 * The Bluetooth has been turned off, but hot. Do bonding, profile,
387 * and internal cleanup
388 */
389 synchronized void finishDisable() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 // mark in progress bondings as cancelled
391 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700392 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
394 }
395
Jaikumar Ganesh2d1fc4e2010-12-13 15:54:33 -0800396 // Stop the profile state machine for bonded devices.
397 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) {
398 removeProfileState(address);
399 }
400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700402 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
403 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
405
The Android Open Source Project10592532009-03-18 17:39:46 -0700406 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700407 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700408 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700409
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700410 mProfilesConnected = 0;
411 mProfilesConnecting = 0;
412 mProfilesDisconnecting = 0;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700413 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800414 mAdapterUuids = null;
415 mAdapterSdpHandles = null;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700416
The Android Open Source Project10592532009-03-18 17:39:46 -0700417 // Log bluetooth off to battery stats.
418 long ident = Binder.clearCallingIdentity();
419 try {
420 mBatteryStats.noteBluetoothOff();
421 } catch (RemoteException e) {
422 } finally {
423 Binder.restoreCallingIdentity(ident);
424 }
Nick Pelly997c7612009-03-24 20:44:48 -0700425
426 if (mRestart) {
427 mRestart = false;
428 enable();
429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 }
431
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700432 /**
433 * power off Bluetooth
434 */
435 synchronized void shutoffBluetooth() {
436 tearDownNativeDataNative();
437 disableNative();
438 if (mRestart) {
439 mRestart = false;
440 enable();
441 }
442 }
443
The Android Open Source Project10592532009-03-18 17:39:46 -0700444 /** Bring up BT and persist BT on in settings */
445 public boolean enable() {
446 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 }
448
449 /**
450 * Enable this Bluetooth device, asynchronously.
451 * This turns on/off the underlying hardware.
452 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700453 * @param saveSetting If true, persist the new state of BT in settings
454 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700456 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
458 "Need BLUETOOTH_ADMIN permission");
459
460 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800461 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 return false;
463 }
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700464 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
465 return true;
466 }
467
468 /**
469 * Turn on Bluetooth Module, Load firmware, and do all the preparation
470 * needed to get the Bluetooth Module ready but keep it not discoverable
471 * and not connectable.
472 */
473 /* package */ synchronized boolean prepareBluetooth() {
474 if (!setupNativeDataNative()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 return false;
476 }
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700477 mIsDiscovering = false;
478
479 switchConnectable(false);
480 updateSdpRecords();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 return true;
482 }
483
Nick Pelly997c7612009-03-24 20:44:48 -0700484 /** Forcibly restart Bluetooth if it is on */
485 /* package */ synchronized void restart() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700486 if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700487 return;
488 }
489 mRestart = true;
490 if (!disable(false)) {
491 mRestart = false;
492 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700493 }
Nick Pelly997c7612009-03-24 20:44:48 -0700494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 private final Handler mHandler = new Handler() {
496 @Override
497 public void handleMessage(Message msg) {
498 switch (msg.what) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700499 case MESSAGE_UUID_INTENT:
500 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200501 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700502 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200503 makeServiceChannelCallbacks(address);
504 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700505 break;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700506 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
507 address = (String)msg.obj;
Jaikumar Ganesh8adcacb2011-07-21 13:49:54 -0700508 if (address == null) return;
509 int attempt = mBondState.getAttempt(address);
510
511 // Try only if attemps are in progress and cap it 2 attempts
512 // The 2 attempts cap is a fail safe if the stack returns
513 // an incorrect error code for bonding failures and if the pin
514 // is entered wrongly twice we should abort.
515 if (attempt > 0 && attempt <= 2) {
516 mBondState.attempt(address);
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700517 createBond(address);
518 return;
519 }
Jaikumar Ganesh8adcacb2011-07-21 13:49:54 -0700520 if (attempt > 0) mBondState.clearPinAttempts(address);
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700521 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 }
523 }
524 };
525
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800526 private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
527 //Register SDP records.
528 int[] svcIdentifiers = new int[uuids.size()];
529 for (int i = 0; i < uuids.size(); i++) {
530 svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
531 }
532 mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
533 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700534
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800535 private synchronized void updateSdpRecords() {
536 ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
537
538 // Add the default records
539 uuids.add(BluetoothUuid.HSP_AG);
540 uuids.add(BluetoothUuid.ObexObjectPush);
541
542 if (mContext.getResources().
543 getBoolean(com.android.internal.R.bool.config_voice_capable)) {
544 uuids.add(BluetoothUuid.Handsfree_AG);
545 uuids.add(BluetoothUuid.PBAP_PSE);
546 }
547
548 // Add SDP records for profiles maintained by Android userspace
549 addReservedSdpRecords(uuids);
550
551 // Enable profiles maintained by Bluez userspace.
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800552 setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
553 BluetoothPanProfileHandler.NAP_BRIDGE);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800554
555 // Add SDP records for profiles maintained by Bluez userspace
556 uuids.add(BluetoothUuid.AudioSource);
557 uuids.add(BluetoothUuid.AvrcpTarget);
558 uuids.add(BluetoothUuid.NAP);
559
560 // Cannot cast uuids.toArray directly since ParcelUuid is parcelable
561 mAdapterUuids = new ParcelUuid[uuids.size()];
562 for (int i = 0; i < uuids.size(); i++) {
563 mAdapterUuids[i] = uuids.get(i);
564 }
565 }
566
Jaikumar Ganesh9efb1342011-02-11 18:10:31 -0800567 /**
568 * This function is called from Bluetooth Event Loop when onPropertyChanged
569 * for adapter comes in with UUID property.
570 * @param uuidsThe uuids of adapter as reported by Bluez.
571 */
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700572 /*package*/ synchronized void updateBluetoothState(String uuids) {
573 ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800574
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700575 if (mAdapterUuids != null &&
576 BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
577 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579 }
580
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700581 /**
582 * This method is called immediately after Bluetooth module is turned on.
583 * It starts auto-connection and places bluetooth on sign onto the battery
584 * stats
585 */
586 /*package*/ void runBluetooth() {
587 mIsDiscovering = false;
588 mBondState.readAutoPairingData();
589 mBondState.initBondState();
590 initProfileState();
591
592 autoConnect();
593
594 // Log bluetooth on to battery stats.
595 long ident = Binder.clearCallingIdentity();
596 try {
597 mBatteryStats.noteBluetoothOn();
598 } catch (RemoteException e) {
599 Log.e(TAG, "", e);
600 } finally {
601 Binder.restoreCallingIdentity(ident);
602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 }
604
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700605 /*package*/ synchronized boolean attemptAutoPair(String address) {
606 if (!mBondState.hasAutoPairingFailed(address) &&
607 !mBondState.isAutoPairingBlacklisted(address)) {
608 mBondState.attempt(address);
609 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
610 return true;
611 }
612 return false;
613 }
614
Jaikumar Ganeshf487d722011-01-12 10:12:01 -0800615 /*package*/ synchronized boolean isFixedPinZerosAutoPairKeyboard(String address) {
616 // Check for keyboards which have fixed PIN 0000 as the pairing pin
617 return mBondState.isFixedPinZerosAutoPairKeyboard(address);
618 }
619
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700620 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
621 if (result == BluetoothDevice.BOND_SUCCESS) {
622 setBondState(address, BluetoothDevice.BOND_BONDED);
623 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
624 mBondState.clearPinAttempts(address);
625 }
626 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
627 mBondState.getAttempt(address) == 1) {
628 mBondState.addAutoPairingFailure(address);
629 pairingAttempt(address, result);
630 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
631 mBondState.isAutoPairingAttemptsInProgress(address)) {
632 pairingAttempt(address, result);
633 } else {
634 setBondState(address, BluetoothDevice.BOND_NONE, result);
635 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
636 mBondState.clearPinAttempts(address);
637 }
638 }
639 }
640
641 /*package*/ synchronized String getPendingOutgoingBonding() {
642 return mBondState.getPendingOutgoingBonding();
643 }
644
645 private void pairingAttempt(String address, int result) {
646 // This happens when our initial guess of "0000" as the pass key
647 // fails. Try to create the bond again and display the pin dialog
648 // to the user. Use back-off while posting the delayed
649 // message. The initial value is
650 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
651 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
652 // reached, display an error to the user.
653 int attempt = mBondState.getAttempt(address);
654 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
655 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
656 mBondState.clearPinAttempts(address);
657 setBondState(address, BluetoothDevice.BOND_NONE, result);
658 return;
659 }
660
661 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
662 message.obj = address;
663 boolean postResult = mHandler.sendMessageDelayed(message,
664 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
665 if (!postResult) {
666 mBondState.clearPinAttempts(address);
667 setBondState(address,
668 BluetoothDevice.BOND_NONE, result);
669 return;
670 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 }
672
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800673 /*package*/ BluetoothDevice getRemoteDevice(String address) {
674 return mAdapter.getRemoteDevice(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676
677 private static String toBondStateString(int bondState) {
678 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700679 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 return "not bonded";
681 case BluetoothDevice.BOND_BONDING:
682 return "bonding";
683 case BluetoothDevice.BOND_BONDED:
684 return "bonded";
685 default:
686 return "??????";
687 }
688 }
689
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 public synchronized boolean setName(String name) {
691 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
692 "Need BLUETOOTH_ADMIN permission");
693 if (name == null) {
694 return false;
695 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700696 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700699 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
700 // Either have a single property function with Object as the parameter
701 // or have a function for each property and then obfuscate in the JNI layer.
702 // The following looks dirty.
703 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800705 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700706 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700709 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800711 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700712 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 }
714
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700715 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800717 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700718 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800719 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720
721 /**
722 * Set the discoverability window for the device. A timeout of zero
723 * makes the device permanently discoverable (if the device is
724 * discoverable). Setting the timeout to a nonzero value does not make
725 * a device discoverable; you need to call setMode() to make the device
726 * explicitly discoverable.
727 *
Jake Hambyf51eada2010-09-21 13:39:53 -0700728 * @param timeout The discoverable timeout in seconds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 */
730 public synchronized boolean setDiscoverableTimeout(int timeout) {
731 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
732 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700733 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700735
Nick Pelly12835472009-09-25 15:00:29 -0700736 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -0700737 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
738 "Need WRITE_SECURE_SETTINGS permission");
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700739 return setScanMode(mode, duration, true);
740 }
741
742 /**
743 * @param on true set the local Bluetooth module to be connectable
744 * but not dicoverable
745 * false set the local Bluetooth module to be not connectable
746 * and not dicoverable
747 */
748 /*package*/ synchronized void switchConnectable(boolean on) {
749 if (on) {
750 // 0 is a dummy value, does not apply for SCAN_MODE_CONNECTABLE
751 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, 0, false);
752 } else {
753 // 0 is a dummy value, does not apply for SCAN_MODE_NONE
754 setScanMode(BluetoothAdapter.SCAN_MODE_NONE, 0, false);
755 }
756 }
757
758 private synchronized boolean setScanMode(int mode, int duration, boolean allowOnlyInOnState) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800759 boolean pairable;
760 boolean discoverable;
Nick Pelly12835472009-09-25 15:00:29 -0700761
Nick Pellyde893f52009-09-08 13:15:33 -0700762 switch (mode) {
763 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700764 pairable = false;
765 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700766 break;
767 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700768 pairable = true;
769 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700770 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700771 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700772 pairable = true;
773 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -0700774 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -0700775 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700776 default:
777 Log.w(TAG, "Requested invalid scan mode " + mode);
778 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700779 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700780
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700781 if (allowOnlyInOnState) {
782 setPropertyBoolean("Pairable", pairable);
783 setPropertyBoolean("Discoverable", discoverable);
784 } else {
785 // allowed to set the property through native layer directly
786 setAdapterPropertyBooleanNative("Pairable", pairable ? 1 : 0);
787 setAdapterPropertyBooleanNative("Discoverable", discoverable ? 1 : 0);
788 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700789 return true;
790 }
791
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800792 /*package*/ synchronized String getProperty(String name) {
793 if (!isEnabledInternal()) return null;
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800794 return mAdapterProperties.getProperty(name);
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800795 }
796
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800797 BluetoothAdapterProperties getAdapterProperties() {
798 return mAdapterProperties;
799 }
800
801 BluetoothDeviceProperties getDeviceProperties() {
802 return mDeviceProperties;
803 }
804
805 boolean isRemoteDeviceInCache(String address) {
806 return mDeviceProperties.isInCache(address);
807 }
808
809 void setRemoteDeviceProperty(String address, String name, String value) {
810 mDeviceProperties.setProperty(address, name, value);
811 }
812
813 void updateRemoteDevicePropertiesCache(String address) {
814 mDeviceProperties.updateCache(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700815 }
816
817 public synchronized String getAddress() {
818 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
819 return getProperty("Address");
820 }
821
822 public synchronized String getName() {
823 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
824 return getProperty("Name");
825 }
826
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800827 public synchronized ParcelUuid[] getUuids() {
828 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
829 String value = getProperty("UUIDs");
830 if (value == null) return null;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800831 return convertStringToParcelUuid(value);
832 }
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800833
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800834 private synchronized ParcelUuid[] convertStringToParcelUuid(String value) {
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800835 String[] uuidStrings = null;
836 // The UUIDs are stored as a "," separated string.
837 uuidStrings = value.split(",");
838 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
839
840 for (int i = 0; i < uuidStrings.length; i++) {
841 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
842 }
843 return uuids;
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800844 }
845
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700846 /**
847 * Returns the user-friendly name of a remote device. This value is
848 * returned from our local cache, which is updated when onPropertyChange
849 * event is received.
850 * Do not expect to retrieve the updated remote name immediately after
851 * changing the name on the remote device.
852 *
853 * @param address Bluetooth address of remote device.
854 *
855 * @return The user-friendly name of the specified remote device.
856 */
857 public synchronized String getRemoteName(String address) {
858 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700859 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700860 return null;
861 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800862 return mDeviceProperties.getProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864
865 /**
Matthew Xie269e81a2011-07-26 18:36:49 -0700866 * Returns alias of a remote device. This value is returned from our
867 * local cache, which is updated when onPropertyChange event is received.
868 *
869 * @param address Bluetooth address of remote device.
870 *
871 * @return The alias of the specified remote device.
872 */
873 public synchronized String getRemoteAlias(String address) {
874
875 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
876 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
877 return null;
878 }
879 return mDeviceProperties.getProperty(address, "Alias");
880 }
881
882 /**
883 * Set the alias of a remote device.
884 *
885 * @param address Bluetooth address of remote device.
886 * @param alias new alias to change to
887 * @return true on success, false on error
888 */
889 public synchronized boolean setRemoteAlias(String address, String alias) {
890 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
891 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
892 return false;
893 }
894
895 return setDevicePropertyStringNative(getObjectPathFromAddress(address),
896 "Alias", alias);
897 }
898
899 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 * Get the discoverability window for the device. A timeout of zero
901 * means that the device is permanently discoverable (if the device is
902 * in the discoverable mode).
903 *
904 * @return The discoverability window of the device, in seconds. A negative
905 * value indicates an error.
906 */
907 public synchronized int getDiscoverableTimeout() {
908 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700909 String timeout = getProperty("DiscoverableTimeout");
910 if (timeout != null)
911 return Integer.valueOf(timeout);
912 else
913 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915
916 public synchronized int getScanMode() {
917 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800918 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -0700919 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700921 boolean pairable = getProperty("Pairable").equals("true");
922 boolean discoverable = getProperty("Discoverable").equals("true");
923 return bluezStringToScanMode (pairable, discoverable);
924 }
925
926 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
928 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800929 if (!isEnabledInternal()) return false;
930
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700931 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700933
934 public synchronized boolean cancelDiscovery() {
935 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
936 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800937 if (!isEnabledInternal()) return false;
938
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700939 return stopDiscoveryNative();
940 }
941
942 public synchronized boolean isDiscovering() {
943 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
944 return mIsDiscovering;
945 }
946
947 /* package */ void setIsDiscovering(boolean isDiscovering) {
948 mIsDiscovering = isDiscovering;
949 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700951 private boolean isBondingFeasible(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
953 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800954 if (!isEnabledInternal()) return false;
955
Nick Pelly005b2282009-09-10 10:21:56 -0700956 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 return false;
958 }
959 address = address.toUpperCase();
960
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700961 if (mBondState.getPendingOutgoingBonding() != null) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800962 Log.d(TAG, "Ignoring createBond(): another device is bonding");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 // a different device is currently bonding, fail
964 return false;
965 }
966
967 // Check for bond state only if we are not performing auto
968 // pairing exponential back-off attempts.
969 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -0700970 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800971 Log.d(TAG, "Ignoring createBond(): this device is already bonding or bonded");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 return false;
973 }
974
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800975 if (address.equals(mDockAddress)) {
976 if (!writeDockPin()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800977 Log.e(TAG, "Error while writing Pin for the dock");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800978 return false;
979 }
980 }
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700981 return true;
982 }
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800983
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700984 public synchronized boolean createBond(String address) {
985 if (!isBondingFeasible(address)) return false;
986
987 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 return false;
989 }
990
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700991 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700993
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 return true;
995 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700997 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
998 byte[] randomizer) {
999 if (!isBondingFeasible(address)) return false;
1000
1001 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1002 return false;
1003 }
1004
1005 setDeviceOutOfBandData(address, hash, randomizer);
1006 mBondState.setPendingOutgoingBonding(address);
1007 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1008
1009 return true;
1010 }
1011
1012 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1013 byte[] randomizer) {
1014 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1015 "Need BLUETOOTH_ADMIN permission");
1016 if (!isEnabledInternal()) return false;
1017
1018 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1019
1020 if (DBG) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001021 Log.d(TAG, "Setting out of band data for: " + address + ":" +
1022 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001023 }
1024
1025 mDeviceOobData.put(address, value);
1026 return true;
1027 }
1028
1029 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1030 return mDeviceOobData.get(device.getAddress());
1031 }
1032
1033
1034 public synchronized byte[] readOutOfBandData() {
1035 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1036 "Need BLUETOOTH permission");
1037 if (!isEnabledInternal()) return null;
1038
1039 return readAdapterOutOfBandDataNative();
1040 }
1041
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 public synchronized boolean cancelBondProcess(String address) {
1043 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1044 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001045 if (!isEnabledInternal()) return false;
1046
Nick Pelly005b2282009-09-10 10:21:56 -07001047 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 return false;
1049 }
1050 address = address.toUpperCase();
1051 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1052 return false;
1053 }
1054
Nick Pelly005b2282009-09-10 10:21:56 -07001055 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001057 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 return true;
1059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060
1061 public synchronized boolean removeBond(String address) {
1062 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1063 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001064 if (!isEnabledInternal()) return false;
1065
Nick Pelly005b2282009-09-10 10:21:56 -07001066 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 return false;
1068 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001069 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001070 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001071 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001072 return true;
1073 } else {
1074 return false;
1075 }
1076 }
1077
1078 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshcc2c0662011-02-25 12:27:48 -08001079 // Unset the trusted device state and then unpair
1080 setTrust(address, false);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001081 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083
1084 public synchronized String[] listBonds() {
1085 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1086 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1087 }
1088
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001089 /*package*/ synchronized String[] listInState(int state) {
1090 return mBondState.listInState(state);
1091 }
1092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 public synchronized int getBondState(String address) {
1094 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001095 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001096 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
1098 return mBondState.getBondState(address.toUpperCase());
1099 }
1100
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001101 /*package*/ synchronized boolean setBondState(String address, int state) {
1102 return setBondState(address, state, 0);
1103 }
1104
1105 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
Arek Lichwaae5fbb02011-02-23 11:52:34 +01001106 mBondState.setBondState(address.toUpperCase(), state, reason);
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001107 return true;
1108 }
1109
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001110 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001111 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001112 Context.MODE_PRIVATE);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001113
1114 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001115 }
1116
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001117 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001118 if (!isEnabledInternal()) return null;
1119
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001120 String objectPath = getObjectPathFromAddress(address);
1121 return (String [])getDevicePropertiesNative(objectPath);
1122 }
1123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001125 * Sets the remote device trust state.
1126 *
1127 * @return boolean to indicate operation success or fail
1128 */
1129 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001130 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001131 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1132 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001133 return false;
1134 }
1135
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001136 if (!isEnabledInternal()) return false;
1137
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001138 return setDevicePropertyBooleanNative(
1139 getObjectPathFromAddress(address), "Trusted", value ? 1 : 0);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001140 }
1141
1142 /**
1143 * Gets the remote device trust state as boolean.
1144 * Note: this value may be
1145 * retrieved from cache if we retrieved the data before *
1146 *
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001147 * @return boolean to indicate trusted or untrusted state
Lixin Yueefa1dd72009-08-31 15:55:13 +08001148 */
1149 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001150 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001151 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1152 return false;
1153 }
1154
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001155 String val = mDeviceProperties.getProperty(address, "Trusted");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001156 if (val == null) {
1157 return false;
1158 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001159 return val.equals("true");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001160 }
1161 }
1162
1163 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001164 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 * integer.
1166 *
1167 * Note: this value is retrieved from cache, because we get it during
1168 * remote-device discovery.
1169 *
1170 * @return 32-bit integer encoding the remote major, minor, and service
1171 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 */
1173 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001174 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001175 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1176 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001178 String val = mDeviceProperties.getProperty(address, "Class");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001179 if (val == null)
1180 return BluetoothClass.ERROR;
1181 else {
1182 return Integer.valueOf(val);
1183 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186
1187 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001188 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001190 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001192 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001193 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001194 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 return null;
1196 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001197 return getUuidFromCache(address);
1198 }
1199
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001200 ParcelUuid[] getUuidFromCache(String address) {
1201 String value = mDeviceProperties.getProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001202 if (value == null) return null;
1203
1204 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001205 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001206 uuidStrings = value.split(",");
1207 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1208
1209 for (int i = 0; i < uuidStrings.length; i++) {
1210 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1211 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001212 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214
Nick Pelly16fb88a2009-10-07 07:44:03 +02001215 /**
1216 * Connect and fetch new UUID's using SDP.
1217 * The UUID's found are broadcast as intents.
1218 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1219 * a given uuid.
1220 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1221 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1222 * callback and broadcast intents.
1223 */
1224 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1225 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001226 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001227 if (!isEnabledInternal()) return false;
1228
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001229 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1230 return false;
1231 }
1232
Nick Pelly16fb88a2009-10-07 07:44:03 +02001233 RemoteService service = new RemoteService(address, uuid);
1234 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1235 // An SDP query for this address & uuid is already in progress
1236 // Do not add this callback for the uuid
1237 return false;
1238 }
1239
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001240 if (mUuidIntentTracker.contains(address)) {
1241 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02001242 // Add this uuid onto the in-progress SDP query
1243 if (uuid != null) {
1244 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1245 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001246 return true;
1247 }
1248
Jaikumar Ganesh421f0102011-06-06 17:31:58 +09001249 // If the device is already created, we will
1250 // do the SDP on the callback of createDeviceNative.
1251 boolean ret= createDeviceNative(address);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001252
1253 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001254 if (uuid != null) {
1255 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1256 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001257
1258 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1259 message.obj = address;
1260 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1261 return ret;
1262 }
1263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001265 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02001266 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001268 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001269 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001271 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001272 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001274 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001276 if (!isEnabledInternal()) return -1;
1277
Nick Pelly005b2282009-09-10 10:21:56 -07001278 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001279 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001281 // Check if we are recovering from a crash.
1282 if (mDeviceProperties.isEmpty()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001283 if (mDeviceProperties.updateCache(address) == null)
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001284 return -1;
1285 }
1286
1287 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1288 if (value != null && value.containsKey(uuid))
1289 return value.get(uuid);
1290 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292
1293 public synchronized boolean setPin(String address, byte[] pin) {
1294 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1295 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001296 if (!isEnabledInternal()) return false;
1297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001299 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 return false;
1301 }
1302 address = address.toUpperCase();
1303 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1304 if (data == null) {
1305 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1306 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1307 " or by bluez.\n");
1308 return false;
1309 }
1310 // bluez API wants pin as a string
1311 String pinString;
1312 try {
1313 pinString = new String(pin, "UTF8");
1314 } catch (UnsupportedEncodingException uee) {
1315 Log.e(TAG, "UTF8 not supported?!?");
1316 return false;
1317 }
1318 return setPinNative(address, pinString, data.intValue());
1319 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001321 public synchronized boolean setPasskey(String address, int passkey) {
1322 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1323 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001324 if (!isEnabledInternal()) return false;
1325
Nick Pelly005b2282009-09-10 10:21:56 -07001326 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001327 return false;
1328 }
1329 address = address.toUpperCase();
1330 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1331 if (data == null) {
1332 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1333 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1334 " or by bluez.\n");
1335 return false;
1336 }
1337 return setPasskeyNative(address, passkey, data.intValue());
1338 }
1339
1340 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1341 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1342 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001343 if (!isEnabledInternal()) return false;
1344
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001345 address = address.toUpperCase();
1346 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1347 if (data == null) {
1348 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1349 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1350 " or by bluez.\n");
1351 return false;
1352 }
1353 return setPairingConfirmationNative(address, confirm, data.intValue());
1354 }
1355
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001356 public synchronized boolean setRemoteOutOfBandData(String address) {
1357 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1358 "Need BLUETOOTH_ADMIN permission");
1359 if (!isEnabledInternal()) return false;
1360 address = address.toUpperCase();
1361 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1362 if (data == null) {
1363 Log.w(TAG, "setRemoteOobData(" + 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
1369 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
1370 byte[] hash, randomizer;
1371 if (val == null) {
1372 // TODO: check what should be passed in this case.
1373 hash = new byte[16];
1374 randomizer = new byte[16];
1375 } else {
1376 hash = val.first;
1377 randomizer = val.second;
1378 }
1379 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
1380 }
1381
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001382 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1384 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001385 if (!isEnabledInternal()) return false;
1386
Nick Pelly005b2282009-09-10 10:21:56 -07001387 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 return false;
1389 }
Nick Pelly005b2282009-09-10 10:21:56 -07001390 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001391 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 address = address.toUpperCase();
1393 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1394 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001395 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1396 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1397 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 return false;
1399 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001400 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001403 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001404 if (DBG) Log.d(TAG, "updateDeviceServiceChannelCache(" + address + ")");
1405
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001406 // We are storing the rfcomm channel numbers only for the uuids
1407 // we are interested in.
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001408 ParcelUuid[] deviceUuids = getRemoteUuids(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001409
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001410 ArrayList<ParcelUuid> applicationUuids = new ArrayList<ParcelUuid>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001411
1412 synchronized (this) {
1413 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1414 if (service.address.equals(address)) {
1415 applicationUuids.add(service.uuid);
1416 }
1417 }
1418 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001419
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001420 Map <ParcelUuid, Integer> uuidToChannelMap = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001421
1422 // Retrieve RFCOMM channel for default uuids
1423 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001424 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001425 int channel = getDeviceServiceChannelForUuid(address, uuid);
1426 uuidToChannelMap.put(uuid, channel);
1427 if (DBG) Log.d(TAG, "\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001428 }
1429 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02001430 // Retrieve RFCOMM channel for application requested uuids
1431 for (ParcelUuid uuid : applicationUuids) {
1432 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001433 int channel = getDeviceServiceChannelForUuid(address, uuid);
1434 uuidToChannelMap.put(uuid, channel);
1435 if (DBG) Log.d(TAG, "\tuuid(application): " + uuid + " " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001436 }
1437 }
1438
1439 synchronized (this) {
1440 // Make application callbacks
1441 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1442 iter.hasNext();) {
1443 RemoteService service = iter.next();
1444 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001445 if (uuidToChannelMap.containsKey(service.uuid)) {
1446 int channel = uuidToChannelMap.get(service.uuid);
1447
1448 if (DBG) Log.d(TAG, "Making callback for " + service.uuid +
1449 " with result " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001450 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1451 if (callback != null) {
1452 try {
1453 callback.onRfcommChannelFound(channel);
1454 } catch (RemoteException e) {Log.e(TAG, "", e);}
1455 }
1456
1457 iter.remove();
1458 }
1459 }
1460 }
1461
1462 // Update cache
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001463 mDeviceServiceChannelCache.put(address, uuidToChannelMap);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001464 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001465 }
1466
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001467 private int getDeviceServiceChannelForUuid(String address,
1468 ParcelUuid uuid) {
1469 return getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1470 uuid.toString(), 0x0004);
1471 }
1472
Nick Pelly24bb9b82009-10-02 20:34:18 -07001473 /**
1474 * b is a handle to a Binder instance, so that this service can be notified
1475 * for Applications that terminate unexpectedly, to clean there service
1476 * records
1477 */
1478 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1479 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001480 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1481 if (!isEnabledInternal()) return -1;
1482
Nick Pelly24bb9b82009-10-02 20:34:18 -07001483 if (serviceName == null || uuid == null || channel < 1 ||
1484 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1485 return -1;
1486 }
1487 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1488 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1489 return -1;
1490 }
1491 int handle = addRfcommServiceRecordNative(serviceName,
1492 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1493 (short)channel);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001494 if (DBG) Log.d(TAG, "new handle " + Integer.toHexString(handle));
Nick Pelly24bb9b82009-10-02 20:34:18 -07001495 if (handle == -1) {
1496 return -1;
1497 }
1498
1499 int pid = Binder.getCallingPid();
1500 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1501 try {
1502 b.linkToDeath(new Reaper(handle, pid), 0);
1503 } catch (RemoteException e) {}
1504 return handle;
1505 }
1506
1507 public void removeServiceRecord(int handle) {
1508 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1509 "Need BLUETOOTH permission");
1510 checkAndRemoveRecord(handle, Binder.getCallingPid());
1511 }
1512
1513 private synchronized void checkAndRemoveRecord(int handle, int pid) {
1514 Integer handleInt = new Integer(handle);
1515 Integer owner = mServiceRecordToPid.get(handleInt);
1516 if (owner != null && pid == owner.intValue()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001517 if (DBG) Log.d(TAG, "Removing service record " +
1518 Integer.toHexString(handle) + " for pid " + pid);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001519 mServiceRecordToPid.remove(handleInt);
1520 removeServiceRecordNative(handle);
1521 }
1522 }
1523
1524 private class Reaper implements IBinder.DeathRecipient {
1525 int pid;
1526 int handle;
1527 Reaper(int handle, int pid) {
1528 this.pid = pid;
1529 this.handle = handle;
1530 }
1531 public void binderDied() {
1532 synchronized (BluetoothService.this) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001533 if (DBG) Log.d(TAG, "Tracked app " + pid + " died");
Nick Pelly24bb9b82009-10-02 20:34:18 -07001534 checkAndRemoveRecord(handle, pid);
1535 }
1536 }
1537 }
1538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1540 @Override
1541 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001542 if (intent == null) return;
1543
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 String action = intent.getAction();
1545 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1546 ContentResolver resolver = context.getContentResolver();
1547 // Query the airplane mode from Settings.System just to make sure that
1548 // some random app is not sending this intent and disabling bluetooth
Matthew Xie7f9ecca82011-07-15 13:03:58 -07001549 if (isAirplaneModeOn()) {
1550 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_ON);
1551 } else {
1552 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001553 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001554 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1555 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1556 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1557 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1558 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1559 mDockAddress = null;
1560 mDockPin = null;
1561 } else {
1562 SharedPreferences.Editor editor =
1563 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1564 mContext.MODE_PRIVATE).edit();
1565 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07001566 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001567 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001568 }
1569 }
1570 };
1571
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001572 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08001573 final ContentResolver resolver = mContext.getContentResolver();
1574 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08001576 final String toggleableRadios = Settings.System.getString(resolver,
1577 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1578
1579 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1580 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1581 mIsAirplaneToggleable = toggleableRadios == null ? false :
1582 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001585 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 }
1587 }
1588
1589 /* Returns true if airplane mode is currently on */
1590 private final boolean isAirplaneModeOn() {
1591 return Settings.System.getInt(mContext.getContentResolver(),
1592 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1593 }
1594
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001595 /* Broadcast the Uuid intent */
1596 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001597 ParcelUuid[] uuid = getUuidFromCache(address);
1598 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07001599 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001600 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1601 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001602 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001603 }
1604
1605 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1606 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1607 iter.hasNext();) {
1608 RemoteService service = iter.next();
1609 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001610 if (DBG) Log.d(TAG, "Cleaning up failed UUID channel lookup: "
1611 + service.address + " " + service.uuid);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001612 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1613 if (callback != null) {
1614 try {
1615 callback.onRfcommChannelFound(-1);
1616 } catch (RemoteException e) {Log.e(TAG, "", e);}
1617 }
1618
1619 iter.remove();
1620 }
1621 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001622 }
1623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 @Override
1625 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Matthew Xie7f9ecca82011-07-15 13:03:58 -07001626 if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001627 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001629
Nick Pelly24bb9b82009-10-02 20:34:18 -07001630 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08001631 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001632
1633 pw.println("Local address = " + getAddress());
1634 pw.println("Local name = " + getName());
1635 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07001636
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001637 mAdapter.getProfileProxy(mContext,
1638 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001639 mAdapter.getProfileProxy(mContext,
1640 mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001641 mAdapter.getProfileProxy(mContext,
1642 mBluetoothProfileServiceListener, BluetoothProfile.PAN);
The Android Open Source Project10592532009-03-18 17:39:46 -07001643
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001644 dumpKnownDevices(pw);
1645 dumpAclConnectedDevices(pw);
1646 dumpHeadsetService(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001647 dumpInputDeviceProfile(pw);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001648 dumpPanProfile(pw);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001649 dumpApplicationServiceRecords(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001650 }
1651
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001652 private void dumpHeadsetService(PrintWriter pw) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001653 pw.println("\n--Headset Service--");
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001654 if (mBluetoothHeadset != null) {
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001655 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
1656 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001657 pw.println("No headsets connected");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001658 } else {
1659 BluetoothDevice device = deviceList.get(0);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001660 pw.println("\ngetConnectedDevices[0] = " + device);
1661 dumpHeadsetConnectionState(pw, device);
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001662 pw.println("getBatteryUsageHint() = " +
1663 mBluetoothHeadset.getBatteryUsageHint(device));
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001664 }
1665
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001666 deviceList.clear();
1667 deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001668 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001669 pw.println("--Connected and Disconnected Headsets");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001670 for (BluetoothDevice device: deviceList) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001671 pw.println(device);
1672 if (mBluetoothHeadset.isAudioConnected(device)) {
1673 pw.println("SCO audio connected to device:" + device);
1674 }
1675 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001676 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001677 mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
1678 }
Nick Pelly6c901db2009-06-19 10:08:09 -07001679
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001680 private void dumpInputDeviceProfile(PrintWriter pw) {
1681 pw.println("\n--Bluetooth Service- Input Device Profile");
1682 if (mInputDevice != null) {
1683 List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices();
1684 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001685 pw.println("No input devices connected");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001686 } else {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001687 pw.println("Number of connected devices:" + deviceList.size());
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001688 BluetoothDevice device = deviceList.get(0);
1689 pw.println("getConnectedDevices[0] = " + device);
1690 pw.println("Priority of Connected device = " + mInputDevice.getPriority(device));
1691
1692 switch (mInputDevice.getConnectionState(device)) {
1693 case BluetoothInputDevice.STATE_CONNECTING:
1694 pw.println("getConnectionState() = STATE_CONNECTING");
1695 break;
1696 case BluetoothInputDevice.STATE_CONNECTED:
1697 pw.println("getConnectionState() = STATE_CONNECTED");
1698 break;
1699 case BluetoothInputDevice.STATE_DISCONNECTING:
1700 pw.println("getConnectionState() = STATE_DISCONNECTING");
1701 break;
1702 }
1703 }
1704 deviceList.clear();
1705 deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] {
1706 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1707 pw.println("--Connected and Disconnected input devices");
1708 for (BluetoothDevice device: deviceList) {
1709 pw.println(device);
1710 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001711 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001712 mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001715 private void dumpPanProfile(PrintWriter pw) {
1716 pw.println("\n--Bluetooth Service- Pan Profile");
1717 if (mPan != null) {
1718 List<BluetoothDevice> deviceList = mPan.getConnectedDevices();
1719 if (deviceList.size() == 0) {
1720 pw.println("No Pan devices connected");
1721 } else {
1722 pw.println("Number of connected devices:" + deviceList.size());
1723 BluetoothDevice device = deviceList.get(0);
1724 pw.println("getConnectedDevices[0] = " + device);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001725
1726 switch (mPan.getConnectionState(device)) {
1727 case BluetoothInputDevice.STATE_CONNECTING:
1728 pw.println("getConnectionState() = STATE_CONNECTING");
1729 break;
1730 case BluetoothInputDevice.STATE_CONNECTED:
1731 pw.println("getConnectionState() = STATE_CONNECTED");
1732 break;
1733 case BluetoothInputDevice.STATE_DISCONNECTING:
1734 pw.println("getConnectionState() = STATE_DISCONNECTING");
1735 break;
1736 }
1737 }
1738 deviceList.clear();
1739 deviceList = mPan.getDevicesMatchingConnectionStates(new int[] {
1740 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1741 pw.println("--Connected and Disconnected Pan devices");
1742 for (BluetoothDevice device: deviceList) {
1743 pw.println(device);
1744 }
1745 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001746 }
1747
1748 private void dumpHeadsetConnectionState(PrintWriter pw,
1749 BluetoothDevice device) {
1750 switch (mBluetoothHeadset.getConnectionState(device)) {
1751 case BluetoothHeadset.STATE_CONNECTING:
1752 pw.println("getConnectionState() = STATE_CONNECTING");
1753 break;
1754 case BluetoothHeadset.STATE_CONNECTED:
1755 pw.println("getConnectionState() = STATE_CONNECTED");
1756 break;
1757 case BluetoothHeadset.STATE_DISCONNECTING:
1758 pw.println("getConnectionState() = STATE_DISCONNECTING");
1759 break;
1760 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
1761 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
1762 break;
1763 }
1764 }
1765
1766 private void dumpApplicationServiceRecords(PrintWriter pw) {
1767 pw.println("\n--Application Service Records--");
1768 for (Integer handle : mServiceRecordToPid.keySet()) {
1769 Integer pid = mServiceRecordToPid.get(handle);
1770 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
1771 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001772 mAdapter.closeProfileProxy(BluetoothProfile.PAN, mBluetoothHeadset);
1773 }
1774
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001775 private void dumpAclConnectedDevices(PrintWriter pw) {
1776 String[] devicesObjectPath = getKnownDevices();
1777 pw.println("\n--ACL connected devices--");
1778 if (devicesObjectPath != null) {
1779 for (String device : devicesObjectPath) {
1780 pw.println(getAddressFromObjectPath(device));
1781 }
1782 }
1783 }
1784
1785 private void dumpKnownDevices(PrintWriter pw) {
1786 pw.println("\n--Known devices--");
1787 for (String address : mDeviceProperties.keySet()) {
1788 int bondState = mBondState.getBondState(address);
1789 pw.printf("%s %10s (%d) %s\n", address,
1790 toBondStateString(bondState),
1791 mBondState.getAttempt(address),
1792 getRemoteName(address));
1793
1794 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
1795 if (uuidChannels == null) {
1796 pw.println("\tuuids = null");
1797 } else {
1798 for (ParcelUuid uuid : uuidChannels.keySet()) {
1799 Integer channel = uuidChannels.get(uuid);
1800 if (channel == null) {
1801 pw.println("\t" + uuid);
1802 } else {
1803 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
1804 }
1805 }
1806 }
1807 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1808 if (service.address.equals(address)) {
1809 pw.println("\tPENDING CALLBACK: " + service.uuid);
1810 }
1811 }
1812 }
1813 }
1814
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001815 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1816 new BluetoothProfile.ServiceListener() {
1817 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001818 if (profile == BluetoothProfile.HEADSET) {
1819 mBluetoothHeadset = (BluetoothHeadset) proxy;
1820 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1821 mInputDevice = (BluetoothInputDevice) proxy;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001822 } else if (profile == BluetoothProfile.PAN) {
1823 mPan = (BluetoothPan) proxy;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001824 }
1825 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001826 public void onServiceDisconnected(int profile) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001827 if (profile == BluetoothProfile.HEADSET) {
1828 mBluetoothHeadset = null;
1829 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1830 mInputDevice = null;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001831 } else if (profile == BluetoothProfile.PAN) {
1832 mPan = null;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001833 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001834 }
1835 };
1836
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001837 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1838 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001839 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001840 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001841 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001842 else
Nick Pellybd022f42009-08-14 18:33:38 -07001843 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 }
1845
1846 /* package */ static String scanModeToBluezString(int mode) {
1847 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07001848 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001849 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07001850 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07001852 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 return "discoverable";
1854 }
1855 return null;
1856 }
1857
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001858 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001859 String adapterObjectPath = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001860 if (adapterObjectPath == null || objectPath == null) {
Jake Hambyf51eada2010-09-21 13:39:53 -07001861 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001862 " or deviceObjectPath:" + objectPath + " is null");
1863 return null;
1864 }
1865 if (!objectPath.startsWith(adapterObjectPath)) {
Jake Hambyf51eada2010-09-21 13:39:53 -07001866 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001867 " is not a prefix of deviceObjectPath:" + objectPath +
1868 "bluetoothd crashed ?");
1869 return null;
1870 }
1871 String address = objectPath.substring(adapterObjectPath.length());
1872 if (address != null) return address.replace('_', ':');
1873
1874 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1875 return null;
1876 }
1877
1878 /*package*/ String getObjectPathFromAddress(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001879 String path = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001880 if (path == null) {
1881 Log.e(TAG, "Error: Object Path is null");
1882 return null;
1883 }
1884 path = path + address.replace(":", "_");
1885 return path;
1886 }
1887
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08001888 /*package */ void setLinkTimeout(String address, int num_slots) {
1889 String path = getObjectPathFromAddress(address);
1890 boolean result = setLinkTimeoutNative(path, num_slots);
1891
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001892 if (!result) Log.d(TAG, "Set Link Timeout to " + num_slots + " slots failed");
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08001893 }
1894
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001895 /**** Handlers for PAN Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001896 // TODO: This needs to be converted to a state machine.
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001897
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001898 public boolean isTetheringOn() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001899 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001900 synchronized (mBluetoothPanProfileHandler) {
1901 return mBluetoothPanProfileHandler.isTetheringOn();
1902 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001903 }
1904
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001905 /*package*/boolean allowIncomingTethering() {
1906 synchronized (mBluetoothPanProfileHandler) {
1907 return mBluetoothPanProfileHandler.allowIncomingTethering();
1908 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001909 }
1910
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001911 public void setBluetoothTethering(boolean value) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001912 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001913 synchronized (mBluetoothPanProfileHandler) {
1914 mBluetoothPanProfileHandler.setBluetoothTethering(value);
1915 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001916 }
1917
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001918 public int getPanDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001919 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001920 synchronized (mBluetoothPanProfileHandler) {
1921 return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
1922 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001923 }
1924
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001925 public boolean connectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001926 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1927 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001928 synchronized (mBluetoothPanProfileHandler) {
1929 return mBluetoothPanProfileHandler.connectPanDevice(device);
1930 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001931 }
1932
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001933 public List<BluetoothDevice> getConnectedPanDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001934 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001935 synchronized (mBluetoothPanProfileHandler) {
1936 return mBluetoothPanProfileHandler.getConnectedPanDevices();
1937 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001938 }
1939
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001940 public List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001941 int[] states) {
1942 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001943 synchronized (mBluetoothPanProfileHandler) {
1944 return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
1945 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001946 }
1947
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001948 public boolean disconnectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001949 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1950 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001951 synchronized (mBluetoothPanProfileHandler) {
1952 return mBluetoothPanProfileHandler.disconnectPanDevice(device);
1953 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001954 }
1955
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001956 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001957 String iface,
1958 int state,
1959 int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001960 synchronized (mBluetoothPanProfileHandler) {
1961 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
1962 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001963 }
1964
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001965 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001966 int state, int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001967 synchronized (mBluetoothPanProfileHandler) {
1968 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
1969 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001970 }
1971
1972 /**** Handlers for Input Device Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001973 // This needs to be converted to state machine
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001974
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001975 public boolean connectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001976 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1977 "Need BLUETOOTH_ADMIN permission");
1978 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001979 synchronized (mBluetoothInputProfileHandler) {
1980 return mBluetoothInputProfileHandler.connectInputDevice(device, state);
1981 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001982 }
1983
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001984 public boolean connectInputDeviceInternal(BluetoothDevice device) {
1985 synchronized (mBluetoothInputProfileHandler) {
1986 return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
1987 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001988 }
1989
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001990 public boolean disconnectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001991 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1992 "Need BLUETOOTH_ADMIN permission");
1993 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001994 synchronized (mBluetoothInputProfileHandler) {
1995 return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
1996 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001997 }
1998
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001999 public boolean disconnectInputDeviceInternal(BluetoothDevice device) {
2000 synchronized (mBluetoothInputProfileHandler) {
2001 return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
2002 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002003 }
2004
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002005 public int getInputDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002006 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002007 synchronized (mBluetoothInputProfileHandler) {
2008 return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
2009 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002010 }
2011
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002012 public List<BluetoothDevice> getConnectedInputDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002013 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002014 synchronized (mBluetoothInputProfileHandler) {
2015 return mBluetoothInputProfileHandler.getConnectedInputDevices();
2016 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002017 }
2018
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002019 public List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002020 int[] states) {
2021 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002022 synchronized (mBluetoothInputProfileHandler) {
2023 return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
2024 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002025 }
2026
2027
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002028 public int getInputDevicePriority(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002029 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002030 synchronized (mBluetoothInputProfileHandler) {
2031 return mBluetoothInputProfileHandler.getInputDevicePriority(device);
2032 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002033 }
2034
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002035 public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002036 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2037 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002038 synchronized (mBluetoothInputProfileHandler) {
2039 return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
2040 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002041 }
2042
Matthew Xiea0c68032011-06-25 21:47:07 -07002043 public boolean allowIncomingHidConnect(BluetoothDevice device, boolean allow) {
2044 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2045 "Need BLUETOOTH_ADMIN permission");
2046 String address = device.getAddress();
2047 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2048 return false;
2049 }
2050
2051 Integer data = getAuthorizationAgentRequestData(address);
2052 if (data == null) {
2053 Log.w(TAG, "allowIncomingHidConnect(" + device +
2054 ") called but no native data available");
2055 return false;
2056 }
2057 if (DBG) log("allowIncomingHidConnect: " + device + " : " + allow + " : " + data);
2058 return setAuthorizationNative(address, allow, data.intValue());
2059 }
2060
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002061 /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
2062 synchronized (mBluetoothInputProfileHandler) {
2063 return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
2064 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002065 }
2066
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002067 /*package*/void handleInputDevicePropertyChange(String address, boolean connected) {
2068 synchronized (mBluetoothInputProfileHandler) {
2069 mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
2070 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002071 }
2072
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002073 /**** Handlers for Health Device Profile ****/
2074 // TODO: All these need to be converted to a state machine.
2075
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002076 public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
2077 IBluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002078 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2079 "Need BLUETOOTH permission");
2080 synchronized (mBluetoothHealthProfileHandler) {
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002081 return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002082 }
2083 }
2084
2085 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
2086 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2087 "Need BLUETOOTH permission");
2088 synchronized (mBluetoothHealthProfileHandler) {
2089 return mBluetoothHealthProfileHandler.unregisterAppConfiguration(config);
2090 }
2091 }
2092
2093
2094 public boolean connectChannelToSource(BluetoothDevice device,
2095 BluetoothHealthAppConfiguration config) {
2096 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2097 "Need BLUETOOTH permission");
2098 synchronized (mBluetoothHealthProfileHandler) {
2099 return mBluetoothHealthProfileHandler.connectChannelToSource(device,
2100 config);
2101 }
2102 }
2103
2104 public boolean connectChannelToSink(BluetoothDevice device,
2105 BluetoothHealthAppConfiguration config, int channelType) {
2106 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2107 "Need BLUETOOTH permission");
2108 synchronized (mBluetoothHealthProfileHandler) {
2109 return mBluetoothHealthProfileHandler.connectChannel(device, config,
2110 channelType);
2111 }
2112 }
2113
2114 public boolean disconnectChannel(BluetoothDevice device,
2115 BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
2116 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2117 "Need BLUETOOTH permission");
2118 synchronized (mBluetoothHealthProfileHandler) {
2119 return mBluetoothHealthProfileHandler.disconnectChannel(device, config, fd);
2120 }
2121 }
2122
2123 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
2124 BluetoothHealthAppConfiguration config) {
2125 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2126 "Need BLUETOOTH permission");
2127 synchronized (mBluetoothHealthProfileHandler) {
2128 return mBluetoothHealthProfileHandler.getMainChannelFd(device, config);
2129 }
2130 }
2131
2132 /*package*/ void onHealthDevicePropertyChanged(String devicePath,
2133 String channelPath) {
2134 synchronized (mBluetoothHealthProfileHandler) {
2135 mBluetoothHealthProfileHandler.onHealthDevicePropertyChanged(devicePath,
2136 channelPath);
2137 }
2138 }
2139
2140 /*package*/ void onHealthDeviceChannelChanged(String devicePath,
2141 String channelPath, boolean exists) {
2142 synchronized(mBluetoothHealthProfileHandler) {
2143 mBluetoothHealthProfileHandler.onHealthDeviceChannelChanged(devicePath,
2144 channelPath, exists);
2145 }
2146 }
2147
2148 public int getHealthDeviceConnectionState(BluetoothDevice device) {
2149 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2150 "Need BLUETOOTH permission");
2151 synchronized (mBluetoothHealthProfileHandler) {
2152 return mBluetoothHealthProfileHandler.getHealthDeviceConnectionState(device);
2153 }
2154 }
2155
2156 public List<BluetoothDevice> getConnectedHealthDevices() {
2157 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2158 "Need BLUETOOTH permission");
2159 synchronized (mBluetoothHealthProfileHandler) {
2160 return mBluetoothHealthProfileHandler.getConnectedHealthDevices();
2161 }
2162 }
2163
2164 public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(
2165 int[] states) {
2166 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2167 "Need BLUETOOTH permission");
2168 synchronized (mBluetoothHealthProfileHandler) {
2169 return mBluetoothHealthProfileHandler.
2170 getHealthDevicesMatchingConnectionStates(states);
2171 }
2172 }
2173
Matthew Xiea0c68032011-06-25 21:47:07 -07002174 /*package*/boolean notifyIncomingHidConnection(String address) {
2175 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2176 if (state == null) {
2177 return false;
2178 }
2179 Message msg = new Message();
2180 msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING;
2181 state.sendMessage(msg);
2182 return true;
2183 }
2184
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002185 public boolean connectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002186 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2187
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002188 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002189 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002190 Message msg = new Message();
2191 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2192 msg.obj = state;
2193 mHfpProfileState.sendMessage(msg);
2194 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002195 }
2196 return false;
2197 }
2198
2199 public boolean disconnectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002200 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2201
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002202 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002203 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002204 Message msg = new Message();
2205 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2206 msg.obj = state;
2207 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002208 return true;
2209 }
2210 return false;
2211 }
2212
2213 public boolean connectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002214 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2215
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002216 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002217 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002218 Message msg = new Message();
2219 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2220 msg.obj = state;
2221 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002222 return true;
2223 }
2224 return false;
2225 }
2226
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002227 public boolean disconnectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002228 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2229
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002230 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2231 if (state != null) {
2232 Message msg = new Message();
2233 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2234 msg.obj = state;
2235 mA2dpProfileState.sendMessage(msg);
2236 return true;
2237 }
2238 return false;
2239 }
2240
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002241 BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002242 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002243 if (state != null) return state;
2244
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002245 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002246 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002247 state.start();
2248 return state;
2249 }
2250
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002251 void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002252 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002253 }
2254
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002255 synchronized String[] getKnownDevices() {
2256 String[] bonds = null;
2257 String val = getProperty("Devices");
2258 if (val != null) {
2259 bonds = val.split(",");
2260 }
2261 return bonds;
2262 }
2263
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002264 private void initProfileState() {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002265 String[] bonds = null;
2266 String val = mAdapterProperties.getProperty("Devices");
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002267 if (val != null) {
2268 bonds = val.split(",");
2269 }
2270 if (bonds == null) {
2271 return;
2272 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002273 for (String path : bonds) {
2274 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002275 BluetoothDeviceProfileState state = addProfileState(address, false);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002276 }
2277 }
2278
2279 private void autoConnect() {
2280 String[] bonds = getKnownDevices();
2281 if (bonds == null) {
2282 return;
2283 }
2284 for (String path : bonds) {
2285 String address = getAddressFromObjectPath(path);
2286 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2287 if (state != null) {
2288 Message msg = new Message();
2289 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
2290 state.sendMessage(msg);
2291 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002292 }
2293 }
2294
2295 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002296 BluetoothDeviceProfileState state =
2297 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002298 if (state != null) {
2299 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002300 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002301 state.sendMessage(msg);
2302 return true;
2303 }
2304 return false;
2305 }
2306
2307 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002308 BluetoothDeviceProfileState state =
2309 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002310 if (state != null) {
2311 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002312 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002313 state.sendMessage(msg);
2314 return true;
2315 }
2316 return false;
2317 }
2318
2319 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2320 mA2dpService = a2dpService;
2321 }
2322
Matthew Xiea0c68032011-06-25 21:47:07 -07002323 /*package*/ Integer getAuthorizationAgentRequestData(String address) {
2324 Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
2325 return data;
2326 }
2327
Jaikumar Ganesh70a053b2010-10-13 15:54:30 -07002328 public void sendProfileStateMessage(int profile, int cmd) {
2329 Message msg = new Message();
2330 msg.what = cmd;
2331 if (profile == BluetoothProfileState.HFP) {
2332 mHfpProfileState.sendMessage(msg);
2333 } else if (profile == BluetoothProfileState.A2DP) {
2334 mA2dpProfileState.sendMessage(msg);
2335 }
2336 }
2337
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002338 public int getAdapterConnectionState() {
2339 return mAdapterConnectionState;
2340 }
2341
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002342 public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
2343 int prevState) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002344 // Since this is a binder call check if Bluetooth is on still
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002345 if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002346
2347 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
2348 if (!validateProfileConnectionState(state) ||
2349 !validateProfileConnectionState(prevState)) {
2350 // Previously, an invalid state was broadcast anyway,
2351 // with the invalid state converted to -1 in the intent.
2352 // Better to log an error and not send an intent with
2353 // invalid contents or set mAdapterConnectionState to -1.
2354 Log.e(TAG, "Error in sendConnectionStateChange: "
2355 + "prevState " + prevState + " state " + state);
2356 return;
2357 }
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002358
2359 mAdapterConnectionState = state;
2360
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002361 if (state == BluetoothProfile.STATE_DISCONNECTED) {
2362 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.ALL_DEVICES_DISCONNECTED);
2363 }
2364
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002365 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
2366 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002367 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
2368 convertToAdapterState(state));
2369 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
2370 convertToAdapterState(prevState));
Jaikumar Ganesh60b4d2a2011-07-12 14:14:49 -07002371 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002372 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002373 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
2374 + prevState + " -> " + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002375 }
2376 }
2377
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002378 private boolean validateProfileConnectionState(int state) {
2379 return (state == BluetoothProfile.STATE_DISCONNECTED ||
2380 state == BluetoothProfile.STATE_CONNECTING ||
2381 state == BluetoothProfile.STATE_CONNECTED ||
2382 state == BluetoothProfile.STATE_DISCONNECTING);
2383 }
2384
2385 private int convertToAdapterState(int state) {
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002386 switch (state) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002387 case BluetoothProfile.STATE_DISCONNECTED:
2388 return BluetoothAdapter.STATE_DISCONNECTED;
2389 case BluetoothProfile.STATE_DISCONNECTING:
2390 return BluetoothAdapter.STATE_DISCONNECTING;
2391 case BluetoothProfile.STATE_CONNECTED:
2392 return BluetoothAdapter.STATE_CONNECTED;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002393 case BluetoothProfile.STATE_CONNECTING:
2394 return BluetoothAdapter.STATE_CONNECTING;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002395 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002396 Log.e(TAG, "Error in convertToAdapterState");
2397 return -1;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002398 }
2399
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002400 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
2401 switch (prevState) {
2402 case BluetoothProfile.STATE_CONNECTING:
2403 mProfilesConnecting--;
2404 break;
2405
2406 case BluetoothProfile.STATE_CONNECTED:
2407 mProfilesConnected--;
2408 break;
2409
2410 case BluetoothProfile.STATE_DISCONNECTING:
2411 mProfilesDisconnecting--;
2412 break;
2413 }
2414
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002415 switch (state) {
2416 case BluetoothProfile.STATE_CONNECTING:
2417 mProfilesConnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002418 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002419
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002420 case BluetoothProfile.STATE_CONNECTED:
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002421 mProfilesConnected++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002422 return (mProfilesConnected == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002423
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002424 case BluetoothProfile.STATE_DISCONNECTING:
2425 mProfilesDisconnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002426 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002427
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002428 case BluetoothProfile.STATE_DISCONNECTED:
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002429 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002430
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002431 default:
2432 return true;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002433 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002434 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002435
Matthew Xiea0c68032011-06-25 21:47:07 -07002436 private void createIncomingConnectionStateFile() {
2437 File f = new File(INCOMING_CONNECTION_FILE);
2438 if (!f.exists()) {
2439 try {
2440 f.createNewFile();
2441 } catch (IOException e) {
2442 Log.e(TAG, "IOException: cannot create file");
2443 }
2444 }
2445 }
2446
2447 /** @hide */
2448 public Pair<Integer, String> getIncomingState(String address) {
2449 if (mIncomingConnections.isEmpty()) {
2450 createIncomingConnectionStateFile();
2451 readIncomingConnectionState();
2452 }
2453 return mIncomingConnections.get(address);
2454 }
2455
2456 private void readIncomingConnectionState() {
2457 synchronized(mIncomingConnections) {
2458 FileInputStream fstream = null;
2459 try {
2460 fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
2461 DataInputStream in = new DataInputStream(fstream);
2462 BufferedReader file = new BufferedReader(new InputStreamReader(in));
2463 String line;
2464 while((line = file.readLine()) != null) {
2465 line = line.trim();
2466 if (line.length() == 0) continue;
2467 String[] value = line.split(",");
2468 if (value != null && value.length == 3) {
2469 Integer val1 = Integer.parseInt(value[1]);
2470 Pair<Integer, String> val = new Pair(val1, value[2]);
2471 mIncomingConnections.put(value[0], val);
2472 }
2473 }
2474 } catch (FileNotFoundException e) {
2475 log("FileNotFoundException: readIncomingConnectionState" + e.toString());
2476 } catch (IOException e) {
2477 log("IOException: readIncomingConnectionState" + e.toString());
2478 } finally {
2479 if (fstream != null) {
2480 try {
2481 fstream.close();
2482 } catch (IOException e) {
2483 // Ignore
2484 }
2485 }
2486 }
2487 }
2488 }
2489
2490 private void truncateIncomingConnectionFile() {
2491 RandomAccessFile r = null;
2492 try {
2493 r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
2494 r.setLength(0);
2495 } catch (FileNotFoundException e) {
2496 log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
2497 } catch (IOException e) {
2498 log("IOException: truncateIncomingConnectionState" + e.toString());
2499 } finally {
2500 if (r != null) {
2501 try {
2502 r.close();
2503 } catch (IOException e) {
2504 // ignore
2505 }
2506 }
2507 }
2508 }
2509
2510 /** @hide */
2511 public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
2512 synchronized(mIncomingConnections) {
2513 mIncomingConnections.put(address, data);
2514
2515 truncateIncomingConnectionFile();
2516 BufferedWriter out = null;
2517 StringBuilder value = new StringBuilder();
2518 try {
2519 out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
2520 for (String devAddress: mIncomingConnections.keySet()) {
2521 Pair<Integer, String> val = mIncomingConnections.get(devAddress);
2522 value.append(devAddress);
2523 value.append(",");
2524 value.append(val.first.toString());
2525 value.append(",");
2526 value.append(val.second);
2527 value.append("\n");
2528 }
2529 out.write(value.toString());
2530 } catch (FileNotFoundException e) {
2531 log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
2532 } catch (IOException e) {
2533 log("IOException: writeIncomingConnectionState" + e.toString());
2534 } finally {
2535 if (out != null) {
2536 try {
2537 out.close();
2538 } catch (IOException e) {
2539 // Ignore
2540 }
2541 }
2542 }
2543 }
2544 }
2545
2546 private static void log(String msg) {
2547 Log.d(TAG, msg);
2548 }
2549
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002550 private native static void classInitNative();
2551 private native void initializeNativeDataNative();
2552 private native boolean setupNativeDataNative();
2553 private native boolean tearDownNativeDataNative();
2554 private native void cleanupNativeDataNative();
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002555 /*package*/ native String getAdapterPathNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002556
2557 private native int isEnabledNative();
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002558 /*package*/ native int enableNative();
2559 /*package*/ native int disableNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002560
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002561 /*package*/ native Object[] getAdapterPropertiesNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002562 private native Object[] getDevicePropertiesNative(String objectPath);
2563 private native boolean setAdapterPropertyStringNative(String key, String value);
2564 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2565 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2566
2567 private native boolean startDiscoveryNative();
2568 private native boolean stopDiscoveryNative();
2569
2570 private native boolean createPairedDeviceNative(String address, int timeout_ms);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002571 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2572 private native byte[] readAdapterOutOfBandDataNative();
2573
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002574 private native boolean cancelDeviceCreationNative(String address);
2575 private native boolean removeDeviceNative(String objectPath);
2576 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2577 int attributeId);
2578
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002579 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002580 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002581 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2582 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2583 int nativeData);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002584 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2585 byte[] randomizer, int nativeData);
2586
Nick Pelly24bb9b82009-10-02 20:34:18 -07002587 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2588 int value);
Matthew Xie269e81a2011-07-26 18:36:49 -07002589 private native boolean setDevicePropertyStringNative(String objectPath, String key,
2590 String value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002591 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002592 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002593
Nick Pelly24bb9b82009-10-02 20:34:18 -07002594 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2595 short channel);
2596 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002597 private native boolean setLinkTimeoutNative(String path, int num_slots);
Matthew Xiea0c68032011-06-25 21:47:07 -07002598
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002599 native boolean connectInputDeviceNative(String path);
2600 native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002601
Jaikumar Ganesha8571f12011-02-11 15:46:54 -08002602 native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2603 native boolean connectPanDeviceNative(String path, String dstRole);
2604 native boolean disconnectPanDeviceNative(String path);
2605 native boolean disconnectPanServerDeviceNative(String path,
Jaikumar Ganesha44a1e72011-02-11 12:40:44 -08002606 String address, String iface);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -08002607
2608 private native int[] addReservedServiceRecordsNative(int[] uuuids);
2609 private native boolean removeReservedServiceRecordsNative(int[] handles);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002610
2611 // Health API
2612 native String registerHealthApplicationNative(int dataType, String role, String name,
2613 String channelType);
2614 native String registerHealthApplicationNative(int dataType, String role, String name);
2615 native boolean unregisterHealthApplicationNative(String path);
2616 native boolean createChannelNative(String devicePath, String appPath, String channelType);
2617 native boolean destroyChannelNative(String devicePath, String channelpath);
2618 native String getMainChannelNative(String path);
2619 native String getChannelApplicationNative(String channelPath);
2620 native ParcelFileDescriptor getChannelFdNative(String channelPath);
2621 native boolean releaseChannelFdNative(String channelPath);
Matthew Xiea0c68032011-06-25 21:47:07 -07002622 native boolean setAuthorizationNative(String address, boolean value, int data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002623}