summaryrefslogtreecommitdiff
path: root/framework/java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/java')
-rw-r--r--framework/java/android/bluetooth/BluetoothAdapter.java232
-rwxr-xr-xframework/java/android/bluetooth/BluetoothClass.java5
-rw-r--r--framework/java/android/bluetooth/BluetoothCodecConfig.java42
-rw-r--r--framework/java/android/bluetooth/BluetoothDevice.java314
-rw-r--r--framework/java/android/bluetooth/BluetoothGatt.java188
-rw-r--r--framework/java/android/bluetooth/BluetoothGattCallback.java53
-rw-r--r--framework/java/android/bluetooth/BluetoothGattServer.java56
-rw-r--r--framework/java/android/bluetooth/BluetoothGattServerCallback.java53
-rw-r--r--framework/java/android/bluetooth/BluetoothHeadset.java190
-rw-r--r--framework/java/android/bluetooth/BluetoothLeAudio.java4
-rw-r--r--framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java167
-rw-r--r--framework/java/android/bluetooth/BluetoothProfile.java61
-rw-r--r--framework/java/android/bluetooth/BluetoothSocket.java61
-rw-r--r--framework/java/android/bluetooth/BluetoothUuid.java35
-rw-r--r--framework/java/android/bluetooth/DeviceGroup.java177
-rw-r--r--framework/java/android/bluetooth/le/AdvertisingSetParameters.java15
-rw-r--r--framework/java/android/bluetooth/le/BluetoothLeScanner.java22
-rw-r--r--framework/java/android/bluetooth/le/BluetoothLeUtils.java41
-rw-r--r--framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java3
-rw-r--r--framework/java/android/bluetooth/le/ScanFilter.java174
-rw-r--r--framework/java/android/bluetooth/le/ScanRecord.java50
-rw-r--r--framework/java/android/bluetooth/le/ScanResult.java51
-rw-r--r--framework/java/android/bluetooth/le/ScanSettings.java9
23 files changed, 1927 insertions, 76 deletions
diff --git a/framework/java/android/bluetooth/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java
index 0fdadf85eb..67515bef5c 100644
--- a/framework/java/android/bluetooth/BluetoothAdapter.java
+++ b/framework/java/android/bluetooth/BluetoothAdapter.java
@@ -86,7 +86,9 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
-
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
/**
* Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
* lets you perform fundamental Bluetooth tasks, such as initiate
@@ -1016,6 +1018,7 @@ public final class BluetoothAdapter {
*/
@RequiresNoPermission
public BluetoothDevice getRemoteDevice(String address) {
+ android.util.SeempLog.record(62);
final BluetoothDevice res = new BluetoothDevice(address);
res.setAttributionSource(mAttributionSource);
return res;
@@ -1055,6 +1058,7 @@ public final class BluetoothAdapter {
*/
@RequiresNoPermission
public BluetoothDevice getRemoteDevice(byte[] address) {
+ android.util.SeempLog.record(62);
if (address == null || address.length != 6) {
throw new IllegalArgumentException("Bluetooth address must have 6 bytes");
}
@@ -1337,6 +1341,7 @@ public final class BluetoothAdapter {
@RequiresLegacyBluetoothPermission
@RequiresNoPermission
public @AdapterState int getState() {
+ android.util.SeempLog.record(63);
int state = getStateInternal();
// Consider all internal states as OFF
@@ -1434,6 +1439,7 @@ public final class BluetoothAdapter {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean enable() {
+ android.util.SeempLog.record(56);
if (isEnabled()) {
if (DBG) {
Log.d(TAG, "enable(): BT already enabled!");
@@ -1486,6 +1492,7 @@ public final class BluetoothAdapter {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean disable() {
+ android.util.SeempLog.record(57);
try {
return mManagerService.disable(mAttributionSource, true);
} catch (RemoteException e) {
@@ -1509,6 +1516,7 @@ public final class BluetoothAdapter {
android.Manifest.permission.BLUETOOTH_PRIVILEGED,
})
public boolean disable(boolean persist) {
+ android.util.SeempLog.record(57);
try {
return mManagerService.disable(mAttributionSource, persist);
@@ -2128,6 +2136,7 @@ public final class BluetoothAdapter {
@RequiresBluetoothLocationPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
public boolean startDiscovery() {
+ android.util.SeempLog.record(58);
if (getState() != STATE_ON) {
return false;
}
@@ -2323,7 +2332,21 @@ public final class BluetoothAdapter {
return false;
}
-
+ /** @hide */
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public boolean isBroadcastActive() {
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.isBroadcastActive(mAttributionSource);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
/**
* Get the active devices for the BluetoothProfile specified
*
@@ -2750,6 +2773,13 @@ public final class BluetoothAdapter {
}
/**
+ * @hide
+ */
+ public void btCmdGetFunctionCallmap(boolean isdump) {
+ Log.d(TAG, "btCmdGetFunctionCallmap: " + isdump);
+ }
+
+ /**
* Return true if Hearing Aid Profile is supported.
*
* @return true if phone supports Hearing Aid Profile
@@ -2911,6 +2941,7 @@ public final class BluetoothAdapter {
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public Set<BluetoothDevice> getBondedDevices() {
+ android.util.SeempLog.record(61);
if (getState() != STATE_ON) {
return toDeviceSet(Arrays.asList());
}
@@ -3094,6 +3125,7 @@ public final class BluetoothAdapter {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
@SuppressLint("AndroidFrameworkRequiresPermission")
public @ConnectionState int getProfileConnectionState(int profile) {
+ android.util.SeempLog.record(64);
if (getState() != STATE_ON) {
return STATE_DISCONNECTED;
}
@@ -3378,6 +3410,7 @@ public final class BluetoothAdapter {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
throws IOException {
+ android.util.SeempLog.record(59);
return createNewRfcommSocketAndRecord(name, uuid, false, false);
}
@@ -3575,6 +3608,92 @@ public final class BluetoothAdapter {
return null;
}
+ private void closeBCProfile(BluetoothProfile proxy) {
+ Class<?> bshClass = null;
+ Method bshClose = null;
+ try {
+ bshClass = Class.forName("android.bluetooth.BluetoothSyncHelper");
+ } catch (ClassNotFoundException ex) {
+ Log.e(TAG, "no BSH: exists");
+ bshClass = null;
+ }
+ if (bshClass != null) {
+ Log.d(TAG, "Able to get BSH class handle");
+ try {
+ bshClose = bshClass.getDeclaredMethod("close", null);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "no BSH:isSupported method exists");
+ }
+ if (bshClose != null) {
+ try {
+ bshClose.invoke(proxy, null);
+ } catch(IllegalAccessException e) {
+ Log.e(TAG, "bshClose IllegalAccessException");
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "bshClose InvocationTargetException");
+ }
+ }
+ }
+ Log.d(TAG, "CloseBCProfile returns");
+ }
+
+ private boolean getBCProfile(Context context, BluetoothProfile.ServiceListener sl) {
+ boolean ret = true;
+ boolean isProfileSupported = false;
+ Class<?> bshClass = null;
+ Method bshSupported = null;
+ Constructor bshCons = null;
+ Object bshObj = null;
+ try {
+ bshClass = Class.forName("android.bluetooth.BluetoothSyncHelper");
+ } catch (ClassNotFoundException ex) {
+ Log.e(TAG, "no BSH: exists");
+ bshClass = null;
+ }
+ if (bshClass != null) {
+ Log.d(TAG, "Able to get BSH class handle");
+ try {
+ bshSupported = bshClass.getDeclaredMethod("isSupported", null);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "no BSH:isSupported method exists: gdm");
+ }
+ try {
+ bshCons =
+ bshClass.getDeclaredConstructor(
+ new Class[]{Context.class,
+ BluetoothProfile.ServiceListener.class});
+ } catch (NoSuchMethodException ex) {
+ Log.e(TAG, "bshCons: NoSuchMethodException: gdm" + ex);
+ }
+ }
+ if (bshClass != null && bshSupported != null && bshCons != null) {
+ try {
+ isProfileSupported = (boolean)bshSupported.invoke(null, null);
+ } catch(IllegalAccessException e) {
+ Log.e(TAG, "BSH:isSupported IllegalAccessException");
+ } catch (InvocationTargetException e) {
+ Log.e(TAG, "BSH:isSupported InvocationTargetException");
+ }
+ if (isProfileSupported) {
+ try {
+ bshObj = bshCons.newInstance(
+ context, sl);
+ } catch (InstantiationException ex) {
+ Log.e(TAG, "bshCons InstantiationException:" + ex);
+ } catch (IllegalAccessException ex) {
+ Log.e(TAG, "bshCons InstantiationException:" + ex);
+ } catch (InvocationTargetException ex) {
+ Log.e(TAG, "bshCons InvocationTargetException:" + ex);
+ }
+ }
+ }
+ if (bshObj == null) {
+ ret = false;
+ }
+ Log.d(TAG, "getBCService returns" + ret);
+ return ret;
+ }
+
/**
* Get the profile proxy object associated with the profile.
*
@@ -3622,6 +3741,9 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.PBAP) {
BluetoothPbap pbap = new BluetoothPbap(context, listener, this);
return true;
+ } else if (profile == BluetoothProfile.DUN) {
+ BluetoothDun dun = new BluetoothDun(context, listener);
+ return true;
} else if (profile == BluetoothProfile.HEALTH) {
Log.e(TAG, "getProfileProxy(): BluetoothHealth is deprecated");
return false;
@@ -3647,12 +3769,22 @@ public final class BluetoothAdapter {
} else if (profile == BluetoothProfile.HAP_CLIENT) {
BluetoothHapClient HapClient = new BluetoothHapClient(context, listener);
return true;
+ } else if (profile == BluetoothProfile.BROADCAST) {
+ return getBroadcastProfile(context, listener);
+ } else if (profile == BluetoothProfile.BC_PROFILE) {
+ return getBCProfile(context, listener);
} else if (profile == BluetoothProfile.HEARING_AID) {
if (isHearingAidProfileSupported()) {
BluetoothHearingAid hearingAid = new BluetoothHearingAid(context, listener, this);
return true;
}
return false;
+ } else if (profile == BluetoothProfile.GROUP_CLIENT) {
+ BluetoothDeviceGroup groupClient = new BluetoothDeviceGroup(context, listener);
+ return true;
+ } else if (profile == BluetoothProfile.VCP) {
+ BluetoothVcp vcp = new BluetoothVcp(context, listener);
+ return true;
} else if (profile == BluetoothProfile.LE_AUDIO) {
BluetoothLeAudio leAudio = new BluetoothLeAudio(context, listener, this);
return true;
@@ -3726,6 +3858,10 @@ public final class BluetoothAdapter {
BluetoothPbap pbap = (BluetoothPbap) proxy;
pbap.close();
break;
+ case BluetoothProfile.DUN:
+ BluetoothDun dun = (BluetoothDun)proxy;
+ dun.close();
+ break;
case BluetoothProfile.GATT:
BluetoothGatt gatt = (BluetoothGatt) proxy;
gatt.close();
@@ -3762,6 +3898,12 @@ public final class BluetoothAdapter {
BluetoothHapClient HapClient = (BluetoothHapClient) proxy;
HapClient.close();
break;
+ case BluetoothProfile.BROADCAST:
+ closeBroadcastProfile(proxy);
+ break;
+ case BluetoothProfile.BC_PROFILE:
+ closeBCProfile(proxy);
+ break;
case BluetoothProfile.HEARING_AID:
BluetoothHearingAid hearingAid = (BluetoothHearingAid) proxy;
hearingAid.close();
@@ -3774,6 +3916,14 @@ public final class BluetoothAdapter {
BluetoothLeBroadcast leAudioBroadcast = (BluetoothLeBroadcast) proxy;
leAudioBroadcast.close();
break;
+ case BluetoothProfile.GROUP_CLIENT:
+ BluetoothDeviceGroup groupClient = (BluetoothDeviceGroup) proxy;
+ groupClient.close();
+ break;
+ case BluetoothProfile.VCP:
+ BluetoothVcp vcp = (BluetoothVcp) proxy;
+ vcp.close();
+ break;
case BluetoothProfile.VOLUME_CONTROL:
BluetoothVolumeControl vcs = (BluetoothVolumeControl) proxy;
vcs.close();
@@ -3795,6 +3945,63 @@ public final class BluetoothAdapter {
}
}
+ private boolean getBroadcastProfile(Context context,
+ BluetoothProfile.ServiceListener listener) {
+ boolean ret = true;
+ Class<?> broadcastClass = null;
+ Constructor bcastConstructor = null;
+ Object broadcastObj = null;
+ try {
+ broadcastClass = Class.forName("android.bluetooth.BluetoothBroadcast");
+ } catch (ClassNotFoundException ex) {
+ Log.e(TAG, "no BluetoothBroadcast: exists");
+ }
+ if (broadcastClass != null) {
+ try {
+ bcastConstructor =
+ broadcastClass.getDeclaredConstructor(new Class[]{Context.class,
+ BluetoothProfile.ServiceListener.class});
+ } catch (NoSuchMethodException ex) {
+ Log.e(TAG, "bcastConstructor: NoSuchMethodException: gdm" + ex);
+ }
+ }
+ if (bcastConstructor != null) {
+ try {
+ broadcastObj = bcastConstructor.newInstance(context, listener);
+ } catch (InstantiationException | IllegalAccessException |
+ InvocationTargetException ex) {
+ ex.printStackTrace();
+ }
+ }
+ if (broadcastObj == null) {
+ return false;
+ }
+ return true;
+ }
+ private void closeBroadcastProfile(BluetoothProfile proxy) {
+ Class<?> broadcastClass = null;
+ Method broadcastClose = null;
+ try {
+ broadcastClass = Class.forName("android.bluetooth.BluetootBroadcast");
+ } catch (ClassNotFoundException ex) {
+ Log.e(TAG, "no BluetoothBroadcast: exists");
+ }
+ if (broadcastClass != null) {
+ try {
+ broadcastClose = broadcastClass.getDeclaredMethod("close", null);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "no Broadcast:close method exists");
+ }
+ if (broadcastClose != null) {
+ try {
+ broadcastClose.invoke(proxy, null);
+ } catch(IllegalAccessException | InvocationTargetException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ }
+
private static final IBluetoothManagerCallback sManagerCallback =
new IBluetoothManagerCallback.Stub() {
public void onBluetoothServiceUp(IBluetooth bluetoothService) {
@@ -3890,8 +4097,10 @@ public final class BluetoothAdapter {
try {
final SynchronousResultReceiver recv =
new SynchronousResultReceiver();
- mService.registerBluetoothConnectionCallback(mConnectionCallback,
+ if (mService != null) {
+ mService.registerBluetoothConnectionCallback(mConnectionCallback,
mAttributionSource, recv);
+ }
recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(null);
} catch (RemoteException | TimeoutException e) {
Log.e(TAG, "onBluetoothServiceUp: Failed to register bluetooth"
@@ -3918,6 +4127,7 @@ public final class BluetoothAdapter {
} finally {
l.unlock();
}
+ Log.d(TAG, "onBluetoothServiceDown: Finished sending callbacks to registered clients");
}
public void onBrEdrDown() {
@@ -4150,6 +4360,22 @@ public final class BluetoothAdapter {
}
}
+ /**
+ * @hide
+ */
+ public void unregisterAdapter() {
+ try {
+ //mServiceLock.writeLock().lock();
+ if (mManagerService != null){
+ mManagerService.unregisterAdapter(mManagerCallback);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ //mServiceLock.writeLock().unlock();
+ }
+ }
+
private Set<BluetoothDevice> toDeviceSet(List<BluetoothDevice> devices) {
Set<BluetoothDevice> deviceSet = new HashSet<BluetoothDevice>(devices);
return Collections.unmodifiableSet(deviceSet);
diff --git a/framework/java/android/bluetooth/BluetoothClass.java b/framework/java/android/bluetooth/BluetoothClass.java
index 3561eeff90..ea747e1416 100755
--- a/framework/java/android/bluetooth/BluetoothClass.java
+++ b/framework/java/android/bluetooth/BluetoothClass.java
@@ -121,6 +121,11 @@ public final class BluetoothClass implements Parcelable {
public static final int LIMITED_DISCOVERABILITY = 0x002000;
/** Represent devices LE audio service */
public static final int LE_AUDIO = 0x004000;
+ /**
+ * @hide
+ */
+ public static final int GROUP = 0x008000;
+
public static final int POSITIONING = 0x010000;
public static final int NETWORKING = 0x020000;
public static final int RENDER = 0x040000;
diff --git a/framework/java/android/bluetooth/BluetoothCodecConfig.java b/framework/java/android/bluetooth/BluetoothCodecConfig.java
index 9fc9fb3411..d1fb561107 100644
--- a/framework/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/framework/java/android/bluetooth/BluetoothCodecConfig.java
@@ -41,7 +41,11 @@ public final class BluetoothCodecConfig implements Parcelable {
@IntDef(prefix = "SOURCE_CODEC_TYPE_",
value = {SOURCE_CODEC_TYPE_SBC, SOURCE_CODEC_TYPE_AAC, SOURCE_CODEC_TYPE_APTX,
SOURCE_CODEC_TYPE_APTX_HD, SOURCE_CODEC_TYPE_LDAC, SOURCE_CODEC_TYPE_LC3,
- SOURCE_CODEC_TYPE_INVALID})
+ SOURCE_CODEC_TYPE_INVALID,
+ SOURCE_CODEC_TYPE_APTX_ADAPTIVE,
+ SOURCE_CODEC_TYPE_APTX_TWSP,
+ SOURCE_QVA_CODEC_TYPE_MAX
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface SourceCodecType {}
@@ -85,7 +89,18 @@ public final class BluetoothCodecConfig implements Parcelable {
/**
* Represents the count of valid source codec types.
*/
- private static final int SOURCE_CODEC_TYPE_MAX = 6;
+ public static final int SOURCE_CODEC_TYPE_MAX = 6;
+
+ public static final int SOURCE_CODEC_TYPE_APTX_ADAPTIVE = SOURCE_CODEC_TYPE_MAX;
+
+ public static final int SOURCE_CODEC_TYPE_APTX_TWSP = SOURCE_CODEC_TYPE_MAX + 1;
+
+ public static final int SOURCE_QVA_CODEC_TYPE_MAX = SOURCE_CODEC_TYPE_MAX + 2;
+
+ /* CELT is not an A2DP Codec and only used to fetch encoder
+ ** format for BA usecase, moving out of a2dp codec value list
+ */
+ public static final int SOURCE_CODEC_TYPE_CELT = 8;
/** @hide */
@IntDef(prefix = "CODEC_PRIORITY_", value = {
@@ -163,6 +178,14 @@ public final class BluetoothCodecConfig implements Parcelable {
*/
public static final int SAMPLE_RATE_192000 = 0x1 << 5;
+ public static final int SAMPLE_RATE_16000 = 0x1 << 6;
+
+ public static final int SAMPLE_RATE_24000 = 0x1 << 7;
+
+ public static final int SAMPLE_RATE_32000 = 0x1 << 8;
+
+ public static final int SAMPLE_RATE_8000 = 0x1 << 9;
+
/** @hide */
@IntDef(prefix = "BITS_PER_SAMPLE_", value = {
BITS_PER_SAMPLE_NONE,
@@ -218,6 +241,7 @@ public final class BluetoothCodecConfig implements Parcelable {
* Codec channel mode STEREO.
*/
public static final int CHANNEL_MODE_STEREO = 0x1 << 1;
+ public static final int CHANNEL_MODE_JOINT_STEREO = 0x1 << 2;
private final @SourceCodecType int mCodecType;
private @CodecPriority int mCodecPriority;
@@ -463,6 +487,10 @@ public final class BluetoothCodecConfig implements Parcelable {
return "LDAC";
case SOURCE_CODEC_TYPE_LC3:
return "LC3";
+ case SOURCE_CODEC_TYPE_APTX_ADAPTIVE:
+ return "aptX Adaptive";
+ case SOURCE_CODEC_TYPE_APTX_TWSP:
+ return "aptX TWS+";
case SOURCE_CODEC_TYPE_INVALID:
return "INVALID CODEC";
default:
@@ -712,9 +740,13 @@ public final class BluetoothCodecConfig implements Parcelable {
case SOURCE_CODEC_TYPE_AAC:
case SOURCE_CODEC_TYPE_LDAC:
case SOURCE_CODEC_TYPE_LC3:
- if (mCodecSpecific1 != other.mCodecSpecific1) {
- return false;
- }
+ if (mCodecSpecific1 != other.mCodecSpecific1) {
+ return false;
+ }
+ case SOURCE_CODEC_TYPE_APTX_ADAPTIVE:
+ if (other.mCodecSpecific4 > 0) {
+ return false;
+ }
default:
return true;
}
diff --git a/framework/java/android/bluetooth/BluetoothDevice.java b/framework/java/android/bluetooth/BluetoothDevice.java
index e20165f522..5ab621f627 100644
--- a/framework/java/android/bluetooth/BluetoothDevice.java
+++ b/framework/java/android/bluetooth/BluetoothDevice.java
@@ -1,4 +1,39 @@
/*
+ * Copyright (C) 2017, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -226,6 +261,18 @@ public final class BluetoothDevice implements Parcelable, Attributable {
public static final String ACTION_BOND_STATE_CHANGED =
"android.bluetooth.device.action.BOND_STATE_CHANGED";
+ /**
+ * Broadcast Action: Broadcast details of IOT device when an IOT
+ * related issue is observed.
+ * <p>Always contains the extra fields {@link #EXTRA_NAME}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * @hide
+ **/
+
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_REMOTE_ISSUE_OCCURRED =
+ "org.codeaurora.intent.bluetooth.action.REMOTE_ISSUE_OCCURRED";
+
/**
* Broadcast Action: Indicates the battery level of a remote device has
* been retrieved for the first time, or changed since the last retrieval
@@ -294,6 +341,17 @@ public final class BluetoothDevice implements Parcelable, Attributable {
public static final int BATTERY_LEVEL_BLUETOOTH_OFF = -100;
/**
+ * Broadcast Action: Indicates the remote devices are TWS plus earbuds pair.
+ * <p>Always contains the extra fields {@link #EXTRA_TWS_PLUS_DEVICE1},
+ * {@link #EXTRA_TWS_PLUS_DEVICE2}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_TWS_PLUS_DEVICE_PAIR =
+ "android.bluetooth.device.action.TWS_PLUS_DEVICE_PAIR";
+
+ /**
* Used as a Parcelable {@link BluetoothDevice} extra field in every intent
* broadcast by this class. It contains the {@link BluetoothDevice} that
* the intent applies to.
@@ -307,6 +365,77 @@ public final class BluetoothDevice implements Parcelable, Attributable {
public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
/**
+ * Used as a Parcelable {@link BluetoothQualityReport} extra field in
+ * {@link #ACTION_REMOTE_ISSUE_OCCURRED} intent. It contains the {@link BluetoothQualityReport}.
+ * @hide
+ */
+ public static final String EXTRA_BQR = "android.bluetooth.qti.extra.EXTRA_BQR";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED}
+ * intents. It contains the type of IOT issue that occurred.
+ * @hide
+ */
+ public static final String EXTRA_ISSUE_TYPE = "android.bluetooth.qti.extra.ERROR_TYPE";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the details of details of the issue.
+ * @hide
+ */
+ public static final String EXTRA_ERROR_CODE = "android.bluetooth.qti.extra.ERROR_CODE";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the SoC event mask when issue occurred.
+ * @hide
+ */
+ public static final String EXTRA_ERROR_EVENT_MASK = "android.bluetooth.qti.extra.ERROR_EVENT_MASK";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the LMP Version of IOT device.
+ * @hide
+ */
+ public static final String EXTRA_LMP_VERSION = "android.bluetooth.qti.extra.EXTRA_LMP_VERSION";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the LMP Sub Version of IOT device.
+ * @hide
+ */
+ public static final String EXTRA_LMP_SUBVER = "android.bluetooth.qti.extra.EXTRA_LMP_SUBVER";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the Manufacturer ID of IOT device.
+ * @hide
+ */
+ public static final String EXTRA_MANUFACTURER = "android.bluetooth.qti.extra.EXTRA_MANUFACTURER";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the Power level.
+ * @hide
+ */
+ public static final String EXTRA_POWER_LEVEL = "android.bluetooth.qti.extra.EXTRA_POWER_LEVEL";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the Link Quality of the connection.
+ * @hide
+ */
+ public static final String EXTRA_LINK_QUALITY = "android.bluetooth.qti.extra.EXTRA_LINK_QUALITY";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_REMOTE_ISSUE_OCCURRED} intents.
+ * It contains the coutnt of glitches occured since last broadcast.
+ * @hide
+ */
+ public static final String EXTRA_GLITCH_COUNT = "android.bluetooth.qti.extra.EXTRA_GLITCH_COUNT";
+
+
+ /**
* Used as an optional short extra field in {@link #ACTION_FOUND} intents.
* Contains the RSSI value of the remote device as reported by the
* Bluetooth hardware.
@@ -359,6 +488,22 @@ public final class BluetoothDevice implements Parcelable, Attributable {
"android.bluetooth.device.extra.LOW_LATENCY_BUFFER_SIZE";
/**
+ * Used as a String extra field in {@link #ACTION_TWS+_DEVICE_PAIR}
+ * intents. It contains the first TWS+ earbud address of pair.
+ * @hide
+ */
+ public static final String EXTRA_TWS_PLUS_DEVICE1 =
+ "android.bluetooth.device.extra.EXTRA_TWS_PLUS_DEVICE1";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_TWS+_DEVICE_PAIR}
+ * intents. It contains the second TWS+ earbud address of pair.
+ * @hide
+ */
+ public static final String EXTRA_TWS_PLUS_DEVICE2 =
+ "android.bluetooth.device.extra.EXTRA_TWS_PLUS_DEVICE2";
+
+ /**
* Indicates the remote device is not bonded (paired).
* <p>There is no shared link key with the remote device, so communication
* (if it is allowed at all) will be unauthenticated and unencrypted.
@@ -1293,10 +1438,12 @@ public final class BluetoothDevice implements Parcelable, Attributable {
/*package*/
static IBluetooth getService() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetooth tService = adapter.getBluetoothService(sStateChangeCallback);
+
synchronized (BluetoothDevice.class) {
if (sService == null) {
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- sService = adapter.getBluetoothService(sStateChangeCallback);
+ sService = tService;
}
}
return sService;
@@ -1307,9 +1454,10 @@ public final class BluetoothDevice implements Parcelable, Attributable {
public void onBluetoothServiceUp(IBluetooth bluetoothService)
throws RemoteException {
synchronized (BluetoothDevice.class) {
- if (sService == null) {
- sService = bluetoothService;
+ if (sService != null) {
+ Log.w(TAG, "sService is not NULL");
}
+ sService = bluetoothService;
}
}
@@ -1777,6 +1925,16 @@ public final class BluetoothDevice implements Parcelable, Attributable {
final boolean defaultValue = false;
if (service == null || !isBluetoothEnabled()) {
Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
+ return false;
+ }
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null &&
+ (((transport == TRANSPORT_LE || transport == TRANSPORT_AUTO)
+ && !adapter.isLeEnabled())
+ || ((transport == TRANSPORT_BREDR || transport == TRANSPORT_AUTO)
+ && !isBluetoothEnabled()))) {
+ Log.w(TAG, "creatBond() initiated in improper adapter state : " + adapter.getState()
+ + " transport = " + transport);
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else if (NULL_MAC_ADDRESS.equals(mAddress)) {
Log.e(TAG, "Unable to create bond, invalid address " + mAddress);
@@ -1823,6 +1981,25 @@ public final class BluetoothDevice implements Parcelable, Attributable {
return defaultValue;
}
+ /** @hide */
+ @UnsupportedAppUsage
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public void setBondingInitiatedLocally(boolean localInitiated) {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.w(TAG, "BT not enabled, setBondingInitiatedLocally failed");
+ return;
+ }
+ try {
+ service.setBondingInitiatedLocally(this, localInitiated, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return;
+ }
+
/**
* Cancel an in-progress bonding request started with {@link #createBond}.
*
@@ -1870,6 +2047,12 @@ public final class BluetoothDevice implements Parcelable, Attributable {
final boolean defaultValue = false;
if (service == null || !isBluetoothEnabled()) {
Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond");
+ return false;
+ }
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null && !adapter.isLeEnabled()) {
+ Log.w(TAG, "removeBond() initiated in improper adapter state : "
+ + adapter.getState());
if (DBG) log(Log.getStackTraceString(new Throwable()));
} else {
Log.i(TAG, "removeBond() for device " + getAddress()
@@ -2344,6 +2527,47 @@ public final class BluetoothDevice implements Parcelable, Attributable {
}
/**
+ * Returns whether if the device is TWS+ device.
+ *
+ * @return True if the devcie is TWS+ device.
+ * @hide
+ */
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public boolean isTwsPlusDevice() {
+ if (sService == null) {
+ Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
+ return false;
+ }
+ try {
+ return sService.isTwsPlusDevice(this, mAttributionSource);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Get the TWS+ peer address of the remote device.
+ *
+ * @return the TWS+ peer address of the remote device if available, otherwise
+ * null.
+ * @hide
+ */
+ @RequiresLegacyBluetoothPermission
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public String getTwsPlusPeerAddress() {
+ if (sService == null) {
+ Log.e(TAG, "BT not enabled. Cannot get Remote Device name");
+ return null;
+ }
+ try {
+ return sService.getTwsPlusPeerAddress(this, mAttributionSource);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
* Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
*
* @return true pin has been set false for error
@@ -3075,6 +3299,48 @@ public final class BluetoothDevice implements Parcelable, Attributable {
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport,
boolean opportunistic, int phy, Handler handler) {
+ return connectGatt(context, autoConnect, callback, transport, opportunistic,
+ phy, handler, false);
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ *
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false) or to
+ * automatically connect as soon as the remote device becomes available (true).
+ * @param transport preferred transport for GATT connections to remote dual-mode devices {@link
+ * BluetoothDevice#TRANSPORT_AUTO} or {@link BluetoothDevice#TRANSPORT_BREDR} or {@link
+ * BluetoothDevice#TRANSPORT_LE}
+ * @param opportunistic Whether this GATT client is opportunistic. An opportunistic GATT client
+ * does not hold a GATT connection. It automatically disconnects when no other GATT connections
+ * are active for the remote device.
+ * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of {@link
+ * BluetoothDevice#PHY_LE_1M_MASK}, {@link BluetoothDevice#PHY_LE_2M_MASK}, an d{@link
+ * BluetoothDevice#PHY_LE_CODED_MASK}. This option does not take effect if {@code autoConnect}
+ * is set to true.
+ * @param handler The handler to use for the callback. If {@code null}, callbacks will happen on
+ * an un-specified background thread.
+ * @param eattSupport specifies whether client app needs EATT channel for client operations.
+ * If both local and remote devices support EATT and local app asks for EATT, GATT client
+ * operations will be performed using EATT channel.
+ * If either local or remote device doesn't support EATT but local App asks for EATT, GATT
+ * client operations will be performed using unenhanced ATT channel.
+ *
+ * @return A BluetoothGatt instance. You can use BluetoothGatt to conduct GATT client
+ * operations.
+ *
+ * @throws NullPointerException if callback is null
+ *
+ * @hide
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallback callback, int transport, boolean opportunistic,
+ int phy, Handler handler, boolean eattSupport) {
if (callback == null) {
throw new NullPointerException("callback is null");
}
@@ -3091,7 +3357,7 @@ public final class BluetoothDevice implements Parcelable, Attributable {
}
BluetoothGatt gatt = new BluetoothGatt(
iGatt, this, transport, opportunistic, phy, mAttributionSource);
- gatt.connect(autoConnect, callback, handler);
+ gatt.connect(autoConnect, callback, handler, eattSupport);
return gatt;
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -3248,6 +3514,44 @@ public final class BluetoothDevice implements Parcelable, Attributable {
}
/**
+ * Returns Device type.
+ *
+ * @return device type.
+ * @hide
+ */
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+ })
+ public int getDeviceType() {
+ if (sService == null) {
+ Log.e(TAG, "getDeviceType query remote device info failed");
+ return -1;
+ }
+ try {
+ return sService.getDeviceType(this, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "getDeviceType fail ", e);
+ }
+ return -1;
+ }
+
+ /**
+ * Used as a String extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
+ * It contains the Group ID of IOT device.
+ * @hide
+ */
+ public static final String EXTRA_GROUP_ID = "android.bluetooth.qti.extra.GROUP_ID";
+
+ /**
+ * Used as a String extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
+ * It contains the IGNORE DEVICE flag of IOT device.
+ * @hide
+ */
+ public static final String EXTRA_IS_PRIVATE_ADDRESS =
+ "android.bluetooth.qti.extra.IS_PRIVATE_ADDRESS";
+
+ /**
* Enable or disable audio low latency for this {@link BluetoothDevice}.
*
* @param allowed true if low latency is allowed, false if low latency is disallowed.
diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java
index ee9c691134..edfd8ba91f 100644
--- a/framework/java/android/bluetooth/BluetoothGatt.java
+++ b/framework/java/android/bluetooth/BluetoothGatt.java
@@ -12,6 +12,41 @@
* 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.
+ *
+ * Changes from Qualcomm Innovation Center are provided under the following license:
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ *
*/
package android.bluetooth;
@@ -150,6 +185,27 @@ public final class BluetoothGatt implements BluetoothProfile {
public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
/**
+ * Connection subrate request - Balanced.
+ *
+ * @hide
+ */
+ public static final int SUBRATE_REQ_BALANCED = 0;
+
+ /**
+ * Connection subrate request - High.
+ *
+ * @hide
+ */
+ public static final int SUBRATE_REQ_HIGH = 1;
+
+ /**
+ * Connection Subrate Request - Low Power.
+ *
+ * @hide
+ */
+ public static final int SUBRATE_REQ_LOW_POWER = 2;
+
+ /**
* No authentication required.
*
* @hide
@@ -769,6 +825,34 @@ public final class BluetoothGatt implements BluetoothProfile {
}
});
}
+
+ /**
+ * Callback invoked when the given connection's subrate is changed
+ * @hide
+ */
+ @Override
+ public void onSubrateChange(String address, int subrateFactor, int latency,
+ int contNum, int timeout, int status) {
+ Log.d(TAG, "onSubrateChange() - Device=" + address
+ + " subrateFactor=" + subrateFactor + " latency=" + latency
+ + " contNum=" + contNum + " timeout=" + timeout + " status=" + status);
+
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+
+ runOrQueueCallback(new Runnable() {
+ @Override
+ public void run() {
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onSubrateChange(BluetoothGatt.this, subrateFactor, latency,
+ contNum, timeout, status);
+ }
+ }
+ });
+ }
+
};
/* package */ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device, int transport,
@@ -972,9 +1056,45 @@ public final class BluetoothGatt implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
/*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
Handler handler) {
+ return connect(autoConnect, callback, handler, false);
+ }
+
+ /**
+ * Initiate a connection to a Bluetooth GATT capable device.
+ *
+ * <p>The connection may not be established right away, but will be
+ * completed when the remote device is available. A
+ * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
+ * invoked when the connection state changes as a result of this function.
+ *
+ * <p>The autoConnect parameter determines whether to actively connect to
+ * the remote device, or rather passively scan and finalize the connection
+ * when the remote device is in range/available. Generally, the first ever
+ * connection to a device should be direct (autoConnect set to false) and
+ * subsequent connections to known devices should be invoked with the
+ * autoConnect parameter set to true.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * @param device Remote device to connect to
+ * @param autoConnect Whether to directly connect to the remote device (false) or to
+ * automatically connect as soon as the remote device becomes available (true).
+ * @param eattSupport specifies whether client app needs EATT channel for client operations.
+ * If both local and remote devices support EATT and local app asks for EATT, GATT client
+ * operations will be performed using EATT channel.
+ * If either local or remote device doesn't support EATT but local App asks for EATT, GATT
+ * client operations will be performed using unenhanced ATT channel.
+ * @return true, if the connection attempt was initiated successfully
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback,
+ Handler handler, boolean eattSupport) {
if (DBG) {
Log.d(TAG,
- "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
+ "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect
+ + ", eattSupport: " + eattSupport);
}
synchronized (mStateLock) {
if (mConnState != CONN_STATE_IDLE) {
@@ -985,7 +1105,7 @@ public final class BluetoothGatt implements BluetoothProfile {
mAutoConnect = autoConnect;
- if (!registerApp(callback, handler)) {
+ if (!registerApp(callback, handler, eattSupport)) {
synchronized (mStateLock) {
mConnState = CONN_STATE_IDLE;
}
@@ -1875,6 +1995,70 @@ public final class BluetoothGatt implements BluetoothProfile {
}
/**
+ * Request LE subrate mode.
+ *
+ * <p>This function will send a LE subrate request to the
+ * remote device.
+ *
+ * @param subrateMode Request a specific subrate mode. Must be one of {@link
+ * BluetoothGatt#SUBRATE_REQ_BALANCED}, {@link BluetoothGatt#SUBRATE_REQ_HIGH}
+ * or {@link BluetoothGatt#SUBRATE_REQ_LOW_POWER}.
+ * @throws IllegalArgumentException If the parameters are outside of their specified range.
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public boolean requestSubrateMode(int subrateMode) {
+ if (subrateMode < SUBRATE_REQ_BALANCED
+ || subrateMode > SUBRATE_REQ_LOW_POWER) {
+ throw new IllegalArgumentException(" Subrate Mode not within valid range");
+ }
+
+ Log.d(TAG, "requestsubrateMode() - params: " + subrateMode);
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.subrateModeRequest(
+ mClientIf, mDevice.getAddress(), subrateMode, mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Request a LE subrate request.
+ *
+ * <p>This function will send a LE subrate request to the remote device.
+ *
+ * @return true, if the request is send to the Bluetooth stack.
+ * @hide
+ */
+ public boolean bleSubrateRequest(int subrateMin, int subrateMax,
+ int maxLatency, int contNumber,
+ int supervisionTimeout) {
+ Log.d(TAG, "bleSubrateRequest() - subrateMin=" + subrateMin
+ + " subrateMax=" + (subrateMax)
+ + " maxLatency= " + maxLatency + "contNumber="
+ + contNumber + " supervisionTimeout=" + supervisionTimeout);
+ if (mService == null || mClientIf == 0) return false;
+
+ try {
+ mService.leSubrateRequest(mClientIf, mDevice.getAddress(),
+ subrateMin, subrateMax, maxLatency,
+ contNumber, supervisionTimeout,
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
* @throws UnsupportedOperationException
diff --git a/framework/java/android/bluetooth/BluetoothGattCallback.java b/framework/java/android/bluetooth/BluetoothGattCallback.java
index 3852d508c0..5d8b047394 100644
--- a/framework/java/android/bluetooth/BluetoothGattCallback.java
+++ b/framework/java/android/bluetooth/BluetoothGattCallback.java
@@ -12,6 +12,41 @@
* 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.
+ *
+ * Changes from Qualcomm Innovation Center are provided under the following license:
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ *
*/
package android.bluetooth;
@@ -267,4 +302,22 @@ public abstract class BluetoothGattCallback {
*/
public void onServiceChanged(@NonNull BluetoothGatt gatt) {
}
+
+ /**
+ * Callback indicating LE connection's subrate parameters have changed.
+ *
+ * @param gatt GATT client involved
+ * @param subrate factor for the LE connection.
+ * @param peripheral latency for the LE connection in number of subrated connection events.
+ * Valid range is from 0 to 499
+ * @param continuation number. Valid range is from 0 to 499.
+ * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
+ * (0.1s) to 3200 (32s)
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if LE connection subrating has been changed
+ * successfully.
+ * @hide
+ */
+ public void onSubrateChange(BluetoothGatt gatt, int subrateFactor, int latency, int contNum,
+ int timeout, int status) {
+ }
}
diff --git a/framework/java/android/bluetooth/BluetoothGattServer.java b/framework/java/android/bluetooth/BluetoothGattServer.java
index 8b84505e36..e2410aab96 100644
--- a/framework/java/android/bluetooth/BluetoothGattServer.java
+++ b/framework/java/android/bluetooth/BluetoothGattServer.java
@@ -12,6 +12,41 @@
* 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.
+ *
+ * Changes from Qualcomm Innovation Center are provided under the following license:
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ *
*/
package android.bluetooth;
@@ -389,6 +424,27 @@ public final class BluetoothGattServer implements BluetoothProfile {
}
}
+ /**
+ * Callback invoked when the given connection's subrate parameters are changed
+ * @hide
+ */
+ @Override
+ public void onSubrateChange(String address, int subrateFactor, int latency,
+ int contNum, int timeout, int status) {
+ Log.d(TAG, "onSubrateChange() - Device=" + address
+ + " subrateFactor=" + subrateFactor + " latency=" + latency
+ + " contNum=" + contNum + " timeout=" + timeout + " status=" + status);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device == null) return;
+
+ try {
+ mCallback.onSubrateChange(device, subrateFactor, latency,
+ contNum, timeout, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
};
/**
diff --git a/framework/java/android/bluetooth/BluetoothGattServerCallback.java b/framework/java/android/bluetooth/BluetoothGattServerCallback.java
index 0ead5f57e8..f07ce3e13f 100644
--- a/framework/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/framework/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -12,6 +12,41 @@
* 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.
+ *
+ * Changes from Qualcomm Innovation Center are provided under the following license:
+ *
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted (subject to the limitations in the
+ * disclaimer below) provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of Qualcomm Innovation Center, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+ * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
+ * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
+ *
*/
package android.bluetooth;
@@ -199,4 +234,22 @@ public abstract class BluetoothGattServerCallback {
int status) {
}
+ /**
+ * Callback indicating the LE connection's subrate parameters were updated.
+ *
+ * @param device The remote device involved
+ * @param subrate factor for the LE connection.
+ * @param peripheral latency for the LE connection in number of subrated connection events.
+ * Valid range is from 0 to 499.
+ * @param continuation number. Valid range is from 0 to 499.
+ * @param timeout Supervision timeout for this connection, in 10ms unit. Valid range is from 10
+ * (0.1s) to 3200 (32s)
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if LE connection subrating has been changed
+ * successfully.
+ * @hide
+ */
+ public void onSubrateChange(BluetoothDevice device, int subrateFactor, int latency, int contNum,
+ int timeout, int status) {
+ }
+
}
diff --git a/framework/java/android/bluetooth/BluetoothHeadset.java b/framework/java/android/bluetooth/BluetoothHeadset.java
index 434542a5e9..693b86608c 100644
--- a/framework/java/android/bluetooth/BluetoothHeadset.java
+++ b/framework/java/android/bluetooth/BluetoothHeadset.java
@@ -43,12 +43,14 @@ import android.os.RemoteException;
import android.util.CloseGuard;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.SynchronousResultReceiver;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.Objects;
import java.util.concurrent.TimeoutException;
@@ -277,6 +279,13 @@ public final class BluetoothHeadset implements BluetoothProfile {
public static final String VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL = "BATTERY";
/**
+ * Headset state when SCO audio is disconnecting.
+ *
+ * @hide
+ */
+ public static final int STATE_AUDIO_DISCONNECTING = 13;
+
+ /**
* Headset state when SCO audio is not connected.
* This state can be one of
* {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of
@@ -301,6 +310,41 @@ public final class BluetoothHeadset implements BluetoothProfile {
public static final int STATE_AUDIO_CONNECTED = 12;
/**
+ * Intent used to broadcast the Battery status of TWS+ devices
+ *
+ * <p>This intent will have 2 extras:
+ * <ul>
+ * <li> {@link #EXTRA_HF_TWSP_BATTERY_STATE} - Current Battey state of TWS+
+ * device. 0 for Discharging, 1 for Charging
+ * <\li>
+ * <li> {@link #EXTRA_HF_TWSP_BATTERY_LEVEL} - Current Battey charging level
+ * in percentage of TWS+ device.
+ * <\li>
+ *
+ * @hide
+ */
+ public static final String ACTION_HF_TWSP_BATTERY_STATE_CHANGED =
+ "android.bluetooth.headset.action.HF_TWSP_BATTERY_STATE_CHANGED";
+
+ /**
+ * A int extra field in {@link #EXTRA_HF_TWSP_BATTERY_STATE}
+ * intents that contains the battery state of TWS+ device
+ *
+ * @hide
+ */
+ public static final String EXTRA_HF_TWSP_BATTERY_STATE =
+ "android.bluetooth.headset.extra.HF_TWSP_BATTERY_STATE";
+
+ /**
+ * A int extra field in {@link #EXTRA_HF_TWSP_BATTERY_LEVEL}
+ * intents that contains the value of battery level in percentage for TWS+ device
+ * @hide
+ */
+ public static final String EXTRA_HF_TWSP_BATTERY_LEVEL =
+ "android.bluetooth.headset.extra.HF_TWSP_BATTERY_LEVEL";
+
+
+ /**
* Intent used to broadcast the headset's indicator status
*
* <p>This intent will have 3 extras:
@@ -351,7 +395,8 @@ public final class BluetoothHeadset implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
- private volatile IBluetoothHeadset mService;
+ private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
+ @GuardedBy("mServiceLock") private IBluetoothHeadset mService;
private final BluetoothAdapter mAdapter;
private final AttributionSource mAttributionSource;
@@ -402,7 +447,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
private boolean doBind() {
synchronized (mConnection) {
if (mService == null) {
- if (VDBG) Log.d(TAG, "Binding service...");
+ if (DBG) Log.d(TAG, "Binding service...");
try {
return mAdapter.getBluetoothManager().bindBluetoothProfileService(
BluetoothProfile.HEADSET, mConnection);
@@ -416,15 +461,17 @@ public final class BluetoothHeadset implements BluetoothProfile {
private void doUnbind() {
synchronized (mConnection) {
+ if (DBG) Log.d(TAG, "Unbinding service...");
if (mService != null) {
- if (VDBG) Log.d(TAG, "Unbinding service...");
try {
mAdapter.getBluetoothManager().unbindBluetoothProfileService(
BluetoothProfile.HEADSET, mConnection);
} catch (RemoteException e) {
Log.e(TAG, "Unable to unbind HeadsetService", e);
} finally {
+ mServiceLock.writeLock().lock();
mService = null;
+ mServiceLock.writeLock().unlock();
}
}
}
@@ -557,24 +604,29 @@ public final class BluetoothHeadset implements BluetoothProfile {
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- final IBluetoothHeadset service = mService;
- final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
- } else if (isEnabled()) {
+ try {
+ mServiceLock.readLock().lock();
+ final IBluetoothHeadset service = mService;
+ final List<BluetoothDevice> defaultValue = new ArrayList<BluetoothDevice>();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled()) {
try {
- final SynchronousResultReceiver<List<BluetoothDevice>> recv =
- new SynchronousResultReceiver();
- service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
- return Attributable.setAttributionSource(
- recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
- mAttributionSource);
- } catch (RemoteException | TimeoutException e) {
- Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+ new SynchronousResultReceiver();
+ service.getConnectedDevicesWithAttribution(mAttributionSource, recv);
+ return Attributable.setAttributionSource(
+ recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue),
+ mAttributionSource);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
}
+ return defaultValue;
+ } finally {
+ mServiceLock.readLock().unlock();
}
- return defaultValue;
}
/**
@@ -874,17 +926,23 @@ public final class BluetoothHeadset implements BluetoothProfile {
if (VDBG) log("isAudioConnected()");
final IBluetoothHeadset service = mService;
final boolean defaultValue = false;
- if (service == null) {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) log(Log.getStackTraceString(new Throwable()));
- } else if (isEnabled() && isValidDevice(device)) {
- try {
- final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
- service.isAudioConnected(device, mAttributionSource, recv);
- return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
- } catch (RemoteException | TimeoutException e) {
- Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ try {
+ mServiceLock.readLock().lock();
+ if (service == null) {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ } else if (isEnabled() && isValidDevice(device)) {
+ try {
+ final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver();
+ service.isAudioConnected(device, mAttributionSource, recv);
+ return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
+ } catch (RemoteException | TimeoutException e) {
+ Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
}
+ } finally {
+ mServiceLock.readLock().unlock();
}
return defaultValue;
}
@@ -1498,17 +1556,26 @@ public final class BluetoothHeadset implements BluetoothProfile {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
- mService = IBluetoothHeadset.Stub.asInterface(service);
- mHandler.sendMessage(mHandler.obtainMessage(
- MESSAGE_HEADSET_SERVICE_CONNECTED));
+ try {
+ mServiceLock.writeLock().lock();
+ mService = IBluetoothHeadset.Stub.asInterface(service);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MESSAGE_HEADSET_SERVICE_CONNECTED));
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
}
@Override
public void onServiceDisconnected(ComponentName className) {
if (DBG) Log.d(TAG, "Proxy object disconnected");
- doUnbind();
- mHandler.sendMessage(mHandler.obtainMessage(
- MESSAGE_HEADSET_SERVICE_DISCONNECTED));
+ try {
+ mServiceLock.writeLock().lock();
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MESSAGE_HEADSET_SERVICE_DISCONNECTED));
+ } finally {
+ mServiceLock.writeLock().unlock();
+ }
}
};
@@ -1550,4 +1617,59 @@ public final class BluetoothHeadset implements BluetoothProfile {
}
}
};
+
+ /**
+ * Notify Headset of phone state change.
+ * This is a backdoor for phone app to call BluetoothHeadset since
+ * there is currently not a good way to get precise call state change outside
+ * of phone app.
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
+ public void phoneStateChangedDsDa(int numActive, int numHeld, int callState, String number,
+ int type, String name) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
+ try {
+ service.phoneStateChangedDsDa(numActive, numHeld, callState, number, type, name,
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+
+ /**
+ * Send Headset of CLCC response
+ *
+ * @hide
+ */
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.BLUETOOTH_CONNECT,
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ })
+ public void clccResponseDsDa(int index, int direction, int status, int mode, boolean mpty,
+ String number, int type) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
+ try {
+ service.clccResponseDsDa(index, direction, status, mode, mpty, number, type,
+ mAttributionSource);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
}
diff --git a/framework/java/android/bluetooth/BluetoothLeAudio.java b/framework/java/android/bluetooth/BluetoothLeAudio.java
index 6d9fa07d51..4346dae6da 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudio.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudio.java
@@ -62,8 +62,8 @@ import java.util.concurrent.TimeoutException;
*/
public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
private static final String TAG = "BluetoothLeAudio";
- private static final boolean DBG = false;
- private static final boolean VDBG = false;
+ private static final boolean DBG = true;
+ private static final boolean VDBG = true;
private final Map<Callback, Executor> mCallbackExecutorMap = new HashMap<>();
diff --git a/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
index c91d13ffe0..314bb3d1ca 100644
--- a/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
+++ b/framework/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
@@ -39,12 +39,18 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
/** @hide */
@IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
SOURCE_CODEC_TYPE_LC3,
+ SOURCE_CODEC_TYPE_APTX_ADAPTIVE_LE,
SOURCE_CODEC_TYPE_INVALID
})
@Retention(RetentionPolicy.SOURCE)
public @interface SourceCodecType {};
public static final int SOURCE_CODEC_TYPE_LC3 = 0;
+ /**
+ * AptX Adaptive LEA Codec.
+ * @hide
+ */
+ public static final int SOURCE_CODEC_TYPE_APTX_ADAPTIVE_LE = 1;
public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
/** @hide */
@@ -74,7 +80,7 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
/** @hide */
@IntDef(flag = true, prefix = "SAMPLE_RATE_",
value = {SAMPLE_RATE_NONE, SAMPLE_RATE_8000, SAMPLE_RATE_16000, SAMPLE_RATE_24000,
- SAMPLE_RATE_32000, SAMPLE_RATE_44100, SAMPLE_RATE_48000})
+ SAMPLE_RATE_32000, SAMPLE_RATE_44100, SAMPLE_RATE_48000, SAMPLE_RATE_96000})
@Retention(RetentionPolicy.SOURCE)
public @interface SampleRate {}
@@ -118,6 +124,12 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
*/
public static final int SAMPLE_RATE_48000 = 0x01 << 7;
+ /**
+ * Codec sample rate 96000 Hz.
+ * @hide
+ */
+ public static final int SAMPLE_RATE_96000 = 0x01 << 9;
+
/** @hide */
@IntDef(flag = true, prefix = "BITS_PER_SAMPLE_",
value = {BITS_PER_SAMPLE_NONE, BITS_PER_SAMPLE_16, BITS_PER_SAMPLE_24,
@@ -178,9 +190,11 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
* Bluetooth Assigned Numbers, Generic Audio,
* Supported_Frame_Durations table
*
+ * Vendor Specific frame duration starting from bit 16
* @hide */
@IntDef(flag = true, prefix = "FRAME_DURATION_",
- value = {FRAME_DURATION_NONE, FRAME_DURATION_7500, FRAME_DURATION_10000})
+ value = {FRAME_DURATION_NONE, FRAME_DURATION_7500,
+ FRAME_DURATION_10000, FRAME_DURATION_15000})
@Retention(RetentionPolicy.SOURCE)
public @interface FrameDuration {}
@@ -199,6 +213,13 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
*/
public static final int FRAME_DURATION_10000 = 0x01 << 1;
+ /**
+ * Frame duration 15000 us.
+ * @hide
+ */
+ public static final int FRAME_DURATION_15000 = 0x01 << 16;
+
+
private final @SourceCodecType int mCodecType;
private final @CodecPriority int mCodecPriority;
private final @SampleRate int mSampleRate;
@@ -208,7 +229,10 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
private final int mOctetsPerFrame;
private final int mMinOctetsPerFrame;
private final int mMaxOctetsPerFrame;
-
+ private final long mCodecSpecific1;
+ private final long mCodecSpecific2;
+ private final long mCodecSpecific3;
+ private final long mCodecSpecific4;
/**
* Creates a new BluetoothLeAudioCodecConfig.
@@ -222,12 +246,17 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
* @param octetsPerFrame the octets per frame of this codec
* @param minOctetsPerFrame the minimum octets per frame of this codec
* @param maxOctetsPerFrame the maximum octets per frame of this codec
+ * @param codecSpecific1 the specific value 1
+ * @param codecSpecific2 the specific value 2
+ * @param codecSpecific3 the specific value 3
+ * @param codecSpecific4 the specific value 4
*/
private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType,
@CodecPriority int codecPriority, @SampleRate int sampleRate,
@BitsPerSample int bitsPerSample, @ChannelCount int channelCount,
@FrameDuration int frameDuration, int octetsPerFrame,
- int minOctetsPerFrame, int maxOctetsPerFrame) {
+ int minOctetsPerFrame, int maxOctetsPerFrame, long codecSpecific1,
+ long codecSpecific2, long codecSpecific3, long codecSpecific4) {
mCodecType = codecType;
mCodecPriority = codecPriority;
mSampleRate = sampleRate;
@@ -237,6 +266,10 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
mOctetsPerFrame = octetsPerFrame;
mMinOctetsPerFrame = minOctetsPerFrame;
mMaxOctetsPerFrame = maxOctetsPerFrame;
+ mCodecSpecific1 = codecSpecific1;
+ mCodecSpecific2 = codecSpecific2;
+ mCodecSpecific3 = codecSpecific3;
+ mCodecSpecific4 = codecSpecific4;
}
@Override
@@ -260,9 +293,14 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
int octetsPerFrame = in.readInt();
int minOctetsPerFrame = in.readInt();
int maxOctetsPerFrame = in.readInt();
+ long codecSpecific1 = in.readLong();
+ long codecSpecific2 = in.readLong();
+ long codecSpecific3 = in.readLong();
+ long codecSpecific4 = in.readLong();
return new BluetoothLeAudioCodecConfig(codecType, codecPriority, sampleRate,
bitsPerSample, channelCount, frameDuration, octetsPerFrame,
- minOctetsPerFrame, maxOctetsPerFrame);
+ minOctetsPerFrame, maxOctetsPerFrame, codecSpecific1, codecSpecific2,
+ codecSpecific3, codecSpecific4);
}
public BluetoothLeAudioCodecConfig[] newArray(int size) {
@@ -281,6 +319,10 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
out.writeInt(mOctetsPerFrame);
out.writeInt(mMinOctetsPerFrame);
out.writeInt(mMaxOctetsPerFrame);
+ out.writeLong(mCodecSpecific1);
+ out.writeLong(mCodecSpecific2);
+ out.writeLong(mCodecSpecific3);
+ out.writeLong(mCodecSpecific4);
}
@Override
@@ -290,7 +332,10 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
+ ",mBitsPerSample:" + mBitsPerSample + ",mChannelCount:" + mChannelCount
+ ",mFrameDuration:" + mFrameDuration + ",mOctetsPerFrame:" + mOctetsPerFrame
+ ",mMinOctetsPerFrame:" + mMinOctetsPerFrame
- + ",mMaxOctetsPerFrame:" + mMaxOctetsPerFrame + "}";
+ + ",mMaxOctetsPerFrame:" + mMaxOctetsPerFrame
+ + ",mCodecSpecific1:" + mCodecSpecific1 + ",mCodecSpecific2:" + mCodecSpecific2
+ + ",mCodecSpecific3:" + mCodecSpecific3 + ",mCodecSpecific4:" + mCodecSpecific4
+ + "}";
}
/**
@@ -311,6 +356,8 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
switch (mCodecType) {
case SOURCE_CODEC_TYPE_LC3:
return "LC3";
+ case SOURCE_CODEC_TYPE_APTX_ADAPTIVE_LE:
+ return "APTX_ADAPTIVE_LEA";
case SOURCE_CODEC_TYPE_INVALID:
return "INVALID CODEC";
default:
@@ -377,6 +424,46 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
return mMaxOctetsPerFrame;
}
+ /**
+ * Returns the codec specific value1.
+ * As the value and usage differ for each codec, please refer to the concerned
+ * codec specification to obtain the codec specific information.
+ * @hide
+ */
+ public long getCodecSpecific1() {
+ return mCodecSpecific1;
+ }
+
+ /**
+ * Returns the codec specific value2.
+ * As the value and usage differ for each codec, please refer to the concerned
+ * codec specification to obtain the codec specific information.
+ * @hide
+ */
+ public long getCodecSpecific2() {
+ return mCodecSpecific2;
+ }
+
+ /**
+ * Returns the codec specific value3.
+ * As the value and usage differ for each codec, please refer to the concerned
+ * codec specification to obtain the codec specific information.
+ * @hide
+ */
+ public long getCodecSpecific3() {
+ return mCodecSpecific3;
+ }
+
+ /**
+ * Returns the codec specific value4.
+ * As the value and usage differ for each codec, please refer to the concerned
+ * codec specification to obtain the codec specific information.
+ * @hide
+ */
+ public long getCodecSpecific4() {
+ return mCodecSpecific4;
+ }
+
@Override
public boolean equals(@NonNull Object o) {
if (o instanceof BluetoothLeAudioCodecConfig) {
@@ -389,7 +476,11 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
&& other.getFrameDuration() == mFrameDuration
&& other.getOctetsPerFrame() == mOctetsPerFrame
&& other.getMinOctetsPerFrame() == mMinOctetsPerFrame
- && other.getMaxOctetsPerFrame() == mMaxOctetsPerFrame);
+ && other.getMaxOctetsPerFrame() == mMaxOctetsPerFrame
+ && other.mCodecSpecific1 == mCodecSpecific1
+ && other.mCodecSpecific2 == mCodecSpecific2
+ && other.mCodecSpecific3 == mCodecSpecific3
+ && other.mCodecSpecific4 == mCodecSpecific4);
}
return false;
}
@@ -402,7 +493,8 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
public int hashCode() {
return Objects.hash(mCodecType, mCodecPriority, mSampleRate,
mBitsPerSample, mChannelCount, mFrameDuration, mOctetsPerFrame,
- mMinOctetsPerFrame, mMaxOctetsPerFrame);
+ mMinOctetsPerFrame, mMaxOctetsPerFrame, mCodecSpecific1,
+ mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
}
/**
@@ -420,6 +512,10 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
private int mOctetsPerFrame = 0;
private int mMinOctetsPerFrame = 0;
private int mMaxOctetsPerFrame = 0;
+ private long mCodecSpecific1 = 0;
+ private long mCodecSpecific2 = 0;
+ private long mCodecSpecific3 = 0;
+ private long mCodecSpecific4 = 0;
public Builder() {}
@@ -433,6 +529,10 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
mOctetsPerFrame = config.getOctetsPerFrame();
mMinOctetsPerFrame = config.getMinOctetsPerFrame();
mMaxOctetsPerFrame = config.getMaxOctetsPerFrame();
+ mCodecSpecific1 = config.getCodecSpecific1();
+ mCodecSpecific2 = config.getCodecSpecific2();
+ mCodecSpecific3 = config.getCodecSpecific3();
+ mCodecSpecific4 = config.getCodecSpecific4();
}
/**
@@ -535,13 +635,62 @@ public final class BluetoothLeAudioCodecConfig implements Parcelable {
}
/**
+ * Set the codecspecific1 for Bluetooth LE audio codec config.
+ *
+ * @param codecspecific1 of this codec
+ * @return the same Builder instance
+ * @hide
+ */
+ public @NonNull Builder setCodecSpecific1(long codecSpecific1) {
+ mCodecSpecific1 = codecSpecific1;
+ return this;
+ }
+
+ /**
+ * Set the codecspecific2 for Bluetooth LE audio codec config.
+ *
+ * @param codecspecific2 of this codec
+ * @return the same Builder instance
+ * @hide
+ */
+ public @NonNull Builder setCodecSpecific2(long codecSpecific2) {
+ mCodecSpecific2 = codecSpecific2;
+ return this;
+ }
+
+ /**
+ * Set the codecspecific3 for Bluetooth LE audio codec config.
+ *
+ * @param codecspecific3 of this codec
+ * @return the same Builder instance
+ * @hide
+ */
+ public @NonNull Builder setCodecSpecific3(long codecSpecific3) {
+ mCodecSpecific3 = codecSpecific3;
+ return this;
+ }
+
+ /**
+ * Set the codecspecific4 for Bluetooth LE audio codec config.
+ *
+ * @param codecspecific4 of this codec
+ * @return the same Builder instance
+ * @hide
+ */
+ public @NonNull Builder setCodecSpecific4(long codecSpecific4) {
+ mCodecSpecific4 = codecSpecific4;
+ return this;
+ }
+
+ /**
* Build {@link BluetoothLeAudioCodecConfig}.
* @return new BluetoothLeAudioCodecConfig built
*/
public @NonNull BluetoothLeAudioCodecConfig build() {
return new BluetoothLeAudioCodecConfig(mCodecType, mCodecPriority, mSampleRate,
mBitsPerSample, mChannelCount, mFrameDuration, mOctetsPerFrame,
- mMinOctetsPerFrame, mMaxOctetsPerFrame);
+ mMinOctetsPerFrame, mMaxOctetsPerFrame, mCodecSpecific1,
+ mCodecSpecific2, mCodecSpecific3, mCodecSpecific4);
}
}
}
diff --git a/framework/java/android/bluetooth/BluetoothProfile.java b/framework/java/android/bluetooth/BluetoothProfile.java
index 5c80a9bdde..4b8b52b6da 100644
--- a/framework/java/android/bluetooth/BluetoothProfile.java
+++ b/framework/java/android/bluetooth/BluetoothProfile.java
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2010-2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -274,12 +274,55 @@ public interface BluetoothProfile {
int BATTERY = 30;
/**
+ * DUN
+ * @hide
+ */
+ public int DUN = 31;
+
+ /**
+ * Group Operation Profile (Client Role)
+ * @hide
+ */
+ public int GROUP_CLIENT = 32;
+
+ /**
+ * Broadcast
+ * @hide
+ */
+ public int BROADCAST = 33;
+
+ /**
+ * VCP
+ * @hide
+ */
+ public static final int VCP = 34;
+
+ /**
+ * BC_PROFILE
+ * @hide
+ */
+ public static final int BC_PROFILE = 35;
+
+ /**
+ * PC_PROFILE
+ * @hide
+ */
+ public static final int PC_PROFILE = 36;
+
+ /**
+ * CC_SERVER
+ * @hide
+ */
+ public static final int CC_SERVER = 37;
+
+
+ /**
* Max profile ID. This value should be updated whenever a new profile is added to match
* the largest value assigned to a profile.
*
* @hide
*/
- int MAX_PROFILE_ID = 30;
+ int MAX_PROFILE_ID = 37;
/**
* Default priority for devices that we try to auto-connect to and
@@ -501,6 +544,20 @@ public interface BluetoothProfile {
return "LE_AUDIO_BROADCAST_ASSISTANT";
case BATTERY:
return "BATTERY";
+ case BROADCAST:
+ return "BROADCAST";
+ case VCP:
+ return "VCP";
+ case GROUP_CLIENT:
+ return "GROUP_CLIENT";
+ case DUN:
+ return "DUN";
+ case BC_PROFILE:
+ return "BC_PROFILE";
+ case PC_PROFILE:
+ return "PC_PROFILE";
+ case CC_SERVER:
+ return "CC_SERVER";
default:
return "UNKNOWN_PROFILE";
}
diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java
index bf98d97aed..c137f66cef 100644
--- a/framework/java/android/bluetooth/BluetoothSocket.java
+++ b/framework/java/android/bluetooth/BluetoothSocket.java
@@ -298,6 +298,7 @@ public final class BluetoothSocket implements Closeable {
as.mSocketOS = as.mSocket.getOutputStream();
as.mAddress = remoteAddr;
as.mDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(remoteAddr);
+ as.mPort = mPort;
return as;
}
@@ -845,5 +846,65 @@ public final class BluetoothSocket implements Closeable {
return ret;
}
+ /**
+ * setSocketOpt for the Buetooth Socket.
+ *
+ * @param optionName socket option name
+ * @param optionVal socket option value
+ * @param optionLen socket option length
+ * @return -1 on immediate error,
+ * 0 otherwise
+ * @hide
+ */
+ @UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public int setSocketOpt(int optionName, byte [] optionVal, int optionLen) throws IOException {
+ int ret = 0;
+ if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
+ IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+ if (bluetoothProxy == null) {
+ Log.e(TAG, "setSocketOpt fail, reason: bluetooth is off");
+ return -1;
+ }
+ try {
+ if(VDBG) Log.d(TAG, "setSocketOpt(), mType: " + mType + " mPort: " + mPort);
+ ret = bluetoothProxy.setSocketOpt(mType, mPort, optionName, optionVal, optionLen);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return -1;
+ }
+ return ret;
+ }
+
+ /**
+ * getSocketOpt for the Buetooth Socket.
+ *
+ * @param optionName socket option name
+ * @param optionVal socket option value
+ * @return -1 on immediate error,
+ * length of returned socket option otherwise
+ * @hide
+ */
+ @UnsupportedAppUsage
+ @RequiresBluetoothConnectPermission
+ @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+ public int getSocketOpt(int optionName, byte [] optionVal) throws IOException {
+ int ret = 0;
+ if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
+ IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+ if (bluetoothProxy == null) {
+ Log.e(TAG, "getSocketOpt fail, reason: bluetooth is off");
+ return -1;
+ }
+ try {
+ if(VDBG) Log.d(TAG, "getSocketOpt(), mType: " + mType + " mPort: " + mPort);
+ ret = bluetoothProxy.getSocketOpt(mType, mPort, optionName, optionVal);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return -1;
+ }
+ return ret;
+ }
}
diff --git a/framework/java/android/bluetooth/BluetoothUuid.java b/framework/java/android/bluetooth/BluetoothUuid.java
index 63f293ae66..77e518b1d0 100644
--- a/framework/java/android/bluetooth/BluetoothUuid.java
+++ b/framework/java/android/bluetooth/BluetoothUuid.java
@@ -378,6 +378,41 @@ public final class BluetoothUuid {
public static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_HEARINGAID_UUID =
+ ParcelUuid.fromString("00006AD2-0000-1000-8000-00805F9B34FB");
+
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_MEDIA_T_UUID =
+ ParcelUuid.fromString("00006AD0-0000-1000-8000-00805F9B34FB");
+
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_MEDIA_P_UUID =
+ ParcelUuid.fromString("00006AD1-0000-1000-8000-00805F9B34FB");
+
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_MEDIA_G_UUID =
+ ParcelUuid.fromString("00006AD3-0000-1000-8000-00805F9B34FB");
+
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_MEDIA_W_UUID =
+ ParcelUuid.fromString("2587db3c-ce70-4fc9-935f-777ab4188fd7");
+
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_VOICE_P_UUID =
+ ParcelUuid.fromString("00006AD4-0000-1000-8000-00805F9B34FB");
+
+ /** @hide */
+ @NonNull
+ public static ParcelUuid ADVANCE_VOICE_T_UUID =
+ ParcelUuid.fromString("00006AD5-0000-1000-8000-00805F9B34FB");
+
/**
* Length of bytes for 16 bit UUID
*
diff --git a/framework/java/android/bluetooth/DeviceGroup.java b/framework/java/android/bluetooth/DeviceGroup.java
new file mode 100644
index 0000000000..0dac87f3d8
--- /dev/null
+++ b/framework/java/android/bluetooth/DeviceGroup.java
@@ -0,0 +1,177 @@
+/******************************************************************************
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+package android.bluetooth;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides Device Group details.
+ *
+ * {@see BluetoothDeviceGroup}
+ * @hide
+ *
+ */
+
+public final class DeviceGroup implements Parcelable {
+ /** Identifier of the Device Group */
+ private int mGroupId;
+ /** Size of the Device Group. */
+ private int mSize;
+ /** List of all group devices {@link BluetoothDevice} */
+ private CopyOnWriteArrayList <BluetoothDevice> mGroupDevices
+ = new CopyOnWriteArrayList<BluetoothDevice>();
+ /** Primary Service UUID which has included required Device Group service*/
+ private final ParcelUuid mIncludingSrvcUUID;
+ /** Suggests whether exclusive access can be taken for this device group */
+ private final boolean mExclusiveAccessSupport;
+
+ /**
+ * Constructor.
+ * @hide
+ */
+ public DeviceGroup(int groupId, int size, List<BluetoothDevice> groupDevices,
+ ParcelUuid includingSrvcUUID, boolean exclusiveAccessSupport) {
+ mGroupId = groupId;
+ mSize = size;
+ mGroupDevices.addAll(groupDevices);
+ mIncludingSrvcUUID = includingSrvcUUID;
+ mExclusiveAccessSupport = exclusiveAccessSupport;
+ }
+
+ public DeviceGroup(Parcel in) {
+ mGroupId = in.readInt();
+ mSize = in.readInt();
+ in.readList(mGroupDevices, BluetoothDevice.class.getClassLoader());
+ mIncludingSrvcUUID = in.readParcelable(ParcelUuid.class.getClassLoader());
+ mExclusiveAccessSupport = in.readBoolean();
+ }
+
+ /**
+ * Used to retrieve identifier of the Device Group.
+ *
+ * @return Identifier of the Device Group.
+ */
+ public int getDeviceGroupId() {
+ return mGroupId;
+ }
+
+ /**
+ * Used to know total number group devices which are part of this Device Group.
+ *
+ * @return size of the Device Group
+ */
+ public int getDeviceGroupSize() {
+ return mSize;
+ }
+
+ /**
+ * Indicates total number of group devices discovered in Group Discovery procedure.
+ *
+ * @return total group devices discovered in the Device Group.
+ */
+ public int getTotalDiscoveredGroupDevices() {
+ return mGroupDevices.size();
+ }
+
+
+ /**
+ * Used to fetch group devices of the Device Group.
+ *
+ *@return List of group devices {@link BluetoothDevice} in the Device Group.
+ */
+ public List<BluetoothDevice> getDeviceGroupMembers() {
+ return mGroupDevices;
+ }
+
+ /**
+ * Suggests primary GATT service which has included this DeviceGroup Service
+ * for this device group. If remote device is part of multiple Device Groups then
+ * this uuid cant be null. If remote device is part of only one device froup
+ * then this returned parameter can be null.
+ *
+ *@return UUID of the GATT primary Service which has included this device group.
+ */
+ public ParcelUuid getIncludingServiceUUID() {
+ return mIncludingSrvcUUID;
+ }
+
+ /**
+ * Suggests whether exclusive access is supported by this Device Group.
+ *
+ * @return true, if exclusive access operation is supported by this Device Group.
+ * Otherwise, false.
+ */
+ public boolean isExclusiveAccessSupported() {
+ return mExclusiveAccessSupport;
+ }
+
+ /**
+ * Indicates whether all devices of this Device Group are discovered.
+ *
+ * @return true, if all group devices are discovered. Otherwise, false.
+ */
+ public boolean isGroupDiscoveredCompleted() {
+ return (mSize == getTotalDiscoveredGroupDevices());
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mGroupId);
+ dest.writeInt(mSize);
+ dest.writeList(mGroupDevices);
+ dest.writeParcelable(mIncludingSrvcUUID, 0);
+ dest.writeBoolean(mExclusiveAccessSupport);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<DeviceGroup> CREATOR =
+ new Parcelable.Creator<DeviceGroup>() {
+ public DeviceGroup createFromParcel(Parcel in) {
+ return new DeviceGroup(in);
+ }
+
+ public DeviceGroup[] newArray(int size) {
+ return new DeviceGroup[size];
+ }
+ };
+}
diff --git a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java
index 5c8fae6519..ecc2d7ccfe 100644
--- a/framework/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/framework/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -23,6 +23,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import android.app.ActivityThread;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -444,10 +445,20 @@ public final class AdvertisingSetParameters implements Parcelable {
* {@link AdvertisingSetParameters#TX_POWER_MEDIUM},
* or {@link AdvertisingSetParameters#TX_POWER_HIGH}.
* @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
+ * Allow tx power level to be set more than {@link AdvertisingSetParameters#TX_POWER_HIGH},
+ * if the setTxPowerLevel is invoked from com.android.bluetooth process
*/
public Builder setTxPowerLevel(int txPowerLevel) {
- if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
- throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel);
+ String packageName = ActivityThread.currentPackageName();
+ if (packageName.equals("com.android.bluetooth.services")) {
+ int maxPowerLevel = 20;
+ if (txPowerLevel < TX_POWER_MIN || txPowerLevel > maxPowerLevel) {
+ throw new IllegalArgumentException("invalid txPowerLevel " + txPowerLevel);
+ }
+ } else {
+ if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
+ throw new IllegalArgumentException("unknown txPowerLevel " + txPowerLevel);
+ }
}
mTxPowerLevel = txPowerLevel;
return this;
diff --git a/framework/java/android/bluetooth/le/BluetoothLeScanner.java b/framework/java/android/bluetooth/le/BluetoothLeScanner.java
index f5f963e21d..890c259642 100644
--- a/framework/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/framework/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -42,6 +42,7 @@ import android.util.Log;
import com.android.modules.utils.SynchronousResultReceiver;
+import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -264,6 +265,13 @@ public final class BluetoothLeScanner {
if (gatt == null) {
return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
}
+
+ if ((settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_SENSOR_ROUTING)
+ && (filters == null || filters.isEmpty())) {
+ ScanFilter filter = (new ScanFilter.Builder()).build();
+ filters = Arrays.asList(filter);
+ }
+
if (!isSettingsConfigAllowedForScan(settings)) {
return postCallbackErrorOrReturn(callback,
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
@@ -276,6 +284,10 @@ public final class BluetoothLeScanner {
return postCallbackErrorOrReturn(callback,
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
}
+ if (!isRoutingAllowedForScan(settings)) {
+ return postCallbackErrorOrReturn(callback,
+ ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
+ }
if (callback != null) {
BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
settings, workSource, callback);
@@ -675,4 +687,14 @@ public final class BluetoothLeScanner {
}
return true;
}
+
+ private boolean isRoutingAllowedForScan(ScanSettings settings) {
+ final int callbackType = settings.getCallbackType();
+
+ if (callbackType == ScanSettings.CALLBACK_TYPE_SENSOR_ROUTING
+ && settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/framework/java/android/bluetooth/le/BluetoothLeUtils.java b/framework/java/android/bluetooth/le/BluetoothLeUtils.java
index a600e7a62e..943ee534d5 100644
--- a/framework/java/android/bluetooth/le/BluetoothLeUtils.java
+++ b/framework/java/android/bluetooth/le/BluetoothLeUtils.java
@@ -91,6 +91,28 @@ public class BluetoothLeUtils {
}
/**
+ * Returns a string composed from a byte array.
+ */
+ static <T> String toString(byte[] data) {
+ if (data == null) {
+ return "null";
+ }
+ if (data.length == 0) {
+ return "{}";
+ }
+ StringBuilder buffer = new StringBuilder();
+ buffer.append('{');
+ for(int i=0; i < data.length; i++) {
+ buffer.append(data[i]);
+ if ((i+1) < data.length) {
+ buffer.append(", ");
+ }
+ }
+ buffer.append('}');
+ return buffer.toString();
+ }
+
+ /**
* Check whether two {@link SparseArray} equal.
*/
static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) {
@@ -140,6 +162,25 @@ public class BluetoothLeUtils {
}
/**
+ * Check whether two byte arrays are equal.
+ */
+ static <T> boolean equals(byte[] data, byte[] otherData) {
+ if (data == otherData) {
+ return true;
+ }
+ if (data == null || otherData == null) {
+ return false;
+ }
+ if (data.length != otherData.length) {
+ return false;
+ }
+ if (!Objects.deepEquals(data, otherData)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Ensure Bluetooth is turned on.
*
* @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not {@link
diff --git a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java
index 13b7163f30..e8e1d8737b 100644
--- a/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java
+++ b/framework/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -351,6 +351,9 @@ public final class PeriodicAdvertisingManager {
@Override
public void run() {
callback.onSyncTransferred(device, status);
+ // App can still unregister the sync until notified it's lost.
+ // Remove callback after app was notifed.
+ //mCallbackWrappers.remove(callback);
}
});
}
diff --git a/framework/java/android/bluetooth/le/ScanFilter.java b/framework/java/android/bluetooth/le/ScanFilter.java
index bf902e889a..b72e7ef31a 100644
--- a/framework/java/android/bluetooth/le/ScanFilter.java
+++ b/framework/java/android/bluetooth/le/ScanFilter.java
@@ -51,6 +51,12 @@ import java.util.UUID;
*/
public final class ScanFilter implements Parcelable {
+ /**
+ * Provide TDS data scan results for WiFi Alliance Org id
+ * @hide
+ */
+ public static final int WIFI_ALLIANCE_ORG_ID = 2;
+
@Nullable
private final String mDeviceName;
@@ -91,6 +97,15 @@ public final class ScanFilter implements Parcelable {
@Nullable
private final byte[] mAdvertisingDataMask;
+ private final int mOrgId;
+ private final int mTDSFlags;
+ private final int mTDSFlagsMask;
+ private final byte[] mWifiNANHash;
+
+ private final boolean mGroupBasedFiltering;
+
+ private static final int GROUP_DATA_LEN = 6;
+
/** @hide */
public static final ScanFilter EMPTY = new ScanFilter.Builder().build();
@@ -99,7 +114,9 @@ public final class ScanFilter implements Parcelable {
ParcelUuid serviceDataUuid, byte[] serviceData, byte[] serviceDataMask,
int manufacturerId, byte[] manufacturerData, byte[] manufacturerDataMask,
@AddressType int addressType, @Nullable byte[] irk, int advertisingDataType,
- @Nullable byte[] advertisingData, @Nullable byte[] advertisingDataMask) {
+ @Nullable byte[] advertisingData, @Nullable byte[] advertisingDataMask,
+ int orgId, int TDSFlags, int TDSFlagsMask, byte[] wifiNANHash,
+ boolean groupBasedFiltering) {
mDeviceName = name;
mServiceUuid = uuid;
mServiceUuidMask = uuidMask;
@@ -117,6 +134,11 @@ public final class ScanFilter implements Parcelable {
mAdvertisingDataType = advertisingDataType;
mAdvertisingData = advertisingData;
mAdvertisingDataMask = advertisingDataMask;
+ mOrgId = orgId;
+ mTDSFlags = TDSFlags;
+ mTDSFlagsMask = TDSFlagsMask;
+ mWifiNANHash = wifiNANHash;
+ mGroupBasedFiltering = groupBasedFiltering;
}
@Override
@@ -200,6 +222,19 @@ public final class ScanFilter implements Parcelable {
dest.writeByteArray(mAdvertisingDataMask);
}
}
+
+ dest.writeInt(mOrgId);
+ dest.writeInt(mOrgId < 0 ? 0 : 1);
+ if(mOrgId >= 0) {
+ dest.writeInt(mTDSFlags);
+ dest.writeInt(mTDSFlagsMask);
+ dest.writeInt(mWifiNANHash == null ? 0 : 1);
+ if (mWifiNANHash != null) {
+ dest.writeInt(mWifiNANHash.length);
+ dest.writeByteArray(mWifiNANHash);
+ }
+ }
+ dest.writeBoolean(mGroupBasedFiltering);
}
/**
@@ -309,6 +344,24 @@ public final class ScanFilter implements Parcelable {
advertisingDataMask);
}
+ int orgId = in.readInt();
+ if(in.readInt() == 1) {
+ int tdsFlags = in.readInt();
+ int tdsFlagsMask = in.readInt();
+ if (in.readInt() == 1) {
+ int wifiNANHashLength = in.readInt();
+ byte[] wifiNanHash = new byte[wifiNANHashLength];
+ in.readByteArray(wifiNanHash);
+ builder.setTransportDiscoveryData(orgId, tdsFlags, tdsFlagsMask,
+ wifiNanHash);
+ }
+ else {
+ builder.setTransportDiscoveryData(orgId, tdsFlags, tdsFlagsMask, null);
+ }
+ }
+
+ boolean groupBasedFiltering = in.readBoolean();
+ builder.setGroupBasedFiltering(groupBasedFiltering);
return builder.build();
}
};
@@ -405,6 +458,45 @@ public final class ScanFilter implements Parcelable {
}
/**
+ * @hide
+ * Returns the organization id. -1 if the organization id is not set.
+ */
+ public int getOrgId() {
+ return mOrgId;
+ }
+
+ /**
+ * @hide
+ * Returns the TDS flags. -1 if TDS flags is not set.
+ */
+ public int getTDSFlags() {
+ return mTDSFlags;
+ }
+
+ /**
+ * @hide
+ * Returns the TDS flags mask. -1 if TDS flags mask is not set.
+ */
+ public int getTDSFlagsMask() {
+ return mTDSFlagsMask;
+ }
+
+ /**
+ * @hide
+ */
+ public byte[] getWifiNANHash() {
+ return mWifiNANHash;
+ }
+
+ /**
+ * @hide
+ * Returns true, if Group AD Type based filtering is enabled. Otherwise, false.
+ */
+ public boolean getGroupFilteringValue() {
+ return mGroupBasedFiltering;
+ }
+
+ /**
* Returns the advertising data type of this filter.
* Returns {@link ScanRecord#DATA_TYPE_NONE} if the type is not set.
* The values of advertising data type are defined in the Bluetooth Generic Access Profile
@@ -497,6 +589,25 @@ public final class ScanFilter implements Parcelable {
}
}
+ //Transport Discovery data match
+ if(mOrgId >= 0) {
+ byte[] tdsData = scanRecord.getTDSData();
+ if ((tdsData != null) && (tdsData.length > 0)) {
+ if ((mOrgId != tdsData[0]) ||
+ ((mTDSFlags & mTDSFlagsMask) != (tdsData[1] & mTDSFlagsMask))) {
+ return false;
+ }
+ }
+ }
+
+ // Group AD Type filter match
+ if (mGroupBasedFiltering) {
+ byte [] groupIdData = scanRecord.getGroupIdentifierData();
+ if (groupIdData != null && groupIdData.length != GROUP_DATA_LEN) {
+ return false;
+ }
+ }
+
// All filters match.
return true;
}
@@ -593,7 +704,11 @@ public final class ScanFilter implements Parcelable {
+ ", mManufacturerDataMask=" + Arrays.toString(mManufacturerDataMask)
+ ", mAdvertisingDataType=" + mAdvertisingDataType + ", mAdvertisingData="
+ Arrays.toString(mAdvertisingData) + ", mAdvertisingDataMask="
- + Arrays.toString(mAdvertisingDataMask) + "]";
+ + Arrays.toString(mAdvertisingDataMask) +
+ ", mOrganizationId=" + mOrgId + ", mTDSFlags=" + mTDSFlags
+ + ", mTDSFlagsMask=" + mTDSFlagsMask
+ + ", mWifiNANHash=" + Arrays.toString(mWifiNANHash) +"]"
+ + ", mGroupBasedFiltering=" + mGroupBasedFiltering;
}
@Override
@@ -608,7 +723,10 @@ public final class ScanFilter implements Parcelable {
mServiceSolicitationUuid, mServiceSolicitationUuidMask,
mAdvertisingDataType,
Arrays.hashCode(mAdvertisingData),
- Arrays.hashCode(mAdvertisingDataMask));
+ Arrays.hashCode(mAdvertisingDataMask),
+ mServiceSolicitationUuid, mServiceSolicitationUuidMask,
+ mOrgId, mTDSFlags, mTDSFlagsMask, Arrays.hashCode(mWifiNANHash),
+ mGroupBasedFiltering);
}
@Override
@@ -635,7 +753,12 @@ public final class ScanFilter implements Parcelable {
other.mServiceSolicitationUuidMask)
&& mAdvertisingDataType == other.mAdvertisingDataType
&& Objects.deepEquals(mAdvertisingData, other.mAdvertisingData)
- && Objects.deepEquals(mAdvertisingDataMask, other.mAdvertisingDataMask);
+ && Objects.deepEquals(mAdvertisingDataMask, other.mAdvertisingDataMask)
+ && mOrgId == other.mOrgId
+ && mTDSFlags == other.mTDSFlags
+ && mTDSFlagsMask == other.mTDSFlagsMask
+ && Objects.deepEquals(mWifiNANHash, other.mWifiNANHash)
+ && mGroupBasedFiltering == other.mGroupBasedFiltering;
}
/**
@@ -681,6 +804,13 @@ public final class ScanFilter implements Parcelable {
private byte[] mAdvertisingData;
private byte[] mAdvertisingDataMask;
+ private int mOrgId = -1;
+ private int mTDSFlags = -1;
+ private int mTDSFlagsMask = -1;
+ private byte[] mWifiNANHash;
+
+ private boolean mGroupBasedFiltering;
+
/**
* Set filter on device name.
*/
@@ -987,6 +1117,38 @@ public final class ScanFilter implements Parcelable {
return this;
}
+
+ /**
+ * @hide
+ * Set filter on transport discovery data.
+ * @throws IllegalArgumentException If the {@code orgId} is invalid or {@code
+ * wifiNANhash} is not null while {@code orgId} is non-Wifi.
+ */
+ public Builder setTransportDiscoveryData(int orgId, int TDSFlags, int TDSFlagsMask,
+ byte[] wifiNANHash) {
+ if (orgId < 0) {
+ throw new IllegalArgumentException("invalid organization id");
+ }
+ if ((orgId != WIFI_ALLIANCE_ORG_ID) && (wifiNANHash != null)) {
+ throw new IllegalArgumentException("Wifi NAN Hash is not null for non-Wifi Org Id");
+ }
+ mOrgId = orgId;
+ mTDSFlags = TDSFlags;
+ mTDSFlagsMask = TDSFlagsMask;
+ mWifiNANHash = wifiNANHash;
+ return this;
+ }
+
+ /**
+ * @hide
+ * Enable filter on Group AD Type.
+ */
+ public @NonNull Builder setGroupBasedFiltering(
+ boolean enable) {
+ mGroupBasedFiltering = enable;
+ return this;
+ }
+
/**
* Set filter on advertising data with specific advertising data type.
* For any bit in the mask, set it the 1 if it needs to match the one in
@@ -1053,7 +1215,9 @@ public final class ScanFilter implements Parcelable {
mServiceSolicitationUuid, mServiceSolicitationUuidMask, mServiceDataUuid,
mServiceData, mServiceDataMask, mManufacturerId, mManufacturerData,
mManufacturerDataMask, mAddressType, mIrk, mAdvertisingDataType,
- mAdvertisingData, mAdvertisingDataMask);
+ mAdvertisingData, mAdvertisingDataMask,
+ mOrgId, mTDSFlags, mTDSFlagsMask, mWifiNANHash,
+ mGroupBasedFiltering);
}
}
}
diff --git a/framework/java/android/bluetooth/le/ScanRecord.java b/framework/java/android/bluetooth/le/ScanRecord.java
index 375df1d694..ca2fd457ad 100644
--- a/framework/java/android/bluetooth/le/ScanRecord.java
+++ b/framework/java/android/bluetooth/le/ScanRecord.java
@@ -313,6 +313,10 @@ public final class ScanRecord {
* details.
*/
public static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
+ /**
+ * @hide
+ */
+ public static int DATA_TYPE_GROUP_AD_TYPE = 0x00;
// Flags of the advertising data.
private final int mAdvertiseFlags;
@@ -337,6 +341,12 @@ public final class ScanRecord {
private final HashMap<Integer, byte[]> mAdvertisingDataMap;
+ // Transport Discovery data.
+ private final byte[] mTDSData;
+
+ // Group Identifier Data
+ private final byte[] mGroupIdentifierData;
+
/**
* Returns the advertising flags indicating the discoverable mode and capability of the device.
* Returns -1 if the flag field is not set.
@@ -431,6 +441,22 @@ public final class ScanRecord {
}
/**
+ * @hide
+ * Returns Transport Discovery data
+ */
+ public byte[] getTDSData() {
+ return mTDSData;
+ }
+
+ /**
+ * @hide
+ * Returns Group Identifier data
+ */
+ public byte[] getGroupIdentifierData() {
+ return mGroupIdentifierData;
+ }
+
+ /**
* Returns raw bytes of scan record.
*/
public byte[] getBytes() {
@@ -463,7 +489,8 @@ public final class ScanRecord {
SparseArray<byte[]> manufacturerData,
Map<ParcelUuid, byte[]> serviceData,
int advertiseFlags, int txPowerLevel,
- String localName, HashMap<Integer, byte[]> advertisingDataMap, byte[] bytes) {
+ String localName, HashMap<Integer, byte[]> advertisingDataMap,
+ byte[] tdsData, byte[] groupIdentifierData, byte[] bytes) {
mServiceSolicitationUuids = serviceSolicitationUuids;
mServiceUuids = serviceUuids;
mManufacturerSpecificData = manufacturerData;
@@ -472,6 +499,8 @@ public final class ScanRecord {
mAdvertiseFlags = advertiseFlags;
mTxPowerLevel = txPowerLevel;
mAdvertisingDataMap = advertisingDataMap;
+ mTDSData = tdsData;
+ mGroupIdentifierData = groupIdentifierData;
mBytes = bytes;
}
@@ -503,6 +532,9 @@ public final class ScanRecord {
Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();
HashMap<Integer, byte[]> advertisingDataMap = new HashMap<Integer, byte[]>();
+ byte[] tdsData = null;
+ byte[] groupIdentifierData = null;
+
try {
while (currentPos < scanRecord.length) {
// length is unsigned int.
@@ -582,8 +614,15 @@ public final class ScanRecord {
dataLength - 2);
manufacturerData.put(manufacturerId, manufacturerDataBytes);
break;
+ case DATA_TYPE_TRANSPORT_DISCOVERY_DATA:
+ tdsData = extractBytes(scanRecord, currentPos, dataLength);
+ break;
+
default:
- // Just ignore, we don't handle such data type.
+ if (fieldType == DATA_TYPE_GROUP_AD_TYPE) {
+ Log.d(TAG, "Parsing Group Identifier data");
+ groupIdentifierData = extractBytes(scanRecord, currentPos, dataLength);
+ }
break;
}
currentPos += dataLength;
@@ -594,13 +633,13 @@ public final class ScanRecord {
}
return new ScanRecord(serviceUuids, serviceSolicitationUuids, manufacturerData,
serviceData, advertiseFlag, txPowerLevel, localName, advertisingDataMap,
- scanRecord);
+ tdsData, groupIdentifierData, scanRecord);
} catch (Exception e) {
Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
// As the record is invalid, ignore all the parsed results for this packet
// and return an empty record with raw scanRecord bytes in results
return new ScanRecord(null, null, null, null, -1, Integer.MIN_VALUE, null,
- advertisingDataMap, scanRecord);
+ advertisingDataMap, null, null, scanRecord);
}
}
@@ -611,7 +650,8 @@ public final class ScanRecord {
+ ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(
mManufacturerSpecificData)
+ ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
- + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
+ + ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName +
+ ", mTDSData=" + BluetoothLeUtils.toString(mTDSData) +"]";
}
// Parse service UUIDs.
diff --git a/framework/java/android/bluetooth/le/ScanResult.java b/framework/java/android/bluetooth/le/ScanResult.java
index f437d867ea..90a429460b 100644
--- a/framework/java/android/bluetooth/le/ScanResult.java
+++ b/framework/java/android/bluetooth/le/ScanResult.java
@@ -93,6 +93,7 @@ public final class ScanResult implements Parcelable, Attributable {
private int mAdvertisingSid;
private int mTxPower;
private int mPeriodicAdvertisingInterval;
+ private int mAddressType;
/**
* Constructs a new ScanResult.
@@ -117,6 +118,7 @@ public final class ScanResult implements Parcelable, Attributable {
mAdvertisingSid = SID_NOT_PRESENT;
mTxPower = 127;
mPeriodicAdvertisingInterval = 0;
+ mAddressType = -1;
}
/**
@@ -146,6 +148,42 @@ public final class ScanResult implements Parcelable, Attributable {
mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
mScanRecord = scanRecord;
mTimestampNanos = timestampNanos;
+ mAddressType = -1;
+ }
+
+ /**
+ * Constructs a new ScanResult.
+ *
+ * @param device Remote Bluetooth device found.
+ * @param addressType addressType for the Scan result
+ * @param eventType Event type.
+ * @param primaryPhy Primary advertising phy.
+ * @param secondaryPhy Secondary advertising phy.
+ * @param advertisingSid Advertising set ID.
+ * @param txPower Transmit power.
+ * @param rssi Received signal strength.
+ * @param periodicAdvertisingInterval Periodic advertising interval.
+ * @param scanRecord Scan record including both advertising data and scan response data.
+ * @param timestampNanos Timestamp at which the scan result was observed.
+ * @param addressType addressType for the Scan result
+ *
+ *@hide
+ */
+ public ScanResult(BluetoothDevice device, int addressType, int eventType, int primaryPhy,
+ int secondaryPhy,
+ int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
+ ScanRecord scanRecord, long timestampNanos) {
+ mDevice = device;
+ mEventType = eventType;
+ mPrimaryPhy = primaryPhy;
+ mSecondaryPhy = secondaryPhy;
+ mAdvertisingSid = advertisingSid;
+ mTxPower = txPower;
+ mRssi = rssi;
+ mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
+ mScanRecord = scanRecord;
+ mTimestampNanos = timestampNanos;
+ mAddressType = addressType;
}
private ScanResult(Parcel in) {
@@ -174,6 +212,7 @@ public final class ScanResult implements Parcelable, Attributable {
dest.writeInt(mAdvertisingSid);
dest.writeInt(mTxPower);
dest.writeInt(mPeriodicAdvertisingInterval);
+ dest.writeInt(mAddressType);
}
private void readFromParcel(Parcel in) {
@@ -191,6 +230,7 @@ public final class ScanResult implements Parcelable, Attributable {
mAdvertisingSid = in.readInt();
mTxPower = in.readInt();
mPeriodicAdvertisingInterval = in.readInt();
+ mAddressType = in.readInt();
}
@Override
@@ -308,6 +348,14 @@ public final class ScanResult implements Parcelable, Attributable {
return mPeriodicAdvertisingInterval;
}
+ /**
+ *
+ *@hide
+ */
+ public int getAddressType() {
+ return mAddressType;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
@@ -333,7 +381,8 @@ public final class ScanResult implements Parcelable, Attributable {
&& mSecondaryPhy == other.mSecondaryPhy
&& mAdvertisingSid == other.mAdvertisingSid
&& mTxPower == other.mTxPower
- && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
+ && mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval
+ && mAddressType == other.mAddressType;
}
@Override
diff --git a/framework/java/android/bluetooth/le/ScanSettings.java b/framework/java/android/bluetooth/le/ScanSettings.java
index 1f9d4caec5..8f99170662 100644
--- a/framework/java/android/bluetooth/le/ScanSettings.java
+++ b/framework/java/android/bluetooth/le/ScanSettings.java
@@ -96,6 +96,12 @@ public final class ScanSettings implements Parcelable {
*/
public static final int CALLBACK_TYPE_MATCH_LOST = 4;
+ /**
+ * Provide results to sensor router instead of the apps processor
+ * @hide
+ */
+ public static final int CALLBACK_TYPE_SENSOR_ROUTING = 8;
+
/**
* Determines how many advertisements to match per filter, as this is scarce hw resource
@@ -338,7 +344,8 @@ public final class ScanSettings implements Parcelable {
private boolean isValidCallbackType(int callbackType) {
if (callbackType == CALLBACK_TYPE_ALL_MATCHES
|| callbackType == CALLBACK_TYPE_FIRST_MATCH
- || callbackType == CALLBACK_TYPE_MATCH_LOST) {
+ || callbackType == CALLBACK_TYPE_MATCH_LOST
+ || callbackType == CALLBACK_TYPE_SENSOR_ROUTING) {
return true;
}
return callbackType == (CALLBACK_TYPE_FIRST_MATCH | CALLBACK_TYPE_MATCH_LOST);