blob: 9e66957fb246ebc9508bb94303745fed1b0a2109 [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;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -070042import android.bluetooth.IBluetoothStateChangeCallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.content.BroadcastReceiver;
44import android.content.ContentResolver;
45import android.content.Context;
46import android.content.Intent;
47import android.content.IntentFilter;
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -080048import android.content.SharedPreferences;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.os.Binder;
50import android.os.Handler;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080051import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import android.os.Message;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070053import android.os.ParcelFileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080054import android.os.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070056import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.provider.Settings;
58import android.util.Log;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -070059import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -070061import com.android.internal.app.IBatteryStats;
62
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080063import java.io.BufferedInputStream;
Matthew Xiea0c68032011-06-25 21:47:07 -070064import java.io.BufferedReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080065import java.io.BufferedWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070066import java.io.DataInputStream;
67import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import java.io.FileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080069import java.io.FileInputStream;
70import java.io.FileNotFoundException;
71import java.io.FileWriter;
72import java.io.IOException;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -070073import java.io.InputStreamReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import java.io.PrintWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070075import java.io.RandomAccessFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import java.io.UnsupportedEncodingException;
77import java.util.ArrayList;
78import java.util.Arrays;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -070079import java.util.Collection;
80import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081import java.util.HashMap;
Nick Pelly16fb88a2009-10-07 07:44:03 +020082import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070083import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import java.util.Map;
85
Nick Pellybd022f42009-08-14 18:33:38 -070086public class BluetoothService extends IBluetooth.Stub {
87 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 private static final boolean DBG = true;
89
90 private int mNativeData;
91 private BluetoothEventLoop mEventLoop;
Jaikumar Ganesh96a79832010-09-27 17:02:01 -070092 private BluetoothHeadset mBluetoothHeadset;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080093 private BluetoothInputDevice mInputDevice;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -080094 private BluetoothPan mPan;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 private boolean mIsAirplaneSensitive;
Jeff Sharkey44303922009-12-01 18:25:02 -080096 private boolean mIsAirplaneToggleable;
Matthew Xie7f9ecca82011-07-15 13:03:58 -070097 private BluetoothAdapterStateMachine mBluetoothState;
Nick Pelly997c7612009-03-24 20:44:48 -070098 private boolean mRestart = false; // need to call enable() after disable()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private boolean mIsDiscovering;
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800100 private int[] mAdapterSdpHandles;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800101 private ParcelUuid[] mAdapterUuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
Nick Pellybd022f42009-08-14 18:33:38 -0700103 private BluetoothAdapter mAdapter; // constant after init()
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800104 private final BluetoothBondState mBondState; // local cache of bondings
Nick Pellybd022f42009-08-14 18:33:38 -0700105 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private final Context mContext;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -0700107 private Map<Integer, IBluetoothStateChangeCallback> mStateChangeTracker =
108 Collections.synchronizedMap(new HashMap<Integer, IBluetoothStateChangeCallback>());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
110 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800111 static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800113 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
114 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
115
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800116 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
117 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
118
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700119 private static final int MESSAGE_UUID_INTENT = 1;
120 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700121
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -0700122 private static final int RFCOMM_RECORD_REAPER = 10;
123 private static final int STATE_CHANGE_REAPER = 11;
124
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700125 // The time (in millisecs) to delay the pairing attempt after the first
126 // auto pairing attempt fails. We use an exponential delay with
127 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
128 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
129 private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
130 private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700131
132 // The timeout used to sent the UUIDs Intent
133 // This timeout should be greater than the page timeout
134 private static final int UUID_INTENT_DELAY = 6000;
The Android Open Source Project10592532009-03-18 17:39:46 -0700135
Nick Pelly16fb88a2009-10-07 07:44:03 +0200136 /** Always retrieve RFCOMM channel for these SDP UUIDs */
137 private static final ParcelUuid[] RFCOMM_UUIDS = {
138 BluetoothUuid.Handsfree,
139 BluetoothUuid.HSP,
140 BluetoothUuid.ObexObjectPush };
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700141
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800142 private final BluetoothAdapterProperties mAdapterProperties;
143 private final BluetoothDeviceProperties mDeviceProperties;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200144
145 private final HashMap<String, Map<ParcelUuid, Integer>> mDeviceServiceChannelCache;
146 private final ArrayList<String> mUuidIntentTracker;
147 private final HashMap<RemoteService, IBluetoothCallback> mUuidCallbackTracker;
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700148
Nick Pelly24bb9b82009-10-02 20:34:18 -0700149 private final HashMap<Integer, Integer> mServiceRecordToPid;
150
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700151 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
152 private final BluetoothProfileState mA2dpProfileState;
153 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700154
155 private BluetoothA2dpService mA2dpService;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700156 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700157
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700158 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700159
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800160 private static String mDockAddress;
161 private String mDockPin;
162
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700163 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800164 private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800165 private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700166 private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler;
Matthew Xiea0c68032011-06-25 21:47:07 -0700167 private static final String INCOMING_CONNECTION_FILE =
168 "/data/misc/bluetooth/incoming_connection.conf";
169 private HashMap<String, Pair<Integer, String>> mIncomingConnections;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700170
Nick Pelly16fb88a2009-10-07 07:44:03 +0200171 private static class RemoteService {
172 public String address;
173 public ParcelUuid uuid;
174 public RemoteService(String address, ParcelUuid uuid) {
175 this.address = address;
176 this.uuid = uuid;
177 }
178 @Override
179 public boolean equals(Object o) {
180 if (o instanceof RemoteService) {
181 RemoteService service = (RemoteService)o;
182 return address.equals(service.address) && uuid.equals(service.uuid);
183 }
184 return false;
185 }
Kenny Root5f614162010-02-17 12:00:37 -0800186
187 @Override
188 public int hashCode() {
189 int hash = 1;
190 hash = hash * 31 + (address == null ? 0 : address.hashCode());
191 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
192 return hash;
193 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200194 }
195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 static {
197 classInitNative();
198 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199
Nick Pellybd022f42009-08-14 18:33:38 -0700200 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700202
203 // Need to do this in place of:
204 // mBatteryStats = BatteryStatsService.getService();
205 // Since we can not import BatteryStatsService from here. This class really needs to be
206 // moved to java/services/com/android/server/
207 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700210
211 if (isEnabledNative() == 1) {
212 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
213 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 mIsDiscovering = false;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800217
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800218 mBondState = new BluetoothBondState(context, this);
219 mAdapterProperties = new BluetoothAdapterProperties(context, this);
220 mDeviceProperties = new BluetoothDeviceProperties(this);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700221
222 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700223 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700224 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200225 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700226 mServiceRecordToPid = new HashMap<Integer, Integer>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700227 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
228 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
229 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
230
231 mHfpProfileState.start();
232 mA2dpProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800233
234 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800235 registerForAirplaneMode(filter);
236
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800237 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800238 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800239 mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800240 mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700241 mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
Matthew Xiea0c68032011-06-25 21:47:07 -0700242 mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800243 }
244
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700245 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800246 if (mDockAddress != null) return mDockAddress;
247
248 BufferedInputStream file = null;
249 String dockAddress;
250 try {
251 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
252 byte[] address = new byte[17];
253 file.read(address);
254 dockAddress = new String(address);
255 dockAddress = dockAddress.toUpperCase();
256 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
257 mDockAddress = dockAddress;
258 return mDockAddress;
259 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800260 Log.e(TAG, "CheckBluetoothAddress failed for car dock address: "
261 + dockAddress);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800262 }
263 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800264 Log.e(TAG, "FileNotFoundException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800265 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800266 Log.e(TAG, "IOException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800267 } finally {
268 if (file != null) {
269 try {
270 file.close();
271 } catch (IOException e) {
272 // Ignore
273 }
274 }
275 }
276 mDockAddress = null;
277 return null;
278 }
279
280 private synchronized boolean writeDockPin() {
281 BufferedWriter out = null;
282 try {
283 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
284
285 // Generate a random 4 digit pin between 0000 and 9999
286 // This is not truly random but good enough for our purposes.
287 int pin = (int) Math.floor(Math.random() * 10000);
288
289 mDockPin = String.format("%04d", pin);
290 out.write(mDockPin);
291 return true;
292 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800293 Log.e(TAG, "FileNotFoundException while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800294 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800295 Log.e(TAG, "IOException while while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800296 } finally {
297 if (out != null) {
298 try {
299 out.close();
300 } catch (IOException e) {
301 // Ignore
302 }
303 }
304 }
305 mDockPin = null;
306 return false;
307 }
308
309 /*package*/ synchronized String getDockPin() {
310 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700311 }
312
313 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200314 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700315 mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter);
316 mBluetoothState.start();
317 mEventLoop = mBluetoothState.getBluetoothEventLoop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319
Jaikumar Ganesh7d0548d2010-10-18 15:29:09 -0700320 public synchronized void initAfterA2dpRegistration() {
321 mEventLoop.getProfileProxy();
322 }
323
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 @Override
325 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800326 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 try {
328 cleanupNativeDataNative();
329 } finally {
330 super.finalize();
331 }
332 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333
334 public boolean isEnabled() {
335 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800336 return isEnabledInternal();
337 }
338
339 private boolean isEnabledInternal() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700340 return (getBluetoothStateInternal() == BluetoothAdapter.STATE_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342
The Android Open Source Project10592532009-03-18 17:39:46 -0700343 public int getBluetoothState() {
344 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700345 return getBluetoothStateInternal();
The Android Open Source Project10592532009-03-18 17:39:46 -0700346 }
347
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800348 int getBluetoothStateInternal() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700349 return mBluetoothState.getBluetoothAdapterState();
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800350 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700351
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 /**
353 * Bring down bluetooth and disable BT in settings. Returns true on success.
354 */
355 public boolean disable() {
356 return disable(true);
357 }
358
359 /**
360 * Bring down bluetooth. Returns true on success.
361 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200362 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 */
364 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200365 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700367 int adapterState = getBluetoothStateInternal();
368
369 switch (adapterState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700370 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700371 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700372 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700373 break;
374 default:
375 return false;
376 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800377
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700378 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_OFF, saveSetting);
The Android Open Source Project10592532009-03-18 17:39:46 -0700379 return true;
380 }
381
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700382 synchronized void disconnectDevices() {
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700383 // Disconnect devices handled by BluetoothService.
384 for (BluetoothDevice device: getConnectedInputDevices()) {
385 disconnectInputDevice(device);
386 }
387
388 for (BluetoothDevice device: getConnectedPanDevices()) {
389 disconnectPanDevice(device);
390 }
391 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700392
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700393 /**
394 * The Bluetooth has been turned off, but hot. Do bonding, profile,
395 * and internal cleanup
396 */
397 synchronized void finishDisable() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 // mark in progress bondings as cancelled
399 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700400 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
402 }
403
Jaikumar Ganesh2d1fc4e2010-12-13 15:54:33 -0800404 // Stop the profile state machine for bonded devices.
405 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) {
406 removeProfileState(address);
407 }
408
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700410 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
411 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
413
The Android Open Source Project10592532009-03-18 17:39:46 -0700414 mIsDiscovering = false;
Nick Pellybd022f42009-08-14 18:33:38 -0700415 mAdapterProperties.clear();
Nick Pelly24bb9b82009-10-02 20:34:18 -0700416 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700417
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700418 mProfilesConnected = 0;
419 mProfilesConnecting = 0;
420 mProfilesDisconnecting = 0;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700421 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800422 mAdapterUuids = null;
423 mAdapterSdpHandles = null;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700424
The Android Open Source Project10592532009-03-18 17:39:46 -0700425 // Log bluetooth off to battery stats.
426 long ident = Binder.clearCallingIdentity();
427 try {
428 mBatteryStats.noteBluetoothOff();
429 } catch (RemoteException e) {
430 } finally {
431 Binder.restoreCallingIdentity(ident);
432 }
Nick Pelly997c7612009-03-24 20:44:48 -0700433
434 if (mRestart) {
435 mRestart = false;
436 enable();
437 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 }
439
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700440 /**
441 * power off Bluetooth
442 */
443 synchronized void shutoffBluetooth() {
444 tearDownNativeDataNative();
445 disableNative();
446 if (mRestart) {
447 mRestart = false;
448 enable();
449 }
450 }
451
The Android Open Source Project10592532009-03-18 17:39:46 -0700452 /** Bring up BT and persist BT on in settings */
453 public boolean enable() {
454 return enable(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
456
457 /**
458 * Enable this Bluetooth device, asynchronously.
459 * This turns on/off the underlying hardware.
460 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700461 * @param saveSetting If true, persist the new state of BT in settings
462 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 */
The Android Open Source Project10592532009-03-18 17:39:46 -0700464 public synchronized boolean enable(boolean saveSetting) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
466 "Need BLUETOOTH_ADMIN permission");
467
468 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800469 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 return false;
471 }
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700472 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
473 return true;
474 }
475
476 /**
477 * Turn on Bluetooth Module, Load firmware, and do all the preparation
478 * needed to get the Bluetooth Module ready but keep it not discoverable
479 * and not connectable.
480 */
481 /* package */ synchronized boolean prepareBluetooth() {
482 if (!setupNativeDataNative()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 return false;
484 }
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700485 mIsDiscovering = false;
486
487 switchConnectable(false);
488 updateSdpRecords();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 return true;
490 }
491
Nick Pelly997c7612009-03-24 20:44:48 -0700492 /** Forcibly restart Bluetooth if it is on */
493 /* package */ synchronized void restart() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700494 if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
Nick Pelly997c7612009-03-24 20:44:48 -0700495 return;
496 }
497 mRestart = true;
498 if (!disable(false)) {
499 mRestart = false;
500 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700501 }
Nick Pelly997c7612009-03-24 20:44:48 -0700502
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 private final Handler mHandler = new Handler() {
504 @Override
505 public void handleMessage(Message msg) {
506 switch (msg.what) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700507 case MESSAGE_UUID_INTENT:
508 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200509 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700510 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200511 makeServiceChannelCallbacks(address);
512 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700513 break;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700514 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
515 address = (String)msg.obj;
Jaikumar Ganesh8adcacb2011-07-21 13:49:54 -0700516 if (address == null) return;
517 int attempt = mBondState.getAttempt(address);
518
519 // Try only if attemps are in progress and cap it 2 attempts
520 // The 2 attempts cap is a fail safe if the stack returns
521 // an incorrect error code for bonding failures and if the pin
522 // is entered wrongly twice we should abort.
523 if (attempt > 0 && attempt <= 2) {
524 mBondState.attempt(address);
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700525 createBond(address);
526 return;
527 }
Jaikumar Ganesh8adcacb2011-07-21 13:49:54 -0700528 if (attempt > 0) mBondState.clearPinAttempts(address);
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700529 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 }
531 }
532 };
533
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800534 private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
535 //Register SDP records.
536 int[] svcIdentifiers = new int[uuids.size()];
537 for (int i = 0; i < uuids.size(); i++) {
538 svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
539 }
540 mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
541 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700542
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800543 private synchronized void updateSdpRecords() {
544 ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
545
546 // Add the default records
547 uuids.add(BluetoothUuid.HSP_AG);
548 uuids.add(BluetoothUuid.ObexObjectPush);
549
550 if (mContext.getResources().
551 getBoolean(com.android.internal.R.bool.config_voice_capable)) {
552 uuids.add(BluetoothUuid.Handsfree_AG);
553 uuids.add(BluetoothUuid.PBAP_PSE);
554 }
555
556 // Add SDP records for profiles maintained by Android userspace
557 addReservedSdpRecords(uuids);
558
559 // Enable profiles maintained by Bluez userspace.
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -0800560 setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
561 BluetoothPanProfileHandler.NAP_BRIDGE);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800562
563 // Add SDP records for profiles maintained by Bluez userspace
564 uuids.add(BluetoothUuid.AudioSource);
565 uuids.add(BluetoothUuid.AvrcpTarget);
566 uuids.add(BluetoothUuid.NAP);
567
568 // Cannot cast uuids.toArray directly since ParcelUuid is parcelable
569 mAdapterUuids = new ParcelUuid[uuids.size()];
570 for (int i = 0; i < uuids.size(); i++) {
571 mAdapterUuids[i] = uuids.get(i);
572 }
573 }
574
Jaikumar Ganesh9efb1342011-02-11 18:10:31 -0800575 /**
576 * This function is called from Bluetooth Event Loop when onPropertyChanged
577 * for adapter comes in with UUID property.
578 * @param uuidsThe uuids of adapter as reported by Bluez.
579 */
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700580 /*package*/ synchronized void updateBluetoothState(String uuids) {
581 ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800582
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700583 if (mAdapterUuids != null &&
584 BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
585 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 }
587 }
588
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700589 /**
590 * This method is called immediately after Bluetooth module is turned on.
591 * It starts auto-connection and places bluetooth on sign onto the battery
592 * stats
593 */
594 /*package*/ void runBluetooth() {
595 mIsDiscovering = false;
596 mBondState.readAutoPairingData();
597 mBondState.initBondState();
598 initProfileState();
599
600 autoConnect();
601
602 // Log bluetooth on to battery stats.
603 long ident = Binder.clearCallingIdentity();
604 try {
605 mBatteryStats.noteBluetoothOn();
606 } catch (RemoteException e) {
607 Log.e(TAG, "", e);
608 } finally {
609 Binder.restoreCallingIdentity(ident);
610 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 }
612
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700613 /*package*/ synchronized boolean attemptAutoPair(String address) {
614 if (!mBondState.hasAutoPairingFailed(address) &&
615 !mBondState.isAutoPairingBlacklisted(address)) {
616 mBondState.attempt(address);
617 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
618 return true;
619 }
620 return false;
621 }
622
Jaikumar Ganeshf487d722011-01-12 10:12:01 -0800623 /*package*/ synchronized boolean isFixedPinZerosAutoPairKeyboard(String address) {
624 // Check for keyboards which have fixed PIN 0000 as the pairing pin
625 return mBondState.isFixedPinZerosAutoPairKeyboard(address);
626 }
627
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700628 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
629 if (result == BluetoothDevice.BOND_SUCCESS) {
630 setBondState(address, BluetoothDevice.BOND_BONDED);
631 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
632 mBondState.clearPinAttempts(address);
633 }
634 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
635 mBondState.getAttempt(address) == 1) {
636 mBondState.addAutoPairingFailure(address);
637 pairingAttempt(address, result);
638 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
639 mBondState.isAutoPairingAttemptsInProgress(address)) {
640 pairingAttempt(address, result);
641 } else {
642 setBondState(address, BluetoothDevice.BOND_NONE, result);
643 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
644 mBondState.clearPinAttempts(address);
645 }
646 }
647 }
648
649 /*package*/ synchronized String getPendingOutgoingBonding() {
650 return mBondState.getPendingOutgoingBonding();
651 }
652
653 private void pairingAttempt(String address, int result) {
654 // This happens when our initial guess of "0000" as the pass key
655 // fails. Try to create the bond again and display the pin dialog
656 // to the user. Use back-off while posting the delayed
657 // message. The initial value is
658 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
659 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
660 // reached, display an error to the user.
661 int attempt = mBondState.getAttempt(address);
662 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
663 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
664 mBondState.clearPinAttempts(address);
665 setBondState(address, BluetoothDevice.BOND_NONE, result);
666 return;
667 }
668
669 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
670 message.obj = address;
671 boolean postResult = mHandler.sendMessageDelayed(message,
672 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
673 if (!postResult) {
674 mBondState.clearPinAttempts(address);
675 setBondState(address,
676 BluetoothDevice.BOND_NONE, result);
677 return;
678 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800681 /*package*/ BluetoothDevice getRemoteDevice(String address) {
682 return mAdapter.getRemoteDevice(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684
685 private static String toBondStateString(int bondState) {
686 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700687 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 return "not bonded";
689 case BluetoothDevice.BOND_BONDING:
690 return "bonding";
691 case BluetoothDevice.BOND_BONDED:
692 return "bonded";
693 default:
694 return "??????";
695 }
696 }
697
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 public synchronized boolean setName(String name) {
699 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
700 "Need BLUETOOTH_ADMIN permission");
701 if (name == null) {
702 return false;
703 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700704 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700707 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
708 // Either have a single property function with Object as the parameter
709 // or have a function for each property and then obfuscate in the JNI layer.
710 // The following looks dirty.
711 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800713 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700714 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700717 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800719 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700720 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 }
722
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700723 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800725 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700726 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728
729 /**
730 * Set the discoverability window for the device. A timeout of zero
731 * makes the device permanently discoverable (if the device is
732 * discoverable). Setting the timeout to a nonzero value does not make
733 * a device discoverable; you need to call setMode() to make the device
734 * explicitly discoverable.
735 *
Jake Hambyf51eada2010-09-21 13:39:53 -0700736 * @param timeout The discoverable timeout in seconds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 */
738 public synchronized boolean setDiscoverableTimeout(int timeout) {
739 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
740 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700741 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700743
Nick Pelly12835472009-09-25 15:00:29 -0700744 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -0700745 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
746 "Need WRITE_SECURE_SETTINGS permission");
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700747 return setScanMode(mode, duration, true);
748 }
749
750 /**
751 * @param on true set the local Bluetooth module to be connectable
752 * but not dicoverable
753 * false set the local Bluetooth module to be not connectable
754 * and not dicoverable
755 */
756 /*package*/ synchronized void switchConnectable(boolean on) {
757 if (on) {
758 // 0 is a dummy value, does not apply for SCAN_MODE_CONNECTABLE
759 setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, 0, false);
760 } else {
761 // 0 is a dummy value, does not apply for SCAN_MODE_NONE
762 setScanMode(BluetoothAdapter.SCAN_MODE_NONE, 0, false);
763 }
764 }
765
766 private synchronized boolean setScanMode(int mode, int duration, boolean allowOnlyInOnState) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800767 boolean pairable;
768 boolean discoverable;
Nick Pelly12835472009-09-25 15:00:29 -0700769
Nick Pellyde893f52009-09-08 13:15:33 -0700770 switch (mode) {
771 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700772 pairable = false;
773 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700774 break;
775 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700776 pairable = true;
777 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700778 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700779 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700780 pairable = true;
781 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -0700782 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -0700783 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700784 default:
785 Log.w(TAG, "Requested invalid scan mode " + mode);
786 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700787 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700788
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700789 if (allowOnlyInOnState) {
790 setPropertyBoolean("Pairable", pairable);
791 setPropertyBoolean("Discoverable", discoverable);
792 } else {
793 // allowed to set the property through native layer directly
794 setAdapterPropertyBooleanNative("Pairable", pairable ? 1 : 0);
795 setAdapterPropertyBooleanNative("Discoverable", discoverable ? 1 : 0);
796 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700797 return true;
798 }
799
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800800 /*package*/ synchronized String getProperty(String name) {
801 if (!isEnabledInternal()) return null;
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800802 return mAdapterProperties.getProperty(name);
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800803 }
804
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800805 BluetoothAdapterProperties getAdapterProperties() {
806 return mAdapterProperties;
807 }
808
809 BluetoothDeviceProperties getDeviceProperties() {
810 return mDeviceProperties;
811 }
812
813 boolean isRemoteDeviceInCache(String address) {
814 return mDeviceProperties.isInCache(address);
815 }
816
817 void setRemoteDeviceProperty(String address, String name, String value) {
818 mDeviceProperties.setProperty(address, name, value);
819 }
820
821 void updateRemoteDevicePropertiesCache(String address) {
822 mDeviceProperties.updateCache(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700823 }
824
825 public synchronized String getAddress() {
826 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
827 return getProperty("Address");
828 }
829
830 public synchronized String getName() {
831 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
832 return getProperty("Name");
833 }
834
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800835 public synchronized ParcelUuid[] getUuids() {
836 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
837 String value = getProperty("UUIDs");
838 if (value == null) return null;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800839 return convertStringToParcelUuid(value);
840 }
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800841
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800842 private synchronized ParcelUuid[] convertStringToParcelUuid(String value) {
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800843 String[] uuidStrings = null;
844 // The UUIDs are stored as a "," separated string.
845 uuidStrings = value.split(",");
846 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
847
848 for (int i = 0; i < uuidStrings.length; i++) {
849 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
850 }
851 return uuids;
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800852 }
853
854
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700855 /**
856 * Returns the user-friendly name of a remote device. This value is
857 * returned from our local cache, which is updated when onPropertyChange
858 * event is received.
859 * Do not expect to retrieve the updated remote name immediately after
860 * changing the name on the remote device.
861 *
862 * @param address Bluetooth address of remote device.
863 *
864 * @return The user-friendly name of the specified remote device.
865 */
866 public synchronized String getRemoteName(String address) {
867 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700868 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700869 return null;
870 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800871 return mDeviceProperties.getProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700872 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873
874 /**
875 * Get the discoverability window for the device. A timeout of zero
876 * means that the device is permanently discoverable (if the device is
877 * in the discoverable mode).
878 *
879 * @return The discoverability window of the device, in seconds. A negative
880 * value indicates an error.
881 */
882 public synchronized int getDiscoverableTimeout() {
883 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700884 String timeout = getProperty("DiscoverableTimeout");
885 if (timeout != null)
886 return Integer.valueOf(timeout);
887 else
888 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890
891 public synchronized int getScanMode() {
892 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800893 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -0700894 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700896 boolean pairable = getProperty("Pairable").equals("true");
897 boolean discoverable = getProperty("Discoverable").equals("true");
898 return bluezStringToScanMode (pairable, discoverable);
899 }
900
901 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
903 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800904 if (!isEnabledInternal()) return false;
905
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700906 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700908
909 public synchronized boolean cancelDiscovery() {
910 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
911 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800912 if (!isEnabledInternal()) return false;
913
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700914 return stopDiscoveryNative();
915 }
916
917 public synchronized boolean isDiscovering() {
918 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
919 return mIsDiscovering;
920 }
921
922 /* package */ void setIsDiscovering(boolean isDiscovering) {
923 mIsDiscovering = isDiscovering;
924 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700926 private boolean isBondingFeasible(String address) {
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
Nick Pelly005b2282009-09-10 10:21:56 -0700931 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 return false;
933 }
934 address = address.toUpperCase();
935
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700936 if (mBondState.getPendingOutgoingBonding() != null) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800937 Log.d(TAG, "Ignoring createBond(): another device is bonding");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 // a different device is currently bonding, fail
939 return false;
940 }
941
942 // Check for bond state only if we are not performing auto
943 // pairing exponential back-off attempts.
944 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -0700945 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800946 Log.d(TAG, "Ignoring createBond(): this device is already bonding or bonded");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 return false;
948 }
949
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800950 if (address.equals(mDockAddress)) {
951 if (!writeDockPin()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800952 Log.e(TAG, "Error while writing Pin for the dock");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800953 return false;
954 }
955 }
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700956 return true;
957 }
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800958
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700959 public synchronized boolean createBond(String address) {
960 if (!isBondingFeasible(address)) return false;
961
962 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 return false;
964 }
965
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700966 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -0700968
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 return true;
970 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700972 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
973 byte[] randomizer) {
974 if (!isBondingFeasible(address)) return false;
975
976 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
977 return false;
978 }
979
980 setDeviceOutOfBandData(address, hash, randomizer);
981 mBondState.setPendingOutgoingBonding(address);
982 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
983
984 return true;
985 }
986
987 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
988 byte[] randomizer) {
989 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
990 "Need BLUETOOTH_ADMIN permission");
991 if (!isEnabledInternal()) return false;
992
993 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
994
995 if (DBG) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800996 Log.d(TAG, "Setting out of band data for: " + address + ":" +
997 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700998 }
999
1000 mDeviceOobData.put(address, value);
1001 return true;
1002 }
1003
1004 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1005 return mDeviceOobData.get(device.getAddress());
1006 }
1007
1008
1009 public synchronized byte[] readOutOfBandData() {
1010 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1011 "Need BLUETOOTH permission");
1012 if (!isEnabledInternal()) return null;
1013
1014 return readAdapterOutOfBandDataNative();
1015 }
1016
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 public synchronized boolean cancelBondProcess(String address) {
1018 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1019 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001020 if (!isEnabledInternal()) return false;
1021
Nick Pelly005b2282009-09-10 10:21:56 -07001022 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 return false;
1024 }
1025 address = address.toUpperCase();
1026 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1027 return false;
1028 }
1029
Nick Pelly005b2282009-09-10 10:21:56 -07001030 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001032 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 return true;
1034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035
1036 public synchronized boolean removeBond(String address) {
1037 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1038 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001039 if (!isEnabledInternal()) return false;
1040
Nick Pelly005b2282009-09-10 10:21:56 -07001041 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 return false;
1043 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001044 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001045 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001046 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001047 return true;
1048 } else {
1049 return false;
1050 }
1051 }
1052
1053 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshcc2c0662011-02-25 12:27:48 -08001054 // Unset the trusted device state and then unpair
1055 setTrust(address, false);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001056 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058
1059 public synchronized String[] listBonds() {
1060 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1061 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1062 }
1063
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001064 /*package*/ synchronized String[] listInState(int state) {
1065 return mBondState.listInState(state);
1066 }
1067
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 public synchronized int getBondState(String address) {
1069 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001070 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001071 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 }
1073 return mBondState.getBondState(address.toUpperCase());
1074 }
1075
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001076 /*package*/ synchronized boolean setBondState(String address, int state) {
1077 return setBondState(address, state, 0);
1078 }
1079
1080 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
Arek Lichwaae5fbb02011-02-23 11:52:34 +01001081 mBondState.setBondState(address.toUpperCase(), state, reason);
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001082 return true;
1083 }
1084
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001085 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001086 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001087 Context.MODE_PRIVATE);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001088
1089 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001090 }
1091
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001092 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001093 if (!isEnabledInternal()) return null;
1094
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001095 String objectPath = getObjectPathFromAddress(address);
1096 return (String [])getDevicePropertiesNative(objectPath);
1097 }
1098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001100 * Sets the remote device trust state.
1101 *
1102 * @return boolean to indicate operation success or fail
1103 */
1104 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001105 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001106 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1107 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001108 return false;
1109 }
1110
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001111 if (!isEnabledInternal()) return false;
1112
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001113 return setDevicePropertyBooleanNative(
1114 getObjectPathFromAddress(address), "Trusted", value ? 1 : 0);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001115 }
1116
1117 /**
1118 * Gets the remote device trust state as boolean.
1119 * Note: this value may be
1120 * retrieved from cache if we retrieved the data before *
1121 *
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001122 * @return boolean to indicate trusted or untrusted state
Lixin Yueefa1dd72009-08-31 15:55:13 +08001123 */
1124 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001125 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001126 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1127 return false;
1128 }
1129
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001130 String val = mDeviceProperties.getProperty(address, "Trusted");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001131 if (val == null) {
1132 return false;
1133 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001134 return val.equals("true");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001135 }
1136 }
1137
1138 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001139 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 * integer.
1141 *
1142 * Note: this value is retrieved from cache, because we get it during
1143 * remote-device discovery.
1144 *
1145 * @return 32-bit integer encoding the remote major, minor, and service
1146 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 */
1148 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001149 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001150 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1151 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001153 String val = mDeviceProperties.getProperty(address, "Class");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001154 if (val == null)
1155 return BluetoothClass.ERROR;
1156 else {
1157 return Integer.valueOf(val);
1158 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161
1162 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001163 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001165 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001167 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001169 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 return null;
1171 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001172 return getUuidFromCache(address);
1173 }
1174
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001175 ParcelUuid[] getUuidFromCache(String address) {
1176 String value = mDeviceProperties.getProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001177 if (value == null) return null;
1178
1179 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001180 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001181 uuidStrings = value.split(",");
1182 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1183
1184 for (int i = 0; i < uuidStrings.length; i++) {
1185 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1186 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001187 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189
Nick Pelly16fb88a2009-10-07 07:44:03 +02001190 /**
1191 * Connect and fetch new UUID's using SDP.
1192 * The UUID's found are broadcast as intents.
1193 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1194 * a given uuid.
1195 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1196 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1197 * callback and broadcast intents.
1198 */
1199 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1200 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001201 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001202 if (!isEnabledInternal()) return false;
1203
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001204 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1205 return false;
1206 }
1207
Nick Pelly16fb88a2009-10-07 07:44:03 +02001208 RemoteService service = new RemoteService(address, uuid);
1209 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1210 // An SDP query for this address & uuid is already in progress
1211 // Do not add this callback for the uuid
1212 return false;
1213 }
1214
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001215 if (mUuidIntentTracker.contains(address)) {
1216 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02001217 // Add this uuid onto the in-progress SDP query
1218 if (uuid != null) {
1219 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1220 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001221 return true;
1222 }
1223
Jaikumar Ganesh421f0102011-06-06 17:31:58 +09001224 // If the device is already created, we will
1225 // do the SDP on the callback of createDeviceNative.
1226 boolean ret= createDeviceNative(address);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001227
1228 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001229 if (uuid != null) {
1230 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1231 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001232
1233 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1234 message.obj = address;
1235 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1236 return ret;
1237 }
1238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001240 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02001241 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001243 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001244 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001246 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001247 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001248 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001249 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001251 if (!isEnabledInternal()) return -1;
1252
Nick Pelly005b2282009-09-10 10:21:56 -07001253 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001254 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001256 // Check if we are recovering from a crash.
1257 if (mDeviceProperties.isEmpty()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001258 if (mDeviceProperties.updateCache(address) == null)
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001259 return -1;
1260 }
1261
1262 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1263 if (value != null && value.containsKey(uuid))
1264 return value.get(uuid);
1265 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001267
1268 public synchronized boolean setPin(String address, byte[] pin) {
1269 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1270 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001271 if (!isEnabledInternal()) return false;
1272
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001274 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 return false;
1276 }
1277 address = address.toUpperCase();
1278 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1279 if (data == null) {
1280 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1281 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1282 " or by bluez.\n");
1283 return false;
1284 }
1285 // bluez API wants pin as a string
1286 String pinString;
1287 try {
1288 pinString = new String(pin, "UTF8");
1289 } catch (UnsupportedEncodingException uee) {
1290 Log.e(TAG, "UTF8 not supported?!?");
1291 return false;
1292 }
1293 return setPinNative(address, pinString, data.intValue());
1294 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001296 public synchronized boolean setPasskey(String address, int passkey) {
1297 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1298 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001299 if (!isEnabledInternal()) return false;
1300
Nick Pelly005b2282009-09-10 10:21:56 -07001301 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001302 return false;
1303 }
1304 address = address.toUpperCase();
1305 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1306 if (data == null) {
1307 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1308 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1309 " or by bluez.\n");
1310 return false;
1311 }
1312 return setPasskeyNative(address, passkey, data.intValue());
1313 }
1314
1315 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1316 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1317 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001318 if (!isEnabledInternal()) return false;
1319
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001320 address = address.toUpperCase();
1321 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1322 if (data == null) {
1323 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1324 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1325 " or by bluez.\n");
1326 return false;
1327 }
1328 return setPairingConfirmationNative(address, confirm, data.intValue());
1329 }
1330
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001331 public synchronized boolean setRemoteOutOfBandData(String address) {
1332 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1333 "Need BLUETOOTH_ADMIN permission");
1334 if (!isEnabledInternal()) return false;
1335 address = address.toUpperCase();
1336 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1337 if (data == null) {
1338 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
1339 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1340 " or by bluez.\n");
1341 return false;
1342 }
1343
1344 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
1345 byte[] hash, randomizer;
1346 if (val == null) {
1347 // TODO: check what should be passed in this case.
1348 hash = new byte[16];
1349 randomizer = new byte[16];
1350 } else {
1351 hash = val.first;
1352 randomizer = val.second;
1353 }
1354 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
1355 }
1356
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001357 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1359 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001360 if (!isEnabledInternal()) return false;
1361
Nick Pelly005b2282009-09-10 10:21:56 -07001362 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 return false;
1364 }
Nick Pelly005b2282009-09-10 10:21:56 -07001365 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001366 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 address = address.toUpperCase();
1368 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1369 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001370 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1371 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1372 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 return false;
1374 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001375 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001378 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001379 if (DBG) Log.d(TAG, "updateDeviceServiceChannelCache(" + address + ")");
1380
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001381 // We are storing the rfcomm channel numbers only for the uuids
1382 // we are interested in.
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001383 ParcelUuid[] deviceUuids = getRemoteUuids(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001384
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001385 ArrayList<ParcelUuid> applicationUuids = new ArrayList<ParcelUuid>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001386
1387 synchronized (this) {
1388 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1389 if (service.address.equals(address)) {
1390 applicationUuids.add(service.uuid);
1391 }
1392 }
1393 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001394
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001395 Map <ParcelUuid, Integer> uuidToChannelMap = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001396
1397 // Retrieve RFCOMM channel for default uuids
1398 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001399 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001400 int channel = getDeviceServiceChannelForUuid(address, uuid);
1401 uuidToChannelMap.put(uuid, channel);
1402 if (DBG) Log.d(TAG, "\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001403 }
1404 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02001405 // Retrieve RFCOMM channel for application requested uuids
1406 for (ParcelUuid uuid : applicationUuids) {
1407 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001408 int channel = getDeviceServiceChannelForUuid(address, uuid);
1409 uuidToChannelMap.put(uuid, channel);
1410 if (DBG) Log.d(TAG, "\tuuid(application): " + uuid + " " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001411 }
1412 }
1413
1414 synchronized (this) {
1415 // Make application callbacks
1416 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1417 iter.hasNext();) {
1418 RemoteService service = iter.next();
1419 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001420 if (uuidToChannelMap.containsKey(service.uuid)) {
1421 int channel = uuidToChannelMap.get(service.uuid);
1422
1423 if (DBG) Log.d(TAG, "Making callback for " + service.uuid +
1424 " with result " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001425 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1426 if (callback != null) {
1427 try {
1428 callback.onRfcommChannelFound(channel);
1429 } catch (RemoteException e) {Log.e(TAG, "", e);}
1430 }
1431
1432 iter.remove();
1433 }
1434 }
1435 }
1436
1437 // Update cache
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001438 mDeviceServiceChannelCache.put(address, uuidToChannelMap);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001439 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001440 }
1441
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001442 private int getDeviceServiceChannelForUuid(String address,
1443 ParcelUuid uuid) {
1444 return getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1445 uuid.toString(), 0x0004);
1446 }
1447
Nick Pelly24bb9b82009-10-02 20:34:18 -07001448 /**
1449 * b is a handle to a Binder instance, so that this service can be notified
1450 * for Applications that terminate unexpectedly, to clean there service
1451 * records
1452 */
1453 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1454 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001455 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1456 if (!isEnabledInternal()) return -1;
1457
Nick Pelly24bb9b82009-10-02 20:34:18 -07001458 if (serviceName == null || uuid == null || channel < 1 ||
1459 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1460 return -1;
1461 }
1462 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1463 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1464 return -1;
1465 }
1466 int handle = addRfcommServiceRecordNative(serviceName,
1467 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1468 (short)channel);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001469 if (DBG) Log.d(TAG, "new handle " + Integer.toHexString(handle));
Nick Pelly24bb9b82009-10-02 20:34:18 -07001470 if (handle == -1) {
1471 return -1;
1472 }
1473
1474 int pid = Binder.getCallingPid();
1475 mServiceRecordToPid.put(new Integer(handle), new Integer(pid));
1476 try {
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001477 b.linkToDeath(new Reaper(handle, pid, RFCOMM_RECORD_REAPER), 0);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001478 } catch (RemoteException e) {}
1479 return handle;
1480 }
1481
1482 public void removeServiceRecord(int handle) {
1483 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1484 "Need BLUETOOTH permission");
1485 checkAndRemoveRecord(handle, Binder.getCallingPid());
1486 }
1487
1488 private synchronized void checkAndRemoveRecord(int handle, int pid) {
1489 Integer handleInt = new Integer(handle);
1490 Integer owner = mServiceRecordToPid.get(handleInt);
1491 if (owner != null && pid == owner.intValue()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001492 if (DBG) Log.d(TAG, "Removing service record " +
1493 Integer.toHexString(handle) + " for pid " + pid);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001494 mServiceRecordToPid.remove(handleInt);
1495 removeServiceRecordNative(handle);
1496 }
1497 }
1498
1499 private class Reaper implements IBinder.DeathRecipient {
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001500 int mPid;
1501 int mHandle;
1502 int mType;
1503
1504 Reaper(int handle, int pid, int type) {
1505 mPid = pid;
1506 mHandle = handle;
1507 mType = type;
Nick Pelly24bb9b82009-10-02 20:34:18 -07001508 }
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001509
1510 Reaper(int pid, int type) {
1511 mPid = pid;
1512 mType = type;
1513 }
1514
1515 @Override
Nick Pelly24bb9b82009-10-02 20:34:18 -07001516 public void binderDied() {
1517 synchronized (BluetoothService.this) {
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001518 if (DBG) Log.d(TAG, "Tracked app " + mPid + " died" + "Type:" + mType);
1519 if (mType == RFCOMM_RECORD_REAPER) {
1520 checkAndRemoveRecord(mHandle, mPid);
1521 } else if (mType == STATE_CHANGE_REAPER) {
1522 mStateChangeTracker.remove(mPid);
1523 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001524 }
1525 }
1526 }
1527
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001528
1529 @Override
1530 public boolean changeApplicationBluetoothState(boolean on,
1531 IBluetoothStateChangeCallback callback, IBinder binder) {
1532 int pid = Binder.getCallingPid();
1533 //mStateChangeTracker is a synchronized map
1534 if (!mStateChangeTracker.containsKey(pid)) {
1535 if (on) {
1536 mStateChangeTracker.put(pid, callback);
1537 } else {
1538 return false;
1539 }
1540 } else if (!on) {
1541 mStateChangeTracker.remove(pid);
1542 }
1543
1544 if (binder != null) {
1545 try {
1546 binder.linkToDeath(new Reaper(pid, STATE_CHANGE_REAPER), 0);
1547 } catch (RemoteException e) {
1548 return false;
1549 }
1550 }
1551
1552 int type;
1553 if (on) {
1554 type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_ON;
1555 } else {
1556 type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_OFF;
1557 }
1558
1559 mBluetoothState.sendMessage(type, callback);
1560 return true;
1561 }
1562
1563 boolean isApplicationStateChangeTrackerEmpty() {
1564 return mStateChangeTracker.isEmpty();
1565 }
1566
1567 void clearApplicationStateChangeTracker() {
1568 mStateChangeTracker.clear();
1569 }
1570
1571 Collection<IBluetoothStateChangeCallback> getApplicationStateChangeCallbacks() {
1572 return mStateChangeTracker.values();
1573 }
1574
1575 int getNumberOfApplicationStateChangeTrackers() {
1576 return mStateChangeTracker.size();
1577 }
1578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001579 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1580 @Override
1581 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001582 if (intent == null) return;
1583
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 String action = intent.getAction();
1585 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1586 ContentResolver resolver = context.getContentResolver();
1587 // Query the airplane mode from Settings.System just to make sure that
1588 // some random app is not sending this intent and disabling bluetooth
Matthew Xie7f9ecca82011-07-15 13:03:58 -07001589 if (isAirplaneModeOn()) {
1590 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_ON);
1591 } else {
1592 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001594 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1595 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1596 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1597 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1598 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1599 mDockAddress = null;
1600 mDockPin = null;
1601 } else {
1602 SharedPreferences.Editor editor =
1603 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1604 mContext.MODE_PRIVATE).edit();
1605 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07001606 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608 }
1609 }
1610 };
1611
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001612 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08001613 final ContentResolver resolver = mContext.getContentResolver();
1614 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08001616 final String toggleableRadios = Settings.System.getString(resolver,
1617 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1618
1619 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1620 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1621 mIsAirplaneToggleable = toggleableRadios == null ? false :
1622 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001625 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 }
1627 }
1628
1629 /* Returns true if airplane mode is currently on */
1630 private final boolean isAirplaneModeOn() {
1631 return Settings.System.getInt(mContext.getContentResolver(),
1632 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1633 }
1634
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001635 /* Broadcast the Uuid intent */
1636 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001637 ParcelUuid[] uuid = getUuidFromCache(address);
1638 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07001639 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001640 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1641 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001642 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001643 }
1644
1645 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1646 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1647 iter.hasNext();) {
1648 RemoteService service = iter.next();
1649 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001650 if (DBG) Log.d(TAG, "Cleaning up failed UUID channel lookup: "
1651 + service.address + " " + service.uuid);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001652 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1653 if (callback != null) {
1654 try {
1655 callback.onRfcommChannelFound(-1);
1656 } catch (RemoteException e) {Log.e(TAG, "", e);}
1657 }
1658
1659 iter.remove();
1660 }
1661 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001662 }
1663
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 @Override
1665 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Matthew Xie7f9ecca82011-07-15 13:03:58 -07001666 if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001667 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001669
Nick Pelly24bb9b82009-10-02 20:34:18 -07001670 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08001671 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001672
1673 pw.println("Local address = " + getAddress());
1674 pw.println("Local name = " + getName());
1675 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07001676
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001677 mAdapter.getProfileProxy(mContext,
1678 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001679 mAdapter.getProfileProxy(mContext,
1680 mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001681 mAdapter.getProfileProxy(mContext,
1682 mBluetoothProfileServiceListener, BluetoothProfile.PAN);
The Android Open Source Project10592532009-03-18 17:39:46 -07001683
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001684 dumpKnownDevices(pw);
1685 dumpAclConnectedDevices(pw);
1686 dumpHeadsetService(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001687 dumpInputDeviceProfile(pw);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001688 dumpPanProfile(pw);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001689 dumpApplicationServiceRecords(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001690 }
1691
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001692 private void dumpHeadsetService(PrintWriter pw) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001693 pw.println("\n--Headset Service--");
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001694 if (mBluetoothHeadset != null) {
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001695 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
1696 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001697 pw.println("No headsets connected");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001698 } else {
1699 BluetoothDevice device = deviceList.get(0);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001700 pw.println("\ngetConnectedDevices[0] = " + device);
1701 dumpHeadsetConnectionState(pw, device);
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001702 pw.println("getBatteryUsageHint() = " +
1703 mBluetoothHeadset.getBatteryUsageHint(device));
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001704 }
1705
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001706 deviceList.clear();
1707 deviceList = mBluetoothHeadset.getDevicesMatchingConnectionStates(new int[] {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001708 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001709 pw.println("--Connected and Disconnected Headsets");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001710 for (BluetoothDevice device: deviceList) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001711 pw.println(device);
1712 if (mBluetoothHeadset.isAudioConnected(device)) {
1713 pw.println("SCO audio connected to device:" + device);
1714 }
1715 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001716 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001717 mAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
1718 }
Nick Pelly6c901db2009-06-19 10:08:09 -07001719
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001720 private void dumpInputDeviceProfile(PrintWriter pw) {
1721 pw.println("\n--Bluetooth Service- Input Device Profile");
1722 if (mInputDevice != null) {
1723 List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices();
1724 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001725 pw.println("No input devices connected");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001726 } else {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001727 pw.println("Number of connected devices:" + deviceList.size());
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001728 BluetoothDevice device = deviceList.get(0);
1729 pw.println("getConnectedDevices[0] = " + device);
1730 pw.println("Priority of Connected device = " + mInputDevice.getPriority(device));
1731
1732 switch (mInputDevice.getConnectionState(device)) {
1733 case BluetoothInputDevice.STATE_CONNECTING:
1734 pw.println("getConnectionState() = STATE_CONNECTING");
1735 break;
1736 case BluetoothInputDevice.STATE_CONNECTED:
1737 pw.println("getConnectionState() = STATE_CONNECTED");
1738 break;
1739 case BluetoothInputDevice.STATE_DISCONNECTING:
1740 pw.println("getConnectionState() = STATE_DISCONNECTING");
1741 break;
1742 }
1743 }
1744 deviceList.clear();
1745 deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] {
1746 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1747 pw.println("--Connected and Disconnected input devices");
1748 for (BluetoothDevice device: deviceList) {
1749 pw.println(device);
1750 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001751 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001752 mAdapter.closeProfileProxy(BluetoothProfile.INPUT_DEVICE, mBluetoothHeadset);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 }
1754
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001755 private void dumpPanProfile(PrintWriter pw) {
1756 pw.println("\n--Bluetooth Service- Pan Profile");
1757 if (mPan != null) {
1758 List<BluetoothDevice> deviceList = mPan.getConnectedDevices();
1759 if (deviceList.size() == 0) {
1760 pw.println("No Pan devices connected");
1761 } else {
1762 pw.println("Number of connected devices:" + deviceList.size());
1763 BluetoothDevice device = deviceList.get(0);
1764 pw.println("getConnectedDevices[0] = " + device);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001765
1766 switch (mPan.getConnectionState(device)) {
1767 case BluetoothInputDevice.STATE_CONNECTING:
1768 pw.println("getConnectionState() = STATE_CONNECTING");
1769 break;
1770 case BluetoothInputDevice.STATE_CONNECTED:
1771 pw.println("getConnectionState() = STATE_CONNECTED");
1772 break;
1773 case BluetoothInputDevice.STATE_DISCONNECTING:
1774 pw.println("getConnectionState() = STATE_DISCONNECTING");
1775 break;
1776 }
1777 }
1778 deviceList.clear();
1779 deviceList = mPan.getDevicesMatchingConnectionStates(new int[] {
1780 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1781 pw.println("--Connected and Disconnected Pan devices");
1782 for (BluetoothDevice device: deviceList) {
1783 pw.println(device);
1784 }
1785 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001786 }
1787
1788 private void dumpHeadsetConnectionState(PrintWriter pw,
1789 BluetoothDevice device) {
1790 switch (mBluetoothHeadset.getConnectionState(device)) {
1791 case BluetoothHeadset.STATE_CONNECTING:
1792 pw.println("getConnectionState() = STATE_CONNECTING");
1793 break;
1794 case BluetoothHeadset.STATE_CONNECTED:
1795 pw.println("getConnectionState() = STATE_CONNECTED");
1796 break;
1797 case BluetoothHeadset.STATE_DISCONNECTING:
1798 pw.println("getConnectionState() = STATE_DISCONNECTING");
1799 break;
1800 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
1801 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
1802 break;
1803 }
1804 }
1805
1806 private void dumpApplicationServiceRecords(PrintWriter pw) {
1807 pw.println("\n--Application Service Records--");
1808 for (Integer handle : mServiceRecordToPid.keySet()) {
1809 Integer pid = mServiceRecordToPid.get(handle);
1810 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
1811 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001812 mAdapter.closeProfileProxy(BluetoothProfile.PAN, mBluetoothHeadset);
1813 }
1814
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001815 private void dumpAclConnectedDevices(PrintWriter pw) {
1816 String[] devicesObjectPath = getKnownDevices();
1817 pw.println("\n--ACL connected devices--");
1818 if (devicesObjectPath != null) {
1819 for (String device : devicesObjectPath) {
1820 pw.println(getAddressFromObjectPath(device));
1821 }
1822 }
1823 }
1824
1825 private void dumpKnownDevices(PrintWriter pw) {
1826 pw.println("\n--Known devices--");
1827 for (String address : mDeviceProperties.keySet()) {
1828 int bondState = mBondState.getBondState(address);
1829 pw.printf("%s %10s (%d) %s\n", address,
1830 toBondStateString(bondState),
1831 mBondState.getAttempt(address),
1832 getRemoteName(address));
1833
1834 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
1835 if (uuidChannels == null) {
1836 pw.println("\tuuids = null");
1837 } else {
1838 for (ParcelUuid uuid : uuidChannels.keySet()) {
1839 Integer channel = uuidChannels.get(uuid);
1840 if (channel == null) {
1841 pw.println("\t" + uuid);
1842 } else {
1843 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
1844 }
1845 }
1846 }
1847 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1848 if (service.address.equals(address)) {
1849 pw.println("\tPENDING CALLBACK: " + service.uuid);
1850 }
1851 }
1852 }
1853 }
1854
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001855 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1856 new BluetoothProfile.ServiceListener() {
1857 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001858 if (profile == BluetoothProfile.HEADSET) {
1859 mBluetoothHeadset = (BluetoothHeadset) proxy;
1860 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1861 mInputDevice = (BluetoothInputDevice) proxy;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001862 } else if (profile == BluetoothProfile.PAN) {
1863 mPan = (BluetoothPan) proxy;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001864 }
1865 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001866 public void onServiceDisconnected(int profile) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001867 if (profile == BluetoothProfile.HEADSET) {
1868 mBluetoothHeadset = null;
1869 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
1870 mInputDevice = null;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001871 } else if (profile == BluetoothProfile.PAN) {
1872 mPan = null;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001873 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001874 }
1875 };
1876
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001877 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
1878 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001879 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001880 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07001881 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001882 else
Nick Pellybd022f42009-08-14 18:33:38 -07001883 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 }
1885
1886 /* package */ static String scanModeToBluezString(int mode) {
1887 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07001888 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07001890 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001891 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07001892 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001893 return "discoverable";
1894 }
1895 return null;
1896 }
1897
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001898 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001899 String adapterObjectPath = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001900 if (adapterObjectPath == null || objectPath == null) {
Jake Hambyf51eada2010-09-21 13:39:53 -07001901 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001902 " or deviceObjectPath:" + objectPath + " is null");
1903 return null;
1904 }
1905 if (!objectPath.startsWith(adapterObjectPath)) {
Jake Hambyf51eada2010-09-21 13:39:53 -07001906 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001907 " is not a prefix of deviceObjectPath:" + objectPath +
1908 "bluetoothd crashed ?");
1909 return null;
1910 }
1911 String address = objectPath.substring(adapterObjectPath.length());
1912 if (address != null) return address.replace('_', ':');
1913
1914 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
1915 return null;
1916 }
1917
1918 /*package*/ String getObjectPathFromAddress(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001919 String path = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001920 if (path == null) {
1921 Log.e(TAG, "Error: Object Path is null");
1922 return null;
1923 }
1924 path = path + address.replace(":", "_");
1925 return path;
1926 }
1927
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08001928 /*package */ void setLinkTimeout(String address, int num_slots) {
1929 String path = getObjectPathFromAddress(address);
1930 boolean result = setLinkTimeoutNative(path, num_slots);
1931
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001932 if (!result) Log.d(TAG, "Set Link Timeout to " + num_slots + " slots failed");
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08001933 }
1934
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001935 /**** Handlers for PAN Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001936 // TODO: This needs to be converted to a state machine.
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001937
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001938 public boolean isTetheringOn() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001939 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001940 synchronized (mBluetoothPanProfileHandler) {
1941 return mBluetoothPanProfileHandler.isTetheringOn();
1942 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001943 }
1944
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001945 /*package*/boolean allowIncomingTethering() {
1946 synchronized (mBluetoothPanProfileHandler) {
1947 return mBluetoothPanProfileHandler.allowIncomingTethering();
1948 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001949 }
1950
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001951 public void setBluetoothTethering(boolean value) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001952 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001953 synchronized (mBluetoothPanProfileHandler) {
1954 mBluetoothPanProfileHandler.setBluetoothTethering(value);
1955 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001956 }
1957
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001958 public int getPanDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001959 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001960 synchronized (mBluetoothPanProfileHandler) {
1961 return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
1962 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001963 }
1964
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001965 public boolean connectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001966 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1967 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001968 synchronized (mBluetoothPanProfileHandler) {
1969 return mBluetoothPanProfileHandler.connectPanDevice(device);
1970 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001971 }
1972
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001973 public List<BluetoothDevice> getConnectedPanDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001974 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001975 synchronized (mBluetoothPanProfileHandler) {
1976 return mBluetoothPanProfileHandler.getConnectedPanDevices();
1977 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001978 }
1979
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001980 public List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001981 int[] states) {
1982 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001983 synchronized (mBluetoothPanProfileHandler) {
1984 return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
1985 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001986 }
1987
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001988 public boolean disconnectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001989 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1990 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001991 synchronized (mBluetoothPanProfileHandler) {
1992 return mBluetoothPanProfileHandler.disconnectPanDevice(device);
1993 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001994 }
1995
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07001996 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08001997 String iface,
1998 int state,
1999 int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002000 synchronized (mBluetoothPanProfileHandler) {
2001 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
2002 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002003 }
2004
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002005 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002006 int state, int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002007 synchronized (mBluetoothPanProfileHandler) {
2008 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
2009 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002010 }
2011
2012 /**** Handlers for Input Device Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002013 // This needs to be converted to state machine
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002014
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002015 public boolean connectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002016 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2017 "Need BLUETOOTH_ADMIN permission");
2018 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002019 synchronized (mBluetoothInputProfileHandler) {
2020 return mBluetoothInputProfileHandler.connectInputDevice(device, state);
2021 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002022 }
2023
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002024 public boolean connectInputDeviceInternal(BluetoothDevice device) {
2025 synchronized (mBluetoothInputProfileHandler) {
2026 return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
2027 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002028 }
2029
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002030 public boolean disconnectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002031 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2032 "Need BLUETOOTH_ADMIN permission");
2033 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002034 synchronized (mBluetoothInputProfileHandler) {
2035 return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
2036 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002037 }
2038
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002039 public boolean disconnectInputDeviceInternal(BluetoothDevice device) {
2040 synchronized (mBluetoothInputProfileHandler) {
2041 return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
2042 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002043 }
2044
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002045 public int getInputDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002046 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002047 synchronized (mBluetoothInputProfileHandler) {
2048 return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
2049 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002050 }
2051
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002052 public List<BluetoothDevice> getConnectedInputDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002053 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002054 synchronized (mBluetoothInputProfileHandler) {
2055 return mBluetoothInputProfileHandler.getConnectedInputDevices();
2056 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002057 }
2058
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002059 public List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002060 int[] states) {
2061 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002062 synchronized (mBluetoothInputProfileHandler) {
2063 return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
2064 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002065 }
2066
2067
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002068 public int getInputDevicePriority(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002069 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002070 synchronized (mBluetoothInputProfileHandler) {
2071 return mBluetoothInputProfileHandler.getInputDevicePriority(device);
2072 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002073 }
2074
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002075 public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002076 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2077 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002078 synchronized (mBluetoothInputProfileHandler) {
2079 return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
2080 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002081 }
2082
Matthew Xiea0c68032011-06-25 21:47:07 -07002083 public boolean allowIncomingHidConnect(BluetoothDevice device, boolean allow) {
2084 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2085 "Need BLUETOOTH_ADMIN permission");
2086 String address = device.getAddress();
2087 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2088 return false;
2089 }
2090
2091 Integer data = getAuthorizationAgentRequestData(address);
2092 if (data == null) {
2093 Log.w(TAG, "allowIncomingHidConnect(" + device +
2094 ") called but no native data available");
2095 return false;
2096 }
2097 if (DBG) log("allowIncomingHidConnect: " + device + " : " + allow + " : " + data);
2098 return setAuthorizationNative(address, allow, data.intValue());
2099 }
2100
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002101 /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
2102 synchronized (mBluetoothInputProfileHandler) {
2103 return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
2104 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002105 }
2106
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002107 /*package*/void handleInputDevicePropertyChange(String address, boolean connected) {
2108 synchronized (mBluetoothInputProfileHandler) {
2109 mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
2110 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002111 }
2112
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002113 /**** Handlers for Health Device Profile ****/
2114 // TODO: All these need to be converted to a state machine.
2115
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002116 public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
2117 IBluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002118 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2119 "Need BLUETOOTH permission");
2120 synchronized (mBluetoothHealthProfileHandler) {
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002121 return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002122 }
2123 }
2124
2125 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
2126 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2127 "Need BLUETOOTH permission");
2128 synchronized (mBluetoothHealthProfileHandler) {
2129 return mBluetoothHealthProfileHandler.unregisterAppConfiguration(config);
2130 }
2131 }
2132
2133
2134 public boolean connectChannelToSource(BluetoothDevice device,
2135 BluetoothHealthAppConfiguration config) {
2136 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2137 "Need BLUETOOTH permission");
2138 synchronized (mBluetoothHealthProfileHandler) {
2139 return mBluetoothHealthProfileHandler.connectChannelToSource(device,
2140 config);
2141 }
2142 }
2143
2144 public boolean connectChannelToSink(BluetoothDevice device,
2145 BluetoothHealthAppConfiguration config, int channelType) {
2146 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2147 "Need BLUETOOTH permission");
2148 synchronized (mBluetoothHealthProfileHandler) {
2149 return mBluetoothHealthProfileHandler.connectChannel(device, config,
2150 channelType);
2151 }
2152 }
2153
2154 public boolean disconnectChannel(BluetoothDevice device,
2155 BluetoothHealthAppConfiguration config, ParcelFileDescriptor fd) {
2156 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2157 "Need BLUETOOTH permission");
2158 synchronized (mBluetoothHealthProfileHandler) {
2159 return mBluetoothHealthProfileHandler.disconnectChannel(device, config, fd);
2160 }
2161 }
2162
2163 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
2164 BluetoothHealthAppConfiguration config) {
2165 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2166 "Need BLUETOOTH permission");
2167 synchronized (mBluetoothHealthProfileHandler) {
2168 return mBluetoothHealthProfileHandler.getMainChannelFd(device, config);
2169 }
2170 }
2171
2172 /*package*/ void onHealthDevicePropertyChanged(String devicePath,
2173 String channelPath) {
2174 synchronized (mBluetoothHealthProfileHandler) {
2175 mBluetoothHealthProfileHandler.onHealthDevicePropertyChanged(devicePath,
2176 channelPath);
2177 }
2178 }
2179
2180 /*package*/ void onHealthDeviceChannelChanged(String devicePath,
2181 String channelPath, boolean exists) {
2182 synchronized(mBluetoothHealthProfileHandler) {
2183 mBluetoothHealthProfileHandler.onHealthDeviceChannelChanged(devicePath,
2184 channelPath, exists);
2185 }
2186 }
2187
2188 public int getHealthDeviceConnectionState(BluetoothDevice device) {
2189 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2190 "Need BLUETOOTH permission");
2191 synchronized (mBluetoothHealthProfileHandler) {
2192 return mBluetoothHealthProfileHandler.getHealthDeviceConnectionState(device);
2193 }
2194 }
2195
2196 public List<BluetoothDevice> getConnectedHealthDevices() {
2197 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2198 "Need BLUETOOTH permission");
2199 synchronized (mBluetoothHealthProfileHandler) {
2200 return mBluetoothHealthProfileHandler.getConnectedHealthDevices();
2201 }
2202 }
2203
2204 public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(
2205 int[] states) {
2206 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2207 "Need BLUETOOTH permission");
2208 synchronized (mBluetoothHealthProfileHandler) {
2209 return mBluetoothHealthProfileHandler.
2210 getHealthDevicesMatchingConnectionStates(states);
2211 }
2212 }
2213
Matthew Xiea0c68032011-06-25 21:47:07 -07002214 /*package*/boolean notifyIncomingHidConnection(String address) {
2215 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2216 if (state == null) {
2217 return false;
2218 }
2219 Message msg = new Message();
2220 msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING;
2221 state.sendMessage(msg);
2222 return true;
2223 }
2224
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002225 public boolean connectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002226 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2227
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002228 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002229 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002230 Message msg = new Message();
2231 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2232 msg.obj = state;
2233 mHfpProfileState.sendMessage(msg);
2234 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002235 }
2236 return false;
2237 }
2238
2239 public boolean disconnectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002240 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2241
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002242 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002243 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002244 Message msg = new Message();
2245 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2246 msg.obj = state;
2247 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002248 return true;
2249 }
2250 return false;
2251 }
2252
2253 public boolean connectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002254 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2255
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002256 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002257 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002258 Message msg = new Message();
2259 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2260 msg.obj = state;
2261 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002262 return true;
2263 }
2264 return false;
2265 }
2266
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002267 public boolean disconnectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002268 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2269
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002270 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2271 if (state != null) {
2272 Message msg = new Message();
2273 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2274 msg.obj = state;
2275 mA2dpProfileState.sendMessage(msg);
2276 return true;
2277 }
2278 return false;
2279 }
2280
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002281 BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002282 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002283 if (state != null) return state;
2284
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002285 state = new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002286 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002287 state.start();
2288 return state;
2289 }
2290
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002291 void removeProfileState(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002292 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002293 }
2294
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002295 synchronized String[] getKnownDevices() {
2296 String[] bonds = null;
2297 String val = getProperty("Devices");
2298 if (val != null) {
2299 bonds = val.split(",");
2300 }
2301 return bonds;
2302 }
2303
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002304 private void initProfileState() {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002305 String[] bonds = null;
2306 String val = mAdapterProperties.getProperty("Devices");
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002307 if (val != null) {
2308 bonds = val.split(",");
2309 }
2310 if (bonds == null) {
2311 return;
2312 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002313 for (String path : bonds) {
2314 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002315 BluetoothDeviceProfileState state = addProfileState(address, false);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002316 }
2317 }
2318
2319 private void autoConnect() {
2320 String[] bonds = getKnownDevices();
2321 if (bonds == null) {
2322 return;
2323 }
2324 for (String path : bonds) {
2325 String address = getAddressFromObjectPath(path);
2326 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2327 if (state != null) {
2328 Message msg = new Message();
2329 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
2330 state.sendMessage(msg);
2331 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002332 }
2333 }
2334
2335 public boolean notifyIncomingConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002336 BluetoothDeviceProfileState state =
2337 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002338 if (state != null) {
2339 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002340 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002341 state.sendMessage(msg);
2342 return true;
2343 }
2344 return false;
2345 }
2346
2347 /*package*/ boolean notifyIncomingA2dpConnection(String address) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002348 BluetoothDeviceProfileState state =
2349 mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002350 if (state != null) {
2351 Message msg = new Message();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002352 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002353 state.sendMessage(msg);
2354 return true;
2355 }
2356 return false;
2357 }
2358
2359 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2360 mA2dpService = a2dpService;
2361 }
2362
Matthew Xiea0c68032011-06-25 21:47:07 -07002363 /*package*/ Integer getAuthorizationAgentRequestData(String address) {
2364 Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
2365 return data;
2366 }
2367
Jaikumar Ganesh70a053b2010-10-13 15:54:30 -07002368 public void sendProfileStateMessage(int profile, int cmd) {
2369 Message msg = new Message();
2370 msg.what = cmd;
2371 if (profile == BluetoothProfileState.HFP) {
2372 mHfpProfileState.sendMessage(msg);
2373 } else if (profile == BluetoothProfileState.A2DP) {
2374 mA2dpProfileState.sendMessage(msg);
2375 }
2376 }
2377
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002378 public int getAdapterConnectionState() {
2379 return mAdapterConnectionState;
2380 }
2381
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002382 public synchronized void sendConnectionStateChange(BluetoothDevice device, int state,
2383 int prevState) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002384 // Since this is a binder call check if Bluetooth is on still
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002385 if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002386
2387 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
2388 if (!validateProfileConnectionState(state) ||
2389 !validateProfileConnectionState(prevState)) {
2390 // Previously, an invalid state was broadcast anyway,
2391 // with the invalid state converted to -1 in the intent.
2392 // Better to log an error and not send an intent with
2393 // invalid contents or set mAdapterConnectionState to -1.
2394 Log.e(TAG, "Error in sendConnectionStateChange: "
2395 + "prevState " + prevState + " state " + state);
2396 return;
2397 }
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002398
2399 mAdapterConnectionState = state;
2400
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002401 if (state == BluetoothProfile.STATE_DISCONNECTED) {
2402 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.ALL_DEVICES_DISCONNECTED);
2403 }
2404
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002405 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
2406 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002407 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
2408 convertToAdapterState(state));
2409 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
2410 convertToAdapterState(prevState));
Jaikumar Ganesh60b4d2a2011-07-12 14:14:49 -07002411 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002412 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002413 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
2414 + prevState + " -> " + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002415 }
2416 }
2417
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002418 private boolean validateProfileConnectionState(int state) {
2419 return (state == BluetoothProfile.STATE_DISCONNECTED ||
2420 state == BluetoothProfile.STATE_CONNECTING ||
2421 state == BluetoothProfile.STATE_CONNECTED ||
2422 state == BluetoothProfile.STATE_DISCONNECTING);
2423 }
2424
2425 private int convertToAdapterState(int state) {
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002426 switch (state) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002427 case BluetoothProfile.STATE_DISCONNECTED:
2428 return BluetoothAdapter.STATE_DISCONNECTED;
2429 case BluetoothProfile.STATE_DISCONNECTING:
2430 return BluetoothAdapter.STATE_DISCONNECTING;
2431 case BluetoothProfile.STATE_CONNECTED:
2432 return BluetoothAdapter.STATE_CONNECTED;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002433 case BluetoothProfile.STATE_CONNECTING:
2434 return BluetoothAdapter.STATE_CONNECTING;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002435 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002436 Log.e(TAG, "Error in convertToAdapterState");
2437 return -1;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002438 }
2439
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002440 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
2441 switch (prevState) {
2442 case BluetoothProfile.STATE_CONNECTING:
2443 mProfilesConnecting--;
2444 break;
2445
2446 case BluetoothProfile.STATE_CONNECTED:
2447 mProfilesConnected--;
2448 break;
2449
2450 case BluetoothProfile.STATE_DISCONNECTING:
2451 mProfilesDisconnecting--;
2452 break;
2453 }
2454
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002455 switch (state) {
2456 case BluetoothProfile.STATE_CONNECTING:
2457 mProfilesConnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002458 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002459
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002460 case BluetoothProfile.STATE_CONNECTED:
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002461 mProfilesConnected++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002462 return (mProfilesConnected == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002463
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002464 case BluetoothProfile.STATE_DISCONNECTING:
2465 mProfilesDisconnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002466 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002467
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002468 case BluetoothProfile.STATE_DISCONNECTED:
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002469 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002470
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002471 default:
2472 return true;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002473 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002474 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002475
Matthew Xiea0c68032011-06-25 21:47:07 -07002476 private void createIncomingConnectionStateFile() {
2477 File f = new File(INCOMING_CONNECTION_FILE);
2478 if (!f.exists()) {
2479 try {
2480 f.createNewFile();
2481 } catch (IOException e) {
2482 Log.e(TAG, "IOException: cannot create file");
2483 }
2484 }
2485 }
2486
2487 /** @hide */
2488 public Pair<Integer, String> getIncomingState(String address) {
2489 if (mIncomingConnections.isEmpty()) {
2490 createIncomingConnectionStateFile();
2491 readIncomingConnectionState();
2492 }
2493 return mIncomingConnections.get(address);
2494 }
2495
2496 private void readIncomingConnectionState() {
2497 synchronized(mIncomingConnections) {
2498 FileInputStream fstream = null;
2499 try {
2500 fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
2501 DataInputStream in = new DataInputStream(fstream);
2502 BufferedReader file = new BufferedReader(new InputStreamReader(in));
2503 String line;
2504 while((line = file.readLine()) != null) {
2505 line = line.trim();
2506 if (line.length() == 0) continue;
2507 String[] value = line.split(",");
2508 if (value != null && value.length == 3) {
2509 Integer val1 = Integer.parseInt(value[1]);
2510 Pair<Integer, String> val = new Pair(val1, value[2]);
2511 mIncomingConnections.put(value[0], val);
2512 }
2513 }
2514 } catch (FileNotFoundException e) {
2515 log("FileNotFoundException: readIncomingConnectionState" + e.toString());
2516 } catch (IOException e) {
2517 log("IOException: readIncomingConnectionState" + e.toString());
2518 } finally {
2519 if (fstream != null) {
2520 try {
2521 fstream.close();
2522 } catch (IOException e) {
2523 // Ignore
2524 }
2525 }
2526 }
2527 }
2528 }
2529
2530 private void truncateIncomingConnectionFile() {
2531 RandomAccessFile r = null;
2532 try {
2533 r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
2534 r.setLength(0);
2535 } catch (FileNotFoundException e) {
2536 log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
2537 } catch (IOException e) {
2538 log("IOException: truncateIncomingConnectionState" + e.toString());
2539 } finally {
2540 if (r != null) {
2541 try {
2542 r.close();
2543 } catch (IOException e) {
2544 // ignore
2545 }
2546 }
2547 }
2548 }
2549
2550 /** @hide */
2551 public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
2552 synchronized(mIncomingConnections) {
2553 mIncomingConnections.put(address, data);
2554
2555 truncateIncomingConnectionFile();
2556 BufferedWriter out = null;
2557 StringBuilder value = new StringBuilder();
2558 try {
2559 out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
2560 for (String devAddress: mIncomingConnections.keySet()) {
2561 Pair<Integer, String> val = mIncomingConnections.get(devAddress);
2562 value.append(devAddress);
2563 value.append(",");
2564 value.append(val.first.toString());
2565 value.append(",");
2566 value.append(val.second);
2567 value.append("\n");
2568 }
2569 out.write(value.toString());
2570 } catch (FileNotFoundException e) {
2571 log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
2572 } catch (IOException e) {
2573 log("IOException: writeIncomingConnectionState" + e.toString());
2574 } finally {
2575 if (out != null) {
2576 try {
2577 out.close();
2578 } catch (IOException e) {
2579 // Ignore
2580 }
2581 }
2582 }
2583 }
2584 }
2585
2586 private static void log(String msg) {
2587 Log.d(TAG, msg);
2588 }
2589
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002590 private native static void classInitNative();
2591 private native void initializeNativeDataNative();
2592 private native boolean setupNativeDataNative();
2593 private native boolean tearDownNativeDataNative();
2594 private native void cleanupNativeDataNative();
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002595 /*package*/ native String getAdapterPathNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002596
2597 private native int isEnabledNative();
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002598 /*package*/ native int enableNative();
2599 /*package*/ native int disableNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002600
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002601 /*package*/ native Object[] getAdapterPropertiesNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002602 private native Object[] getDevicePropertiesNative(String objectPath);
2603 private native boolean setAdapterPropertyStringNative(String key, String value);
2604 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2605 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2606
2607 private native boolean startDiscoveryNative();
2608 private native boolean stopDiscoveryNative();
2609
2610 private native boolean createPairedDeviceNative(String address, int timeout_ms);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002611 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2612 private native byte[] readAdapterOutOfBandDataNative();
2613
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002614 private native boolean cancelDeviceCreationNative(String address);
2615 private native boolean removeDeviceNative(String objectPath);
2616 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2617 int attributeId);
2618
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002619 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002620 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002621 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2622 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2623 int nativeData);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002624 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2625 byte[] randomizer, int nativeData);
2626
Nick Pelly24bb9b82009-10-02 20:34:18 -07002627 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2628 int value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002629 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002630 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002631
Nick Pelly24bb9b82009-10-02 20:34:18 -07002632 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2633 short channel);
2634 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002635 private native boolean setLinkTimeoutNative(String path, int num_slots);
Matthew Xiea0c68032011-06-25 21:47:07 -07002636
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002637 native boolean connectInputDeviceNative(String path);
2638 native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002639
Jaikumar Ganesha8571f12011-02-11 15:46:54 -08002640 native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2641 native boolean connectPanDeviceNative(String path, String dstRole);
2642 native boolean disconnectPanDeviceNative(String path);
2643 native boolean disconnectPanServerDeviceNative(String path,
Jaikumar Ganesha44a1e72011-02-11 12:40:44 -08002644 String address, String iface);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -08002645
2646 private native int[] addReservedServiceRecordsNative(int[] uuuids);
2647 private native boolean removeReservedServiceRecordsNative(int[] handles);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002648
2649 // Health API
2650 native String registerHealthApplicationNative(int dataType, String role, String name,
2651 String channelType);
2652 native String registerHealthApplicationNative(int dataType, String role, String name);
2653 native boolean unregisterHealthApplicationNative(String path);
2654 native boolean createChannelNative(String devicePath, String appPath, String channelType);
2655 native boolean destroyChannelNative(String devicePath, String channelpath);
2656 native String getMainChannelNative(String path);
2657 native String getChannelApplicationNative(String channelPath);
2658 native ParcelFileDescriptor getChannelFdNative(String channelPath);
2659 native boolean releaseChannelFdNative(String channelPath);
Matthew Xiea0c68032011-06-25 21:47:07 -07002660 native boolean setAuthorizationNative(String address, boolean value, int data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002661}