blob: 97c0209850cafdecadd70c4252cff2be5f25039b [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;
Travis Geiselbrechta91da5d2012-03-06 11:41:58 -080049import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Binder;
51import android.os.Handler;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080052import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.Message;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -070054import android.os.ParcelFileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080055import android.os.ParcelUuid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.os.RemoteException;
The Android Open Source Project10592532009-03-18 17:39:46 -070057import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.provider.Settings;
59import android.util.Log;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -070060import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -070062import com.android.internal.app.IBatteryStats;
63
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080064import java.io.BufferedInputStream;
Matthew Xiea0c68032011-06-25 21:47:07 -070065import java.io.BufferedReader;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080066import java.io.BufferedWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070067import java.io.DataInputStream;
68import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import java.io.FileDescriptor;
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -080070import java.io.FileInputStream;
71import java.io.FileNotFoundException;
72import java.io.FileWriter;
73import java.io.IOException;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -070074import java.io.InputStreamReader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import java.io.PrintWriter;
Matthew Xiea0c68032011-06-25 21:47:07 -070076import java.io.RandomAccessFile;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import java.io.UnsupportedEncodingException;
78import java.util.ArrayList;
79import java.util.Arrays;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -070080import java.util.Collection;
81import java.util.Collections;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082import java.util.HashMap;
Nick Pelly16fb88a2009-10-07 07:44:03 +020083import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070084import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085import java.util.Map;
86
Nick Pellybd022f42009-08-14 18:33:38 -070087public class BluetoothService extends IBluetooth.Stub {
88 private static final String TAG = "BluetoothService";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private static final boolean DBG = true;
90
91 private int mNativeData;
92 private BluetoothEventLoop mEventLoop;
Matthew Xie98f06da2011-11-10 00:03:28 -080093 private BluetoothHeadset mHeadsetProxy;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -080094 private BluetoothInputDevice mInputDevice;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -080095 private BluetoothPan mPan;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private boolean mIsAirplaneSensitive;
Jeff Sharkey44303922009-12-01 18:25:02 -080097 private boolean mIsAirplaneToggleable;
Matthew Xie7f9ecca82011-07-15 13:03:58 -070098 private BluetoothAdapterStateMachine mBluetoothState;
Jaikumar Ganesh84690c82010-12-10 12:48:58 -080099 private int[] mAdapterSdpHandles;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800100 private ParcelUuid[] mAdapterUuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
Nick Pellybd022f42009-08-14 18:33:38 -0700102 private BluetoothAdapter mAdapter; // constant after init()
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800103 private final BluetoothBondState mBondState; // local cache of bondings
Nick Pellybd022f42009-08-14 18:33:38 -0700104 private final IBatteryStats mBatteryStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private final Context mContext;
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -0700106 private Map<Integer, IBluetoothStateChangeCallback> mStateChangeTracker =
107 Collections.synchronizedMap(new HashMap<Integer, IBluetoothStateChangeCallback>());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
109 private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800110 static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800112 private static final String DOCK_ADDRESS_PATH = "/sys/class/switch/dock/bt_addr";
113 private static final String DOCK_PIN_PATH = "/sys/class/switch/dock/bt_pin";
114
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800115 private static final String SHARED_PREFERENCE_DOCK_ADDRESS = "dock_bluetooth_address";
116 private static final String SHARED_PREFERENCES_NAME = "bluetooth_service_settings";
117
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700118 private static final int MESSAGE_UUID_INTENT = 1;
119 private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 2;
Matthew Xie484867a2011-08-25 16:45:58 -0700120 private static final int MESSAGE_REMOVE_SERVICE_RECORD = 3;
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
Jason Simmonsc980a492011-11-16 12:52:45 -0800149 private static class ServiceRecordClient {
150 int pid;
151 IBinder binder;
152 IBinder.DeathRecipient death;
153 }
154 private final HashMap<Integer, ServiceRecordClient> mServiceRecordToPid;
Nick Pelly24bb9b82009-10-02 20:34:18 -0700155
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700156 private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState;
157 private final BluetoothProfileState mA2dpProfileState;
158 private final BluetoothProfileState mHfpProfileState;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700159
160 private BluetoothA2dpService mA2dpService;
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700161 private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700162
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700163 private int mProfilesConnected = 0, mProfilesConnecting = 0, mProfilesDisconnecting = 0;
Jaikumar Ganesh707952e2010-09-13 19:04:54 -0700164
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800165 private static String mDockAddress;
166 private String mDockPin;
167
Martijn Coenen6c614b72012-04-18 13:01:15 -0700168 private boolean mAllowConnect = true;
169
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700170 private int mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800171 private BluetoothPanProfileHandler mBluetoothPanProfileHandler;
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800172 private BluetoothInputProfileHandler mBluetoothInputProfileHandler;
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700173 private BluetoothHealthProfileHandler mBluetoothHealthProfileHandler;
Matthew Xiea0c68032011-06-25 21:47:07 -0700174 private static final String INCOMING_CONNECTION_FILE =
175 "/data/misc/bluetooth/incoming_connection.conf";
176 private HashMap<String, Pair<Integer, String>> mIncomingConnections;
Jaikumar Ganeshcb1d3542011-08-19 10:26:32 -0700177 private HashMap<Integer, Pair<Integer, Integer>> mProfileConnectionState;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700178
Nick Pelly16fb88a2009-10-07 07:44:03 +0200179 private static class RemoteService {
180 public String address;
181 public ParcelUuid uuid;
182 public RemoteService(String address, ParcelUuid uuid) {
183 this.address = address;
184 this.uuid = uuid;
185 }
186 @Override
187 public boolean equals(Object o) {
188 if (o instanceof RemoteService) {
189 RemoteService service = (RemoteService)o;
190 return address.equals(service.address) && uuid.equals(service.uuid);
191 }
192 return false;
193 }
Kenny Root5f614162010-02-17 12:00:37 -0800194
195 @Override
196 public int hashCode() {
197 int hash = 1;
198 hash = hash * 31 + (address == null ? 0 : address.hashCode());
199 hash = hash * 31 + (uuid == null ? 0 : uuid.hashCode());
200 return hash;
201 }
Nick Pelly16fb88a2009-10-07 07:44:03 +0200202 }
203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 static {
205 classInitNative();
206 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207
Nick Pellybd022f42009-08-14 18:33:38 -0700208 public BluetoothService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 mContext = context;
The Android Open Source Project10592532009-03-18 17:39:46 -0700210
211 // Need to do this in place of:
212 // mBatteryStats = BatteryStatsService.getService();
213 // Since we can not import BatteryStatsService from here. This class really needs to be
214 // moved to java/services/com/android/server/
215 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 initializeNativeDataNative();
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700218
219 if (isEnabledNative() == 1) {
220 Log.w(TAG, "Bluetooth daemons already running - runtime restart? ");
221 disableNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700223
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800224 mBondState = new BluetoothBondState(context, this);
225 mAdapterProperties = new BluetoothAdapterProperties(context, this);
226 mDeviceProperties = new BluetoothDeviceProperties(this);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -0700227
228 mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -0700229 mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700230 mUuidIntentTracker = new ArrayList<String>();
Nick Pelly16fb88a2009-10-07 07:44:03 +0200231 mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
Jason Simmonsc980a492011-11-16 12:52:45 -0800232 mServiceRecordToPid = new HashMap<Integer, ServiceRecordClient>();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -0700233 mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>();
234 mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP);
235 mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP);
236
237 mHfpProfileState.start();
238 mA2dpProfileState.start();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800239
240 IntentFilter filter = new IntentFilter();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800241 registerForAirplaneMode(filter);
242
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800243 filter.addAction(Intent.ACTION_DOCK_EVENT);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800244 mContext.registerReceiver(mReceiver, filter);
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -0800245 mBluetoothInputProfileHandler = BluetoothInputProfileHandler.getInstance(mContext, this);
Jaikumar Ganesha8571f12011-02-11 15:46:54 -0800246 mBluetoothPanProfileHandler = BluetoothPanProfileHandler.getInstance(mContext, this);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -0700247 mBluetoothHealthProfileHandler = BluetoothHealthProfileHandler.getInstance(mContext, this);
Matthew Xiea0c68032011-06-25 21:47:07 -0700248 mIncomingConnections = new HashMap<String, Pair<Integer, String>>();
Jaikumar Ganeshcb1d3542011-08-19 10:26:32 -0700249 mProfileConnectionState = new HashMap<Integer, Pair<Integer, Integer>>();
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800250 }
251
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -0700252 public static synchronized String readDockBluetoothAddress() {
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800253 if (mDockAddress != null) return mDockAddress;
254
255 BufferedInputStream file = null;
256 String dockAddress;
257 try {
258 file = new BufferedInputStream(new FileInputStream(DOCK_ADDRESS_PATH));
259 byte[] address = new byte[17];
260 file.read(address);
261 dockAddress = new String(address);
262 dockAddress = dockAddress.toUpperCase();
263 if (BluetoothAdapter.checkBluetoothAddress(dockAddress)) {
264 mDockAddress = dockAddress;
265 return mDockAddress;
266 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800267 Log.e(TAG, "CheckBluetoothAddress failed for car dock address: "
268 + dockAddress);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800269 }
270 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800271 Log.e(TAG, "FileNotFoundException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800272 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800273 Log.e(TAG, "IOException while trying to read dock address");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800274 } finally {
275 if (file != null) {
276 try {
277 file.close();
278 } catch (IOException e) {
279 // Ignore
280 }
281 }
282 }
283 mDockAddress = null;
284 return null;
285 }
286
287 private synchronized boolean writeDockPin() {
288 BufferedWriter out = null;
289 try {
290 out = new BufferedWriter(new FileWriter(DOCK_PIN_PATH));
291
292 // Generate a random 4 digit pin between 0000 and 9999
293 // This is not truly random but good enough for our purposes.
294 int pin = (int) Math.floor(Math.random() * 10000);
295
296 mDockPin = String.format("%04d", pin);
297 out.write(mDockPin);
298 return true;
299 } catch (FileNotFoundException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800300 Log.e(TAG, "FileNotFoundException while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800301 } catch (IOException e) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800302 Log.e(TAG, "IOException while while trying to write dock pairing pin");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -0800303 } finally {
304 if (out != null) {
305 try {
306 out.close();
307 } catch (IOException e) {
308 // Ignore
309 }
310 }
311 }
312 mDockPin = null;
313 return false;
314 }
315
316 /*package*/ synchronized String getDockPin() {
317 return mDockPin;
Nick Pellybd022f42009-08-14 18:33:38 -0700318 }
319
320 public synchronized void initAfterRegistration() {
Nick Pellyf242b7b2009-10-08 00:12:45 +0200321 mAdapter = BluetoothAdapter.getDefaultAdapter();
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700322 mBluetoothState = new BluetoothAdapterStateMachine(mContext, this, mAdapter);
323 mBluetoothState.start();
Matthew Xie9de1feb2011-10-03 12:50:27 -0700324 if (mContext.getResources().getBoolean
325 (com.android.internal.R.bool.config_bluetooth_adapter_quick_switch)) {
326 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.TURN_HOT);
327 }
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700328 mEventLoop = mBluetoothState.getBluetoothEventLoop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330
Jaikumar Ganesh7d0548d2010-10-18 15:29:09 -0700331 public synchronized void initAfterA2dpRegistration() {
332 mEventLoop.getProfileProxy();
333 }
334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 @Override
336 protected void finalize() throws Throwable {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -0800337 mContext.unregisterReceiver(mReceiver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 try {
339 cleanupNativeDataNative();
340 } finally {
341 super.finalize();
342 }
343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344
345 public boolean isEnabled() {
346 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800347 return isEnabledInternal();
348 }
349
350 private boolean isEnabledInternal() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700351 return (getBluetoothStateInternal() == BluetoothAdapter.STATE_ON);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353
The Android Open Source Project10592532009-03-18 17:39:46 -0700354 public int getBluetoothState() {
355 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700356 return getBluetoothStateInternal();
The Android Open Source Project10592532009-03-18 17:39:46 -0700357 }
358
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800359 int getBluetoothStateInternal() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700360 return mBluetoothState.getBluetoothAdapterState();
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800361 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 /**
364 * Bring down bluetooth and disable BT in settings. Returns true on success.
365 */
366 public boolean disable() {
367 return disable(true);
368 }
369
370 /**
371 * Bring down bluetooth. Returns true on success.
372 *
Nick Pellye6ee3be2009-10-08 23:27:28 +0200373 * @param saveSetting If true, persist the new setting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 */
375 public synchronized boolean disable(boolean saveSetting) {
Nick Pellye6ee3be2009-10-08 23:27:28 +0200376 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700378 int adapterState = getBluetoothStateInternal();
379
380 switch (adapterState) {
Nick Pellyde893f52009-09-08 13:15:33 -0700381 case BluetoothAdapter.STATE_OFF:
The Android Open Source Project10592532009-03-18 17:39:46 -0700382 return true;
Nick Pellyde893f52009-09-08 13:15:33 -0700383 case BluetoothAdapter.STATE_ON:
The Android Open Source Project10592532009-03-18 17:39:46 -0700384 break;
385 default:
386 return false;
387 }
Jaikumar Ganesh84690c82010-12-10 12:48:58 -0800388
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700389 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_OFF, saveSetting);
The Android Open Source Project10592532009-03-18 17:39:46 -0700390 return true;
391 }
392
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700393 synchronized void disconnectDevices() {
Jaikumar Ganesh7a0f8162010-11-01 14:57:36 -0700394 // Disconnect devices handled by BluetoothService.
395 for (BluetoothDevice device: getConnectedInputDevices()) {
396 disconnectInputDevice(device);
397 }
398
399 for (BluetoothDevice device: getConnectedPanDevices()) {
400 disconnectPanDevice(device);
401 }
402 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700403
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700404 /**
Matthew Xiedcbc97f2011-09-16 16:57:24 -0700405 * The Bluetooth has been turned off, but hot. Do bonding, profile cleanup
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700406 */
407 synchronized void finishDisable() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 // mark in progress bondings as cancelled
409 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDING)) {
Nick Pelly005b2282009-09-10 10:21:56 -0700410 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
412 }
413
Jaikumar Ganesh2d1fc4e2010-12-13 15:54:33 -0800414 // Stop the profile state machine for bonded devices.
415 for (String address : mBondState.listInState(BluetoothDevice.BOND_BONDED)) {
416 removeProfileState(address);
417 }
418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 // update mode
Nick Pellyde893f52009-09-08 13:15:33 -0700420 Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
421 intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, BluetoothAdapter.SCAN_MODE_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
Matthew Xiedcbc97f2011-09-16 16:57:24 -0700423 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424
Matthew Xiedcbc97f2011-09-16 16:57:24 -0700425 /**
426 * Local clean up after broadcasting STATE_OFF intent
427 */
428 synchronized void cleanupAfterFinishDisable() {
Nick Pellybd022f42009-08-14 18:33:38 -0700429 mAdapterProperties.clear();
Matthew Xiedcbc97f2011-09-16 16:57:24 -0700430
431 for (Integer srHandle : mServiceRecordToPid.keySet()) {
432 removeServiceRecordNative(srHandle);
433 }
Nick Pelly24bb9b82009-10-02 20:34:18 -0700434 mServiceRecordToPid.clear();
The Android Open Source Project10592532009-03-18 17:39:46 -0700435
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700436 mProfilesConnected = 0;
437 mProfilesConnecting = 0;
438 mProfilesDisconnecting = 0;
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -0700439 mAdapterConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800440 mAdapterUuids = null;
441 mAdapterSdpHandles = null;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -0700442
The Android Open Source Project10592532009-03-18 17:39:46 -0700443 // Log bluetooth off to battery stats.
444 long ident = Binder.clearCallingIdentity();
445 try {
446 mBatteryStats.noteBluetoothOff();
447 } catch (RemoteException e) {
448 } finally {
449 Binder.restoreCallingIdentity(ident);
450 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700453 /**
454 * power off Bluetooth
455 */
456 synchronized void shutoffBluetooth() {
Matthew Xie14e48e92011-08-15 00:31:50 -0700457 if (mAdapterSdpHandles != null) removeReservedServiceRecordsNative(mAdapterSdpHandles);
458 setBluetoothTetheringNative(false, BluetoothPanProfileHandler.NAP_ROLE,
459 BluetoothPanProfileHandler.NAP_BRIDGE);
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700460 tearDownNativeDataNative();
Matthew Xie14e48e92011-08-15 00:31:50 -0700461 }
462
463 /**
464 * Data clean up after Bluetooth shutoff
465 */
466 synchronized void cleanNativeAfterShutoffBluetooth() {
467 // Ths method is called after shutdown of event loop in the Bluetooth shut down
468 // procedure
469
470 // the adapter property could be changed before event loop is stoped, clear it again
471 mAdapterProperties.clear();
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700472 disableNative();
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700473 }
474
The Android Open Source Project10592532009-03-18 17:39:46 -0700475 /** Bring up BT and persist BT on in settings */
476 public boolean enable() {
Martijn Coenen6c614b72012-04-18 13:01:15 -0700477 return enable(true, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 }
479
480 /**
481 * Enable this Bluetooth device, asynchronously.
482 * This turns on/off the underlying hardware.
483 *
The Android Open Source Project10592532009-03-18 17:39:46 -0700484 * @param saveSetting If true, persist the new state of BT in settings
Martijn Coenen6c614b72012-04-18 13:01:15 -0700485 * @param allowConnect If true, auto-connects device when BT is turned on
486 * and allows incoming A2DP/HSP connections
The Android Open Source Project10592532009-03-18 17:39:46 -0700487 * @return True on success (so far)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800488 */
Martijn Coenen6c614b72012-04-18 13:01:15 -0700489 public synchronized boolean enable(boolean saveSetting, boolean allowConnect) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
491 "Need BLUETOOTH_ADMIN permission");
492
493 // Airplane mode can prevent Bluetooth radio from being turned on.
Jeff Sharkey44303922009-12-01 18:25:02 -0800494 if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495 return false;
496 }
Martijn Coenen6c614b72012-04-18 13:01:15 -0700497 mAllowConnect = allowConnect;
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700498 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.USER_TURN_ON, saveSetting);
499 return true;
500 }
501
502 /**
Martijn Coenen6c614b72012-04-18 13:01:15 -0700503 * Enable this Bluetooth device, asynchronously, but does not
504 * auto-connect devices. In this state the Bluetooth adapter
505 * also does not allow incoming A2DP/HSP connections (that
506 * must go through this service), but does allow communication
507 * on RFCOMM sockets implemented outside of this service (ie BTOPP).
508 * This method is used to temporarily enable Bluetooth
509 * for data transfer, without changing
510 *
511 * This turns on/off the underlying hardware.
512 *
513 * @return True on success (so far)
514 */
515 public boolean enableNoAutoConnect() {
516 return enable(false, false);
517 }
518
519 /**
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700520 * Turn on Bluetooth Module, Load firmware, and do all the preparation
521 * needed to get the Bluetooth Module ready but keep it not discoverable
522 * and not connectable.
523 */
524 /* package */ synchronized boolean prepareBluetooth() {
525 if (!setupNativeDataNative()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 return false;
527 }
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700528 switchConnectable(false);
Matthew Xieb12d6bc2012-05-07 11:01:23 -0700529
530 // Bluetooth stack needs a small delay here before adding
531 // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
532 try {
Matthew Xie8b4ca7a2012-05-17 17:43:00 -0700533 Thread.sleep(50);
Matthew Xieb12d6bc2012-05-07 11:01:23 -0700534 } catch (InterruptedException e) {}
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700535 updateSdpRecords();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 return true;
537 }
538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 private final Handler mHandler = new Handler() {
540 @Override
541 public void handleMessage(Message msg) {
542 switch (msg.what) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700543 case MESSAGE_UUID_INTENT:
544 String address = (String)msg.obj;
Nick Pelly16fb88a2009-10-07 07:44:03 +0200545 if (address != null) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700546 sendUuidIntent(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +0200547 makeServiceChannelCallbacks(address);
548 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700549 break;
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700550 case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
551 address = (String)msg.obj;
Jaikumar Ganesh8adcacb2011-07-21 13:49:54 -0700552 if (address == null) return;
553 int attempt = mBondState.getAttempt(address);
554
555 // Try only if attemps are in progress and cap it 2 attempts
556 // The 2 attempts cap is a fail safe if the stack returns
557 // an incorrect error code for bonding failures and if the pin
558 // is entered wrongly twice we should abort.
559 if (attempt > 0 && attempt <= 2) {
560 mBondState.attempt(address);
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700561 createBond(address);
562 return;
563 }
Jaikumar Ganesh8adcacb2011-07-21 13:49:54 -0700564 if (attempt > 0) mBondState.clearPinAttempts(address);
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700565 break;
Matthew Xie484867a2011-08-25 16:45:58 -0700566 case MESSAGE_REMOVE_SERVICE_RECORD:
567 Pair<Integer, Integer> pair = (Pair<Integer, Integer>) msg.obj;
568 checkAndRemoveRecord(pair.first, pair.second);
569 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 }
571 }
572 };
573
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800574 private synchronized void addReservedSdpRecords(final ArrayList<ParcelUuid> uuids) {
575 //Register SDP records.
576 int[] svcIdentifiers = new int[uuids.size()];
577 for (int i = 0; i < uuids.size(); i++) {
578 svcIdentifiers[i] = BluetoothUuid.getServiceIdentifierFromParcelUuid(uuids.get(i));
579 }
580 mAdapterSdpHandles = addReservedServiceRecordsNative(svcIdentifiers);
581 }
The Android Open Source Project10592532009-03-18 17:39:46 -0700582
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800583 private synchronized void updateSdpRecords() {
584 ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>();
585
Travis Geiselbrechta91da5d2012-03-06 11:41:58 -0800586 Resources R = mContext.getResources();
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800587
Travis Geiselbrechta91da5d2012-03-06 11:41:58 -0800588 // Add the default records
589 if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
590 uuids.add(BluetoothUuid.HSP_AG);
591 uuids.add(BluetoothUuid.ObexObjectPush);
592 }
593
594 if (R.getBoolean(com.android.internal.R.bool.config_voice_capable)) {
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800595 uuids.add(BluetoothUuid.Handsfree_AG);
596 uuids.add(BluetoothUuid.PBAP_PSE);
597 }
598
599 // Add SDP records for profiles maintained by Android userspace
600 addReservedSdpRecords(uuids);
601
Matthew Xieb12d6bc2012-05-07 11:01:23 -0700602 // Bluetooth stack need some a small delay here before adding more
603 // SDP records, otherwise dbus stalls for over 30 seconds 1 out of 50 runs
604 try {
Matthew Xie8b4ca7a2012-05-17 17:43:00 -0700605 Thread.sleep(50);
Matthew Xieb12d6bc2012-05-07 11:01:23 -0700606 } catch (InterruptedException e) {}
607
Travis Geiselbrechta91da5d2012-03-06 11:41:58 -0800608 if (R.getBoolean(com.android.internal.R.bool.config_bluetooth_default_profiles)) {
609 // Enable profiles maintained by Bluez userspace.
610 setBluetoothTetheringNative(true, BluetoothPanProfileHandler.NAP_ROLE,
611 BluetoothPanProfileHandler.NAP_BRIDGE);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800612
Travis Geiselbrechta91da5d2012-03-06 11:41:58 -0800613 // Add SDP records for profiles maintained by Bluez userspace
614 uuids.add(BluetoothUuid.AudioSource);
615 uuids.add(BluetoothUuid.AvrcpTarget);
616 uuids.add(BluetoothUuid.NAP);
617 }
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800618
619 // Cannot cast uuids.toArray directly since ParcelUuid is parcelable
620 mAdapterUuids = new ParcelUuid[uuids.size()];
621 for (int i = 0; i < uuids.size(); i++) {
622 mAdapterUuids[i] = uuids.get(i);
623 }
624 }
625
Jaikumar Ganesh9efb1342011-02-11 18:10:31 -0800626 /**
627 * This function is called from Bluetooth Event Loop when onPropertyChanged
628 * for adapter comes in with UUID property.
629 * @param uuidsThe uuids of adapter as reported by Bluez.
630 */
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700631 /*package*/ synchronized void updateBluetoothState(String uuids) {
632 ParcelUuid[] adapterUuids = convertStringToParcelUuid(uuids);
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800633
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700634 if (mAdapterUuids != null &&
635 BluetoothUuid.containsAllUuids(adapterUuids, mAdapterUuids)) {
636 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.SERVICE_RECORD_LOADED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638 }
639
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700640 /**
Matthew Xie85943942011-07-29 18:57:58 -0700641 * This method is called immediately before Bluetooth module is turned on after
642 * the adapter became pariable.
643 * It inits bond state and profile state before STATE_ON intent is broadcasted.
644 */
645 /*package*/ void initBluetoothAfterTurningOn() {
Matthew Xied96daf12011-08-22 17:04:51 -0700646 String discoverable = getProperty("Discoverable", false);
647 String timeout = getProperty("DiscoverableTimeout", false);
Matthew Xie43b6fd92012-04-17 11:39:02 -0700648 if (timeout == null) {
649 Log.w(TAG, "Null DiscoverableTimeout property");
650 // assign a number, anything not 0
651 timeout = "1";
652 }
Matthew Xied96daf12011-08-22 17:04:51 -0700653 if (discoverable.equals("true") && Integer.valueOf(timeout) != 0) {
654 setAdapterPropertyBooleanNative("Discoverable", 0);
655 }
Matthew Xie85943942011-07-29 18:57:58 -0700656 mBondState.initBondState();
657 initProfileState();
Matthew Xie98f06da2011-11-10 00:03:28 -0800658 getProfileProxy();
Matthew Xie85943942011-07-29 18:57:58 -0700659 }
660
661 /**
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700662 * This method is called immediately after Bluetooth module is turned on.
663 * It starts auto-connection and places bluetooth on sign onto the battery
664 * stats
665 */
666 /*package*/ void runBluetooth() {
Matthew Xie7f9ecca82011-07-15 13:03:58 -0700667 autoConnect();
668
669 // Log bluetooth on to battery stats.
670 long ident = Binder.clearCallingIdentity();
671 try {
672 mBatteryStats.noteBluetoothOn();
673 } catch (RemoteException e) {
674 Log.e(TAG, "", e);
675 } finally {
676 Binder.restoreCallingIdentity(ident);
677 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 }
679
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700680 /*package*/ synchronized boolean attemptAutoPair(String address) {
681 if (!mBondState.hasAutoPairingFailed(address) &&
682 !mBondState.isAutoPairingBlacklisted(address)) {
683 mBondState.attempt(address);
684 setPin(address, BluetoothDevice.convertPinToBytes("0000"));
685 return true;
686 }
687 return false;
688 }
689
Jaikumar Ganeshf487d722011-01-12 10:12:01 -0800690 /*package*/ synchronized boolean isFixedPinZerosAutoPairKeyboard(String address) {
691 // Check for keyboards which have fixed PIN 0000 as the pairing pin
692 return mBondState.isFixedPinZerosAutoPairKeyboard(address);
693 }
694
Jaikumar Ganesha224f702010-09-10 15:09:54 -0700695 /*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
696 if (result == BluetoothDevice.BOND_SUCCESS) {
697 setBondState(address, BluetoothDevice.BOND_BONDED);
698 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
699 mBondState.clearPinAttempts(address);
700 }
701 } else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
702 mBondState.getAttempt(address) == 1) {
703 mBondState.addAutoPairingFailure(address);
704 pairingAttempt(address, result);
705 } else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
706 mBondState.isAutoPairingAttemptsInProgress(address)) {
707 pairingAttempt(address, result);
708 } else {
709 setBondState(address, BluetoothDevice.BOND_NONE, result);
710 if (mBondState.isAutoPairingAttemptsInProgress(address)) {
711 mBondState.clearPinAttempts(address);
712 }
713 }
714 }
715
716 /*package*/ synchronized String getPendingOutgoingBonding() {
717 return mBondState.getPendingOutgoingBonding();
718 }
719
720 private void pairingAttempt(String address, int result) {
721 // This happens when our initial guess of "0000" as the pass key
722 // fails. Try to create the bond again and display the pin dialog
723 // to the user. Use back-off while posting the delayed
724 // message. The initial value is
725 // INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
726 // MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
727 // reached, display an error to the user.
728 int attempt = mBondState.getAttempt(address);
729 if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
730 MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
731 mBondState.clearPinAttempts(address);
732 setBondState(address, BluetoothDevice.BOND_NONE, result);
733 return;
734 }
735
736 Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
737 message.obj = address;
738 boolean postResult = mHandler.sendMessageDelayed(message,
739 attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
740 if (!postResult) {
741 mBondState.clearPinAttempts(address);
742 setBondState(address,
743 BluetoothDevice.BOND_NONE, result);
744 return;
745 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746 }
747
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800748 /*package*/ BluetoothDevice getRemoteDevice(String address) {
749 return mAdapter.getRemoteDevice(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751
752 private static String toBondStateString(int bondState) {
753 switch (bondState) {
Nick Pelly005b2282009-09-10 10:21:56 -0700754 case BluetoothDevice.BOND_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 return "not bonded";
756 case BluetoothDevice.BOND_BONDING:
757 return "bonding";
758 case BluetoothDevice.BOND_BONDED:
759 return "bonded";
760 default:
761 return "??????";
762 }
763 }
764
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800765 public synchronized boolean setName(String name) {
766 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
767 "Need BLUETOOTH_ADMIN permission");
768 if (name == null) {
769 return false;
770 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700771 return setPropertyString("Name", name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700774 //TODO(): setPropertyString, setPropertyInteger, setPropertyBoolean
775 // Either have a single property function with Object as the parameter
776 // or have a function for each property and then obfuscate in the JNI layer.
777 // The following looks dirty.
778 private boolean setPropertyString(String key, String value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800780 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700781 return setAdapterPropertyStringNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700784 private boolean setPropertyInteger(String key, int value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800786 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700787 return setAdapterPropertyIntegerNative(key, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700790 private boolean setPropertyBoolean(String key, boolean value) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800792 if (!isEnabledInternal()) return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700793 return setAdapterPropertyBooleanNative(key, value ? 1 : 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795
796 /**
797 * Set the discoverability window for the device. A timeout of zero
798 * makes the device permanently discoverable (if the device is
799 * discoverable). Setting the timeout to a nonzero value does not make
800 * a device discoverable; you need to call setMode() to make the device
801 * explicitly discoverable.
802 *
Jake Hambyf51eada2010-09-21 13:39:53 -0700803 * @param timeout The discoverable timeout in seconds.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 */
805 public synchronized boolean setDiscoverableTimeout(int timeout) {
806 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
807 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700808 return setPropertyInteger("DiscoverableTimeout", timeout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700810
Nick Pelly12835472009-09-25 15:00:29 -0700811 public synchronized boolean setScanMode(int mode, int duration) {
Nick Pelly18b1e792009-09-24 11:14:15 -0700812 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS,
813 "Need WRITE_SECURE_SETTINGS permission");
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800814 boolean pairable;
815 boolean discoverable;
Nick Pelly12835472009-09-25 15:00:29 -0700816
Nick Pellyde893f52009-09-08 13:15:33 -0700817 switch (mode) {
818 case BluetoothAdapter.SCAN_MODE_NONE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700819 pairable = false;
820 discoverable = false;
Nick Pellyde893f52009-09-08 13:15:33 -0700821 break;
822 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700823 pairable = true;
824 discoverable = false;
Nick Pelly005b2282009-09-10 10:21:56 -0700825 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700826 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700827 pairable = true;
828 discoverable = true;
Nick Pelly12835472009-09-25 15:00:29 -0700829 if (DBG) Log.d(TAG, "BT Discoverable for " + duration + " seconds");
Nick Pelly005b2282009-09-10 10:21:56 -0700830 break;
Nick Pellyde893f52009-09-08 13:15:33 -0700831 default:
832 Log.w(TAG, "Requested invalid scan mode " + mode);
833 return false;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700834 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700835
Matthew Xie14e48e92011-08-15 00:31:50 -0700836 setPropertyBoolean("Discoverable", discoverable);
837 setPropertyBoolean("Pairable", pairable);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700838 return true;
839 }
840
Matthew Xie14e48e92011-08-15 00:31:50 -0700841 /**
842 * @param on true set the local Bluetooth module to be connectable
843 * The dicoverability is recovered to what it was before
844 * switchConnectable(false) call
845 * false set the local Bluetooth module to be not connectable
846 * and not dicoverable
847 */
848 /*package*/ synchronized void switchConnectable(boolean on) {
849 setAdapterPropertyBooleanNative("Powered", on ? 1 : 0);
850 }
851
852 /*package*/ synchronized void setPairable() {
853 String pairableString = getProperty("Pairable", false);
854 if (pairableString == null) {
855 Log.e(TAG, "null pairableString");
856 return;
857 }
858 if (pairableString.equals("false")) {
859 setAdapterPropertyBooleanNative("Pairable", 1);
860 }
861 }
862
Matthew Xie44b58ab2011-11-16 12:27:57 -0800863 /*package*/ String getProperty(String name, boolean checkState) {
Jaikumar Ganesha66590e2011-08-04 15:39:44 -0700864 // If checkState is false, check if the event loop is running.
865 // before making the call to Bluez
866 if (checkState) {
867 if (!isEnabledInternal()) return null;
868 } else if (!mEventLoop.isEventLoopRunning()) {
869 return null;
870 }
871
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800872 return mAdapterProperties.getProperty(name);
Jaikumar Ganeshb148bc82009-11-20 13:41:07 -0800873 }
874
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800875 BluetoothAdapterProperties getAdapterProperties() {
876 return mAdapterProperties;
877 }
878
879 BluetoothDeviceProperties getDeviceProperties() {
880 return mDeviceProperties;
881 }
882
883 boolean isRemoteDeviceInCache(String address) {
884 return mDeviceProperties.isInCache(address);
885 }
886
887 void setRemoteDeviceProperty(String address, String name, String value) {
888 mDeviceProperties.setProperty(address, name, value);
889 }
890
891 void updateRemoteDevicePropertiesCache(String address) {
892 mDeviceProperties.updateCache(address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700893 }
894
895 public synchronized String getAddress() {
896 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesha66590e2011-08-04 15:39:44 -0700897 // Don't check state since we want to provide address, even if BT is off
898 return getProperty("Address", false);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700899 }
900
901 public synchronized String getName() {
902 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesha66590e2011-08-04 15:39:44 -0700903 // Don't check state since we want to provide name, even if BT is off
904 return getProperty("Name", false);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700905 }
906
Matthew Xie44b58ab2011-11-16 12:27:57 -0800907 public ParcelUuid[] getUuids() {
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800908 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesha66590e2011-08-04 15:39:44 -0700909 String value = getProperty("UUIDs", true);
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800910 if (value == null) return null;
Jaikumar Ganesh50b40ce2011-02-02 14:44:49 -0800911 return convertStringToParcelUuid(value);
912 }
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800913
Matthew Xie44b58ab2011-11-16 12:27:57 -0800914 private ParcelUuid[] convertStringToParcelUuid(String value) {
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800915 String[] uuidStrings = null;
916 // The UUIDs are stored as a "," separated string.
917 uuidStrings = value.split(",");
918 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
919
920 for (int i = 0; i < uuidStrings.length; i++) {
921 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
922 }
923 return uuids;
Jaikumar Ganesh58b93c32010-11-23 20:03:10 -0800924 }
925
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700926 /**
927 * Returns the user-friendly name of a remote device. This value is
928 * returned from our local cache, which is updated when onPropertyChange
929 * event is received.
930 * Do not expect to retrieve the updated remote name immediately after
931 * changing the name on the remote device.
932 *
933 * @param address Bluetooth address of remote device.
934 *
935 * @return The user-friendly name of the specified remote device.
936 */
937 public synchronized String getRemoteName(String address) {
938 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -0700939 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700940 return null;
941 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -0800942 return mDeviceProperties.getProperty(address, "Name");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700943 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944
945 /**
Matthew Xie269e81a2011-07-26 18:36:49 -0700946 * Returns alias of a remote device. This value is returned from our
947 * local cache, which is updated when onPropertyChange event is received.
948 *
949 * @param address Bluetooth address of remote device.
950 *
951 * @return The alias of the specified remote device.
952 */
953 public synchronized String getRemoteAlias(String address) {
954
955 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
956 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
957 return null;
958 }
959 return mDeviceProperties.getProperty(address, "Alias");
960 }
961
962 /**
963 * Set the alias of a remote device.
964 *
965 * @param address Bluetooth address of remote device.
966 * @param alias new alias to change to
967 * @return true on success, false on error
968 */
969 public synchronized boolean setRemoteAlias(String address, String alias) {
970 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
971 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
972 return false;
973 }
974
975 return setDevicePropertyStringNative(getObjectPathFromAddress(address),
976 "Alias", alias);
977 }
978
979 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 * Get the discoverability window for the device. A timeout of zero
981 * means that the device is permanently discoverable (if the device is
982 * in the discoverable mode).
983 *
984 * @return The discoverability window of the device, in seconds. A negative
985 * value indicates an error.
986 */
Matthew Xie44b58ab2011-11-16 12:27:57 -0800987 public int getDiscoverableTimeout() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesha66590e2011-08-04 15:39:44 -0700989 String timeout = getProperty("DiscoverableTimeout", true);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700990 if (timeout != null)
991 return Integer.valueOf(timeout);
992 else
993 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995
Matthew Xie44b58ab2011-11-16 12:27:57 -0800996 public int getScanMode() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -0800998 if (!isEnabledInternal())
Nick Pellyde893f52009-09-08 13:15:33 -0700999 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000
Jaikumar Ganesha66590e2011-08-04 15:39:44 -07001001 boolean pairable = getProperty("Pairable", true).equals("true");
1002 boolean discoverable = getProperty("Discoverable", true).equals("true");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001003 return bluezStringToScanMode (pairable, discoverable);
1004 }
1005
1006 public synchronized boolean startDiscovery() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1008 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001009 if (!isEnabledInternal()) return false;
1010
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001011 return startDiscoveryNative();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001013
1014 public synchronized boolean cancelDiscovery() {
1015 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1016 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001017 if (!isEnabledInternal()) return false;
1018
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001019 return stopDiscoveryNative();
1020 }
1021
Matthew Xie44b58ab2011-11-16 12:27:57 -08001022 public boolean isDiscovering() {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001023 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001024
Jaikumar Ganesha66590e2011-08-04 15:39:44 -07001025 String discoveringProperty = getProperty("Discovering", false);
Matthew Xie85943942011-07-29 18:57:58 -07001026 if (discoveringProperty == null) {
1027 return false;
1028 }
1029
1030 return discoveringProperty.equals("true");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001031 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001033 private boolean isBondingFeasible(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1035 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001036 if (!isEnabledInternal()) return false;
1037
Nick Pelly005b2282009-09-10 10:21:56 -07001038 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 return false;
1040 }
1041 address = address.toUpperCase();
1042
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001043 if (mBondState.getPendingOutgoingBonding() != null) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001044 Log.d(TAG, "Ignoring createBond(): another device is bonding");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 // a different device is currently bonding, fail
1046 return false;
1047 }
1048
1049 // Check for bond state only if we are not performing auto
1050 // pairing exponential back-off attempts.
1051 if (!mBondState.isAutoPairingAttemptsInProgress(address) &&
Nick Pelly005b2282009-09-10 10:21:56 -07001052 mBondState.getBondState(address) != BluetoothDevice.BOND_NONE) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001053 Log.d(TAG, "Ignoring createBond(): this device is already bonding or bonded");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 return false;
1055 }
1056
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001057 if (address.equals(mDockAddress)) {
1058 if (!writeDockPin()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001059 Log.e(TAG, "Error while writing Pin for the dock");
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001060 return false;
1061 }
1062 }
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001063 return true;
1064 }
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001065
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001066 public synchronized boolean createBond(String address) {
1067 if (!isBondingFeasible(address)) return false;
1068
1069 if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 return false;
1071 }
1072
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001073 mBondState.setPendingOutgoingBonding(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
Jaikumar Ganesh20923612009-09-20 12:56:21 -07001075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 return true;
1077 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001079 public synchronized boolean createBondOutOfBand(String address, byte[] hash,
1080 byte[] randomizer) {
1081 if (!isBondingFeasible(address)) return false;
1082
1083 if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
1084 return false;
1085 }
1086
1087 setDeviceOutOfBandData(address, hash, randomizer);
1088 mBondState.setPendingOutgoingBonding(address);
1089 mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
1090
1091 return true;
1092 }
1093
1094 public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
1095 byte[] randomizer) {
1096 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1097 "Need BLUETOOTH_ADMIN permission");
1098 if (!isEnabledInternal()) return false;
1099
1100 Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
1101
1102 if (DBG) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001103 Log.d(TAG, "Setting out of band data for: " + address + ":" +
1104 Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001105 }
1106
1107 mDeviceOobData.put(address, value);
1108 return true;
1109 }
1110
1111 Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
1112 return mDeviceOobData.get(device.getAddress());
1113 }
1114
1115
1116 public synchronized byte[] readOutOfBandData() {
1117 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1118 "Need BLUETOOTH permission");
1119 if (!isEnabledInternal()) return null;
1120
1121 return readAdapterOutOfBandDataNative();
1122 }
1123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 public synchronized boolean cancelBondProcess(String address) {
1125 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1126 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001127 if (!isEnabledInternal()) return false;
1128
Nick Pelly005b2282009-09-10 10:21:56 -07001129 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 return false;
1131 }
1132 address = address.toUpperCase();
1133 if (mBondState.getBondState(address) != BluetoothDevice.BOND_BONDING) {
1134 return false;
1135 }
1136
Nick Pelly005b2282009-09-10 10:21:56 -07001137 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001139 cancelDeviceCreationNative(address);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 return true;
1141 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142
1143 public synchronized boolean removeBond(String address) {
1144 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1145 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001146 if (!isEnabledInternal()) return false;
1147
Nick Pelly005b2282009-09-10 10:21:56 -07001148 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 return false;
1150 }
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001151 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001152 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07001153 state.sendMessage(BluetoothDeviceProfileState.UNPAIR);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07001154 return true;
1155 } else {
1156 return false;
1157 }
1158 }
1159
1160 public synchronized boolean removeBondInternal(String address) {
Jaikumar Ganeshcc2c0662011-02-25 12:27:48 -08001161 // Unset the trusted device state and then unpair
1162 setTrust(address, false);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001163 return removeDeviceNative(getObjectPathFromAddress(address));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165
1166 public synchronized String[] listBonds() {
1167 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1168 return mBondState.listInState(BluetoothDevice.BOND_BONDED);
1169 }
1170
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001171 /*package*/ synchronized String[] listInState(int state) {
1172 return mBondState.listInState(state);
1173 }
1174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 public synchronized int getBondState(String address) {
1176 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001177 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001178 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 }
1180 return mBondState.getBondState(address.toUpperCase());
1181 }
1182
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001183 /*package*/ synchronized boolean setBondState(String address, int state) {
1184 return setBondState(address, state, 0);
1185 }
1186
1187 /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
Arek Lichwaae5fbb02011-02-23 11:52:34 +01001188 mBondState.setBondState(address.toUpperCase(), state, reason);
Jaikumar Ganesha224f702010-09-10 15:09:54 -07001189 return true;
1190 }
1191
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001192 public synchronized boolean isBluetoothDock(String address) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001193 SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001194 Context.MODE_PRIVATE);
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001195
1196 return sp.contains(SHARED_PREFERENCE_DOCK_ADDRESS + address);
Jaikumar Ganesh3fbf7b62009-12-02 17:28:38 -08001197 }
1198
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001199 /*package*/ String[] getRemoteDeviceProperties(String address) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001200 if (!isEnabledInternal()) return null;
1201
Jaikumar Ganesh9488cbd2009-08-03 17:17:37 -07001202 String objectPath = getObjectPathFromAddress(address);
1203 return (String [])getDevicePropertiesNative(objectPath);
1204 }
1205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 /**
Lixin Yueefa1dd72009-08-31 15:55:13 +08001207 * Sets the remote device trust state.
1208 *
1209 * @return boolean to indicate operation success or fail
1210 */
1211 public synchronized boolean setTrust(String address, boolean value) {
Nick Pelly005b2282009-09-10 10:21:56 -07001212 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellye6ee3be2009-10-08 23:27:28 +02001213 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1214 "Need BLUETOOTH_ADMIN permission");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001215 return false;
1216 }
1217
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001218 if (!isEnabledInternal()) return false;
1219
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001220 return setDevicePropertyBooleanNative(
1221 getObjectPathFromAddress(address), "Trusted", value ? 1 : 0);
Lixin Yueefa1dd72009-08-31 15:55:13 +08001222 }
1223
1224 /**
1225 * Gets the remote device trust state as boolean.
1226 * Note: this value may be
1227 * retrieved from cache if we retrieved the data before *
1228 *
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001229 * @return boolean to indicate trusted or untrusted state
Lixin Yueefa1dd72009-08-31 15:55:13 +08001230 */
1231 public synchronized boolean getTrustState(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001232 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Lixin Yueefa1dd72009-08-31 15:55:13 +08001233 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1234 return false;
1235 }
1236
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001237 String val = mDeviceProperties.getProperty(address, "Trusted");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001238 if (val == null) {
1239 return false;
1240 } else {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001241 return val.equals("true");
Lixin Yueefa1dd72009-08-31 15:55:13 +08001242 }
1243 }
1244
1245 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001246 * Gets the remote major, minor classes encoded as a 32-bit
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 * integer.
1248 *
1249 * Note: this value is retrieved from cache, because we get it during
1250 * remote-device discovery.
1251 *
1252 * @return 32-bit integer encoding the remote major, minor, and service
1253 * classes.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 */
1255 public synchronized int getRemoteClass(String address) {
Nick Pelly005b2282009-09-10 10:21:56 -07001256 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyea600cc2009-03-31 14:56:26 -07001257 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1258 return BluetoothClass.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001259 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001260 String val = mDeviceProperties.getProperty(address, "Class");
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001261 if (val == null)
1262 return BluetoothClass.ERROR;
1263 else {
1264 return Integer.valueOf(val);
1265 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268
1269 /**
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001270 * Gets the UUIDs supported by the remote device
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 *
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001272 * @return array of 128bit ParcelUuids
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001274 public synchronized ParcelUuid[] getRemoteUuids(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001275 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Nick Pelly005b2282009-09-10 10:21:56 -07001276 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 return null;
1278 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001279 return getUuidFromCache(address);
1280 }
1281
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001282 ParcelUuid[] getUuidFromCache(String address) {
1283 String value = mDeviceProperties.getProperty(address, "UUIDs");
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001284 if (value == null) return null;
1285
1286 String[] uuidStrings = null;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001287 // The UUIDs are stored as a "," separated string.
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001288 uuidStrings = value.split(",");
1289 ParcelUuid[] uuids = new ParcelUuid[uuidStrings.length];
1290
1291 for (int i = 0; i < uuidStrings.length; i++) {
1292 uuids[i] = ParcelUuid.fromString(uuidStrings[i]);
1293 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001294 return uuids;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296
Nick Pelly16fb88a2009-10-07 07:44:03 +02001297 /**
1298 * Connect and fetch new UUID's using SDP.
1299 * The UUID's found are broadcast as intents.
1300 * Optionally takes a uuid and callback to fetch the RFCOMM channel for the
1301 * a given uuid.
1302 * TODO: Don't wait UUID_INTENT_DELAY to broadcast UUID intents on success
1303 * TODO: Don't wait UUID_INTENT_DELAY to handle the failure case for
1304 * callback and broadcast intents.
1305 */
1306 public synchronized boolean fetchRemoteUuids(String address, ParcelUuid uuid,
1307 IBluetoothCallback callback) {
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001308 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001309 if (!isEnabledInternal()) return false;
1310
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001311 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
1312 return false;
1313 }
1314
Nick Pelly16fb88a2009-10-07 07:44:03 +02001315 RemoteService service = new RemoteService(address, uuid);
1316 if (uuid != null && mUuidCallbackTracker.get(service) != null) {
1317 // An SDP query for this address & uuid is already in progress
1318 // Do not add this callback for the uuid
1319 return false;
1320 }
1321
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001322 if (mUuidIntentTracker.contains(address)) {
1323 // An SDP query for this address is already in progress
Nick Pelly16fb88a2009-10-07 07:44:03 +02001324 // Add this uuid onto the in-progress SDP query
1325 if (uuid != null) {
1326 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1327 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001328 return true;
1329 }
1330
Jaikumar Ganesh421f0102011-06-06 17:31:58 +09001331 // If the device is already created, we will
1332 // do the SDP on the callback of createDeviceNative.
1333 boolean ret= createDeviceNative(address);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001334
1335 mUuidIntentTracker.add(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001336 if (uuid != null) {
1337 mUuidCallbackTracker.put(new RemoteService(address, uuid), callback);
1338 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001339
1340 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
1341 message.obj = address;
1342 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
1343 return ret;
1344 }
1345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 /**
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001347 * Gets the rfcomm channel associated with the UUID.
Nick Pelly16fb88a2009-10-07 07:44:03 +02001348 * Pulls records from the cache only.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001350 * @param address Address of the remote device
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001351 * @param uuid ParcelUuid of the service attribute
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 *
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07001353 * @return rfcomm channel associated with the service attribute
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001354 * -1 on error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 */
Jaikumar Ganeshdd0463a2009-09-16 12:30:02 -07001356 public int getRemoteServiceChannel(String address, ParcelUuid uuid) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001358 if (!isEnabledInternal()) return -1;
1359
Nick Pelly005b2282009-09-10 10:21:56 -07001360 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
Nick Pellyb24e11b2009-09-08 17:40:43 -07001361 return BluetoothDevice.ERROR;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001363 // Check if we are recovering from a crash.
1364 if (mDeviceProperties.isEmpty()) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001365 if (mDeviceProperties.updateCache(address) == null)
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001366 return -1;
1367 }
1368
1369 Map<ParcelUuid, Integer> value = mDeviceServiceChannelCache.get(address);
1370 if (value != null && value.containsKey(uuid))
1371 return value.get(uuid);
1372 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374
1375 public synchronized boolean setPin(String address, byte[] pin) {
1376 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1377 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001378 if (!isEnabledInternal()) return false;
1379
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 if (pin == null || pin.length <= 0 || pin.length > 16 ||
Nick Pelly005b2282009-09-10 10:21:56 -07001381 !BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 return false;
1383 }
1384 address = address.toUpperCase();
1385 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1386 if (data == null) {
1387 Log.w(TAG, "setPin(" + address + ") called but no native data available, " +
1388 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1389 " or by bluez.\n");
1390 return false;
1391 }
1392 // bluez API wants pin as a string
1393 String pinString;
1394 try {
1395 pinString = new String(pin, "UTF8");
1396 } catch (UnsupportedEncodingException uee) {
1397 Log.e(TAG, "UTF8 not supported?!?");
1398 return false;
1399 }
1400 return setPinNative(address, pinString, data.intValue());
1401 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001403 public synchronized boolean setPasskey(String address, int passkey) {
1404 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1405 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001406 if (!isEnabledInternal()) return false;
1407
Nick Pelly005b2282009-09-10 10:21:56 -07001408 if (passkey < 0 || passkey > 999999 || !BluetoothAdapter.checkBluetoothAddress(address)) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001409 return false;
1410 }
1411 address = address.toUpperCase();
1412 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1413 if (data == null) {
1414 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1415 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1416 " or by bluez.\n");
1417 return false;
1418 }
1419 return setPasskeyNative(address, passkey, data.intValue());
1420 }
1421
1422 public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
1423 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1424 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001425 if (!isEnabledInternal()) return false;
1426
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001427 address = address.toUpperCase();
1428 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1429 if (data == null) {
1430 Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
1431 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1432 " or by bluez.\n");
1433 return false;
1434 }
1435 return setPairingConfirmationNative(address, confirm, data.intValue());
1436 }
1437
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07001438 public synchronized boolean setRemoteOutOfBandData(String address) {
1439 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1440 "Need BLUETOOTH_ADMIN permission");
1441 if (!isEnabledInternal()) return false;
1442 address = address.toUpperCase();
1443 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1444 if (data == null) {
1445 Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
1446 "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
1447 " or by bluez.\n");
1448 return false;
1449 }
1450
1451 Pair<byte[], byte[]> val = mDeviceOobData.get(address);
1452 byte[] hash, randomizer;
1453 if (val == null) {
1454 // TODO: check what should be passed in this case.
1455 hash = new byte[16];
1456 randomizer = new byte[16];
1457 } else {
1458 hash = val.first;
1459 randomizer = val.second;
1460 }
1461 return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
1462 }
1463
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001464 public synchronized boolean cancelPairingUserInput(String address) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001465 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
1466 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001467 if (!isEnabledInternal()) return false;
1468
Nick Pelly005b2282009-09-10 10:21:56 -07001469 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 return false;
1471 }
Nick Pelly005b2282009-09-10 10:21:56 -07001472 mBondState.setBondState(address, BluetoothDevice.BOND_NONE,
Jaikumar Ganesh397d8f42009-08-21 11:50:17 -07001473 BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 address = address.toUpperCase();
1475 Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
1476 if (data == null) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001477 Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
1478 "available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
1479 "by the remote or by bluez.\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001480 return false;
1481 }
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07001482 return cancelPairingUserInputNative(address, data.intValue());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001483 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001485 /*package*/ void updateDeviceServiceChannelCache(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001486 if (DBG) Log.d(TAG, "updateDeviceServiceChannelCache(" + address + ")");
1487
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001488 // We are storing the rfcomm channel numbers only for the uuids
1489 // we are interested in.
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001490 ParcelUuid[] deviceUuids = getRemoteUuids(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001491
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001492 ArrayList<ParcelUuid> applicationUuids = new ArrayList<ParcelUuid>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001493
1494 synchronized (this) {
1495 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1496 if (service.address.equals(address)) {
1497 applicationUuids.add(service.uuid);
1498 }
1499 }
1500 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001501
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001502 Map <ParcelUuid, Integer> uuidToChannelMap = new HashMap<ParcelUuid, Integer>();
Nick Pelly16fb88a2009-10-07 07:44:03 +02001503
1504 // Retrieve RFCOMM channel for default uuids
1505 for (ParcelUuid uuid : RFCOMM_UUIDS) {
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001506 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001507 int channel = getDeviceServiceChannelForUuid(address, uuid);
1508 uuidToChannelMap.put(uuid, channel);
1509 if (DBG) Log.d(TAG, "\tuuid(system): " + uuid + " " + channel);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001510 }
1511 }
Nick Pelly16fb88a2009-10-07 07:44:03 +02001512 // Retrieve RFCOMM channel for application requested uuids
1513 for (ParcelUuid uuid : applicationUuids) {
1514 if (BluetoothUuid.isUuidPresent(deviceUuids, uuid)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001515 int channel = getDeviceServiceChannelForUuid(address, uuid);
1516 uuidToChannelMap.put(uuid, channel);
1517 if (DBG) Log.d(TAG, "\tuuid(application): " + uuid + " " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001518 }
1519 }
1520
1521 synchronized (this) {
1522 // Make application callbacks
1523 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1524 iter.hasNext();) {
1525 RemoteService service = iter.next();
1526 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001527 if (uuidToChannelMap.containsKey(service.uuid)) {
1528 int channel = uuidToChannelMap.get(service.uuid);
1529
1530 if (DBG) Log.d(TAG, "Making callback for " + service.uuid +
1531 " with result " + channel);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001532 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1533 if (callback != null) {
1534 try {
1535 callback.onRfcommChannelFound(channel);
1536 } catch (RemoteException e) {Log.e(TAG, "", e);}
1537 }
1538
1539 iter.remove();
1540 }
1541 }
1542 }
1543
1544 // Update cache
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001545 mDeviceServiceChannelCache.put(address, uuidToChannelMap);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001546 }
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07001547 }
1548
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001549 private int getDeviceServiceChannelForUuid(String address,
1550 ParcelUuid uuid) {
1551 return getDeviceServiceChannelNative(getObjectPathFromAddress(address),
1552 uuid.toString(), 0x0004);
1553 }
1554
Nick Pelly24bb9b82009-10-02 20:34:18 -07001555 /**
1556 * b is a handle to a Binder instance, so that this service can be notified
1557 * for Applications that terminate unexpectedly, to clean there service
1558 * records
1559 */
1560 public synchronized int addRfcommServiceRecord(String serviceName, ParcelUuid uuid,
1561 int channel, IBinder b) {
Jaikumar Ganesh8c9dd7d2009-11-16 16:23:20 -08001562 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1563 if (!isEnabledInternal()) return -1;
1564
Nick Pelly24bb9b82009-10-02 20:34:18 -07001565 if (serviceName == null || uuid == null || channel < 1 ||
1566 channel > BluetoothSocket.MAX_RFCOMM_CHANNEL) {
1567 return -1;
1568 }
1569 if (BluetoothUuid.isUuidPresent(BluetoothUuid.RESERVED_UUIDS, uuid)) {
1570 Log.w(TAG, "Attempted to register a reserved UUID: " + uuid);
1571 return -1;
1572 }
1573 int handle = addRfcommServiceRecordNative(serviceName,
1574 uuid.getUuid().getMostSignificantBits(), uuid.getUuid().getLeastSignificantBits(),
1575 (short)channel);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001576 if (DBG) Log.d(TAG, "new handle " + Integer.toHexString(handle));
Nick Pelly24bb9b82009-10-02 20:34:18 -07001577 if (handle == -1) {
1578 return -1;
1579 }
1580
Jason Simmonsc980a492011-11-16 12:52:45 -08001581 ServiceRecordClient client = new ServiceRecordClient();
1582 client.pid = Binder.getCallingPid();
1583 client.binder = b;
1584 client.death = new Reaper(handle, client.pid, RFCOMM_RECORD_REAPER);
1585 mServiceRecordToPid.put(new Integer(handle), client);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001586 try {
Jason Simmonsc980a492011-11-16 12:52:45 -08001587 b.linkToDeath(client.death, 0);
1588 } catch (RemoteException e) {
1589 Log.e(TAG, "", e);
1590 client.death = null;
1591 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001592 return handle;
1593 }
1594
1595 public void removeServiceRecord(int handle) {
1596 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
1597 "Need BLUETOOTH permission");
Matthew Xiedcbc97f2011-09-16 16:57:24 -07001598 // Since this is a binder call check if Bluetooth is off
1599 if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
Matthew Xie484867a2011-08-25 16:45:58 -07001600 Message message = mHandler.obtainMessage(MESSAGE_REMOVE_SERVICE_RECORD);
1601 message.obj = new Pair<Integer, Integer>(handle, Binder.getCallingPid());
1602 mHandler.sendMessage(message);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001603 }
1604
1605 private synchronized void checkAndRemoveRecord(int handle, int pid) {
Jason Simmonsc980a492011-11-16 12:52:45 -08001606 ServiceRecordClient client = mServiceRecordToPid.get(handle);
1607 if (client != null && pid == client.pid) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001608 if (DBG) Log.d(TAG, "Removing service record " +
1609 Integer.toHexString(handle) + " for pid " + pid);
Jason Simmonsc980a492011-11-16 12:52:45 -08001610
1611 if (client.death != null) {
1612 client.binder.unlinkToDeath(client.death, 0);
1613 }
1614
Matthew Xief1754052011-09-14 09:05:27 -07001615 mServiceRecordToPid.remove(handle);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001616 removeServiceRecordNative(handle);
1617 }
1618 }
1619
1620 private class Reaper implements IBinder.DeathRecipient {
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001621 int mPid;
1622 int mHandle;
1623 int mType;
1624
1625 Reaper(int handle, int pid, int type) {
1626 mPid = pid;
1627 mHandle = handle;
1628 mType = type;
Nick Pelly24bb9b82009-10-02 20:34:18 -07001629 }
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001630
1631 Reaper(int pid, int type) {
1632 mPid = pid;
1633 mType = type;
1634 }
1635
1636 @Override
Nick Pelly24bb9b82009-10-02 20:34:18 -07001637 public void binderDied() {
1638 synchronized (BluetoothService.this) {
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001639 if (DBG) Log.d(TAG, "Tracked app " + mPid + " died" + "Type:" + mType);
1640 if (mType == RFCOMM_RECORD_REAPER) {
1641 checkAndRemoveRecord(mHandle, mPid);
1642 } else if (mType == STATE_CHANGE_REAPER) {
1643 mStateChangeTracker.remove(mPid);
1644 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001645 }
1646 }
1647 }
1648
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001649
1650 @Override
1651 public boolean changeApplicationBluetoothState(boolean on,
1652 IBluetoothStateChangeCallback callback, IBinder binder) {
Jaikumar Ganeshf5fb6c82011-08-03 14:17:22 -07001653 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
1654
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001655 int pid = Binder.getCallingPid();
1656 //mStateChangeTracker is a synchronized map
1657 if (!mStateChangeTracker.containsKey(pid)) {
1658 if (on) {
1659 mStateChangeTracker.put(pid, callback);
1660 } else {
1661 return false;
1662 }
1663 } else if (!on) {
1664 mStateChangeTracker.remove(pid);
1665 }
1666
1667 if (binder != null) {
1668 try {
1669 binder.linkToDeath(new Reaper(pid, STATE_CHANGE_REAPER), 0);
1670 } catch (RemoteException e) {
Matthew Xief1754052011-09-14 09:05:27 -07001671 Log.e(TAG, "", e);
Jaikumar Ganeshef2cb7c2011-07-21 18:13:38 -07001672 return false;
1673 }
1674 }
1675
1676 int type;
1677 if (on) {
1678 type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_ON;
1679 } else {
1680 type = BluetoothAdapterStateMachine.PER_PROCESS_TURN_OFF;
1681 }
1682
1683 mBluetoothState.sendMessage(type, callback);
1684 return true;
1685 }
1686
1687 boolean isApplicationStateChangeTrackerEmpty() {
1688 return mStateChangeTracker.isEmpty();
1689 }
1690
1691 void clearApplicationStateChangeTracker() {
1692 mStateChangeTracker.clear();
1693 }
1694
1695 Collection<IBluetoothStateChangeCallback> getApplicationStateChangeCallbacks() {
1696 return mStateChangeTracker.values();
1697 }
1698
1699 int getNumberOfApplicationStateChangeTrackers() {
1700 return mStateChangeTracker.size();
1701 }
1702
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1704 @Override
1705 public void onReceive(Context context, Intent intent) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001706 if (intent == null) return;
1707
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 String action = intent.getAction();
1709 if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
1710 ContentResolver resolver = context.getContentResolver();
1711 // Query the airplane mode from Settings.System just to make sure that
1712 // some random app is not sending this intent and disabling bluetooth
Matthew Xie7f9ecca82011-07-15 13:03:58 -07001713 if (isAirplaneModeOn()) {
1714 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_ON);
1715 } else {
1716 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.AIRPLANE_MODE_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001717 }
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001718 } else if (Intent.ACTION_DOCK_EVENT.equals(action)) {
1719 int state = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1720 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1721 if (DBG) Log.v(TAG, "Received ACTION_DOCK_EVENT with State:" + state);
1722 if (state == Intent.EXTRA_DOCK_STATE_UNDOCKED) {
1723 mDockAddress = null;
1724 mDockPin = null;
1725 } else {
1726 SharedPreferences.Editor editor =
1727 mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
1728 mContext.MODE_PRIVATE).edit();
1729 editor.putBoolean(SHARED_PREFERENCE_DOCK_ADDRESS + mDockAddress, true);
Brad Fitzpatrick66fce502010-08-30 18:10:49 -07001730 editor.apply();
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001731 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001732 }
1733 }
1734 };
1735
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001736 private void registerForAirplaneMode(IntentFilter filter) {
Jeff Sharkey44303922009-12-01 18:25:02 -08001737 final ContentResolver resolver = mContext.getContentResolver();
1738 final String airplaneModeRadios = Settings.System.getString(resolver,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739 Settings.System.AIRPLANE_MODE_RADIOS);
Jeff Sharkey44303922009-12-01 18:25:02 -08001740 final String toggleableRadios = Settings.System.getString(resolver,
1741 Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
1742
1743 mIsAirplaneSensitive = airplaneModeRadios == null ? true :
1744 airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH);
1745 mIsAirplaneToggleable = toggleableRadios == null ? false :
1746 toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH);
1747
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001748 if (mIsAirplaneSensitive) {
Jaikumar Ganesh6e9c4432009-12-09 12:09:21 -08001749 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
1751 }
1752
1753 /* Returns true if airplane mode is currently on */
Matthew Xief3171fb2012-01-11 14:46:13 -08001754 /*package*/ final boolean isAirplaneModeOn() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 return Settings.System.getInt(mContext.getContentResolver(),
1756 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
1757 }
1758
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001759 /* Broadcast the Uuid intent */
1760 /*package*/ synchronized void sendUuidIntent(String address) {
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001761 ParcelUuid[] uuid = getUuidFromCache(address);
1762 Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
Jaikumar Ganesh2d3b98d2009-09-21 16:11:01 -07001763 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
Jaikumar Ganesh61799652009-09-20 16:01:21 -07001764 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
1765 mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001766 mUuidIntentTracker.remove(address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001767 }
1768
1769 /*package*/ synchronized void makeServiceChannelCallbacks(String address) {
1770 for (Iterator<RemoteService> iter = mUuidCallbackTracker.keySet().iterator();
1771 iter.hasNext();) {
1772 RemoteService service = iter.next();
1773 if (service.address.equals(address)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001774 if (DBG) Log.d(TAG, "Cleaning up failed UUID channel lookup: "
1775 + service.address + " " + service.uuid);
Nick Pelly16fb88a2009-10-07 07:44:03 +02001776 IBluetoothCallback callback = mUuidCallbackTracker.get(service);
1777 if (callback != null) {
1778 try {
1779 callback.onRfcommChannelFound(-1);
1780 } catch (RemoteException e) {Log.e(TAG, "", e);}
1781 }
1782
1783 iter.remove();
1784 }
1785 }
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07001786 }
1787
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 @Override
1789 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyeb4cc4922012-04-26 18:17:29 -07001790 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1791
Matthew Xie7f9ecca82011-07-15 13:03:58 -07001792 if (getBluetoothStateInternal() != BluetoothAdapter.STATE_ON) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001793 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001794 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001795
Nick Pelly24bb9b82009-10-02 20:34:18 -07001796 pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive);
Jeff Sharkey44303922009-12-01 18:25:02 -08001797 pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable);
Nick Pelly24bb9b82009-10-02 20:34:18 -07001798
1799 pw.println("Local address = " + getAddress());
1800 pw.println("Local name = " + getName());
1801 pw.println("isDiscovering() = " + isDiscovering());
The Android Open Source Project10592532009-03-18 17:39:46 -07001802
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001803 mAdapter.getProfileProxy(mContext,
1804 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001805 mAdapter.getProfileProxy(mContext,
1806 mBluetoothProfileServiceListener, BluetoothProfile.INPUT_DEVICE);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001807 mAdapter.getProfileProxy(mContext,
1808 mBluetoothProfileServiceListener, BluetoothProfile.PAN);
The Android Open Source Project10592532009-03-18 17:39:46 -07001809
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001810 dumpKnownDevices(pw);
1811 dumpAclConnectedDevices(pw);
1812 dumpHeadsetService(pw);
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001813 dumpInputDeviceProfile(pw);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001814 dumpPanProfile(pw);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001815 dumpApplicationServiceRecords(pw);
Jaikumar Ganeshcb1d3542011-08-19 10:26:32 -07001816 dumpProfileState(pw);
1817 }
1818
1819 private void dumpProfileState(PrintWriter pw) {
1820 pw.println("\n--Profile State dump--");
1821 pw.println("\n Headset profile state:" +
1822 mAdapter.getProfileConnectionState(BluetoothProfile.HEADSET));
1823 pw.println("\n A2dp profile state:" +
1824 mAdapter.getProfileConnectionState(BluetoothProfile.A2DP));
1825 pw.println("\n HID profile state:" +
1826 mAdapter.getProfileConnectionState(BluetoothProfile.INPUT_DEVICE));
1827 pw.println("\n PAN profile state:" +
1828 mAdapter.getProfileConnectionState(BluetoothProfile.PAN));
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001829 }
1830
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001831 private void dumpHeadsetService(PrintWriter pw) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001832 pw.println("\n--Headset Service--");
Matthew Xie98f06da2011-11-10 00:03:28 -08001833 if (mHeadsetProxy != null) {
1834 List<BluetoothDevice> deviceList = mHeadsetProxy.getConnectedDevices();
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001835 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001836 pw.println("No headsets connected");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001837 } else {
1838 BluetoothDevice device = deviceList.get(0);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001839 pw.println("\ngetConnectedDevices[0] = " + device);
1840 dumpHeadsetConnectionState(pw, device);
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001841 pw.println("getBatteryUsageHint() = " +
Matthew Xie98f06da2011-11-10 00:03:28 -08001842 mHeadsetProxy.getBatteryUsageHint(device));
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001843 }
1844
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -07001845 deviceList.clear();
Matthew Xie98f06da2011-11-10 00:03:28 -08001846 deviceList = mHeadsetProxy.getDevicesMatchingConnectionStates(new int[] {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001847 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001848 pw.println("--Connected and Disconnected Headsets");
Jaikumar Ganeshbb0773f2010-11-11 15:10:46 -08001849 for (BluetoothDevice device: deviceList) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001850 pw.println(device);
Matthew Xie98f06da2011-11-10 00:03:28 -08001851 if (mHeadsetProxy.isAudioConnected(device)) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001852 pw.println("SCO audio connected to device:" + device);
1853 }
1854 }
The Android Open Source Project10592532009-03-18 17:39:46 -07001855 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001856 }
Nick Pelly6c901db2009-06-19 10:08:09 -07001857
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001858 private void dumpInputDeviceProfile(PrintWriter pw) {
1859 pw.println("\n--Bluetooth Service- Input Device Profile");
1860 if (mInputDevice != null) {
1861 List<BluetoothDevice> deviceList = mInputDevice.getConnectedDevices();
1862 if (deviceList.size() == 0) {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001863 pw.println("No input devices connected");
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001864 } else {
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001865 pw.println("Number of connected devices:" + deviceList.size());
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001866 BluetoothDevice device = deviceList.get(0);
1867 pw.println("getConnectedDevices[0] = " + device);
1868 pw.println("Priority of Connected device = " + mInputDevice.getPriority(device));
1869
1870 switch (mInputDevice.getConnectionState(device)) {
1871 case BluetoothInputDevice.STATE_CONNECTING:
1872 pw.println("getConnectionState() = STATE_CONNECTING");
1873 break;
1874 case BluetoothInputDevice.STATE_CONNECTED:
1875 pw.println("getConnectionState() = STATE_CONNECTED");
1876 break;
1877 case BluetoothInputDevice.STATE_DISCONNECTING:
1878 pw.println("getConnectionState() = STATE_DISCONNECTING");
1879 break;
1880 }
1881 }
1882 deviceList.clear();
1883 deviceList = mInputDevice.getDevicesMatchingConnectionStates(new int[] {
1884 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1885 pw.println("--Connected and Disconnected input devices");
1886 for (BluetoothDevice device: deviceList) {
1887 pw.println(device);
1888 }
Nick Pelly24bb9b82009-10-02 20:34:18 -07001889 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001890 }
1891
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001892 private void dumpPanProfile(PrintWriter pw) {
1893 pw.println("\n--Bluetooth Service- Pan Profile");
1894 if (mPan != null) {
1895 List<BluetoothDevice> deviceList = mPan.getConnectedDevices();
1896 if (deviceList.size() == 0) {
1897 pw.println("No Pan devices connected");
1898 } else {
1899 pw.println("Number of connected devices:" + deviceList.size());
1900 BluetoothDevice device = deviceList.get(0);
1901 pw.println("getConnectedDevices[0] = " + device);
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001902
1903 switch (mPan.getConnectionState(device)) {
1904 case BluetoothInputDevice.STATE_CONNECTING:
1905 pw.println("getConnectionState() = STATE_CONNECTING");
1906 break;
1907 case BluetoothInputDevice.STATE_CONNECTED:
1908 pw.println("getConnectionState() = STATE_CONNECTED");
1909 break;
1910 case BluetoothInputDevice.STATE_DISCONNECTING:
1911 pw.println("getConnectionState() = STATE_DISCONNECTING");
1912 break;
1913 }
1914 }
1915 deviceList.clear();
1916 deviceList = mPan.getDevicesMatchingConnectionStates(new int[] {
1917 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED});
1918 pw.println("--Connected and Disconnected Pan devices");
1919 for (BluetoothDevice device: deviceList) {
1920 pw.println(device);
1921 }
1922 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001923 }
1924
1925 private void dumpHeadsetConnectionState(PrintWriter pw,
1926 BluetoothDevice device) {
Matthew Xie98f06da2011-11-10 00:03:28 -08001927 switch (mHeadsetProxy.getConnectionState(device)) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001928 case BluetoothHeadset.STATE_CONNECTING:
1929 pw.println("getConnectionState() = STATE_CONNECTING");
1930 break;
1931 case BluetoothHeadset.STATE_CONNECTED:
1932 pw.println("getConnectionState() = STATE_CONNECTED");
1933 break;
1934 case BluetoothHeadset.STATE_DISCONNECTING:
1935 pw.println("getConnectionState() = STATE_DISCONNECTING");
1936 break;
1937 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
1938 pw.println("getConnectionState() = STATE_AUDIO_CONNECTED");
1939 break;
1940 }
1941 }
1942
1943 private void dumpApplicationServiceRecords(PrintWriter pw) {
1944 pw.println("\n--Application Service Records--");
1945 for (Integer handle : mServiceRecordToPid.keySet()) {
Jason Simmonsc980a492011-11-16 12:52:45 -08001946 Integer pid = mServiceRecordToPid.get(handle).pid;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001947 pw.println("\tpid " + pid + " handle " + Integer.toHexString(handle));
1948 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08001949 }
1950
Jake Hamby9a62c9c2010-12-09 14:47:57 -08001951 private void dumpAclConnectedDevices(PrintWriter pw) {
1952 String[] devicesObjectPath = getKnownDevices();
1953 pw.println("\n--ACL connected devices--");
1954 if (devicesObjectPath != null) {
1955 for (String device : devicesObjectPath) {
1956 pw.println(getAddressFromObjectPath(device));
1957 }
1958 }
1959 }
1960
1961 private void dumpKnownDevices(PrintWriter pw) {
1962 pw.println("\n--Known devices--");
1963 for (String address : mDeviceProperties.keySet()) {
1964 int bondState = mBondState.getBondState(address);
1965 pw.printf("%s %10s (%d) %s\n", address,
1966 toBondStateString(bondState),
1967 mBondState.getAttempt(address),
1968 getRemoteName(address));
1969
1970 Map<ParcelUuid, Integer> uuidChannels = mDeviceServiceChannelCache.get(address);
1971 if (uuidChannels == null) {
1972 pw.println("\tuuids = null");
1973 } else {
1974 for (ParcelUuid uuid : uuidChannels.keySet()) {
1975 Integer channel = uuidChannels.get(uuid);
1976 if (channel == null) {
1977 pw.println("\t" + uuid);
1978 } else {
1979 pw.println("\t" + uuid + " RFCOMM channel = " + channel);
1980 }
1981 }
1982 }
1983 for (RemoteService service : mUuidCallbackTracker.keySet()) {
1984 if (service.address.equals(address)) {
1985 pw.println("\tPENDING CALLBACK: " + service.uuid);
1986 }
1987 }
1988 }
1989 }
1990
Matthew Xie98f06da2011-11-10 00:03:28 -08001991 private void getProfileProxy() {
1992 mAdapter.getProfileProxy(mContext,
1993 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
1994 }
1995
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07001996 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1997 new BluetoothProfile.ServiceListener() {
1998 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08001999 if (profile == BluetoothProfile.HEADSET) {
Matthew Xie98f06da2011-11-10 00:03:28 -08002000 mHeadsetProxy = (BluetoothHeadset) proxy;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002001 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
2002 mInputDevice = (BluetoothInputDevice) proxy;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08002003 } else if (profile == BluetoothProfile.PAN) {
2004 mPan = (BluetoothPan) proxy;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002005 }
2006 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002007 public void onServiceDisconnected(int profile) {
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002008 if (profile == BluetoothProfile.HEADSET) {
Matthew Xie98f06da2011-11-10 00:03:28 -08002009 mHeadsetProxy = null;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002010 } else if (profile == BluetoothProfile.INPUT_DEVICE) {
2011 mInputDevice = null;
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08002012 } else if (profile == BluetoothProfile.PAN) {
2013 mPan = null;
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002014 }
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002015 }
2016 };
2017
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002018 /* package */ static int bluezStringToScanMode(boolean pairable, boolean discoverable) {
2019 if (pairable && discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002020 return BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002021 else if (pairable && !discoverable)
Nick Pellybd022f42009-08-14 18:33:38 -07002022 return BluetoothAdapter.SCAN_MODE_CONNECTABLE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002023 else
Nick Pellybd022f42009-08-14 18:33:38 -07002024 return BluetoothAdapter.SCAN_MODE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 }
2026
2027 /* package */ static String scanModeToBluezString(int mode) {
2028 switch (mode) {
Nick Pellybd022f42009-08-14 18:33:38 -07002029 case BluetoothAdapter.SCAN_MODE_NONE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002030 return "off";
Nick Pellybd022f42009-08-14 18:33:38 -07002031 case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 return "connectable";
Nick Pellybd022f42009-08-14 18:33:38 -07002033 case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 return "discoverable";
2035 }
2036 return null;
2037 }
2038
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002039 /*package*/ String getAddressFromObjectPath(String objectPath) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002040 String adapterObjectPath = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002041 if (adapterObjectPath == null || objectPath == null) {
Jake Hambyf51eada2010-09-21 13:39:53 -07002042 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002043 " or deviceObjectPath:" + objectPath + " is null");
2044 return null;
2045 }
2046 if (!objectPath.startsWith(adapterObjectPath)) {
Jake Hambyf51eada2010-09-21 13:39:53 -07002047 Log.e(TAG, "getAddressFromObjectPath: AdapterObjectPath:" + adapterObjectPath +
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002048 " is not a prefix of deviceObjectPath:" + objectPath +
2049 "bluetoothd crashed ?");
2050 return null;
2051 }
2052 String address = objectPath.substring(adapterObjectPath.length());
2053 if (address != null) return address.replace('_', ':');
2054
2055 Log.e(TAG, "getAddressFromObjectPath: Address being returned is null");
2056 return null;
2057 }
2058
2059 /*package*/ String getObjectPathFromAddress(String address) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002060 String path = mAdapterProperties.getObjectPath();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002061 if (path == null) {
2062 Log.e(TAG, "Error: Object Path is null");
2063 return null;
2064 }
2065 path = path + address.replace(":", "_");
2066 return path;
2067 }
2068
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002069 /*package */ void setLinkTimeout(String address, int num_slots) {
2070 String path = getObjectPathFromAddress(address);
2071 boolean result = setLinkTimeoutNative(path, num_slots);
2072
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002073 if (!result) Log.d(TAG, "Set Link Timeout to " + num_slots + " slots failed");
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002074 }
2075
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002076 /**** Handlers for PAN Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002077 // TODO: This needs to be converted to a state machine.
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002078
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002079 public boolean isTetheringOn() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002080 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002081 synchronized (mBluetoothPanProfileHandler) {
2082 return mBluetoothPanProfileHandler.isTetheringOn();
2083 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002084 }
2085
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002086 /*package*/boolean allowIncomingTethering() {
2087 synchronized (mBluetoothPanProfileHandler) {
2088 return mBluetoothPanProfileHandler.allowIncomingTethering();
2089 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002090 }
2091
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002092 public void setBluetoothTethering(boolean value) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002093 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002094 synchronized (mBluetoothPanProfileHandler) {
2095 mBluetoothPanProfileHandler.setBluetoothTethering(value);
2096 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002097 }
2098
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002099 public int getPanDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002100 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002101 synchronized (mBluetoothPanProfileHandler) {
2102 return mBluetoothPanProfileHandler.getPanDeviceConnectionState(device);
2103 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002104 }
2105
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002106 public boolean connectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002107 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2108 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002109 synchronized (mBluetoothPanProfileHandler) {
2110 return mBluetoothPanProfileHandler.connectPanDevice(device);
2111 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002112 }
2113
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002114 public List<BluetoothDevice> getConnectedPanDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002115 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002116 synchronized (mBluetoothPanProfileHandler) {
2117 return mBluetoothPanProfileHandler.getConnectedPanDevices();
2118 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002119 }
2120
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002121 public List<BluetoothDevice> getPanDevicesMatchingConnectionStates(
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08002122 int[] states) {
2123 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002124 synchronized (mBluetoothPanProfileHandler) {
2125 return mBluetoothPanProfileHandler.getPanDevicesMatchingConnectionStates(states);
2126 }
Jaikumar Ganesh74ef1192011-02-23 10:22:15 -08002127 }
2128
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002129 public boolean disconnectPanDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002130 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2131 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002132 synchronized (mBluetoothPanProfileHandler) {
2133 return mBluetoothPanProfileHandler.disconnectPanDevice(device);
2134 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002135 }
2136
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002137 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002138 String iface,
2139 int state,
2140 int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002141 synchronized (mBluetoothPanProfileHandler) {
2142 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, iface, state, role);
2143 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002144 }
2145
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002146 /*package*/void handlePanDeviceStateChange(BluetoothDevice device,
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002147 int state, int role) {
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002148 synchronized (mBluetoothPanProfileHandler) {
2149 mBluetoothPanProfileHandler.handlePanDeviceStateChange(device, null, state, role);
2150 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002151 }
2152
2153 /**** Handlers for Input Device Profile ****/
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002154 // This needs to be converted to state machine
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002155
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002156 public boolean connectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002157 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2158 "Need BLUETOOTH_ADMIN permission");
2159 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002160 synchronized (mBluetoothInputProfileHandler) {
2161 return mBluetoothInputProfileHandler.connectInputDevice(device, state);
2162 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002163 }
2164
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002165 public boolean connectInputDeviceInternal(BluetoothDevice device) {
2166 synchronized (mBluetoothInputProfileHandler) {
2167 return mBluetoothInputProfileHandler.connectInputDeviceInternal(device);
2168 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002169 }
2170
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002171 public boolean disconnectInputDevice(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002172 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2173 "Need BLUETOOTH_ADMIN permission");
2174 BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress());
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002175 synchronized (mBluetoothInputProfileHandler) {
2176 return mBluetoothInputProfileHandler.disconnectInputDevice(device, state);
2177 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002178 }
2179
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002180 public boolean disconnectInputDeviceInternal(BluetoothDevice device) {
2181 synchronized (mBluetoothInputProfileHandler) {
2182 return mBluetoothInputProfileHandler.disconnectInputDeviceInternal(device);
2183 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002184 }
2185
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002186 public int getInputDeviceConnectionState(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002187 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002188 synchronized (mBluetoothInputProfileHandler) {
2189 return mBluetoothInputProfileHandler.getInputDeviceConnectionState(device);
2190 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002191 }
2192
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002193 public List<BluetoothDevice> getConnectedInputDevices() {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002194 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002195 synchronized (mBluetoothInputProfileHandler) {
2196 return mBluetoothInputProfileHandler.getConnectedInputDevices();
2197 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002198 }
2199
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002200 public List<BluetoothDevice> getInputDevicesMatchingConnectionStates(
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002201 int[] states) {
2202 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002203 synchronized (mBluetoothInputProfileHandler) {
2204 return mBluetoothInputProfileHandler.getInputDevicesMatchingConnectionStates(states);
2205 }
Jaikumar Ganesh4ab0e772011-02-18 14:52:32 -08002206 }
2207
2208
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002209 public int getInputDevicePriority(BluetoothDevice device) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002210 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002211 synchronized (mBluetoothInputProfileHandler) {
2212 return mBluetoothInputProfileHandler.getInputDevicePriority(device);
2213 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002214 }
2215
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002216 public boolean setInputDevicePriority(BluetoothDevice device, int priority) {
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002217 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2218 "Need BLUETOOTH_ADMIN permission");
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002219 synchronized (mBluetoothInputProfileHandler) {
2220 return mBluetoothInputProfileHandler.setInputDevicePriority(device, priority);
2221 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002222 }
2223
Jaikumar Ganeshbbd86752011-08-01 19:11:18 -07002224 /**
2225 * Handle incoming profile acceptance for profiles handled by Bluetooth Service,
2226 * currently PAN and HID. This also is the catch all for all rejections for profiles
2227 * that is not supported.
2228 *
2229 * @param device - Bluetooth Device
2230 * @param allow - true / false
2231 * @return
2232 */
2233 public boolean allowIncomingProfileConnect(BluetoothDevice device, boolean allow) {
Matthew Xiea0c68032011-06-25 21:47:07 -07002234 mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
2235 "Need BLUETOOTH_ADMIN permission");
2236 String address = device.getAddress();
2237 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2238 return false;
2239 }
2240
2241 Integer data = getAuthorizationAgentRequestData(address);
2242 if (data == null) {
Jaikumar Ganeshbbd86752011-08-01 19:11:18 -07002243 Log.w(TAG, "allowIncomingProfileConnect(" + device +
Matthew Xiea0c68032011-06-25 21:47:07 -07002244 ") called but no native data available");
2245 return false;
2246 }
Jaikumar Ganeshbbd86752011-08-01 19:11:18 -07002247 if (DBG) log("allowIncomingProfileConnect: " + device + " : " + allow + " : " + data);
Matthew Xiea0c68032011-06-25 21:47:07 -07002248 return setAuthorizationNative(address, allow, data.intValue());
2249 }
2250
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002251 /*package*/List<BluetoothDevice> lookupInputDevicesMatchingStates(int[] states) {
2252 synchronized (mBluetoothInputProfileHandler) {
2253 return mBluetoothInputProfileHandler.lookupInputDevicesMatchingStates(states);
2254 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002255 }
2256
Jaikumar Ganesh6003fe92011-04-08 10:32:48 -07002257 /*package*/void handleInputDevicePropertyChange(String address, boolean connected) {
2258 synchronized (mBluetoothInputProfileHandler) {
2259 mBluetoothInputProfileHandler.handleInputDevicePropertyChange(address, connected);
2260 }
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002261 }
2262
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002263 /**** Handlers for Health Device Profile ****/
2264 // TODO: All these need to be converted to a state machine.
2265
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002266 public boolean registerAppConfiguration(BluetoothHealthAppConfiguration config,
2267 IBluetoothHealthCallback callback) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002268 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2269 "Need BLUETOOTH permission");
2270 synchronized (mBluetoothHealthProfileHandler) {
Jaikumar Ganeshfb658c72011-07-06 17:37:02 -07002271 return mBluetoothHealthProfileHandler.registerAppConfiguration(config, callback);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002272 }
2273 }
2274
2275 public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
2276 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2277 "Need BLUETOOTH permission");
2278 synchronized (mBluetoothHealthProfileHandler) {
2279 return mBluetoothHealthProfileHandler.unregisterAppConfiguration(config);
2280 }
2281 }
2282
2283
2284 public boolean connectChannelToSource(BluetoothDevice device,
2285 BluetoothHealthAppConfiguration config) {
2286 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2287 "Need BLUETOOTH permission");
2288 synchronized (mBluetoothHealthProfileHandler) {
2289 return mBluetoothHealthProfileHandler.connectChannelToSource(device,
2290 config);
2291 }
2292 }
2293
2294 public boolean connectChannelToSink(BluetoothDevice device,
2295 BluetoothHealthAppConfiguration config, int channelType) {
2296 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2297 "Need BLUETOOTH permission");
2298 synchronized (mBluetoothHealthProfileHandler) {
2299 return mBluetoothHealthProfileHandler.connectChannel(device, config,
2300 channelType);
2301 }
2302 }
2303
2304 public boolean disconnectChannel(BluetoothDevice device,
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -07002305 BluetoothHealthAppConfiguration config, int id) {
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002306 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2307 "Need BLUETOOTH permission");
2308 synchronized (mBluetoothHealthProfileHandler) {
Jaikumar Ganesheb9d3462011-08-31 15:36:05 -07002309 return mBluetoothHealthProfileHandler.disconnectChannel(device, config, id);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002310 }
2311 }
2312
2313 public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
2314 BluetoothHealthAppConfiguration config) {
2315 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2316 "Need BLUETOOTH permission");
2317 synchronized (mBluetoothHealthProfileHandler) {
2318 return mBluetoothHealthProfileHandler.getMainChannelFd(device, config);
2319 }
2320 }
2321
2322 /*package*/ void onHealthDevicePropertyChanged(String devicePath,
2323 String channelPath) {
2324 synchronized (mBluetoothHealthProfileHandler) {
2325 mBluetoothHealthProfileHandler.onHealthDevicePropertyChanged(devicePath,
2326 channelPath);
2327 }
2328 }
2329
2330 /*package*/ void onHealthDeviceChannelChanged(String devicePath,
2331 String channelPath, boolean exists) {
2332 synchronized(mBluetoothHealthProfileHandler) {
2333 mBluetoothHealthProfileHandler.onHealthDeviceChannelChanged(devicePath,
2334 channelPath, exists);
2335 }
2336 }
2337
Jaikumar Ganeshb5d2d452011-09-07 14:16:52 -07002338 /*package*/ void onHealthDeviceChannelConnectionError(int channelCode,
2339 int newState) {
2340 synchronized(mBluetoothHealthProfileHandler) {
2341 mBluetoothHealthProfileHandler.onHealthDeviceChannelConnectionError(channelCode,
2342 newState);
2343 }
2344 }
2345
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002346 public int getHealthDeviceConnectionState(BluetoothDevice device) {
2347 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2348 "Need BLUETOOTH permission");
2349 synchronized (mBluetoothHealthProfileHandler) {
2350 return mBluetoothHealthProfileHandler.getHealthDeviceConnectionState(device);
2351 }
2352 }
2353
2354 public List<BluetoothDevice> getConnectedHealthDevices() {
2355 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2356 "Need BLUETOOTH permission");
2357 synchronized (mBluetoothHealthProfileHandler) {
2358 return mBluetoothHealthProfileHandler.getConnectedHealthDevices();
2359 }
2360 }
2361
2362 public List<BluetoothDevice> getHealthDevicesMatchingConnectionStates(
2363 int[] states) {
2364 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
2365 "Need BLUETOOTH permission");
2366 synchronized (mBluetoothHealthProfileHandler) {
2367 return mBluetoothHealthProfileHandler.
2368 getHealthDevicesMatchingConnectionStates(states);
2369 }
2370 }
2371
Matthew Xiea0c68032011-06-25 21:47:07 -07002372 /*package*/boolean notifyIncomingHidConnection(String address) {
2373 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2374 if (state == null) {
2375 return false;
2376 }
2377 Message msg = new Message();
2378 msg.what = BluetoothDeviceProfileState.CONNECT_HID_INCOMING;
2379 state.sendMessage(msg);
2380 return true;
2381 }
2382
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002383 public boolean connectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002384 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2385
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002386 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002387 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002388 Message msg = new Message();
2389 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2390 msg.obj = state;
2391 mHfpProfileState.sendMessage(msg);
2392 return true;
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002393 }
2394 return false;
2395 }
2396
2397 public boolean disconnectHeadset(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002398 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2399
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002400 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002401 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002402 Message msg = new Message();
2403 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HFP_OUTGOING;
2404 msg.obj = state;
2405 mHfpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002406 return true;
2407 }
2408 return false;
2409 }
2410
2411 public boolean connectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002412 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2413
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002414 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002415 if (state != null) {
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002416 Message msg = new Message();
2417 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2418 msg.obj = state;
2419 mA2dpProfileState.sendMessage(msg);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002420 return true;
2421 }
2422 return false;
2423 }
2424
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002425 public boolean disconnectSink(String address) {
Jaikumar Ganesh96a79832010-09-27 17:02:01 -07002426 if (getBondState(address) != BluetoothDevice.BOND_BONDED) return false;
2427
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002428 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2429 if (state != null) {
2430 Message msg = new Message();
2431 msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_A2DP_OUTGOING;
2432 msg.obj = state;
2433 mA2dpProfileState.sendMessage(msg);
2434 return true;
2435 }
2436 return false;
2437 }
2438
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002439 BluetoothDeviceProfileState addProfileState(String address, boolean setTrust) {
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -08002440 BluetoothDeviceProfileState state =
2441 new BluetoothDeviceProfileState(mContext, address, this, mA2dpService, setTrust);
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002442 mDeviceProfileState.put(address, state);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002443 state.start();
2444 return state;
2445 }
2446
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002447 void removeProfileState(String address) {
Jaikumar Ganesh9bb27512011-11-28 09:59:08 -08002448 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2449 if (state == null) return;
2450
2451 state.quit();
Jaikumar Ganeshf1048cd2010-06-02 20:30:34 -07002452 mDeviceProfileState.remove(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002453 }
2454
Matthew Xie44b58ab2011-11-16 12:27:57 -08002455 String[] getKnownDevices() {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002456 String[] bonds = null;
Jaikumar Ganesha66590e2011-08-04 15:39:44 -07002457 String val = getProperty("Devices", true);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002458 if (val != null) {
2459 bonds = val.split(",");
2460 }
2461 return bonds;
2462 }
2463
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002464 private void initProfileState() {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002465 String[] bonds = null;
Jaikumar Ganesha66590e2011-08-04 15:39:44 -07002466 String val = getProperty("Devices", false);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002467 if (val != null) {
2468 bonds = val.split(",");
2469 }
2470 if (bonds == null) {
2471 return;
2472 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002473 for (String path : bonds) {
2474 String address = getAddressFromObjectPath(path);
Jaikumar Ganeshd3728cb2011-07-19 15:16:18 -07002475 BluetoothDeviceProfileState state = addProfileState(address, false);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002476 }
2477 }
2478
2479 private void autoConnect() {
Martijn Coenen6c614b72012-04-18 13:01:15 -07002480 synchronized (this) {
2481 if (!mAllowConnect) {
2482 Log.d(TAG, "Not auto-connecting devices because of temporary BT on state.");
2483 return;
2484 }
2485 }
2486
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002487 String[] bonds = getKnownDevices();
2488 if (bonds == null) {
2489 return;
2490 }
2491 for (String path : bonds) {
2492 String address = getAddressFromObjectPath(path);
2493 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
2494 if (state != null) {
2495 Message msg = new Message();
2496 msg.what = BluetoothDeviceProfileState.AUTO_CONNECT_PROFILES;
2497 state.sendMessage(msg);
2498 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002499 }
2500 }
2501
Matthew Xie98f06da2011-11-10 00:03:28 -08002502 public boolean notifyIncomingConnection(String address, boolean rejected) {
Martijn Coenen6c614b72012-04-18 13:01:15 -07002503 synchronized (this) {
2504 if (!mAllowConnect) {
2505 Log.d(TAG, "Not allowing incoming connection because of temporary BT on state.");
2506 return false;
2507 }
2508 }
Matthew Xie98f06da2011-11-10 00:03:28 -08002509 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002510 if (state != null) {
2511 Message msg = new Message();
Matthew Xie98f06da2011-11-10 00:03:28 -08002512 if (rejected) {
2513 if (mA2dpService.getPriority(getRemoteDevice(address)) >=
2514 BluetoothProfile.PRIORITY_ON) {
2515 msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES;
2516 msg.arg1 = BluetoothDeviceProfileState.CONNECT_A2DP_OUTGOING;
2517 state.sendMessageDelayed(msg,
2518 BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY);
2519 }
2520 } else {
2521 msg.what = BluetoothDeviceProfileState.CONNECT_HFP_INCOMING;
2522 state.sendMessage(msg);
2523 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002524 return true;
2525 }
2526 return false;
2527 }
2528
Matthew Xie98f06da2011-11-10 00:03:28 -08002529 /*package*/ boolean notifyIncomingA2dpConnection(String address, boolean rejected) {
Martijn Coenen6c614b72012-04-18 13:01:15 -07002530 synchronized (this) {
2531 if (!mAllowConnect) {
2532 Log.d(TAG, "Not allowing a2dp connection because of temporary BT on state.");
2533 return false;
2534 }
2535 }
2536
Matthew Xie98f06da2011-11-10 00:03:28 -08002537 BluetoothDeviceProfileState state = mDeviceProfileState.get(address);
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002538 if (state != null) {
2539 Message msg = new Message();
Matthew Xie98f06da2011-11-10 00:03:28 -08002540 if (rejected) {
2541 if (mHeadsetProxy.getPriority(getRemoteDevice(address)) >=
2542 BluetoothProfile.PRIORITY_ON) {
2543 msg.what = BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES;
2544 msg.arg1 = BluetoothDeviceProfileState.CONNECT_HFP_OUTGOING;
2545 state.sendMessageDelayed(msg,
2546 BluetoothDeviceProfileState.CONNECT_OTHER_PROFILES_DELAY);
2547 }
2548 } else {
2549 msg.what = BluetoothDeviceProfileState.CONNECT_A2DP_INCOMING;
2550 state.sendMessage(msg);
2551 }
Jaikumar Ganesh9b637e52010-06-02 14:36:14 -07002552 return true;
2553 }
2554 return false;
2555 }
2556
2557 /*package*/ void setA2dpService(BluetoothA2dpService a2dpService) {
2558 mA2dpService = a2dpService;
2559 }
2560
Matthew Xiea0c68032011-06-25 21:47:07 -07002561 /*package*/ Integer getAuthorizationAgentRequestData(String address) {
2562 Integer data = mEventLoop.getAuthorizationAgentRequestData().remove(address);
2563 return data;
2564 }
2565
Jaikumar Ganesh70a053b2010-10-13 15:54:30 -07002566 public void sendProfileStateMessage(int profile, int cmd) {
2567 Message msg = new Message();
2568 msg.what = cmd;
2569 if (profile == BluetoothProfileState.HFP) {
2570 mHfpProfileState.sendMessage(msg);
2571 } else if (profile == BluetoothProfileState.A2DP) {
2572 mA2dpProfileState.sendMessage(msg);
2573 }
2574 }
2575
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002576 public int getAdapterConnectionState() {
2577 return mAdapterConnectionState;
2578 }
2579
Jaikumar Ganeshcb1d3542011-08-19 10:26:32 -07002580 public int getProfileConnectionState(int profile) {
2581 mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
2582
2583 Pair<Integer, Integer> state = mProfileConnectionState.get(profile);
2584 if (state == null) return BluetoothProfile.STATE_DISCONNECTED;
2585
2586 return state.first;
2587 }
2588
2589 private void updateProfileConnectionState(int profile, int newState, int oldState) {
2590 // mProfileConnectionState is a hashmap -
2591 // <Integer, Pair<Integer, Integer>>
2592 // The key is the profile, the value is a pair. first element
2593 // is the state and the second element is the number of devices
2594 // in that state.
2595 int numDev = 1;
2596 int newHashState = newState;
2597 boolean update = true;
2598
2599 // The following conditions are considered in this function:
2600 // 1. If there is no record of profile and state - update
2601 // 2. If a new device's state is current hash state - increment
2602 // number of devices in the state.
2603 // 3. If a state change has happened to Connected or Connecting
2604 // (if current state is not connected), update.
2605 // 4. If numDevices is 1 and that device state is being updated, update
2606 // 5. If numDevices is > 1 and one of the devices is changing state,
2607 // decrement numDevices but maintain oldState if it is Connected or
2608 // Connecting
2609 Pair<Integer, Integer> stateNumDev = mProfileConnectionState.get(profile);
2610 if (stateNumDev != null) {
2611 int currHashState = stateNumDev.first;
2612 numDev = stateNumDev.second;
2613
2614 if (newState == currHashState) {
2615 numDev ++;
2616 } else if (newState == BluetoothProfile.STATE_CONNECTED ||
2617 (newState == BluetoothProfile.STATE_CONNECTING &&
2618 currHashState != BluetoothProfile.STATE_CONNECTED)) {
2619 numDev = 1;
2620 } else if (numDev == 1 && oldState == currHashState) {
2621 update = true;
2622 } else if (numDev > 1 && oldState == currHashState) {
2623 numDev --;
2624
2625 if (currHashState == BluetoothProfile.STATE_CONNECTED ||
2626 currHashState == BluetoothProfile.STATE_CONNECTING) {
2627 newHashState = currHashState;
2628 }
2629 } else {
2630 update = false;
2631 }
2632 }
2633
2634 if (update) {
2635 mProfileConnectionState.put(profile, new Pair<Integer, Integer>(newHashState,
2636 numDev));
2637 }
2638 }
2639
2640 public synchronized void sendConnectionStateChange(BluetoothDevice
2641 device, int profile, int state, int prevState) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002642 // Since this is a binder call check if Bluetooth is on still
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002643 if (getBluetoothStateInternal() == BluetoothAdapter.STATE_OFF) return;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002644
Jaikumar Ganeshcb1d3542011-08-19 10:26:32 -07002645 if (!validateProfileConnectionState(state) ||
2646 !validateProfileConnectionState(prevState)) {
2647 // Previously, an invalid state was broadcast anyway,
2648 // with the invalid state converted to -1 in the intent.
2649 // Better to log an error and not send an intent with
2650 // invalid contents or set mAdapterConnectionState to -1.
2651 Log.e(TAG, "Error in sendConnectionStateChange: "
2652 + "prevState " + prevState + " state " + state);
2653 return;
2654 }
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002655
Jaikumar Ganeshcb1d3542011-08-19 10:26:32 -07002656 updateProfileConnectionState(profile, state, prevState);
2657
2658 if (updateCountersAndCheckForConnectionStateChange(state, prevState)) {
Jaikumar Ganeshc53cab22010-10-26 16:02:26 -07002659 mAdapterConnectionState = state;
2660
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002661 if (state == BluetoothProfile.STATE_DISCONNECTED) {
2662 mBluetoothState.sendMessage(BluetoothAdapterStateMachine.ALL_DEVICES_DISCONNECTED);
2663 }
2664
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002665 Intent intent = new Intent(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
2666 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002667 intent.putExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
2668 convertToAdapterState(state));
2669 intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_CONNECTION_STATE,
2670 convertToAdapterState(prevState));
Jaikumar Ganesh60b4d2a2011-07-12 14:14:49 -07002671 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002672 mContext.sendBroadcast(intent, BLUETOOTH_PERM);
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002673 Log.d(TAG, "CONNECTION_STATE_CHANGE: " + device + ": "
2674 + prevState + " -> " + state);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002675 }
2676 }
2677
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002678 private boolean validateProfileConnectionState(int state) {
2679 return (state == BluetoothProfile.STATE_DISCONNECTED ||
2680 state == BluetoothProfile.STATE_CONNECTING ||
2681 state == BluetoothProfile.STATE_CONNECTED ||
2682 state == BluetoothProfile.STATE_DISCONNECTING);
2683 }
2684
2685 private int convertToAdapterState(int state) {
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002686 switch (state) {
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002687 case BluetoothProfile.STATE_DISCONNECTED:
2688 return BluetoothAdapter.STATE_DISCONNECTED;
2689 case BluetoothProfile.STATE_DISCONNECTING:
2690 return BluetoothAdapter.STATE_DISCONNECTING;
2691 case BluetoothProfile.STATE_CONNECTED:
2692 return BluetoothAdapter.STATE_CONNECTED;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002693 case BluetoothProfile.STATE_CONNECTING:
2694 return BluetoothAdapter.STATE_CONNECTING;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002695 }
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002696 Log.e(TAG, "Error in convertToAdapterState");
2697 return -1;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002698 }
2699
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002700 private boolean updateCountersAndCheckForConnectionStateChange(int state, int prevState) {
2701 switch (prevState) {
2702 case BluetoothProfile.STATE_CONNECTING:
2703 mProfilesConnecting--;
2704 break;
2705
2706 case BluetoothProfile.STATE_CONNECTED:
2707 mProfilesConnected--;
2708 break;
2709
2710 case BluetoothProfile.STATE_DISCONNECTING:
2711 mProfilesDisconnecting--;
2712 break;
2713 }
2714
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002715 switch (state) {
2716 case BluetoothProfile.STATE_CONNECTING:
2717 mProfilesConnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002718 return (mProfilesConnected == 0 && mProfilesConnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002719
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002720 case BluetoothProfile.STATE_CONNECTED:
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002721 mProfilesConnected++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002722 return (mProfilesConnected == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002723
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002724 case BluetoothProfile.STATE_DISCONNECTING:
2725 mProfilesDisconnecting++;
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002726 return (mProfilesConnected == 0 && mProfilesDisconnecting == 1);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002727
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002728 case BluetoothProfile.STATE_DISCONNECTED:
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002729 return (mProfilesConnected == 0 && mProfilesConnecting == 0);
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002730
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002731 default:
2732 return true;
Jaikumar Ganesha46f2fb2010-10-21 14:55:05 -07002733 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002734 }
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002735
Matthew Xiea0c68032011-06-25 21:47:07 -07002736 private void createIncomingConnectionStateFile() {
2737 File f = new File(INCOMING_CONNECTION_FILE);
2738 if (!f.exists()) {
2739 try {
2740 f.createNewFile();
2741 } catch (IOException e) {
2742 Log.e(TAG, "IOException: cannot create file");
2743 }
2744 }
2745 }
2746
2747 /** @hide */
2748 public Pair<Integer, String> getIncomingState(String address) {
2749 if (mIncomingConnections.isEmpty()) {
2750 createIncomingConnectionStateFile();
2751 readIncomingConnectionState();
2752 }
2753 return mIncomingConnections.get(address);
2754 }
2755
2756 private void readIncomingConnectionState() {
2757 synchronized(mIncomingConnections) {
2758 FileInputStream fstream = null;
2759 try {
2760 fstream = new FileInputStream(INCOMING_CONNECTION_FILE);
2761 DataInputStream in = new DataInputStream(fstream);
2762 BufferedReader file = new BufferedReader(new InputStreamReader(in));
2763 String line;
2764 while((line = file.readLine()) != null) {
2765 line = line.trim();
2766 if (line.length() == 0) continue;
2767 String[] value = line.split(",");
2768 if (value != null && value.length == 3) {
2769 Integer val1 = Integer.parseInt(value[1]);
2770 Pair<Integer, String> val = new Pair(val1, value[2]);
2771 mIncomingConnections.put(value[0], val);
2772 }
2773 }
2774 } catch (FileNotFoundException e) {
2775 log("FileNotFoundException: readIncomingConnectionState" + e.toString());
2776 } catch (IOException e) {
2777 log("IOException: readIncomingConnectionState" + e.toString());
2778 } finally {
2779 if (fstream != null) {
2780 try {
2781 fstream.close();
2782 } catch (IOException e) {
2783 // Ignore
2784 }
2785 }
2786 }
2787 }
2788 }
2789
2790 private void truncateIncomingConnectionFile() {
2791 RandomAccessFile r = null;
2792 try {
2793 r = new RandomAccessFile(INCOMING_CONNECTION_FILE, "rw");
2794 r.setLength(0);
2795 } catch (FileNotFoundException e) {
2796 log("FileNotFoundException: truncateIncomingConnectionState" + e.toString());
2797 } catch (IOException e) {
2798 log("IOException: truncateIncomingConnectionState" + e.toString());
2799 } finally {
2800 if (r != null) {
2801 try {
2802 r.close();
2803 } catch (IOException e) {
2804 // ignore
2805 }
2806 }
2807 }
2808 }
2809
2810 /** @hide */
2811 public void writeIncomingConnectionState(String address, Pair<Integer, String> data) {
2812 synchronized(mIncomingConnections) {
2813 mIncomingConnections.put(address, data);
2814
2815 truncateIncomingConnectionFile();
2816 BufferedWriter out = null;
2817 StringBuilder value = new StringBuilder();
2818 try {
2819 out = new BufferedWriter(new FileWriter(INCOMING_CONNECTION_FILE, true));
2820 for (String devAddress: mIncomingConnections.keySet()) {
2821 Pair<Integer, String> val = mIncomingConnections.get(devAddress);
2822 value.append(devAddress);
2823 value.append(",");
2824 value.append(val.first.toString());
2825 value.append(",");
2826 value.append(val.second);
2827 value.append("\n");
2828 }
2829 out.write(value.toString());
2830 } catch (FileNotFoundException e) {
2831 log("FileNotFoundException: writeIncomingConnectionState" + e.toString());
2832 } catch (IOException e) {
2833 log("IOException: writeIncomingConnectionState" + e.toString());
2834 } finally {
2835 if (out != null) {
2836 try {
2837 out.close();
2838 } catch (IOException e) {
2839 // Ignore
2840 }
2841 }
2842 }
2843 }
2844 }
2845
2846 private static void log(String msg) {
2847 Log.d(TAG, msg);
2848 }
2849
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002850 private native static void classInitNative();
2851 private native void initializeNativeDataNative();
2852 private native boolean setupNativeDataNative();
2853 private native boolean tearDownNativeDataNative();
2854 private native void cleanupNativeDataNative();
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002855 /*package*/ native String getAdapterPathNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002856
2857 private native int isEnabledNative();
Matthew Xie7f9ecca82011-07-15 13:03:58 -07002858 /*package*/ native int enableNative();
2859 /*package*/ native int disableNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002860
Jake Hamby9a62c9c2010-12-09 14:47:57 -08002861 /*package*/ native Object[] getAdapterPropertiesNative();
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002862 private native Object[] getDevicePropertiesNative(String objectPath);
2863 private native boolean setAdapterPropertyStringNative(String key, String value);
2864 private native boolean setAdapterPropertyIntegerNative(String key, int value);
2865 private native boolean setAdapterPropertyBooleanNative(String key, int value);
2866
2867 private native boolean startDiscoveryNative();
2868 private native boolean stopDiscoveryNative();
2869
2870 private native boolean createPairedDeviceNative(String address, int timeout_ms);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002871 private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
2872 private native byte[] readAdapterOutOfBandDataNative();
2873
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002874 private native boolean cancelDeviceCreationNative(String address);
2875 private native boolean removeDeviceNative(String objectPath);
2876 private native int getDeviceServiceChannelNative(String objectPath, String uuid,
2877 int attributeId);
2878
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002879 private native boolean cancelPairingUserInputNative(String address, int nativeData);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -07002880 private native boolean setPinNative(String address, String pin, int nativeData);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -07002881 private native boolean setPasskeyNative(String address, int passkey, int nativeData);
2882 private native boolean setPairingConfirmationNative(String address, boolean confirm,
2883 int nativeData);
Jaikumar Ganeshcc5494c2010-09-09 15:37:57 -07002884 private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
2885 byte[] randomizer, int nativeData);
2886
Nick Pelly24bb9b82009-10-02 20:34:18 -07002887 private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
2888 int value);
Matthew Xie269e81a2011-07-26 18:36:49 -07002889 private native boolean setDevicePropertyStringNative(String objectPath, String key,
2890 String value);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -07002891 private native boolean createDeviceNative(String address);
Nick Pelly16fb88a2009-10-07 07:44:03 +02002892 /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
Jaikumar Ganesh10eac972009-09-21 12:48:51 -07002893
Nick Pelly24bb9b82009-10-02 20:34:18 -07002894 private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb,
2895 short channel);
2896 private native boolean removeServiceRecordNative(int handle);
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -08002897 private native boolean setLinkTimeoutNative(String path, int num_slots);
Matthew Xiea0c68032011-06-25 21:47:07 -07002898
Jaikumar Ganesh30b8cbe2011-02-11 16:26:29 -08002899 native boolean connectInputDeviceNative(String path);
2900 native boolean disconnectInputDeviceNative(String path);
Danica Chang6fdd0c62010-08-11 14:54:43 -07002901
Jaikumar Ganesha8571f12011-02-11 15:46:54 -08002902 native boolean setBluetoothTetheringNative(boolean value, String nap, String bridge);
2903 native boolean connectPanDeviceNative(String path, String dstRole);
2904 native boolean disconnectPanDeviceNative(String path);
2905 native boolean disconnectPanServerDeviceNative(String path,
Jaikumar Ganesha44a1e72011-02-11 12:40:44 -08002906 String address, String iface);
Jaikumar Ganesh84690c82010-12-10 12:48:58 -08002907
2908 private native int[] addReservedServiceRecordsNative(int[] uuuids);
2909 private native boolean removeReservedServiceRecordsNative(int[] handles);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002910
2911 // Health API
2912 native String registerHealthApplicationNative(int dataType, String role, String name,
2913 String channelType);
2914 native String registerHealthApplicationNative(int dataType, String role, String name);
2915 native boolean unregisterHealthApplicationNative(String path);
Jaikumar Ganeshb5d2d452011-09-07 14:16:52 -07002916 native boolean createChannelNative(String devicePath, String appPath, String channelType,
2917 int code);
2918 native boolean destroyChannelNative(String devicePath, String channelpath, int code);
Jaikumar Ganesh2ea1e852011-04-01 16:33:09 -07002919 native String getMainChannelNative(String path);
2920 native String getChannelApplicationNative(String channelPath);
2921 native ParcelFileDescriptor getChannelFdNative(String channelPath);
2922 native boolean releaseChannelFdNative(String channelPath);
Matthew Xiea0c68032011-06-25 21:47:07 -07002923 native boolean setAuthorizationNative(String address, boolean value, int data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002924}