diff options
| -rw-r--r-- | api/current.txt | 16 | ||||
| -rw-r--r-- | core/java/android/bluetooth/BluetoothAdapter.java | 422 | ||||
| -rw-r--r-- | core/java/android/bluetooth/BluetoothAdapterCallback.java | 57 | ||||
| -rw-r--r-- | core/java/android/bluetooth/BluetoothManager.java | 6 | ||||
| -rw-r--r-- | core/java/android/provider/Settings.java | 11 |
5 files changed, 276 insertions, 236 deletions
diff --git a/api/current.txt b/api/current.txt index 6eb8545ab317..803bb0253475 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4628,13 +4628,11 @@ package android.bluetooth { method public boolean isEnabled(); method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException; method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException; - method public boolean registerCallback(android.bluetooth.BluetoothAdapterCallback); method public boolean setName(java.lang.String); method public boolean startDiscovery(); - method public boolean startLeScan(); - method public boolean startLeScan(java.util.UUID[]); - method public void stopLeScan(); - method public boolean unRegisterCallback(android.bluetooth.BluetoothAdapterCallback); + method public boolean startLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback); + method public boolean startLeScan(java.util.UUID[], android.bluetooth.BluetoothAdapter.LeScanCallback); + method public void stopLeScan(android.bluetooth.BluetoothAdapter.LeScanCallback); field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED"; field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED"; @@ -4665,12 +4663,8 @@ package android.bluetooth { field public static final int STATE_TURNING_ON = 11; // 0xb } - public abstract class BluetoothAdapterCallback { - ctor public BluetoothAdapterCallback(); - method public void onCallbackRegistration(int); - method public void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]); - field public static final int CALLBACK_REGISTERED = 0; // 0x0 - field public static final int CALLBACK_REGISTRATION_FAILURE = 1; // 0x1 + public static abstract interface BluetoothAdapter.LeScanCallback { + method public abstract void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]); } public class BluetoothAssignedNumbers { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 2e9c9e334d68..3498bb8e47be 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -20,7 +20,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; import android.os.Binder; -import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.ParcelUuid; @@ -30,11 +29,14 @@ import android.util.Log; import android.util.Pair; import java.io.IOException; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.HashMap; import java.util.LinkedList; +import java.util.Map; import java.util.Random; import java.util.Set; import java.util.UUID; @@ -357,9 +359,7 @@ public final class BluetoothAdapter { private final IBluetoothManager mManagerService; private IBluetooth mService; - private Handler mServiceRecordHandler; - private BluetoothAdapterCallback mCallback; - private int mClientIf; + private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients; /** * Get a handle to the default local Bluetooth adapter. @@ -394,7 +394,7 @@ public final class BluetoothAdapter { mService = managerService.registerAdapter(mManagerCallback); } catch (RemoteException e) {Log.e(TAG, "", e);} mManagerService = managerService; - mServiceRecordHandler = null; + mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>(); } /** @@ -1409,72 +1409,38 @@ public final class BluetoothAdapter { } /** - * Register an callback to receive async results, such as LE scan result. + * Callback interface used to deliver LE scan results. * - * <p>This is an asynchronous call. The callback - * {@link BluetoothAdapterCallback#onCallbackRegistration} - * is used to notify success or failure if the function returns true. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks. - * @return If true, the callback will be called to notify success or failure, - * false on immediate error - */ - public boolean registerCallback(BluetoothAdapterCallback callback) { - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - mCallback = callback; - UUID uuid = UUID.randomUUID(); - if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid); - - iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback); - return true; - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - } - - /** - * Unregister the registered callback. - */ - public boolean unRegisterCallback(BluetoothAdapterCallback callback) { - if (callback != mCallback) return false; - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - - iGatt.unregisterClient(mClientIf); - return true; - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } + * @see #startLeScan(LeScanCallback) + * @see #startLeScan(UUID[], LeScanCallback) + */ + public interface LeScanCallback { + /** + * Callback reporting an LE device found during a device scan initiated + * by the {@link BluetoothAdapter#startLeScan} function. + * + * @param device Identifies the remote device + * @param rssi The RSSI value for the remote device as reported by the + * Bluetooth hardware. 0 if no RSSI value is available. + * @param scanRecord The content of the advertisement record offered by + * the remote device. + */ + public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord); } /** * Starts a scan for Bluetooth LE devices. * * <p>Results of the scan are reported using the - * {@link BluetoothAdapterCallback#onLeScan} callback. + * {@link LeScanCallback#onLeScan} callback. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * + * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully */ - public boolean startLeScan() { - if (DBG) Log.d(TAG, "startLeScan()"); - if (mClientIf == 0) return false; - - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - iGatt.startScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); - return false; - } - - return true; + public boolean startLeScan(LeScanCallback callback) { + return startLeScan(null, callback); } /** @@ -1482,155 +1448,281 @@ public final class BluetoothAdapter { * advertise given services. * * <p>Devices which advertise all specified services are reported using the - * {@link BluetoothAdapterCallback#onLeScan} callback. + * {@link LeScanCallback#onLeScan} callback. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. * * @param serviceUuids Array of services to look for + * @param callback the callback LE scan results are delivered * @return true, if the scan was started successfully */ - public boolean startLeScan(UUID[] serviceUuids) { - if (DBG) Log.d(TAG, "startLeScan() - with UUIDs"); - if (mClientIf == 0) return false; + public boolean startLeScan(UUID[] serviceUuids, LeScanCallback callback) { + if (DBG) Log.d(TAG, "startLeScan(): " + serviceUuids); - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length]; - for(int i = 0; i != uuids.length; ++i) { - uuids[i] = new ParcelUuid(serviceUuids[i]); - } - iGatt.startScanWithUuids(mClientIf, false, uuids); - } catch (RemoteException e) { - Log.e(TAG,"",e); + if (callback == null) { + if (DBG) Log.e(TAG, "startLeScan: null callback"); return false; } - return true; + synchronized(mLeScanClients) { + if (mLeScanClients.containsKey(callback)) { + if (DBG) Log.e(TAG, "LE Scan has already started"); + return false; + } + + try { + IBluetoothGatt iGatt = mManagerService.getBluetoothGatt(); + UUID uuid = UUID.randomUUID(); + GattCallbackWrapper wrapper = new GattCallbackWrapper(this, callback, serviceUuids); + + iGatt.registerClient(new ParcelUuid(uuid), wrapper); + if (wrapper.scanStarted()) { + mLeScanClients.put(callback, wrapper); + return true; + } + } catch (RemoteException e) { + Log.e(TAG,"",e); + } + } + return false; } /** * Stops an ongoing Bluetooth LE device scan. * * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - */ - public void stopLeScan() { - if (DBG) Log.d(TAG, "stopScan()"); - if (mClientIf == 0) return; - - try { - IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt(); - iGatt.stopScan(mClientIf, false); - } catch (RemoteException e) { - Log.e(TAG,"",e); + * + * @param callback used to identify which scan to stop + * must be the same handle used to start the scan + */ + public void stopLeScan(LeScanCallback callback) { + if (DBG) Log.d(TAG, "stopLeScan()"); + GattCallbackWrapper wrapper; + synchronized(mLeScanClients) { + wrapper = mLeScanClients.remove(callback); + if (wrapper == null) return; } + wrapper.stopLeScan(); } /** * Bluetooth GATT interface callbacks */ - private final IBluetoothGattCallback mBluetoothGattCallback = - new IBluetoothGattCallback.Stub() { - /** - * Application interface registered - app is ready to go - */ - public void onClientRegistered(int status, int clientIf) { - if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status - + " clientIf=" + clientIf); - mClientIf = clientIf; - mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ? - BluetoothAdapterCallback.CALLBACK_REGISTERED : - BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE); - } + private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub { + private static final int LE_CALLBACK_REG_TIMEOUT = 2000; + private static final int LE_CALLBACK_REG_WAIT_COUNT = 5; + + private final LeScanCallback mLeScanCb; + // mLeHandle 0: not registered + // -1: scan stopped + // >0: registered and scan started + private int mLeHandle; + private final UUID[] mScanFilter; + private WeakReference<BluetoothAdapter> mBluetoothAdapter; + + public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, + LeScanCallback leScanCb, UUID[] uuid) { + mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter); + mLeScanCb = leScanCb; + mScanFilter = uuid; + mLeHandle = 0; + } - public void onClientConnectionState(int status, int clientIf, - boolean connected, String address) { - // no op + public boolean scanStarted() { + boolean started = false; + synchronized(this) { + if (mLeHandle == -1) return false; + + int count = 0; + // wait for callback registration and LE scan to start + while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) { + try { + wait(LE_CALLBACK_REG_TIMEOUT); + } catch (InterruptedException e) { + Log.e(TAG, "Callback reg wait interrupted: " + e); + } + count++; + } + started = (mLeHandle > 0); } + return started; + } - /** - * Callback reporting an LE scan result. - * @hide - */ - public void onScanResult(String address, int rssi, byte[] advData) { - if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - - try { - mCallback.onLeScan(getRemoteDevice(address), rssi, advData); - } catch (Exception ex) { - Log.w(TAG, "Unhandled exception: " + ex); + public void stopLeScan() { + synchronized(this) { + if (mLeHandle <= 0) { + Log.e(TAG, "Error state, mLeHandle: " + mLeHandle); + return; + } + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + try { + IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + iGatt.stopScan(mLeHandle, false); + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "Failed to stop scan and unregister" + e); + } + } else { + Log.e(TAG, "stopLeScan, BluetoothAdapter is null"); } + mLeHandle = -1; + notifyAll(); } + } - public void onGetService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid) { - // no op - } + /** + * Application interface registered - app is ready to go + */ + public void onClientRegistered(int status, int clientIf) { + if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status + + " clientIf=" + clientIf); + synchronized(this) { + if (mLeHandle == -1) { + if (DBG) Log.d(TAG, "onClientRegistered LE scan canceled"); + } - public void onGetIncludedService(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int inclSrvcType, int inclSrvcInstId, - ParcelUuid inclSrvcUuid) { - // no op + if (status == BluetoothGatt.GATT_SUCCESS) { + mLeHandle = clientIf; + IBluetoothGatt iGatt = null; + try { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter != null) { + iGatt = adapter.getBluetoothManager().getBluetoothGatt(); + if (mScanFilter == null) { + iGatt.startScan(mLeHandle, false); + } else { + ParcelUuid[] uuids = new ParcelUuid[mScanFilter.length]; + for(int i = 0; i != uuids.length; ++i) { + uuids[i] = new ParcelUuid(mScanFilter[i]); + } + iGatt.startScanWithUuids(mLeHandle, false, uuids); + } + } else { + Log.e(TAG, "onClientRegistered, BluetoothAdapter null"); + mLeHandle = -1; + } + } catch (RemoteException e) { + Log.e(TAG, "fail to start le scan: " + e); + mLeHandle = -1; + } + if (mLeHandle == -1) { + // registration succeeded but start scan failed + if (iGatt != null) { + try { + iGatt.unregisterClient(mLeHandle); + } catch (RemoteException e) { + Log.e(TAG, "fail to unregister callback: " + mLeHandle + + " error: " + e); + } + } + } + } else { + // registration failed + mLeHandle = -1; + } + notifyAll(); } + } - public void onGetCharacteristic(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - int charProps) { - // no op - } + public void onClientConnectionState(int status, int clientIf, + boolean connected, String address) { + // no op + } - public void onGetDescriptor(String address, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - ParcelUuid descUuid) { - // no op - } + /** + * Callback reporting an LE scan result. + * @hide + */ + public void onScanResult(String address, int rssi, byte[] advData) { + if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi); - public void onSearchComplete(String address, int status) { - // no op + // Check null in case the scan has been stopped + synchronized(this) { + if (mLeHandle <= 0) return; } - - public void onCharacteristicRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, byte[] value) { - // no op + try { + BluetoothAdapter adapter = mBluetoothAdapter.get(); + if (adapter == null) { + Log.d(TAG, "onScanResult, BluetoothAdapter null"); + return; + } + mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData); + } catch (Exception ex) { + Log.w(TAG, "Unhandled exception: " + ex); } + } - public void onCharacteristicWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid) { - // no op - } + public void onGetService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid) { + // no op + } + + public void onGetIncludedService(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int inclSrvcType, int inclSrvcInstId, + ParcelUuid inclSrvcUuid) { + // no op + } - public void onNotify(String address, int srvcType, + public void onGetCharacteristic(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + int charProps) { + // no op + } + + public void onGetDescriptor(String address, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descUuid) { + // no op + } + + public void onSearchComplete(String address, int status) { + // no op + } + + public void onCharacteristicRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, byte[] value) { + // no op + } + + public void onCharacteristicWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid) { + // no op + } + + public void onNotify(String address, int srvcType, int srvcInstId, ParcelUuid srvcUuid, int charInstId, ParcelUuid charUuid, byte[] value) { - // no op - } + // no op + } - public void onDescriptorRead(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid, byte[] value) { - // no op - } + public void onDescriptorRead(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid, byte[] value) { + // no op + } - public void onDescriptorWrite(String address, int status, int srvcType, - int srvcInstId, ParcelUuid srvcUuid, - int charInstId, ParcelUuid charUuid, - ParcelUuid descrUuid) { - // no op - } + public void onDescriptorWrite(String address, int status, int srvcType, + int srvcInstId, ParcelUuid srvcUuid, + int charInstId, ParcelUuid charUuid, + ParcelUuid descrUuid) { + // no op + } - public void onExecuteWrite(String address, int status) { - // no op - } + public void onExecuteWrite(String address, int status) { + // no op + } - public void onReadRemoteRssi(String address, int rssi, int status) { - // no op - } - }; + public void onReadRemoteRssi(String address, int rssi, int status) { + // no op + } + } } diff --git a/core/java/android/bluetooth/BluetoothAdapterCallback.java b/core/java/android/bluetooth/BluetoothAdapterCallback.java deleted file mode 100644 index a726bc91895b..000000000000 --- a/core/java/android/bluetooth/BluetoothAdapterCallback.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.bluetooth; - -import android.bluetooth.BluetoothDevice; - -/** - * This abstract class is used to implement {@link BluetoothAdapter} callbacks. - */ -public abstract class BluetoothAdapterCallback { - - /** - * Indicates the callback has been registered successfully - */ - public static final int CALLBACK_REGISTERED = 0; - - /** - * Indicates the callback registration has failed - */ - public static final int CALLBACK_REGISTRATION_FAILURE = 1; - - /** - * Callback to inform change in registration state of the application. - * - * @param status Returns {@link #CALLBACK_REGISTERED} if the application - * was successfully registered. - */ - public void onCallbackRegistration(int status) { - } - - /** - * Callback reporting an LE device found during a device scan initiated - * by the {@link BluetoothAdapter#startLeScan} function. - * - * @param device Identifies the remote device - * @param rssi The RSSI value for the remote device as reported by the - * Bluetooth hardware. 0 if no RSSI value is available. - * @param scanRecord The content of the advertisement record offered by - * the remote device. - */ - public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { - } -} diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 19083b55dac2..172f3bcdefed 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -127,7 +127,7 @@ public final class BluetoothManager { try { IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return connectedDevices; connectedDevices = iGatt.getDevicesMatchingConnectionStates( @@ -172,7 +172,7 @@ public final class BluetoothManager { try { IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) return devices; devices = iGatt.getDevicesMatchingConnectionStates(states); } catch (RemoteException e) { @@ -203,7 +203,7 @@ public final class BluetoothManager { try { IBluetoothManager managerService = mAdapter.getBluetoothManager(); - IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt(); + IBluetoothGatt iGatt = managerService.getBluetoothGatt(); if (iGatt == null) { Log.e(TAG, "Fail to get GATT Server connection"); return null; diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 3df4e99f66b4..4de5933585a3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -714,6 +714,17 @@ public final class Settings { */ public static final String EXTRA_AUTHORITIES = "authorities"; + /** + * Activity Extra: Limit available options in launched activity based on the given account + * types. + * <p> + * This can be passed as an extra field in an Activity Intent with one or more account types + * as a String[]. This field is used by some intents to alter the behavior of the called + * activity. + * <p> + * Example: The {@link #ACTION_ADD_ACCOUNT} intent restricts the account types to the specified + * list. + */ public static final String EXTRA_ACCOUNT_TYPES = "account_types"; public static final String EXTRA_INPUT_METHOD_ID = "input_method_id"; |