();
+ mHandler = new Handler(Looper.getMainLooper());
}
/**
@@ -477,6 +479,7 @@ public final class BluetoothAdapter {
/**
* Returns a {@link BluetoothAdvScanData} object representing advertising data.
+ * Data will be reset when bluetooth service is turned off.
* @hide
*/
public BluetoothAdvScanData getAdvScanData() {
@@ -497,19 +500,34 @@ public final class BluetoothAdapter {
}
}
+ /**
+ * Interface for BLE advertising callback.
+ *
+ * @hide
+ */
+ public interface AdvertiseCallback {
+ /**
+ * Callback when advertise starts.
+ * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+ */
+ void onAdvertiseStart(int status);
+ /**
+ * Callback when advertise stops.
+ * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+ */
+ void onAdvertiseStop(int status);
+ }
/**
* 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.
* Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
*
- * @return true if BLE avertising succeeds, false otherwise.
+ * @param callback - {@link AdvertiseCallback}
+ * @return true if BLE advertising succeeds, false otherwise.
* @hide
*/
- public boolean startAdvertising() {
+ public boolean startAdvertising(final AdvertiseCallback callback) {
if (getState() != STATE_ON) return false;
-
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
@@ -519,18 +537,31 @@ public final class BluetoothAdapter {
// Restart/reset advertising packets if advertising is in progress.
if (isAdvertising()) {
// Invalid advertising callback.
- if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) {
+ if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) {
Log.e(TAG, "failed to restart advertising, invalid callback");
return false;
}
- iGatt.startAdvertising(mAdvertisingCallback.mLeHandle);
+ iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle);
+ // Run the callback from main thread.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ // callback with status success.
+ callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS);
+ }
+ });
return true;
}
UUID uuid = UUID.randomUUID();
GattCallbackWrapper wrapper =
- new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV);
+ new GattCallbackWrapper(this, null, null, callback);
iGatt.registerClient(new ParcelUuid(uuid), wrapper);
- mAdvertisingCallback = wrapper;
+ if (!wrapper.advertiseStarted()) {
+ return false;
+ }
+ synchronized (mLock) {
+ mAdvertisingGattCallback = wrapper;
+ }
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -540,25 +571,29 @@ public final class BluetoothAdapter {
/**
* Stop BLE advertising.
- * An app should stop advertising by requesting
- * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly.
- *
Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
+ *
+ * @param callback - {@link AdvertiseCallback}
* @return true if BLE advertising stops, false otherwise.
* @hide
*/
- public boolean stopAdvertisting() {
+ public boolean stopAdvertising(AdvertiseCallback callback) {
try {
IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
if (iGatt == null) {
// BLE is not supported
return false;
}
- if (mAdvertisingCallback == null) {
+ if (mAdvertisingGattCallback == null) {
// no callback.
return false;
}
- mAdvertisingCallback.stopAdvertising();
- mAdvertisingCallback = null;
+ // Make sure same callback is used for start and stop advertising.
+ if (callback != mAdvertisingGattCallback.mAdvertiseCallback) {
+ Log.e(TAG, "must use the same callback for star/stop advertising");
+ return false;
+ }
+ mAdvertisingGattCallback.stopAdvertising();
+ mAdvertisingGattCallback = null;
return true;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1418,6 +1453,8 @@ public final class BluetoothAdapter {
if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
synchronized (mManagerCallback) {
mService = null;
+ // Reset bluetooth adv scan data when Gatt service is down.
+ mBluetoothAdvScanData = null;
for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
try {
if (cb != null) {
@@ -1692,11 +1729,9 @@ 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 AdvertiseCallback mAdvertiseCallback;
private final LeScanCallback mLeScanCb;
- private int mCallbackType;
// mLeHandle 0: not registered
// -1: scan stopped
@@ -1711,26 +1746,34 @@ public final class BluetoothAdapter {
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
- mCallbackType = CALLBACK_TYPE_SCAN;
+ mAdvertiseCallback = null;
}
public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
- UUID[] uuid, int type) {
+ UUID[] uuid, AdvertiseCallback callback) {
mBluetoothAdapter = new WeakReference(bluetoothAdapter);
mLeScanCb = leScanCb;
mScanFilter = uuid;
mLeHandle = 0;
- mCallbackType = type;
+ mAdvertiseCallback = callback;
}
public boolean scanStarted() {
+ return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
+ }
+
+ public boolean advertiseStarted() {
+ // Wait for registeration callback.
+ return waitForRegisteration(1);
+ }
+
+ private boolean waitForRegisteration(int maxWaitCount) {
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) {
+ while (mLeHandle == 0 && count < maxWaitCount) {
try {
wait(LE_CALLBACK_REG_TIMEOUT);
} catch (InterruptedException e) {
@@ -1754,7 +1797,7 @@ public final class BluetoothAdapter {
try {
IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
iGatt.stopAdvertising();
- Log.d(TAG, "unregeistering client " + mLeHandle);
+ Log.d(TAG, "unregistering client " + mLeHandle);
iGatt.unregisterClient(mLeHandle);
} catch (RemoteException e) {
Log.e(TAG, "Failed to stop advertising and unregister" + e);
@@ -1808,7 +1851,7 @@ public final class BluetoothAdapter {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter != null) {
iGatt = adapter.getBluetoothManager().getBluetoothGatt();
- if (mCallbackType == CALLBACK_TYPE_ADV) {
+ if (mAdvertiseCallback != null) {
iGatt.startAdvertising(mLeHandle);
} else {
if (mScanFilter == null) {
@@ -1858,7 +1901,7 @@ public final class BluetoothAdapter {
* @hide
*/
public void onScanResult(String address, int rssi, byte[] advData) {
- if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+ if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
// Check null in case the scan has been stopped
synchronized(this) {
@@ -1947,9 +1990,13 @@ public final class BluetoothAdapter {
// no op
}
- public void onListen(int status) {
- // no op
+ public void onAdvertiseStateChange(int advertiseState, int status) {
+ Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status);
+ if (advertiseState == STATE_ADVERTISE_STARTED) {
+ mAdvertiseCallback.onAdvertiseStart(status);
+ } else {
+ mAdvertiseCallback.onAdvertiseStop(status);
+ }
}
}
-
}
diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java
index a97b0a806e38f..df2c25629a3be 100644
--- a/core/java/android/bluetooth/BluetoothAdvScanData.java
+++ b/core/java/android/bluetooth/BluetoothAdvScanData.java
@@ -77,6 +77,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to set manufacturer id and data.", e);
return false;
}
}
@@ -92,6 +93,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.setAdvServiceData(serviceData);
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to set service data.", e);
return false;
}
}
@@ -103,6 +105,7 @@ public final class BluetoothAdvScanData {
try {
return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get service uuids.", e);
return null;
}
}
@@ -115,6 +118,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.getAdvManufacturerData();
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get manufacturer data.", e);
return null;
}
}
@@ -127,6 +131,7 @@ public final class BluetoothAdvScanData {
try {
return mBluetoothGatt.getAdvServiceData();
} catch (RemoteException e) {
+ Log.e(TAG, "Unable to get service data.", e);
return null;
}
}
@@ -140,7 +145,7 @@ public final class BluetoothAdvScanData {
try {
mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
} catch (RemoteException e) {
- Log.e(TAG, e.toString());
+ Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e);
}
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index cd093c5a44198..ae6ad3b4f269f 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -553,6 +553,15 @@ public final class BluetoothGatt implements BluetoothProfile {
Log.w(TAG, "Unhandled exception in callback", ex);
}
}
+
+ /**
+ * Advertise state change callback
+ * @hide
+ */
+ public void onAdvertiseStateChange(int state, int status) {
+ if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
+ + state + " status=" + status);
+ }
};
/*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index e3563fcc3a77c..7c69a066b4651 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -63,4 +63,5 @@ interface IBluetoothGattCallback {
in int charInstId, in ParcelUuid charUuid,
in byte[] value);
void onReadRemoteRssi(in String address, in int rssi, in int status);
+ oneway void onAdvertiseStateChange(in int advertiseState, in int status);
}