Merge "DO NOT MERGE BLE peripheral mode (3/4): Add peripheral mode API." into klp-dev

This commit is contained in:
Wei Wang
2014-03-08 02:01:01 +00:00
committed by Android (Google) Code Review
8 changed files with 394 additions and 93 deletions

View File

@@ -27,14 +27,14 @@ import android.os.RemoteException;
import android.os.ServiceManager;
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.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
@@ -182,6 +182,43 @@ public final class BluetoothAdapter {
public static final String EXTRA_DISCOVERABLE_DURATION =
"android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
/**
* Activity Action: Show a system activity to request BLE advertising.<br>
* If the device is not doing BLE advertising, this activity will start BLE advertising for the
* device, otherwise it will continue BLE advertising using the current
* {@link BluetoothAdvScanData}. <br>
* Note this activity will also request the user to turn on Bluetooth if it's not currently
* enabled.
* @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_START_ADVERTISING =
"android.bluetooth.adapter.action.START_ADVERTISING";
/**
* Activity Action: Stop the current BLE advertising.
* @hide
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_STOP_ADVERTISING =
"android.bluetooth.adapter.action.STOP_ADVERTISING";
/**
* Broadcast Action: Indicate BLE Advertising is started.
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
"android.bluetooth.adapter.action.ADVERTISING_STARTED";
/**
* Broadcast Action: Indicated BLE Advertising is stopped.
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
"android.bluetooth.adapter.action.ADVERTISING_STOPPED";
/**
* Activity Action: Show a system activity that allows the user to turn on
* Bluetooth.
@@ -251,7 +288,6 @@ public final class BluetoothAdapter {
*/
public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
/**
* Broadcast Action: The local Bluetooth adapter has started the remote
* device discovery process.
@@ -365,6 +401,8 @@ public final class BluetoothAdapter {
private IBluetooth mService;
private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
private BluetoothAdvScanData mBluetoothAdvScanData = null;
private GattCallbackWrapper mAdvertisingCallback;
/**
* Get a handle to the default local Bluetooth adapter.
@@ -437,6 +475,97 @@ public final class BluetoothAdapter {
address[0], address[1], address[2], address[3], address[4], address[5]));
}
/**
* Returns a {@link BluetoothAdvScanData} object representing advertising data.
* @hide
*/
public BluetoothAdvScanData getAdvScanData() {
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
Log.e(TAG, "failed to start, iGatt null");
return null;
}
if (mBluetoothAdvScanData == null) {
mBluetoothAdvScanData = new BluetoothAdvScanData(iGatt, BluetoothAdvScanData.AD);
}
return mBluetoothAdvScanData;
} catch (RemoteException e) {
Log.e(TAG, "failed to get advScanData, error: " + e);
return null;
}
}
/**
* Start BLE advertising using current {@link BluetoothAdvScanData}.
* An app should start advertising by requesting
* {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
*
* @return true if BLE avertising succeeds, false otherwise.
* @hide
*/
public boolean startAdvertising() {
if (getState() != STATE_ON) return false;
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported.
return false;
}
// Restart/reset advertising packets if advertising is in progress.
if (isAdvertising()) {
// Invalid advertising callback.
if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) {
Log.e(TAG, "failed to restart advertising, invalid callback");
return false;
}
iGatt.startAdvertising(mAdvertisingCallback.mLeHandle);
return true;
}
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper =
new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV);
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
mAdvertisingCallback = wrapper;
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
/**
* Stop BLE advertising.
* An app should stop advertising by requesting
* {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
* @return true if BLE advertising stops, false otherwise.
* @hide
*/
public boolean stopAdvertisting() {
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return false;
}
if (mAdvertisingCallback == null) {
// no callback.
return false;
}
mAdvertisingCallback.stopAdvertising();
mAdvertisingCallback = null;
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
}
}
/**
* Return true if Bluetooth is currently enabled and ready for use.
* <p>Equivalent to:
@@ -848,6 +977,23 @@ public final class BluetoothAdapter {
return false;
}
/**
* Returns whether BLE is currently advertising.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
*
* @hide
*/
public boolean isAdvertising() {
if (getState() != STATE_ON) return false;
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
return iGatt.isAdvertising();
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
return false;
}
/**
* Return the set of {@link BluetoothDevice} objects that are bonded
* (paired) to the local adapter.
@@ -1546,8 +1692,12 @@ public final class BluetoothAdapter {
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 static final int CALLBACK_TYPE_SCAN = 0;
private static final int CALLBACK_TYPE_ADV = 1;
private final LeScanCallback mLeScanCb;
private int mCallbackType;
// mLeHandle 0: not registered
// -1: scan stopped
// >0: registered and scan started
@@ -1561,6 +1711,16 @@ public final class BluetoothAdapter {
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
mCallbackType = CALLBACK_TYPE_SCAN;
}
public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
UUID[] uuid, int type) {
mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
mCallbackType = type;
}
public boolean scanStarted() {
@@ -1583,6 +1743,30 @@ public final class BluetoothAdapter {
return started;
}
public void stopAdvertising() {
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.stopAdvertising();
Log.d(TAG, "unregeistering client " + mLeHandle);
iGatt.unregisterClient(mLeHandle);
} catch (RemoteException e) {
Log.e(TAG, "Failed to stop advertising and unregister" + e);
}
} else {
Log.e(TAG, "stopAdvertising, BluetoothAdapter is null");
}
mLeHandle = -1;
notifyAll();
}
}
public void stopLeScan() {
synchronized(this) {
if (mLeHandle <= 0) {
@@ -1624,14 +1808,18 @@ public final class BluetoothAdapter {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter != null) {
iGatt = adapter.getBluetoothManager().getBluetoothGatt();
if (mScanFilter == null) {
iGatt.startScan(mLeHandle, false);
if (mCallbackType == CALLBACK_TYPE_ADV) {
iGatt.startAdvertising(mLeHandle);
} 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);
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");
@@ -1642,7 +1830,7 @@ public final class BluetoothAdapter {
mLeHandle = -1;
}
if (mLeHandle == -1) {
// registration succeeded but start scan failed
// registration succeeded but start scan or advertise failed
if (iGatt != null) {
try {
iGatt.unregisterClient(mLeHandle);

View File

@@ -0,0 +1,147 @@
/*
* 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.os.ParcelUuid;
import android.os.RemoteException;
import android.util.Log;
import java.util.Collections;
import java.util.List;
/**
* This class provides the public APIs to set advertising and scan response data when BLE device
* operates in peripheral mode. <br>
* The exact format is defined in Bluetooth 4.0 specification, Volume 3, Part C, Section 11
* @hide
*/
public final class BluetoothAdvScanData {
/**
* Available data types of {@link BluetoothAdvScanData}.
*/
public static final int AD = 0; // Advertising Data
public static final int SCAN_RESPONSE = 1; // Scan Response
public static final int EIR = 2; // Extended Inquiry Response
private static final String TAG = "BluetoothAdvScanData";
/**
* Data type of BluetoothAdvScanData.
*/
private final int mDataType;
/**
* Bluetooth Gatt Service.
*/
private IBluetoothGatt mBluetoothGatt;
/**
* @param mBluetoothGatt
* @param dataType
*/
public BluetoothAdvScanData(IBluetoothGatt mBluetoothGatt, int dataType) {
this.mBluetoothGatt = mBluetoothGatt;
this.mDataType = dataType;
}
/**
* @return advertising data type.
*/
public int getDataType() {
return mDataType;
}
/**
* Set manufactureCode and manufactureData.
* Returns true if manufacturer data is set, false if there is no enough room to set
* manufacturer data or the data is already set.
* @param manufacturerCode - unique identifier for the manufacturer
* @param manufacturerData - data associated with the specific manufacturer.
*/
public boolean setManufacturerData(int manufacturerCode, byte[] manufacturerData) {
if (mDataType != AD) return false;
try {
return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
} catch (RemoteException e) {
return false;
}
}
/**
* Set service data. Note the service data can only be set when the data type is {@code AD};
* @param serviceData
*/
public boolean setServiceData(byte[] serviceData) {
if (mDataType != AD) return false;
if (serviceData == null) return false;
try {
return mBluetoothGatt.setAdvServiceData(serviceData);
} catch (RemoteException e) {
return false;
}
}
/**
* Returns an immutable list of service uuids that will be advertised.
*/
public List<ParcelUuid> getServiceUuids() {
try {
return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
} catch (RemoteException e) {
return null;
}
}
/**
* Returns manufacturer data.
*/
public byte[] getManufacturerData() {
if (mBluetoothGatt == null) return null;
try {
return mBluetoothGatt.getAdvManufacturerData();
} catch (RemoteException e) {
return null;
}
}
/**
* Returns service data.
*/
public byte[] getServiceData() {
if (mBluetoothGatt == null) return null;
try {
return mBluetoothGatt.getAdvServiceData();
} catch (RemoteException e) {
return null;
}
}
/**
* Remove manufacturer data based on given manufacturer code.
* @param manufacturerCode
*/
public void removeManufacturerCodeAndData(int manufacturerCode) {
if (mBluetoothGatt != null) {
try {
mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
}
}
}

View File

@@ -553,14 +553,6 @@ public final class BluetoothGatt implements BluetoothProfile {
Log.w(TAG, "Unhandled exception in callback", ex);
}
}
/**
* Listen command status callback
* @hide
*/
public void onListen(int status) {
if (DBG) Log.d(TAG, "onListen() - status=" + status);
}
};
/*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
@@ -693,71 +685,6 @@ public final class BluetoothGatt implements BluetoothProfile {
return true;
}
/**
* Starts or stops sending of advertisement packages to listen for connection
* requests from a central devices.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param start Start or stop advertising
*/
/*package*/ void listen(boolean start) {
if (mContext == null || !mContext.getResources().
getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) {
throw new UnsupportedOperationException("BluetoothGatt#listen is blocked");
}
if (DBG) Log.d(TAG, "listen() - start: " + start);
if (mService == null || mClientIf == 0) return;
try {
mService.clientListen(mClientIf, start);
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
/**
* Sets the advertising data contained in the adv. response packet.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
*
* @param advData true to set adv. data, false to set scan response
* @param includeName Inlucde the name in the adv. response
* @param includeTxPower Include TX power value
* @param minInterval Minimum desired scan interval (optional)
* @param maxInterval Maximum desired scan interval (optional)
* @param appearance The appearance flags for the device (optional)
* @param manufacturerData Manufacturer specific data including company ID (optional)
*/
/*package*/ void setAdvData(boolean advData, boolean includeName, boolean includeTxPower,
Integer minInterval, Integer maxInterval,
Integer appearance, Byte[] manufacturerData) {
if (mContext == null || !mContext.getResources().
getBoolean(com.android.internal.R.bool.config_bluetooth_le_peripheral_mode_supported)) {
throw new UnsupportedOperationException("BluetoothGatt#setAdvData is blocked");
}
if (DBG) Log.d(TAG, "setAdvData()");
if (mService == null || mClientIf == 0) return;
byte[] data = new byte[0];
if (manufacturerData != null) {
data = new byte[manufacturerData.length];
for(int i = 0; i != manufacturerData.length; ++i) {
data[i] = manufacturerData[i];
}
}
try {
mService.setAdvData(mClientIf, !advData,
includeName, includeTxPower,
minInterval != null ? minInterval : 0,
maxInterval != null ? maxInterval : 0,
appearance != null ? appearance : 0, data);
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
/**
* Disconnects an established connection, or cancels a connection attempt
* currently in progress.

View File

@@ -537,7 +537,7 @@ public final class BluetoothGattServer implements BluetoothProfile {
try {
mService.beginServiceDeclaration(mServerIf, service.getType(),
service.getInstanceId(), service.getHandles(),
new ParcelUuid(service.getUuid()));
new ParcelUuid(service.getUuid()), service.isAdvertisePreferred());
List<BluetoothGattService> includedServices = service.getIncludedServices();
for (BluetoothGattService includedService : includedServices) {

View File

@@ -15,8 +15,6 @@
*/
package android.bluetooth;
import android.bluetooth.BluetoothDevice;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -81,6 +79,11 @@ public class BluetoothGattService {
*/
protected List<BluetoothGattService> mIncludedServices;
/**
* Whether the service uuid should be advertised.
*/
private boolean mAdvertisePreferred;
/**
* Create a new BluetoothGattService.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -263,4 +266,20 @@ public class BluetoothGattService {
}
return null;
}
/**
* Returns whether the uuid of the service should be advertised.
* @hide
*/
public boolean isAdvertisePreferred() {
return mAdvertisePreferred;
}
/**
* Set whether the service uuid should be advertised.
* @hide
*/
public void setAdvertisePreferred(boolean advertisePreferred) {
this.mAdvertisePreferred = advertisePreferred;
}
}

View File

@@ -73,6 +73,9 @@ public final class BluetoothUuid {
public static final ParcelUuid MAS =
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
public static final ParcelUuid[] RESERVED_UUIDS = {
AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
@@ -211,4 +214,17 @@ public final class BluetoothUuid {
long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32;
return (int)value;
}
/**
* Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
* @param parcelUuid
* @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
*/
public static boolean isShortUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
return false;
}
return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
}
}

View File

@@ -37,10 +37,15 @@ interface IBluetoothGatt {
void unregisterClient(in int clientIf);
void clientConnect(in int clientIf, in String address, in boolean isDirect);
void clientDisconnect(in int clientIf, in String address);
void clientListen(in int clientIf, in boolean start);
void setAdvData(in int clientIf, in boolean setScanRsp, in boolean inclName,
in boolean inclTxPower, in int minInterval, in int maxInterval,
in int appearance, in byte[] manufacturerData);
void startAdvertising(in int appIf);
void stopAdvertising();
boolean setAdvServiceData(in byte[] serviceData);
byte[] getAdvServiceData();
boolean setAdvManufacturerCodeAndData(int manufactureCode, in byte[] manufacturerData);
byte[] getAdvManufacturerData();
List<ParcelUuid> getAdvServiceUuids();
void removeAdvManufacturerCodeAndData(int manufacturerCode);
boolean isAdvertising();
void refreshDevice(in int clientIf, in String address);
void discoverServices(in int clientIf, in String address);
void readCharacteristic(in int clientIf, in String address, in int srvcType,
@@ -75,7 +80,7 @@ interface IBluetoothGatt {
void serverDisconnect(in int serverIf, in String address);
void beginServiceDeclaration(in int serverIf, in int srvcType,
in int srvcInstanceId, in int minHandles,
in ParcelUuid srvcId);
in ParcelUuid srvcId, boolean advertisePreferred);
void addIncludedService(in int serverIf, in int srvcType,
in int srvcInstanceId, in ParcelUuid srvcId);
void addCharacteristic(in int serverIf, in ParcelUuid charId,

View File

@@ -63,5 +63,4 @@ interface IBluetoothGattCallback {
in int charInstId, in ParcelUuid charUuid,
in byte[] value);
void onReadRemoteRssi(in String address, in int rssi, in int status);
void onListen(in int status);
}