diff options
184 files changed, 3415 insertions, 2244 deletions
diff --git a/TEST_MAPPING b/TEST_MAPPING index 562f9717c8..d3205af4c9 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -136,9 +136,6 @@ "name": "net_test_performance" }, { - "name": "net_test_stack_a2dp_native" - }, - { "name": "net_test_stack_acl" }, { @@ -335,9 +332,6 @@ "name": "net_test_performance" }, { - "name": "net_test_stack_a2dp_native" - }, - { "name": "net_test_stack_acl" }, { diff --git a/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl b/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl index bcfc95585d..fa5d1362ac 100644 --- a/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl +++ b/android/app/aidl/android/bluetooth/IBluetoothA2dp.aidl @@ -53,7 +53,7 @@ interface IBluetoothA2dp { boolean isA2dpPlaying(in BluetoothDevice device, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED)") List<BluetoothCodecType> getSupportedCodecTypes(); - @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})") + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)") BluetoothCodecStatus getCodecStatus(in BluetoothDevice device, in AttributionSource attributionSource); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true)") oneway void setCodecConfigPreference(in BluetoothDevice device, in BluetoothCodecConfig codecConfig, in AttributionSource attributionSource); diff --git a/android/app/jni/com_android_bluetooth_gatt.cpp b/android/app/jni/com_android_bluetooth_gatt.cpp index 014668bfd0..704f7d7341 100644 --- a/android/app/jni/com_android_bluetooth_gatt.cpp +++ b/android/app/jni/com_android_bluetooth_gatt.cpp @@ -1524,16 +1524,6 @@ static void gattClientReadRemoteRssiNative(JNIEnv* env, jobject /* object */, ji sGattIf->client->read_remote_rssi(clientif, str2addr(env, address)); } -void set_scan_params_cmpl_cb(int client_if, uint8_t status) { - std::shared_lock<std::shared_mutex> lock(callbacks_mutex); - CallbackEnv sCallbackEnv(__func__); - if (!sCallbackEnv.valid() || !mScanCallbacksObj) { - return; - } - sCallbackEnv->CallVoidMethod(mScanCallbacksObj, method_onScanParamSetupCompleted, status, - client_if); -} - static void gattSetScanParametersNative(JNIEnv* /* env */, jobject /* object */, jint client_if, jint scan_interval_unit, jint scan_window_unit, jint scan_phy) { @@ -1541,8 +1531,7 @@ static void gattSetScanParametersNative(JNIEnv* /* env */, jobject /* object */, return; } sScanner->SetScanParameters(client_if, /* use active scan */ 0x01, scan_interval_unit, - scan_window_unit, scan_phy, - base::Bind(&set_scan_params_cmpl_cb, client_if)); + scan_window_unit, scan_phy); } void scan_filter_param_cb(uint8_t client_if, uint8_t avbl_space, uint8_t action, uint8_t status) { diff --git a/android/app/res/values/config.xml b/android/app/res/values/config.xml index 7418000e8e..0bb72b5ca8 100644 --- a/android/app/res/values/config.xml +++ b/android/app/res/values/config.xml @@ -138,12 +138,6 @@ <integer name="a2dp_source_codec_priority_lc3">6001</integer> <integer name="a2dp_source_codec_priority_opus">7001</integer> - <!-- For enabling the AVRCP Target Cover Artowrk feature--> - <bool name="avrcp_target_enable_cover_art">true</bool> - - <!-- Enable support for URI based images. Off by default due to increased memory usage --> - <bool name="avrcp_target_cover_art_uri_images">false</bool> - <!-- Package that is responsible for user interaction on pairing request, success or cancel. Receives: diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java index 136a0d4621..100076127b 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java @@ -50,7 +50,6 @@ import android.media.BluetoothProfileConnectionInfo; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.sysprop.BluetoothProperties; import android.util.Log; @@ -1515,12 +1514,14 @@ public class A2dpService extends ProfileService { @Override public BluetoothCodecStatus getCodecStatus( BluetoothDevice device, AttributionSource source) { + requireNonNull(device); A2dpService service = getServiceAndEnforceConnect(source); if (service == null) { return null; } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + Utils.enforceCdmAssociationIfNotBluetoothPrivileged( + service, service.mCompanionDeviceManager, source, device); return service.getCodecStatus(device); } @@ -1530,6 +1531,7 @@ public class A2dpService extends ProfileService { BluetoothDevice device, BluetoothCodecConfig codecConfig, AttributionSource source) { + requireNonNull(device); A2dpService service = getServiceAndEnforceConnect(source); if (service == null) { return; diff --git a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java index b04f684f34..e062814766 100644 --- a/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java +++ b/android/app/src/com/android/bluetooth/audio_util/MediaPlayerList.java @@ -784,6 +784,10 @@ public class MediaPlayerList { } } + public boolean isVfsCoverArtEnabled() { + return Util.areUriImagesSupported(); + } + /** * Adds a {@link MediaController} to the {@link #mMediaPlayers} map and returns its ID. * @@ -985,6 +989,19 @@ public class MediaPlayerList { mActivePlayerId = playerId; + if (Utils.isPtsTestMode()) { + sendFolderUpdate(true, true, false); + } else if (Flags.setAddressedPlayer() && Flags.browsingRefactor()) { + // If the browsing refactor flag is not active, addressed player should always be 0. + // If the new active player has been set by Addressed player key event + // We don't send an addressed player update. + if (mActivePlayerId != mAddressedPlayerId) { + mAddressedPlayerId = mActivePlayerId; + Log.d(TAG, "setActivePlayer AddressedPlayer changed to " + mAddressedPlayerId); + sendFolderUpdate(false, true, false); + } + } + MediaPlayerWrapper player = getActivePlayer(); if (player == null) return; @@ -1002,19 +1019,6 @@ public class MediaPlayerList { return; } - if (Utils.isPtsTestMode()) { - sendFolderUpdate(true, true, false); - } else if (Flags.setAddressedPlayer() && Flags.browsingRefactor()) { - // If the browsing refactor flag is not active, addressed player should always be 0. - // If the new active player has been set by Addressed player key event - // We don't send an addressed player update. - if (mActivePlayerId != mAddressedPlayerId) { - mAddressedPlayerId = mActivePlayerId; - Log.d(TAG, "setActivePlayer AddressedPlayer changed to " + mAddressedPlayerId); - sendFolderUpdate(false, true, false); - } - } - MediaData data = player.getCurrentMediaData(); if (mAudioPlaybackIsActive) { data.state = mCurrMediaData.state; diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java index fc106e9a95..d89a6afddf 100644 --- a/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java +++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Image.java @@ -60,7 +60,7 @@ public class Image { Bitmap bmp_album_art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); Bitmap bmp_icon = metadata.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON); - if (mContext != null && Util.areUriImagesSupported(mContext)) { + if (Util.areUriImagesSupported()) { uri_art = metadata.getString(MediaMetadata.METADATA_KEY_ART_URI); uri_album_art = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI); uri_icon = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI); @@ -92,7 +92,7 @@ public class Image { Bitmap bmp_album_art = bundle.getParcelable(MediaMetadata.METADATA_KEY_ALBUM_ART); Bitmap bmp_icon = bundle.getParcelable(MediaMetadata.METADATA_KEY_DISPLAY_ICON); - if (mContext != null && Util.areUriImagesSupported(mContext)) { + if (Util.areUriImagesSupported()) { uri_art = bundle.getString(MediaMetadata.METADATA_KEY_ART_URI); uri_album_art = bundle.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI); uri_icon = bundle.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI); diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java index 02ca4fbd5c..63791acb29 100644 --- a/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java +++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Metadata.java @@ -23,8 +23,6 @@ import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaSession; import android.os.Bundle; -import com.android.bluetooth.R; - import java.util.Objects; public class Metadata implements Cloneable { @@ -201,7 +199,7 @@ public class Metadata implements Cloneable { mMetadata.duration = "" + data.getLong(MediaMetadata.METADATA_KEY_DURATION); } if ((mContext != null - && Util.areUriImagesSupported(mContext) + && Util.areUriImagesSupported() && (data.containsKey(MediaMetadata.METADATA_KEY_ART_URI) || data.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART_URI) || data.containsKey( @@ -233,7 +231,7 @@ public class Metadata implements Cloneable { if (desc.getIconBitmap() != null) { mMetadata.image = new Image(mContext, desc.getIconBitmap()); } else if (mContext != null - && Util.areUriImagesSupported(mContext) + && Util.areUriImagesSupported() && desc.getIconUri() != null) { mMetadata.image = new Image(mContext, desc.getIconUri()); } @@ -281,7 +279,7 @@ public class Metadata implements Cloneable { mMetadata.duration = "" + bundle.getLong(MediaMetadata.METADATA_KEY_DURATION); } if ((mContext != null - && Util.areUriImagesSupported(mContext) + && Util.areUriImagesSupported() && (bundle.containsKey(MediaMetadata.METADATA_KEY_ART_URI) || bundle.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART_URI) || bundle.containsKey( @@ -296,13 +294,8 @@ public class Metadata implements Cloneable { /** Elect to use default values in the Metadata in place of any missing values */ public Builder useDefaults() { - if (mMetadata.mediaId == null) { - mMetadata.mediaId = EMPTY_MEDIA_ID; - } - if (mMetadata.title == null) { - mMetadata.title = - mContext != null ? mContext.getString(R.string.not_provided) : EMPTY_TITLE; - } + if (mMetadata.mediaId == null) mMetadata.mediaId = EMPTY_MEDIA_ID; + if (mMetadata.title == null) mMetadata.title = EMPTY_TITLE; if (mMetadata.artist == null) mMetadata.artist = EMPTY_ARTIST; if (mMetadata.album == null) mMetadata.album = EMPTY_ALBUM; if (mMetadata.trackNum == null) mMetadata.trackNum = EMPTY_TRACK_NUM; diff --git a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java index 32597edc75..877a315c88 100644 --- a/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java +++ b/android/app/src/com/android/bluetooth/audio_util/helpers/Util.java @@ -21,9 +21,10 @@ import android.content.pm.PackageManager; import android.media.MediaMetadata; import android.media.browse.MediaBrowser.MediaItem; import android.media.session.MediaSession; +import android.os.SystemProperties; import android.util.Log; -import com.android.bluetooth.R; +import androidx.annotation.VisibleForTesting; import java.util.ArrayList; import java.util.List; @@ -31,6 +32,13 @@ import java.util.List; class Util { public static String TAG = "audio_util.Util"; + private static final String VFS_COVER_ART_ENABLED_PROPERTY = + "bluetooth.profile.avrcp.target.vfs_coverart.enabled"; + + @VisibleForTesting + static Boolean sUriImagesSupport = + SystemProperties.getBoolean(VFS_COVER_ART_ENABLED_PROPERTY, false); + // TODO (apanicke): Remove this prefix later, for now it makes debugging easier. public static final String NOW_PLAYING_PREFIX = "NowPlayingId"; @@ -49,13 +57,12 @@ class Util { } /** - * Get whether or not Bluetooth is configured to support URI images or not. + * Get whether or not Bluetooth is configured to support URI images. * * <p>Note that creating URI images will dramatically increase memory usage. */ - public static boolean areUriImagesSupported(Context context) { - if (context == null) return false; - return context.getResources().getBoolean(R.bool.avrcp_target_cover_art_uri_images); + public static boolean areUriImagesSupported() { + return sUriImagesSupport.booleanValue(); } /** Translate a MediaItem to audio_util's Metadata */ diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java index 26d7d3e48d..46805f404c 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpCoverArtService.java @@ -62,8 +62,9 @@ public class AvrcpCoverArtService { // Native interface private AvrcpNativeInterface mNativeInterface; - public AvrcpCoverArtService() { - mNativeInterface = AvrcpNativeInterface.getInstance(); + // The native interface must be a parameter here in order to be able to mock AvrcpTargetService + public AvrcpCoverArtService(AvrcpNativeInterface nativeInterface) { + mNativeInterface = nativeInterface; mAcceptThread = new SocketAcceptor(); mStorage = new AvrcpCoverArtStorage(COVER_ART_STORAGE_MAX_ITEMS); } diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java index b6aa7cf8b6..068db0f024 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpTargetService.java @@ -36,8 +36,8 @@ import android.view.KeyEvent; import com.android.bluetooth.BluetoothEventLogger; import com.android.bluetooth.BluetoothMetricsProto; -import com.android.bluetooth.R; import com.android.bluetooth.a2dp.A2dpService; +import com.android.bluetooth.audio_util.ListItem; import com.android.bluetooth.audio_util.MediaData; import com.android.bluetooth.audio_util.MediaPlayerList; import com.android.bluetooth.audio_util.MediaPlayerWrapper; @@ -98,6 +98,8 @@ public class AvrcpTargetService extends ProfileService { private static AvrcpTargetService sInstance = null; + private final boolean mIsVfsCoverArtEnabled; + public AvrcpTargetService(AdapterService adapterService) { this( requireNonNull(adapterService), @@ -142,13 +144,11 @@ public class AvrcpTargetService extends ProfileService { mMediaPlayerList.init(new ListCallback()); } - if (!getResources().getBoolean(R.bool.avrcp_target_enable_cover_art)) { - mAvrcpCoverArtService = null; - } else if (!mAvrcpVersion.isAtleastVersion(AvrcpVersion.AVRCP_VERSION_1_6)) { + if (!mAvrcpVersion.isAtleastVersion(AvrcpVersion.AVRCP_VERSION_1_6)) { Log.e(TAG, "Please use AVRCP version 1.6 to enable cover art"); mAvrcpCoverArtService = null; } else { - AvrcpCoverArtService coverArtService = new AvrcpCoverArtService(); + AvrcpCoverArtService coverArtService = new AvrcpCoverArtService(mNativeInterface); if (coverArtService.start()) { mAvrcpCoverArtService = coverArtService; } else { @@ -157,6 +157,8 @@ public class AvrcpTargetService extends ProfileService { } } + mIsVfsCoverArtEnabled = mMediaPlayerList.isVfsCoverArtEnabled(); + mReceiver = new AvrcpBroadcastReceiver(); IntentFilter filter = new IntentFilter(); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); @@ -399,8 +401,7 @@ public class AvrcpTargetService extends ProfileService { Metadata getCurrentSongInfo() { Metadata metadata = mMediaPlayerList.getCurrentSongInfo(); if (mAvrcpCoverArtService != null && metadata.image != null) { - String imageHandle = mAvrcpCoverArtService.storeImage(metadata.image); - if (imageHandle != null) metadata.image.setImageHandle(imageHandle); + metadata.image.setImageHandle(mAvrcpCoverArtService.storeImage(metadata.image)); } return metadata; } @@ -433,26 +434,20 @@ public class AvrcpTargetService extends ProfileService { List<Metadata> getNowPlayingList() { String currentMediaId = getCurrentMediaId(); Metadata currentTrack = null; - String imageHandle = null; List<Metadata> nowPlayingList = mMediaPlayerList.getNowPlayingList(); if (mAvrcpCoverArtService != null) { for (Metadata metadata : nowPlayingList) { if (TextUtils.equals(metadata.mediaId, currentMediaId)) { currentTrack = metadata; } else if (metadata.image != null) { - imageHandle = mAvrcpCoverArtService.storeImage(metadata.image); - if (imageHandle != null) { - metadata.image.setImageHandle(imageHandle); - } + metadata.image.setImageHandle(mAvrcpCoverArtService.storeImage(metadata.image)); } } // Always store the current item from the queue last so we know the image is in storage if (currentTrack != null) { - imageHandle = mAvrcpCoverArtService.storeImage(currentTrack.image); - if (imageHandle != null) { - currentTrack.image.setImageHandle(imageHandle); - } + currentTrack.image.setImageHandle( + mAvrcpCoverArtService.storeImage(currentTrack.image)); } } return nowPlayingList; @@ -491,7 +486,20 @@ public class AvrcpTargetService extends ProfileService { /** See {@link MediaPlayerList#getFolderItems}. */ void getFolderItems(int playerId, String mediaId, MediaPlayerList.GetFolderItemsCallback cb) { - mMediaPlayerList.getFolderItems(playerId, mediaId, cb); + mMediaPlayerList.getFolderItems( + playerId, + mediaId, + (id, results) -> { + if (mIsVfsCoverArtEnabled && mAvrcpCoverArtService != null) { + for (ListItem item : results) { + if (item != null && item.song != null && item.song.image != null) { + item.song.image.setImageHandle( + mAvrcpCoverArtService.storeImage(item.song.image)); + } + } + } + cb.run(id, results); + }); } /** See {@link MediaPlayerList#playItem}. */ diff --git a/android/app/src/com/android/bluetooth/avrcp/helpers/AvrcpVersion.java b/android/app/src/com/android/bluetooth/avrcp/helpers/AvrcpVersion.java index a3ab9192e9..c475db1ef1 100644 --- a/android/app/src/com/android/bluetooth/avrcp/helpers/AvrcpVersion.java +++ b/android/app/src/com/android/bluetooth/avrcp/helpers/AvrcpVersion.java @@ -18,6 +18,8 @@ package com.android.bluetooth.avrcp; import android.os.SystemProperties; +import com.android.bluetooth.flags.Flags; + /** A class to represent an AVRCP version */ final class AvrcpVersion { public static final AvrcpVersion AVRCP_VERSION_1_3 = new AvrcpVersion(1, 3); @@ -36,8 +38,12 @@ final class AvrcpVersion { public int minor; public static AvrcpVersion getCurrentSystemPropertiesValue() { - // Make sure this default version agrees with avrc_api.h's "AVRC_DEFAULT_VERSION" - String version = SystemProperties.get(AVRCP_VERSION_PROPERTY, AVRCP_VERSION_1_5_STRING); + // Make sure this default version agrees with AVRC_GetProfileVersion + + String defaultVersion = + Flags.avrcp16Default() ? AVRCP_VERSION_1_6_STRING : AVRCP_VERSION_1_5_STRING; + String version = SystemProperties.get(AVRCP_VERSION_PROPERTY, defaultVersion); + switch (version) { case AVRCP_VERSION_1_3_STRING: return AVRCP_VERSION_1_3; diff --git a/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java b/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java index 95388baf78..dd720077a0 100644 --- a/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java +++ b/android/app/src/com/android/bluetooth/avrcp/helpers/CoverArt.java @@ -40,7 +40,12 @@ import java.security.NoSuchAlgorithmException; */ public class CoverArt { private static final String TAG = CoverArt.class.getSimpleName(); - private static final BipPixel PIXEL_THUMBNAIL = BipPixel.createFixed(200, 200); + + // The size in pixels of the thumbnail sides. + private static final int THUMBNAIL_SIZE = 200; + + private static final BipPixel PIXEL_THUMBNAIL = + BipPixel.createFixed(THUMBNAIL_SIZE, THUMBNAIL_SIZE); private String mImageHandle = null; private Bitmap mImage = null; @@ -50,7 +55,7 @@ public class CoverArt { // Create a scaled version of the image for now, as consumers don't need // anything larger than this at the moment. Also makes each image gathered // the same dimensions for hashing purposes. - mImage = Bitmap.createScaledBitmap(image.getImage(), 200, 200, false); + mImage = Bitmap.createScaledBitmap(image.getImage(), THUMBNAIL_SIZE, THUMBNAIL_SIZE, false); } /** @@ -133,13 +138,15 @@ public class CoverArt { BipEncoding encoding = descriptor.getEncoding(); BipPixel pixel = descriptor.getPixel(); - if (encoding.getType() == BipEncoding.JPEG && PIXEL_THUMBNAIL.equals(pixel)) { + int encodingType = encoding.getType(); + if ((encodingType == BipEncoding.JPEG || encodingType == BipEncoding.PNG) + && PIXEL_THUMBNAIL.equals(pixel)) { return true; } return false; } - /** Get the cover artwork image bytes as a 200 x 200 JPEG thumbnail */ + /** Get the cover artwork image bytes as a THUMBNAIL_SIZE x THUMBNAIL_SIZE JPEG thumbnail */ public byte[] getThumbnail() { debug("GetImageThumbnail()"); if (mImage == null) return null; @@ -160,12 +167,19 @@ public class CoverArt { return null; } BipImageProperties.Builder builder = new BipImageProperties.Builder(); - BipEncoding encoding = new BipEncoding(BipEncoding.JPEG); - BipPixel pixel = BipPixel.createFixed(200, 200); - BipImageFormat format = BipImageFormat.createNative(encoding, pixel, -1); + + BipEncoding jpgEncoding = new BipEncoding(BipEncoding.JPEG); + BipEncoding pngEncoding = new BipEncoding(BipEncoding.PNG); + BipPixel jpgPixel = BipPixel.createFixed(THUMBNAIL_SIZE, THUMBNAIL_SIZE); + BipPixel pngPixel = BipPixel.createFixed(THUMBNAIL_SIZE, THUMBNAIL_SIZE); + + BipImageFormat jpgNativeFormat = BipImageFormat.createNative(jpgEncoding, jpgPixel, -1); + BipImageFormat pngVariantFormat = + BipImageFormat.createVariant(pngEncoding, pngPixel, THUMBNAIL_SIZE, null); builder.setImageHandle(mImageHandle); - builder.addNativeFormat(format); + builder.addNativeFormat(jpgNativeFormat); + builder.addVariantFormat(pngVariantFormat); BipImageProperties properties = builder.build(); return properties; diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java index 53657b16a6..0daef0175a 100644 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java @@ -2499,6 +2499,28 @@ public class BassClientService extends ProfileService { BluetoothDevice srcDevice = getDeviceForSyncHandle(syncHandle); mSyncHandleToDeviceMap.remove(syncHandle); int broadcastId = getBroadcastIdForSyncHandle(syncHandle); + if (leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) { + synchronized (mPendingSourcesToAdd) { + Iterator<AddSourceData> iterator = mPendingSourcesToAdd.iterator(); + while (iterator.hasNext()) { + AddSourceData pendingSourcesToAdd = iterator.next(); + if (pendingSourcesToAdd.mSourceMetadata.getBroadcastId() == broadcastId) { + iterator.remove(); + } + } + } + synchronized (mSinksWaitingForPast) { + Iterator<Map.Entry<BluetoothDevice, Pair<Integer, Integer>>> iterator = + mSinksWaitingForPast.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry<BluetoothDevice, Pair<Integer, Integer>> entry = iterator.next(); + int broadcastIdForPast = entry.getValue().first; + if (broadcastId == broadcastIdForPast) { + iterator.remove(); + } + } + } + } mSyncHandleToBroadcastIdMap.remove(syncHandle); if (srcDevice != null) { mPeriodicAdvertisementResultMap.get(srcDevice).remove(broadcastId); @@ -4054,7 +4076,10 @@ public class BassClientService extends ProfileService { mUnicastSourceStreamStatus = Optional.of(status); if (status == STATUS_LOCAL_STREAM_REQUESTED) { - if (areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices())) { + if ((leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator() + && hasPrimaryDeviceManagedExternalBroadcast()) + || (!leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator() + && areReceiversReceivingOnlyExternalBroadcast(getConnectedDevices()))) { if (leaudioBroadcastAssistantPeripheralEntrustment()) { cacheSuspendingSources(BassConstants.INVALID_BROADCAST_ID); List<Pair<BluetoothLeBroadcastReceiveState, BluetoothDevice>> sourcesToStop = @@ -4067,11 +4092,13 @@ public class BassClientService extends ProfileService { suspendAllReceiversSourceSynchronization(); } } - for (Map.Entry<Integer, PauseType> entry : mPausedBroadcastIds.entrySet()) { - Integer broadcastId = entry.getKey(); - PauseType pauseType = entry.getValue(); - if (pauseType != PauseType.HOST_INTENTIONAL) { - suspendReceiversSourceSynchronization(broadcastId); + if (!leaudioMonitorUnicastSourceWhenManagedByBroadcastDelegator()) { + for (Map.Entry<Integer, PauseType> entry : mPausedBroadcastIds.entrySet()) { + Integer broadcastId = entry.getKey(); + PauseType pauseType = entry.getValue(); + if (pauseType != PauseType.HOST_INTENTIONAL) { + suspendReceiversSourceSynchronization(broadcastId); + } } } } else if (status == STATUS_LOCAL_STREAM_SUSPENDED) { @@ -4235,6 +4262,16 @@ public class BassClientService extends ProfileService { return activeSinks; } + /** Get sink devices synced to the broadcasts by broadcast id */ + public List<BluetoothDevice> getSyncedBroadcastSinks(int broadcastId) { + return getConnectedDevices().stream() + .filter( + device -> + getAllSources(device).stream() + .anyMatch(rs -> rs.getBroadcastId() == broadcastId)) + .toList(); + } + private boolean isSyncedToBroadcastStream(Long syncState) { return syncState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_NOT_SYNC_TO_BIS && syncState != BassConstants.BCAST_RCVR_STATE_BIS_SYNC_FAILED_SYNC_TO_BIG; diff --git a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java index 981b2db0c7..9ebe10d181 100644 --- a/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java +++ b/android/app/src/com/android/bluetooth/btservice/AbstractionLayer.java @@ -50,6 +50,7 @@ public final class AbstractionLayer { static final int BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID = 0X16; static final int BT_PROPERTY_REMOTE_MODEL_NUM = 0x17; static final int BT_PROPERTY_LPP_OFFLOAD_FEATURES = 0x1B; + static final int BT_PROPERTY_UUIDS_LE = 0x1C; public static final int BT_DEVICE_TYPE_BREDR = 0x01; public static final int BT_DEVICE_TYPE_BLE = 0x02; diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index c7ce8669bc..cba0af9ae6 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -28,6 +28,7 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERA import static android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE; import static android.bluetooth.BluetoothDevice.BATTERY_LEVEL_UNKNOWN; import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO; +import static android.bluetooth.BluetoothProfile.getProfileName; import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.SECOND_IN_MILLIS; @@ -183,6 +184,7 @@ import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.time.Duration; +import java.time.Instant; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; @@ -1553,46 +1555,45 @@ public class AdapterService extends Service { @VisibleForTesting void setProfileServiceState(int profileId, int state) { + Instant start = Instant.now(); + String logHdr = "setProfileServiceState(" + getProfileName(profileId) + ", " + state + "):"; + if (state == BluetoothAdapter.STATE_ON) { - if (!mStartedProfiles.containsKey(profileId)) { - ProfileService profileService = PROFILE_CONSTRUCTORS.get(profileId).apply(this); - mStartedProfiles.put(profileId, profileService); - addProfile(profileService); - profileService.start(); - profileService.setAvailable(true); - // With `Flags.scanManagerRefactor()` GattService initialization is pushed back to - // `ON` state instead of `BLE_ON`. Here we ensure mGattService is set prior - // to other Profiles using it. - if (profileId == BluetoothProfile.GATT && Flags.scanManagerRefactor()) { - mGattService = GattService.getGattService(); - } - onProfileServiceStateChanged(profileService, BluetoothAdapter.STATE_ON); - } else { - Log.e( - TAG, - "setProfileServiceState(" - + BluetoothProfile.getProfileName(profileId) - + ", STATE_ON): profile is already started"); + if (mStartedProfiles.containsKey(profileId)) { + Log.wtf(TAG, logHdr + " profile is already started"); + return; } + Log.d(TAG, logHdr + " starting profile"); + ProfileService profileService = PROFILE_CONSTRUCTORS.get(profileId).apply(this); + mStartedProfiles.put(profileId, profileService); + addProfile(profileService); + profileService.start(); + profileService.setAvailable(true); + // With `Flags.scanManagerRefactor()` GattService initialization is pushed back to + // `ON` state instead of `BLE_ON`. Here we ensure mGattService is set prior + // to other Profiles using it. + if (profileId == BluetoothProfile.GATT && Flags.scanManagerRefactor()) { + mGattService = GattService.getGattService(); + } + onProfileServiceStateChanged(profileService, BluetoothAdapter.STATE_ON); } else if (state == BluetoothAdapter.STATE_OFF) { ProfileService profileService = mStartedProfiles.remove(profileId); - if (profileService != null) { - profileService.setAvailable(false); - onProfileServiceStateChanged(profileService, BluetoothAdapter.STATE_OFF); - profileService.stop(); - removeProfile(profileService); - profileService.cleanup(); - if (profileService.getBinder() != null) { - profileService.getBinder().cleanup(); - } - } else { - Log.e( - TAG, - "setProfileServiceState(" - + BluetoothProfile.getProfileName(profileId) - + ", STATE_OFF): profile is already stopped"); + if (profileService == null) { + Log.wtf(TAG, logHdr + " profile is already stopped"); + return; + } + Log.d(TAG, logHdr + " stopping profile"); + profileService.setAvailable(false); + onProfileServiceStateChanged(profileService, BluetoothAdapter.STATE_OFF); + profileService.stop(); + removeProfile(profileService); + profileService.cleanup(); + if (profileService.getBinder() != null) { + profileService.getBinder().cleanup(); } } + Instant end = Instant.now(); + Log.d(TAG, logHdr + " completed in " + Duration.between(start, end).toMillis() + "ms"); } private void setAllProfileServiceStates(int[] profileIds, int state) { diff --git a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java index 475da0dba1..47a5d9991f 100644 --- a/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java +++ b/android/app/src/com/android/bluetooth/btservice/PhonePolicy.java @@ -274,6 +274,27 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback { && hap.getConnectionPolicy(device) != CONNECTION_POLICY_FORBIDDEN; } + private boolean shouldBlockBroadcastForHapDevice(BluetoothDevice device, ParcelUuid[] uuids) { + if (!Flags.leaudioDisableBroadcastForHapDevice()) { + Log.i(TAG, "disableBroadcastForHapDevice: Flag is disabled"); + return false; + } + + HapClientService hap = mFactory.getHapClientService(); + if (hap == null) { + Log.e(TAG, "shouldBlockBroadcastForHapDevice: No HapClientService"); + return false; + } + + if (!SystemProperties.getBoolean(SYSPROP_HAP_ENABLED, true)) { + Log.i(TAG, "shouldBlockBroadcastForHapDevice: SystemProperty is overridden to false"); + return false; + } + + return Utils.arrayContains(uuids, BluetoothUuid.HAS) + && hap.getConnectionPolicy(device) == CONNECTION_POLICY_ALLOWED; + } + // Policy implementation, all functions MUST be private private void processInitProfilePriorities(BluetoothDevice device, ParcelUuid[] uuids) { String log = "processInitProfilePriorities(" + device + "): "; @@ -518,7 +539,7 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback { if ((bcService != null) && Utils.arrayContains(uuids, BluetoothUuid.BASS) && (bcService.getConnectionPolicy(device) == CONNECTION_POLICY_UNKNOWN)) { - if (isLeAudioProfileAllowed) { + if (isLeAudioProfileAllowed && !shouldBlockBroadcastForHapDevice(device, uuids)) { Log.d(TAG, log + "Setting BASS priority"); if (mAutoConnectProfilesSupported) { bcService.setConnectionPolicy(device, CONNECTION_POLICY_ALLOWED); @@ -531,7 +552,7 @@ public class PhonePolicy implements AdapterService.BluetoothStateCallback { CONNECTION_POLICY_ALLOWED); } } else { - Log.d(TAG, log + "LE_AUDIO is not allowed: Clear BASS priority"); + Log.d(TAG, log + "LE_AUDIO Broadcast is not allowed: Clear BASS priority"); mAdapterService .getDatabase() .setProfileConnectionPolicy( diff --git a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java index f0bad17e64..f297d905ac 100644 --- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -345,7 +345,8 @@ public class RemoteDevices { private String mModelName; @VisibleForTesting int mBondState; @VisibleForTesting int mDeviceType; - @VisibleForTesting ParcelUuid[] mUuids; + @VisibleForTesting ParcelUuid[] mUuidsBrEdr; + @VisibleForTesting ParcelUuid[] mUuidsLe; private BluetoothSinkAudioPolicy mAudioPolicy; DeviceProperties() { @@ -506,20 +507,71 @@ public class RemoteDevices { } /** - * @return the mUuids + * @return the UUIDs on LE and Classic transport */ ParcelUuid[] getUuids() { synchronized (mObject) { - return mUuids; + /* When we bond dual mode device, and discover LE and Classic services, stack would + * return LE and Classic UUIDs separately, but Java apps expect them merged. + */ + int combinedUuidsLength = + (mUuidsBrEdr != null ? mUuidsBrEdr.length : 0) + + (mUuidsLe != null ? mUuidsLe.length : 0); + if (!Flags.separateServiceStorage() || combinedUuidsLength == 0) { + return mUuidsBrEdr; + } + + java.util.LinkedHashSet<ParcelUuid> result = + new java.util.LinkedHashSet<ParcelUuid>(); + if (mUuidsBrEdr != null) { + for (ParcelUuid uuid : mUuidsBrEdr) { + result.add(uuid); + } + } + + if (mUuidsLe != null) { + for (ParcelUuid uuid : mUuidsLe) { + result.add(uuid); + } + } + + return result.toArray(new ParcelUuid[combinedUuidsLength]); + } + } + + /** + * @return just classic transport UUIDS + */ + ParcelUuid[] getUuidsBrEdr() { + synchronized (mObject) { + return mUuidsBrEdr; + } + } + + /** + * @param uuids the mUuidsBrEdr to set + */ + void setUuidsBrEdr(ParcelUuid[] uuids) { + synchronized (mObject) { + this.mUuidsBrEdr = uuids; + } + } + + /** + * @return the mUuidsLe + */ + ParcelUuid[] getUuidsLe() { + synchronized (mObject) { + return mUuidsLe; } } /** - * @param uuids the mUuids to set + * @param uuids the mUuidsLe to set */ - void setUuids(ParcelUuid[] uuids) { + void setUuidsLe(ParcelUuid[] uuids) { synchronized (mObject) { - this.mUuids = uuids; + this.mUuidsLe = uuids; } } @@ -636,7 +688,8 @@ public class RemoteDevices { cachedBluetoothDevice issued a connect using the local cached copy of uuids, without waiting for the ACTION_UUID intent. This was resulting in multiple calls to connect().*/ - mUuids = null; + mUuidsBrEdr = null; + mUuidsLe = null; mAlias = null; } } @@ -988,147 +1041,168 @@ public class RemoteDevices { return; } + boolean uuids_updated = false; + for (int j = 0; j < types.length; j++) { type = types[j]; val = values[j]; - if (val.length > 0) { - synchronized (mObject) { - debugLog("Update property, device=" + bdDevice + ", type: " + type); - switch (type) { - case AbstractionLayer.BT_PROPERTY_BDNAME: - final String newName = new String(val); - if (newName.equals(deviceProperties.getName())) { - debugLog("Skip name update for " + bdDevice); - break; - } - deviceProperties.setName(newName); - List<String> wordBreakdownList = - MetricsLogger.getInstance().getWordBreakdownList(newName); - if (SdkLevel.isAtLeastU()) { - MetricsLogger.getInstance() - .uploadRestrictedBluetothDeviceName(wordBreakdownList); - } - intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); - intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName()); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mAdapterService.sendBroadcast( - intent, - BLUETOOTH_CONNECT, - Utils.getTempBroadcastOptions().toBundle()); - debugLog("Remote device name is: " + deviceProperties.getName()); - break; - case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: - deviceProperties.setAlias(bdDevice, new String(val)); - debugLog("Remote device alias is: " + deviceProperties.getAlias()); - break; - case AbstractionLayer.BT_PROPERTY_BDADDR: - deviceProperties.setAddress(val); - debugLog( - "Remote Address is:" - + Utils.getRedactedAddressStringFromByte(val)); + if (val.length == 0) { + continue; + } + + synchronized (mObject) { + debugLog("Update property, device=" + bdDevice + ", type: " + type); + switch (type) { + case AbstractionLayer.BT_PROPERTY_BDNAME: + final String newName = new String(val); + if (newName.equals(deviceProperties.getName())) { + debugLog("Skip name update for " + bdDevice); break; - case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: - final int newBluetoothClass = Utils.byteArrayToInt(val); - if (newBluetoothClass == deviceProperties.getBluetoothClass()) { - debugLog( - "Skip class update, device=" - + bdDevice - + ", cod=0x" - + Integer.toHexString(newBluetoothClass)); - break; - } - deviceProperties.setBluetoothClass(newBluetoothClass); - intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED); - intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); - intent.putExtra( - BluetoothDevice.EXTRA_CLASS, - new BluetoothClass(deviceProperties.getBluetoothClass())); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mAdapterService.sendBroadcast( - intent, - BLUETOOTH_CONNECT, - Utils.getTempBroadcastOptions().toBundle()); + } + deviceProperties.setName(newName); + List<String> wordBreakdownList = + MetricsLogger.getInstance().getWordBreakdownList(newName); + if (SdkLevel.isAtLeastU()) { + MetricsLogger.getInstance() + .uploadRestrictedBluetothDeviceName(wordBreakdownList); + } + intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); + intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName()); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mAdapterService.sendBroadcast( + intent, + BLUETOOTH_CONNECT, + Utils.getTempBroadcastOptions().toBundle()); + debugLog("Remote device name is: " + deviceProperties.getName()); + break; + case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: + deviceProperties.setAlias(bdDevice, new String(val)); + debugLog("Remote device alias is: " + deviceProperties.getAlias()); + break; + case AbstractionLayer.BT_PROPERTY_BDADDR: + deviceProperties.setAddress(val); + debugLog( + "Remote Address is:" + Utils.getRedactedAddressStringFromByte(val)); + break; + case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: + final int newBluetoothClass = Utils.byteArrayToInt(val); + if (newBluetoothClass == deviceProperties.getBluetoothClass()) { debugLog( - "Remote class update, device=" + "Skip class update, device=" + bdDevice + ", cod=0x" + Integer.toHexString(newBluetoothClass)); break; - case AbstractionLayer.BT_PROPERTY_UUIDS: + } + deviceProperties.setBluetoothClass(newBluetoothClass); + intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED); + intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); + intent.putExtra( + BluetoothDevice.EXTRA_CLASS, + new BluetoothClass(deviceProperties.getBluetoothClass())); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + mAdapterService.sendBroadcast( + intent, + BLUETOOTH_CONNECT, + Utils.getTempBroadcastOptions().toBundle()); + debugLog( + "Remote class update, device=" + + bdDevice + + ", cod=0x" + + Integer.toHexString(newBluetoothClass)); + break; + case AbstractionLayer.BT_PROPERTY_UUIDS: + case AbstractionLayer.BT_PROPERTY_UUIDS_LE: + if (type == AbstractionLayer.BT_PROPERTY_UUIDS) { final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val); - if (areUuidsEqual(newUuids, deviceProperties.getUuids())) { + if (areUuidsEqual(newUuids, deviceProperties.getUuidsBrEdr())) { // SDP Skip adding UUIDs to property cache if equal debugLog("Skip uuids update for " + bdDevice.getAddress()); MetricsLogger.getInstance() .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1); break; } - deviceProperties.setUuids(newUuids); - if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) { - // SDP Adding UUIDs to property cache and sending intent - MetricsLogger.getInstance() - .cacheCount( - BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1); - mAdapterService.deviceUuidUpdated(bdDevice); - sendUuidIntent(bdDevice, deviceProperties, true); - } else if (mAdapterService.getState() - == BluetoothAdapter.STATE_BLE_ON) { - // SDP Adding UUIDs to property cache but with no intent - MetricsLogger.getInstance() - .cacheCount( - BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1); - mAdapterService.deviceUuidUpdated(bdDevice); - } else { - // SDP Silently dropping UUIDs and with no intent + deviceProperties.setUuidsBrEdr(newUuids); + } else if (type == AbstractionLayer.BT_PROPERTY_UUIDS_LE) { + final ParcelUuid[] newUuidsLe = Utils.byteArrayToUuid(val); + if (areUuidsEqual(newUuidsLe, deviceProperties.getUuidsLe())) { + // SDP Skip adding UUIDs to property cache if equal + debugLog("Skip LE uuids update for " + bdDevice.getAddress()); MetricsLogger.getInstance() - .cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1); - } - break; - case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE: - if (deviceProperties.isConsolidated()) { + .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1); break; } - // The device type from hal layer, defined in bluetooth.h, - // matches the type defined in BluetoothDevice.java - deviceProperties.setDeviceType(Utils.byteArrayToInt(val)); - break; - case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI: - // RSSI from hal is in one byte - deviceProperties.setRssi(val[0]); - break; - case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER: - deviceProperties.setIsCoordinatedSetMember(val[0] != 0); - break; - case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY: - deviceProperties.setAshaCapability(val[0]); - break; - case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID: - deviceProperties.setAshaTruncatedHiSyncId(val[0]); - break; - case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM: - final String modelName = new String(val); - debugLog("Remote device model name: " + modelName); - deviceProperties.setModelName(modelName); - BluetoothStatsLog.write( - BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED, - mAdapterService.obfuscateAddress(bdDevice), - BluetoothProtoEnums.DEVICE_INFO_INTERNAL, - LOG_SOURCE_DIS, - null, - modelName, - null, - null, - mAdapterService.getMetricId(bdDevice), - bdDevice.getAddressType(), - 0, - 0, - 0); + deviceProperties.setUuidsLe(newUuidsLe); + } + uuids_updated = true; + break; + case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE: + if (deviceProperties.isConsolidated()) { break; - } + } + // The device type from hal layer, defined in bluetooth.h, + // matches the type defined in BluetoothDevice.java + deviceProperties.setDeviceType(Utils.byteArrayToInt(val)); + break; + case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI: + // RSSI from hal is in one byte + deviceProperties.setRssi(val[0]); + break; + case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER: + deviceProperties.setIsCoordinatedSetMember(val[0] != 0); + break; + case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY: + deviceProperties.setAshaCapability(val[0]); + break; + case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID: + deviceProperties.setAshaTruncatedHiSyncId(val[0]); + break; + case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM: + final String modelName = new String(val); + debugLog("Remote device model name: " + modelName); + deviceProperties.setModelName(modelName); + BluetoothStatsLog.write( + BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED, + mAdapterService.obfuscateAddress(bdDevice), + BluetoothProtoEnums.DEVICE_INFO_INTERNAL, + LOG_SOURCE_DIS, + null, + modelName, + null, + null, + mAdapterService.getMetricId(bdDevice), + bdDevice.getAddressType(), + 0, + 0, + 0); + break; } } } + + if (!uuids_updated) { + return; + } + + /* uuids_updated == true + * We might have received LE and BREDR UUIDS separately, ensure that UUID intent is sent + * just once */ + + if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) { + // SDP Adding UUIDs to property cache and sending intent + MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1); + mAdapterService.deviceUuidUpdated(bdDevice); + sendUuidIntent(bdDevice, deviceProperties, true); + } else if (mAdapterService.getState() == BluetoothAdapter.STATE_BLE_ON) { + // SDP Adding UUIDs to property cache but with no intent + MetricsLogger.getInstance() + .cacheCount(BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1); + mAdapterService.deviceUuidUpdated(bdDevice); + } else { + // SDP Silently dropping UUIDs and with no intent + MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1); + } } void deviceFoundCallback(byte[] address) { diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt b/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt index 92aaf31101..66415cd82f 100644 --- a/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt +++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseBinder.kt @@ -66,28 +66,31 @@ class AdvertiseBinder( mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null) } - getManager(source) - ?.startAdvertisingSet( - parameters, - advertiseData, - scanResponse, - periodicParameters, - periodicData, - duration, - maxExtAdvEvents, - serverIf, - callback, - source, - ) + getManager(source)?.let { + it.doOnAdvertiseThread { + it.startAdvertisingSet( + parameters, + advertiseData, + scanResponse, + periodicParameters, + periodicData, + duration, + maxExtAdvEvents, + serverIf, + callback, + source, + ) + } + } } override fun stopAdvertisingSet(callback: IAdvertisingSetCallback, source: AttributionSource) { - getManager(source)?.stopAdvertisingSet(callback) + getManager(source)?.let { it.doOnAdvertiseThread { it.stopAdvertisingSet(callback) } } } override fun getOwnAddress(advertiserId: Int, source: AttributionSource) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null) - getManager(source)?.getOwnAddress(advertiserId) + getManager(source)?.let { it.doOnAdvertiseThread { it.getOwnAddress(advertiserId) } } } override fun enableAdvertisingSet( @@ -97,7 +100,11 @@ class AdvertiseBinder( maxExtAdvEvents: Int, source: AttributionSource, ) { - getManager(source)?.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents) + getManager(source)?.let { + it.doOnAdvertiseThread { + it.enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents) + } + } } override fun setAdvertisingData( @@ -105,7 +112,9 @@ class AdvertiseBinder( data: AdvertiseData?, source: AttributionSource, ) { - getManager(source)?.setAdvertisingData(advertiserId, data) + getManager(source)?.let { + it.doOnAdvertiseThread { it.setAdvertisingData(advertiserId, data) } + } } override fun setScanResponseData( @@ -113,7 +122,9 @@ class AdvertiseBinder( data: AdvertiseData?, source: AttributionSource, ) { - getManager(source)?.setScanResponseData(advertiserId, data) + getManager(source)?.let { + it.doOnAdvertiseThread { it.setScanResponseData(advertiserId, data) } + } } override fun setAdvertisingParameters( @@ -127,7 +138,9 @@ class AdvertiseBinder( ) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null) } - getManager(source)?.setAdvertisingParameters(advertiserId, parameters) + getManager(source)?.let { + it.doOnAdvertiseThread { it.setAdvertisingParameters(advertiserId, parameters) } + } } override fun setPeriodicAdvertisingParameters( @@ -135,7 +148,9 @@ class AdvertiseBinder( parameters: PeriodicAdvertisingParameters?, source: AttributionSource, ) { - getManager(source)?.setPeriodicAdvertisingParameters(advertiserId, parameters) + getManager(source)?.let { + it.doOnAdvertiseThread { it.setPeriodicAdvertisingParameters(advertiserId, parameters) } + } } override fun setPeriodicAdvertisingData( @@ -143,7 +158,9 @@ class AdvertiseBinder( data: AdvertiseData?, source: AttributionSource, ) { - getManager(source)?.setPeriodicAdvertisingData(advertiserId, data) + getManager(source)?.let { + it.doOnAdvertiseThread { it.setPeriodicAdvertisingData(advertiserId, data) } + } } override fun setPeriodicAdvertisingEnable( @@ -151,6 +168,8 @@ class AdvertiseBinder( enable: Boolean, source: AttributionSource, ) { - getManager(source)?.setPeriodicAdvertisingEnable(advertiserId, enable) + getManager(source)?.let { + it.doOnAdvertiseThread { it.setPeriodicAdvertisingEnable(advertiserId, enable) } + } } } diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java index 7e89f99055..6ed719cedc 100644 --- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java +++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManager.java @@ -24,45 +24,52 @@ import android.bluetooth.le.PeriodicAdvertisingParameters; import android.content.AttributionSource; import android.os.Binder; import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; -import android.os.IInterface; import android.os.Looper; import android.os.RemoteException; import android.util.Log; +import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; -import com.android.internal.annotations.GuardedBy; +import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -/** - * Manages Bluetooth LE advertising operations and interacts with bluedroid stack. TODO: add tests. - */ -@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +/** Manages Bluetooth LE advertising operations. */ public class AdvertiseManager { private static final String TAG = GattServiceConfig.TAG_PREFIX + "AdvertiseManager"; - private final GattService mService; + private static final long RUN_SYNC_WAIT_TIME_MS = 2000L; + + private final AdapterService mService; private final AdvertiseManagerNativeInterface mNativeInterface; private final AdvertiseBinder mAdvertiseBinder; private final AdvertiserMap mAdvertiserMap; - @GuardedBy("itself") private final Map<IBinder, AdvertiserInfo> mAdvertisers = new HashMap<>(); - private Handler mHandler; - static int sTempRegistrationId = -1; + private final Handler mHandler; + private volatile boolean mIsAvailable = true; + @VisibleForTesting int mTempRegistrationId = -1; - AdvertiseManager(GattService service) { - this(service, AdvertiseManagerNativeInterface.getInstance(), new AdvertiserMap()); + AdvertiseManager(AdapterService service, Looper advertiseLooper) { + this( + service, + advertiseLooper, + AdvertiseManagerNativeInterface.getInstance(), + new AdvertiserMap()); } @VisibleForTesting AdvertiseManager( - GattService service, + AdapterService service, + Looper advertiseLooper, AdvertiseManagerNativeInterface nativeInterface, AdvertiserMap advertiserMap) { Log.d(TAG, "advertise manager created"); @@ -70,42 +77,26 @@ public class AdvertiseManager { mNativeInterface = nativeInterface; mAdvertiserMap = advertiserMap; - // Start a HandlerThread that handles advertising operations mNativeInterface.init(this); - HandlerThread thread = new HandlerThread("BluetoothAdvertiseManager"); - thread.start(); - mHandler = new Handler(thread.getLooper()); + mHandler = new Handler(advertiseLooper); mAdvertiseBinder = new AdvertiseBinder(service, this); } - // TODO(b/327849650): We shouldn't need this, it should be safe to do in the cleanup method. But - // it would be a logic change. - void clear() { - mAdvertiserMap.clear(); - } - void cleanup() { Log.d(TAG, "cleanup()"); - mAdvertiseBinder.cleanup(); - mNativeInterface.cleanup(); - synchronized (mAdvertisers) { - mAdvertisers.clear(); - } - sTempRegistrationId = -1; - - if (mHandler != null) { - // Shut down the thread - mHandler.removeCallbacksAndMessages(null); - Looper looper = mHandler.getLooper(); - if (looper != null) { - looper.quit(); - } - mHandler = null; - } + mIsAvailable = false; + mHandler.removeCallbacksAndMessages(null); + forceRunSyncOnAdvertiseThread( + () -> { + mAdvertiserMap.clear(); + mAdvertiseBinder.cleanup(); + mNativeInterface.cleanup(); + mAdvertisers.clear(); + }); } void dump(StringBuilder sb) { - mAdvertiserMap.dump(sb); + forceRunSyncOnAdvertiseThread(() -> mAdvertiserMap.dump(sb)); } AdvertiseBinder getBinder() { @@ -129,8 +120,12 @@ public class AdvertiseManager { } } + private interface CallbackWrapper { + void call() throws RemoteException; + } + IBinder toBinder(IAdvertisingSetCallback e) { - return ((IInterface) e).asBinder(); + return e.asBinder(); } class AdvertisingSetDeathRecipient implements IBinder.DeathRecipient { @@ -145,25 +140,22 @@ public class AdvertiseManager { @Override public void binderDied() { Log.d(TAG, "Binder is dead - unregistering advertising set (" + mPackageName + ")!"); - stopAdvertisingSet(callback); + doOnAdvertiseThread(() -> stopAdvertisingSet(callback)); } } - Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) { + private Map.Entry<IBinder, AdvertiserInfo> findAdvertiser(int advertiserId) { Map.Entry<IBinder, AdvertiserInfo> entry = null; - synchronized (mAdvertisers) { - for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) { - if (e.getValue().id == advertiserId) { - entry = e; - break; - } + for (Map.Entry<IBinder, AdvertiserInfo> e : mAdvertisers.entrySet()) { + if (e.getValue().id == advertiserId) { + entry = e; + break; } } return entry; } - void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) - throws Exception { + void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) { Log.d( TAG, "onAdvertisingSetStarted() - regId=" @@ -172,6 +164,7 @@ public class AdvertiseManager { + advertiserId + ", status=" + status); + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(regId); @@ -191,26 +184,24 @@ public class AdvertiseManager { } else { IBinder binder = entry.getKey(); binder.unlinkToDeath(entry.getValue().deathRecipient, 0); - synchronized (mAdvertisers) { - mAdvertisers.remove(binder); - } + mAdvertisers.remove(binder); AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(regId); if (stats != null) { - int instanceCount; - synchronized (mAdvertisers) { - instanceCount = mAdvertisers.size(); - } - stats.recordAdvertiseStop(instanceCount); + stats.recordAdvertiseStop(mAdvertisers.size()); stats.recordAdvertiseErrorCount(status); } mAdvertiserMap.removeAppAdvertiseStats(regId); } - callback.onAdvertisingSetStarted(mAdvertiseBinder, advertiserId, txPower, status); + sendToCallback( + advertiserId, + () -> + callback.onAdvertisingSetStarted( + mAdvertiseBinder, advertiserId, txPower, status)); } - void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception { + void onAdvertisingEnabled(int advertiserId, boolean enable, int status) { Log.d( TAG, "onAdvertisingSetEnabled() - advertiserId=" @@ -219,6 +210,7 @@ public class AdvertiseManager { + enable + ", status=" + status); + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { @@ -230,16 +222,13 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onAdvertisingEnabled(advertiserId, enable, status); + sendToCallback( + advertiserId, () -> callback.onAdvertisingEnabled(advertiserId, enable, status)); if (!enable && status != 0) { AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId); if (stats != null) { - int instanceCount; - synchronized (mAdvertisers) { - instanceCount = mAdvertisers.size(); - } - stats.recordAdvertiseStop(instanceCount); + stats.recordAdvertiseStop(mAdvertisers.size()); } } } @@ -255,6 +244,7 @@ public class AdvertiseManager { int serverIf, IAdvertisingSetCallback callback, AttributionSource attrSource) { + checkThread(); // If we are using an isolated server, force usage of an NRPA if (serverIf != 0 && parameters.getOwnAddressType() @@ -289,7 +279,7 @@ public class AdvertiseManager { throw new IllegalArgumentException("Can't link to advertiser's death"); } - String deviceName = AdapterService.getAdapterService().getName(); + String deviceName = mService.getName(); try { byte[] advDataBytes = AdvertiseHelper.advertiseDataToBytes(advertiseData, deviceName); byte[] scanResponseBytes = @@ -297,10 +287,8 @@ public class AdvertiseManager { byte[] periodicDataBytes = AdvertiseHelper.advertiseDataToBytes(periodicData, deviceName); - int cbId = --sTempRegistrationId; - synchronized (mAdvertisers) { - mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback)); - } + int cbId = --mTempRegistrationId; + mAdvertisers.put(binder, new AdvertiserInfo(cbId, deathRecipient, callback)); Log.d(TAG, "startAdvertisingSet() - reg_id=" + cbId + ", callback: " + binder); @@ -340,9 +328,9 @@ public class AdvertiseManager { } } - void onOwnAddressRead(int advertiserId, int addressType, String address) - throws RemoteException { + void onOwnAddressRead(int advertiserId, int addressType, String address) { Log.d(TAG, "onOwnAddressRead() advertiserId=" + advertiserId); + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { @@ -351,10 +339,12 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onOwnAddressRead(advertiserId, addressType, address); + sendToCallback( + advertiserId, () -> callback.onOwnAddressRead(advertiserId, addressType, address)); } void getOwnAddress(int advertiserId) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "getOwnAddress() - bad advertiserId " + advertiserId); @@ -364,13 +354,11 @@ public class AdvertiseManager { } void stopAdvertisingSet(IAdvertisingSetCallback callback) { + checkThread(); IBinder binder = toBinder(callback); Log.d(TAG, "stopAdvertisingSet() " + binder); - AdvertiserInfo adv; - synchronized (mAdvertisers) { - adv = mAdvertisers.remove(binder); - } + AdvertiserInfo adv = mAdvertisers.remove(binder); if (adv == null) { Log.e(TAG, "stopAdvertisingSet() - no client found for callback"); return; @@ -397,6 +385,7 @@ public class AdvertiseManager { } void enableAdvertisingSet(int advertiserId, boolean enable, int duration, int maxExtAdvEvents) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "enableAdvertisingSet() - bad advertiserId " + advertiserId); @@ -408,12 +397,13 @@ public class AdvertiseManager { } void setAdvertisingData(int advertiserId, AdvertiseData data) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "setAdvertisingData() - bad advertiserId " + advertiserId); return; } - String deviceName = AdapterService.getAdapterService().getName(); + String deviceName = mService.getName(); try { mNativeInterface.setAdvertisingData( advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); @@ -430,12 +420,13 @@ public class AdvertiseManager { } void setScanResponseData(int advertiserId, AdvertiseData data) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "setScanResponseData() - bad advertiserId " + advertiserId); return; } - String deviceName = AdapterService.getAdapterService().getName(); + String deviceName = mService.getName(); try { mNativeInterface.setScanResponseData( advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); @@ -452,6 +443,7 @@ public class AdvertiseManager { } void setAdvertisingParameters(int advertiserId, AdvertisingSetParameters parameters) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "setAdvertisingParameters() - bad advertiserId " + advertiserId); @@ -464,6 +456,7 @@ public class AdvertiseManager { void setPeriodicAdvertisingParameters( int advertiserId, PeriodicAdvertisingParameters parameters) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "setPeriodicAdvertisingParameters() - bad advertiserId " + advertiserId); @@ -475,12 +468,13 @@ public class AdvertiseManager { } void setPeriodicAdvertisingData(int advertiserId, AdvertiseData data) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "setPeriodicAdvertisingData() - bad advertiserId " + advertiserId); return; } - String deviceName = AdapterService.getAdapterService().getName(); + String deviceName = mService.getName(); try { mNativeInterface.setPeriodicAdvertisingData( advertiserId, AdvertiseHelper.advertiseDataToBytes(data, deviceName)); @@ -497,6 +491,7 @@ public class AdvertiseManager { } void setPeriodicAdvertisingEnable(int advertiserId, boolean enable) { + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { Log.w(TAG, "setPeriodicAdvertisingEnable() - bad advertiserId " + advertiserId); @@ -505,7 +500,8 @@ public class AdvertiseManager { mNativeInterface.setPeriodicAdvertisingEnable(advertiserId, enable); } - void onAdvertisingDataSet(int advertiserId, int status) throws Exception { + void onAdvertisingDataSet(int advertiserId, int status) { + checkThread(); Log.d(TAG, "onAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); @@ -515,10 +511,11 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onAdvertisingDataSet(advertiserId, status); + sendToCallback(advertiserId, () -> callback.onAdvertisingDataSet(advertiserId, status)); } - void onScanResponseDataSet(int advertiserId, int status) throws Exception { + void onScanResponseDataSet(int advertiserId, int status) { + checkThread(); Log.d(TAG, "onScanResponseDataSet() advertiserId=" + advertiserId + ", status=" + status); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); @@ -528,11 +525,10 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onScanResponseDataSet(advertiserId, status); + sendToCallback(advertiserId, () -> callback.onScanResponseDataSet(advertiserId, status)); } - void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) - throws Exception { + void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) { Log.d( TAG, "onAdvertisingParametersUpdated() advertiserId=" @@ -541,6 +537,7 @@ public class AdvertiseManager { + txPower + ", status=" + status); + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { @@ -549,16 +546,19 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onAdvertisingParametersUpdated(advertiserId, txPower, status); + sendToCallback( + advertiserId, + () -> callback.onAdvertisingParametersUpdated(advertiserId, txPower, status)); } - void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception { + void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) { Log.d( TAG, "onPeriodicAdvertisingParametersUpdated() advertiserId=" + advertiserId + ", status=" + status); + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { @@ -569,16 +569,19 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status); + sendToCallback( + advertiserId, + () -> callback.onPeriodicAdvertisingParametersUpdated(advertiserId, status)); } - void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception { + void onPeriodicAdvertisingDataSet(int advertiserId, int status) { Log.d( TAG, "onPeriodicAdvertisingDataSet() advertiserId=" + advertiserId + ", status=" + status); + checkThread(); Map.Entry<IBinder, AdvertiserInfo> entry = findAdvertiser(advertiserId); if (entry == null) { @@ -587,11 +590,11 @@ public class AdvertiseManager { } IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onPeriodicAdvertisingDataSet(advertiserId, status); + sendToCallback( + advertiserId, () -> callback.onPeriodicAdvertisingDataSet(advertiserId, status)); } - void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) - throws Exception { + void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) { Log.d( TAG, "onPeriodicAdvertisingEnabled() advertiserId=" @@ -604,9 +607,12 @@ public class AdvertiseManager { Log.i(TAG, "onAdvertisingSetEnable() - bad advertiserId " + advertiserId); return; } + checkThread(); IAdvertisingSetCallback callback = entry.getValue().callback; - callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status); + sendToCallback( + advertiserId, + () -> callback.onPeriodicAdvertisingEnabled(advertiserId, enable, status)); AppAdvertiseStats stats = mAdvertiserMap.getAppAdvertiseStatsById(advertiserId); if (stats != null) { @@ -614,4 +620,52 @@ public class AdvertiseManager { } } + void doOnAdvertiseThread(Runnable r) { + if (mIsAvailable) { + if (Flags.advertiseThread()) { + mHandler.post( + () -> { + if (mIsAvailable) { + r.run(); + } + }); + } else { + r.run(); + } + } + } + + private void forceRunSyncOnAdvertiseThread(Runnable r) { + if (!Flags.advertiseThread()) { + r.run(); + return; + } + final CompletableFuture<Void> future = new CompletableFuture<>(); + mHandler.postAtFrontOfQueue( + () -> { + r.run(); + future.complete(null); + }); + try { + future.get(RUN_SYNC_WAIT_TIME_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + Log.w(TAG, "Unable to complete sync task: " + e); + } + } + + private void checkThread() { + if (Flags.advertiseThread() + && !mHandler.getLooper().isCurrentThread() + && !Utils.isInstrumentationTestMode()) { + throw new IllegalStateException("Not on advertise thread"); + } + } + + private void sendToCallback(int advertiserId, CallbackWrapper wrapper) { + try { + wrapper.call(); + } catch (RemoteException e) { + Log.i(TAG, "RemoteException in callback for advertiserId: " + advertiserId); + } + } } diff --git a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java index 215c709970..6cdd47f97e 100644 --- a/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java +++ b/android/app/src/com/android/bluetooth/gatt/AdvertiseManagerNativeInterface.java @@ -19,11 +19,12 @@ package com.android.bluetooth.gatt; import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.PeriodicAdvertisingParameters; +import androidx.annotation.VisibleForTesting; + import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; /** Native interface for AdvertiseManager */ -@VisibleForTesting +@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public class AdvertiseManagerNativeInterface { private static final String TAG = AdvertiseManagerNativeInterface.class.getSimpleName(); @@ -121,43 +122,47 @@ public class AdvertiseManagerNativeInterface { setPeriodicAdvertisingEnableNative(advertiserId, enable); } - void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) - throws Exception { - mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status); + void onAdvertisingSetStarted(int regId, int advertiserId, int txPower, int status) { + mManager.doOnAdvertiseThread( + () -> mManager.onAdvertisingSetStarted(regId, advertiserId, txPower, status)); } - void onOwnAddressRead(int advertiserId, int addressType, String address) throws Exception { - mManager.onOwnAddressRead(advertiserId, addressType, address); + void onOwnAddressRead(int advertiserId, int addressType, String address) { + mManager.doOnAdvertiseThread( + () -> mManager.onOwnAddressRead(advertiserId, addressType, address)); } - void onAdvertisingEnabled(int advertiserId, boolean enable, int status) throws Exception { - mManager.onAdvertisingEnabled(advertiserId, enable, status); + void onAdvertisingEnabled(int advertiserId, boolean enable, int status) { + mManager.doOnAdvertiseThread( + () -> mManager.onAdvertisingEnabled(advertiserId, enable, status)); } - void onAdvertisingDataSet(int advertiserId, int status) throws Exception { - mManager.onAdvertisingDataSet(advertiserId, status); + void onAdvertisingDataSet(int advertiserId, int status) { + mManager.doOnAdvertiseThread(() -> mManager.onAdvertisingDataSet(advertiserId, status)); } - void onScanResponseDataSet(int advertiserId, int status) throws Exception { - mManager.onScanResponseDataSet(advertiserId, status); + void onScanResponseDataSet(int advertiserId, int status) { + mManager.doOnAdvertiseThread(() -> mManager.onScanResponseDataSet(advertiserId, status)); } - void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) - throws Exception { - mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status); + void onAdvertisingParametersUpdated(int advertiserId, int txPower, int status) { + mManager.doOnAdvertiseThread( + () -> mManager.onAdvertisingParametersUpdated(advertiserId, txPower, status)); } - void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) throws Exception { - mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status); + void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) { + mManager.doOnAdvertiseThread( + () -> mManager.onPeriodicAdvertisingParametersUpdated(advertiserId, status)); } - void onPeriodicAdvertisingDataSet(int advertiserId, int status) throws Exception { - mManager.onPeriodicAdvertisingDataSet(advertiserId, status); + void onPeriodicAdvertisingDataSet(int advertiserId, int status) { + mManager.doOnAdvertiseThread( + () -> mManager.onPeriodicAdvertisingDataSet(advertiserId, status)); } - void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) - throws Exception { - mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status); + void onPeriodicAdvertisingEnabled(int advertiserId, boolean enable, int status) { + mManager.doOnAdvertiseThread( + () -> mManager.onPeriodicAdvertisingEnabled(advertiserId, enable, status)); } private native void initializeNative(); diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java index cc2e759f61..e9563f8908 100644 --- a/android/app/src/com/android/bluetooth/gatt/GattService.java +++ b/android/app/src/com/android/bluetooth/gatt/GattService.java @@ -51,6 +51,7 @@ import android.content.pm.PackageManager.PackageInfoFlags; import android.content.res.Resources; import android.os.Binder; import android.os.Build; +import android.os.HandlerThread; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteException; @@ -156,6 +157,7 @@ public class GattService extends ProfileService { private final DistanceMeasurementManager mDistanceMeasurementManager; private final ActivityManager mActivityManager; private final PackageManager mPackageManager; + private final HandlerThread mHandlerThread; public GattService(AdapterService adapterService) { super(requireNonNull(adapterService)); @@ -169,7 +171,12 @@ public class GattService extends ProfileService { mNativeInterface = GattObjectsFactory.getInstance().getNativeInterface(); mNativeInterface.init(this); - mAdvertiseManager = new AdvertiseManager(this); + + // Create a thread to handle LE operations + mHandlerThread = new HandlerThread("Bluetooth LE"); + mHandlerThread.start(); + + mAdvertiseManager = new AdvertiseManager(mAdapterService, mHandlerThread.getLooper()); if (!Flags.scanManagerRefactor()) { mScanController = new ScanController(adapterService); @@ -209,7 +216,6 @@ public class GattService extends ProfileService { if (mScanController != null) { mScanController.stop(); } - mAdvertiseManager.clear(); mClientMap.clear(); mRestrictedHandles.clear(); mServerMap.clear(); @@ -224,6 +230,7 @@ public class GattService extends ProfileService { mNativeInterface.cleanup(); mAdvertiseManager.cleanup(); mDistanceMeasurementManager.cleanup(); + mHandlerThread.quit(); } /** This is only used when Flags.scanManagerRefactor() is true. */ diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java index 949db48483..179b7fafb0 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java @@ -1502,9 +1502,6 @@ public class HeadsetService extends ProfileService { } else { broadcastActiveDevice(mActiveDevice); } - if (Flags.updateActiveDeviceInBandRingtone()) { - updateInbandRinging(device, true); - } } else if (shouldPersistAudio()) { if (Utils.isScoManagedByAudioEnabled()) { // tell Audio Framework that active device changed @@ -1546,9 +1543,9 @@ public class HeadsetService extends ProfileService { } else { broadcastActiveDevice(mActiveDevice); } - if (Flags.updateActiveDeviceInBandRingtone()) { - updateInbandRinging(device, true); - } + } + if (Flags.updateActiveDeviceInBandRingtone()) { + updateInbandRinging(device, true); } } return true; diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index fd56faa613..fb0ed9d2f0 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -55,7 +55,6 @@ import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.storage.DatabaseManager; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.State; import com.android.internal.util.StateMachine; @@ -1122,12 +1121,6 @@ class HeadsetStateMachine extends StateMachine { } } break; - case INTENT_SCO_VOLUME_CHANGED: - if (Flags.hfpAllowVolumeChangeWithoutSco()) { - // when flag is removed, remove INTENT_SCO_VOLUME_CHANGED case in AudioOn - processIntentScoVolume((Intent) message.obj, mDevice); - } - break; case INTENT_CONNECTION_ACCESS_REPLY: handleAccessPermissionResult((Intent) message.obj); break; @@ -1630,8 +1623,6 @@ class HeadsetStateMachine extends StateMachine { break; } case INTENT_SCO_VOLUME_CHANGED: - // TODO: b/362313390 Remove this case once the fix is in place because this - // message will be handled by the ConnectedBase state. processIntentScoVolume((Intent) message.obj, mDevice); break; case STACK_EVENT: diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java index 230526ca1a..9f281bd1e8 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -81,6 +81,7 @@ import android.util.Pair; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; +import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.bass_client.BassClientService; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.MetricsLogger; @@ -90,6 +91,7 @@ import com.android.bluetooth.btservice.storage.DatabaseManager; import com.android.bluetooth.csip.CsipSetCoordinatorService; import com.android.bluetooth.flags.Flags; import com.android.bluetooth.hap.HapClientService; +import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.hfp.HeadsetService; import com.android.bluetooth.mcp.McpService; import com.android.bluetooth.tbs.TbsGatt; @@ -102,6 +104,7 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -1655,6 +1658,31 @@ public class LeAudioService extends ProfileService { && groupId == mUnicastGroupIdDeactivatedForBroadcastTransition; } + /** Get local broadcast receiving devices */ + public Set<BluetoothDevice> getLocalBroadcastReceivers() { + if (mBroadcastDescriptors == null) { + Log.e(TAG, "getLocalBroadcastReceivers: Invalid Broadcast Descriptors"); + return Collections.emptySet(); + } + + BassClientService bassClientService = getBassClientService(); + if (bassClientService == null) { + Log.e(TAG, "getLocalBroadcastReceivers: Bass service not available"); + return Collections.emptySet(); + } + + Set<BluetoothDevice> deviceList = new HashSet<>(); + for (Map.Entry<Integer, LeAudioBroadcastDescriptor> entry : + mBroadcastDescriptors.entrySet()) { + if (!entry.getValue().mState.equals(LeAudioStackEvent.BROADCAST_STATE_STOPPED)) { + List<BluetoothDevice> devices = + bassClientService.getSyncedBroadcastSinks(entry.getKey()); + deviceList.addAll(devices); + } + } + return deviceList; + } + private boolean areBroadcastsAllStopped() { if (mBroadcastDescriptors == null) { Log.e(TAG, "areBroadcastsAllStopped: Invalid Broadcast Descriptors"); @@ -2805,17 +2833,52 @@ public class LeAudioService extends ProfileService { } } - private void handleGroupHealthAction(int groupId, int action) { - Log.d( + private void disableLeAudioAndFallbackToLegacyAudioProfiles(int groupId) { + Log.i( TAG, - "handleGroupHealthAction: groupId: " + "Disabling LE Audio for group: " + groupId - + " action: " - + action - + ", not implemented"); + + " and falling back to legacy profiles"); + A2dpService a2dpService = mServiceFactory.getA2dpService(); + HeadsetService hsService = mServiceFactory.getHeadsetService(); + HearingAidService hearingAidService = mServiceFactory.getHearingAidService(); + boolean isDualMode = Utils.isDualModeAudioEnabled(); + + List<BluetoothDevice> leAudioActiveGroupDevices = getGroupDevices(groupId); + + for (BluetoothDevice activeGroupDevice : leAudioActiveGroupDevices) { + Log.d(TAG, "Disable LE_AUDIO for the device: " + activeGroupDevice); + final ParcelUuid[] uuids = mAdapterService.getRemoteUuids(activeGroupDevice); + + setConnectionPolicy(activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + if (hsService != null && !isDualMode && Utils.arrayContains(uuids, BluetoothUuid.HFP)) { + Log.d(TAG, "Enable HFP for the device: " + activeGroupDevice); + hsService.setConnectionPolicy( + activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + } + if (a2dpService != null + && !isDualMode + && (Utils.arrayContains(uuids, BluetoothUuid.A2DP_SINK) + || Utils.arrayContains(uuids, BluetoothUuid.ADV_AUDIO_DIST))) { + Log.d(TAG, "Enable A2DP for the device: " + activeGroupDevice); + a2dpService.setConnectionPolicy( + activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + } + if (hearingAidService != null + && Utils.arrayContains(uuids, BluetoothUuid.HEARING_AID)) { + Log.d(TAG, "Enable ASHA for the device: " + activeGroupDevice); + hearingAidService.setConnectionPolicy( + activeGroupDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED); + } + } + } + + private void handleGroupHealthAction(int groupId, int action) { + Log.d(TAG, "handleGroupHealthAction: groupId: " + groupId + " action: " + action); BluetoothDevice device = getLeadDeviceForTheGroup(groupId); switch (action) { - case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_DISABLE: + case com.android.bluetooth.le_audio.LeAudioStackEvent + .HEALTH_RECOMMENDATION_ACTION_DISABLE: MetricsLogger.getInstance() .count( mAdapterService.isLeAudioAllowed(device) @@ -2824,6 +2887,7 @@ public class LeAudioService extends ProfileService { : BluetoothProtoEnums .LE_AUDIO_NONALLOWLIST_GROUP_HEALTH_STATUS_BAD, 1); + disableLeAudioAndFallbackToLegacyAudioProfiles(groupId); break; case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_CONSIDER_DISABLING: MetricsLogger.getInstance() diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanController.java b/android/app/src/com/android/bluetooth/le_scan/ScanController.java index a8f5a4025e..7f39fdaaf1 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScanController.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScanController.java @@ -91,7 +91,7 @@ public class ScanController { private static final int TRUNCATED_RESULT_SIZE = 11; /** The default floor value for LE batch scan report delays greater than 0 */ - @VisibleForTesting static final long DEFAULT_REPORT_DELAY_FLOOR = 5000; + static final long DEFAULT_REPORT_DELAY_FLOOR = 5000L; private static final int NUM_SCAN_EVENTS_KEPT = 20; @@ -759,17 +759,19 @@ public class ScanController { } } } - if (permittedResults.isEmpty()) { - return; - } } if (client.hasDisavowedLocation) { permittedResults.removeIf(mLocationDenylistPredicate); } + if (permittedResults.isEmpty()) { + mScanManager.callbackDone(scannerId, status); + return; + } if (app.mCallback != null) { app.mCallback.onBatchScanResults(permittedResults); + mScanManager.batchScanResultDelivered(); } else { // PendingIntent based try { @@ -791,6 +793,9 @@ public class ScanController { @SuppressWarnings("NonApiType") private void sendBatchScanResults( ScannerMap.ScannerApp app, ScanClient client, ArrayList<ScanResult> results) { + if (results.isEmpty()) { + return; + } try { if (app.mCallback != null) { if (mScanManager.isAutoBatchScanClientEnabled(client)) { @@ -811,6 +816,7 @@ public class ScanController { Log.e(TAG, "Exception: " + e); handleDeadScanClient(client); } + mScanManager.batchScanResultDelivered(); } // Check and deliver scan results for different scan clients. @@ -833,14 +839,11 @@ public class ScanController { } } } - if (permittedResults.isEmpty()) { - return; - } } if (client.filters == null || client.filters.isEmpty()) { sendBatchScanResults(app, client, permittedResults); - // TODO: Question to reviewer: Shouldn't there be a return here? + return; } // Reconstruct the scan results. ArrayList<ScanResult> results = new ArrayList<ScanResult>(); diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java index 78f759fb13..e946b3afcf 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java @@ -146,6 +146,7 @@ public class ScanManager { @VisibleForTesting boolean mIsConnecting; @VisibleForTesting int mProfilesConnecting; private int mProfilesConnected, mProfilesDisconnecting; + private final BatchScanThrottler mBatchScanThrottler; @VisibleForTesting static class UidImportance { @@ -158,7 +159,7 @@ public class ScanManager { } } - public ScanManager( + ScanManager( AdapterService adapterService, ScanController scanController, BluetoothAdapterProxy bluetoothAdapterProxy, @@ -202,9 +203,10 @@ public class ScanManager { IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION); locationIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mAdapterService.registerReceiver(mLocationReceiver, locationIntentFilter); + mBatchScanThrottler = new BatchScanThrottler(timeProvider, mScreenOn); } - public void cleanup() { + void cleanup() { mRegularScanClients.clear(); mBatchClients.clear(); mSuspendedScanClients.clear(); @@ -233,16 +235,16 @@ public class ScanManager { } } - public void registerScanner(UUID uuid) { + void registerScanner(UUID uuid) { mScanNative.registerScanner(uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); } - public void unregisterScanner(int scannerId) { + void unregisterScanner(int scannerId) { mScanNative.unregisterScanner(scannerId); } /** Returns the regular scan queue. */ - public Set<ScanClient> getRegularScanQueue() { + Set<ScanClient> getRegularScanQueue() { return mRegularScanClients; } @@ -252,12 +254,12 @@ public class ScanManager { } /** Returns batch scan queue. */ - public Set<ScanClient> getBatchScanQueue() { + Set<ScanClient> getBatchScanQueue() { return mBatchClients; } /** Returns a set of full batch scan clients. */ - public Set<ScanClient> getFullBatchScanQueue() { + Set<ScanClient> getFullBatchScanQueue() { // TODO: split full batch scan clients and truncated batch clients so we don't need to // construct this every time. Set<ScanClient> fullBatchClients = new HashSet<ScanClient>(); @@ -269,12 +271,12 @@ public class ScanManager { return fullBatchClients; } - public void startScan(ScanClient client) { + void startScan(ScanClient client) { Log.d(TAG, "startScan() " + client); sendMessage(MSG_START_BLE_SCAN, client); } - public void stopScan(int scannerId) { + void stopScan(int scannerId) { ScanClient client = mScanNative.getBatchScanClient(scannerId); if (client == null) { client = mScanNative.getRegularScanClient(scannerId); @@ -285,14 +287,18 @@ public class ScanManager { sendMessage(MSG_STOP_BLE_SCAN, client); } - public void flushBatchScanResults(ScanClient client) { + void flushBatchScanResults(ScanClient client) { sendMessage(MSG_FLUSH_BATCH_RESULTS, client); } - public void callbackDone(int scannerId, int status) { + void callbackDone(int scannerId, int status) { mScanNative.callbackDone(scannerId, status); } + void batchScanResultDelivered() { + mBatchScanThrottler.resetBackoff(); + } + private void sendMessage(int what, ScanClient client) { mHandler.obtainMessage(what, client).sendToTarget(); } @@ -305,10 +311,16 @@ public class ScanManager { return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported(); } - public boolean isAutoBatchScanClientEnabled(ScanClient client) { + boolean isAutoBatchScanClientEnabled(ScanClient client) { return mScanNative.isAutoBatchScanClientEnabled(client); } + int getCurrentUsedTrackingAdvertisement() { + synchronized (mCurUsedTrackableAdvertisementsLock) { + return mCurUsedTrackableAdvertisements; + } + } + // Handler class that handles BLE scan operations. @VisibleForTesting class ClientHandler extends Handler { @@ -534,6 +546,7 @@ public class ScanManager { } mScreenOn = false; Log.d(TAG, "handleScreenOff()"); + mBatchScanThrottler.onScreenOn(false); handleSuspendScans(); updateRegularScanClientsScreenOff(); updateRegularScanToBatchScanClients(); @@ -864,6 +877,7 @@ public class ScanManager { } mScreenOn = true; Log.d(TAG, "handleScreenOn()"); + mBatchScanThrottler.onScreenOn(true); updateBatchScanToRegularScanClients(); handleResumeScans(); updateRegularScanClientsScreenOn(); @@ -921,14 +935,14 @@ public class ScanManager { /** Parameters for batch scans. */ static class BatchScanParams { - public int scanMode; - public int fullScanscannerId; - public int truncatedScanscannerId; + @VisibleForTesting int mScanMode; + private int mFullScanscannerId; + private int mTruncatedScanscannerId; BatchScanParams() { - scanMode = -1; - fullScanscannerId = -1; - truncatedScanscannerId = -1; + mScanMode = -1; + mFullScanscannerId = -1; + mTruncatedScanscannerId = -1; } @Override @@ -939,20 +953,14 @@ public class ScanManager { if (!(obj instanceof BatchScanParams other)) { return false; } - return scanMode == other.scanMode - && fullScanscannerId == other.fullScanscannerId - && truncatedScanscannerId == other.truncatedScanscannerId; + return mScanMode == other.mScanMode + && mFullScanscannerId == other.mFullScanscannerId + && mTruncatedScanscannerId == other.mTruncatedScanscannerId; } @Override public int hashCode() { - return Objects.hash(scanMode, fullScanscannerId, truncatedScanscannerId); - } - } - - public int getCurrentUsedTrackingAdvertisement() { - synchronized (mCurUsedTrackableAdvertisementsLock) { - return mCurUsedTrackableAdvertisements; + return Objects.hash(mScanMode, mFullScanscannerId, mTruncatedScanscannerId); } } @@ -1262,9 +1270,9 @@ public class ScanManager { waitForCallback(); resetCountDownLatch(); int scanInterval = - Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode)); + Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.mScanMode)); int scanWindow = - Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode)); + Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.mScanMode)); mNativeInterface.gattClientStartBatchScan( scannerId, resultType, @@ -1298,15 +1306,15 @@ public class ScanManager { BatchScanParams params = new BatchScanParams(); ScanClient winner = getAggressiveClient(mBatchClients); if (winner != null) { - params.scanMode = winner.settings.getScanMode(); + params.mScanMode = winner.settings.getScanMode(); } // TODO: split full batch scan results and truncated batch scan results to different // collections. for (ScanClient client : mBatchClients) { if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) { - params.fullScanscannerId = client.scannerId; + params.mFullScanscannerId = client.scannerId; } else { - params.truncatedScanscannerId = client.scannerId; + params.mTruncatedScanscannerId = client.scannerId; } } return params; @@ -1359,7 +1367,10 @@ public class ScanManager { if (mBatchClients.isEmpty()) { return; } - long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis(); + long batchTriggerIntervalMillis = + Flags.batchScanOptimization() + ? mBatchScanThrottler.getBatchTriggerIntervalMillis(mBatchClients) + : getBatchTriggerIntervalMillis(); // Allows the alarm to be triggered within // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis] long windowLengthMillis = batchTriggerIntervalMillis / 10; @@ -1494,16 +1505,16 @@ public class ScanManager { void flushBatchResults(int scannerId) { Log.d(TAG, "flushPendingBatchResults - scannerId = " + scannerId); - if (mBatchScanParams.fullScanscannerId != -1) { + if (mBatchScanParams.mFullScanscannerId != -1) { resetCountDownLatch(); mNativeInterface.gattClientReadScanReports( - mBatchScanParams.fullScanscannerId, SCAN_RESULT_TYPE_FULL); + mBatchScanParams.mFullScanscannerId, SCAN_RESULT_TYPE_FULL); waitForCallback(); } - if (mBatchScanParams.truncatedScanscannerId != -1) { + if (mBatchScanParams.mTruncatedScanscannerId != -1) { resetCountDownLatch(); mNativeInterface.gattClientReadScanReports( - mBatchScanParams.truncatedScanscannerId, SCAN_RESULT_TYPE_TRUNCATED); + mBatchScanParams.mTruncatedScanscannerId, SCAN_RESULT_TYPE_TRUNCATED); waitForCallback(); } setBatchAlarm(); @@ -1662,13 +1673,13 @@ public class ScanManager { /** Return batch scan result type value defined in bt stack. */ private int getResultType(BatchScanParams params) { - if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) { + if (params.mFullScanscannerId != -1 && params.mTruncatedScanscannerId != -1) { return SCAN_RESULT_TYPE_BOTH; } - if (params.truncatedScanscannerId != -1) { + if (params.mTruncatedScanscannerId != -1) { return SCAN_RESULT_TYPE_TRUNCATED; } - if (params.fullScanscannerId != -1) { + if (params.mFullScanscannerId != -1) { return SCAN_RESULT_TYPE_FULL; } return -1; diff --git a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java index 17b8d0fac9..b49eee0a03 100644 --- a/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java +++ b/android/app/src/com/android/bluetooth/tbs/TbsGeneric.java @@ -828,6 +828,20 @@ public class TbsGeneric { return; } + if (shouldBlockTbsForBroadcastReceiver(device)) { + Log.w( + TAG, + "Blocking TBS operation for non-primary device in broadcast," + + " opcode = " + + callControlRequestOpcodeStr(opcode)); + mTbsGatt.setCallControlPointResult( + device, + opcode, + 0, + TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE); + return; + } + int result; switch (opcode) { @@ -1229,6 +1243,20 @@ public class TbsGeneric { return false; } + private boolean shouldBlockTbsForBroadcastReceiver(BluetoothDevice device) { + if (device == null) { + Log.w(TAG, "shouldBlockTbsForBroadcastReceiver: Ignore null device"); + return false; + } + if (!isLeAudioServiceAvailable()) { + Log.w(TAG, "shouldBlockTbsForBroadcastReceiver: LeAudioService is not available"); + return false; + } + + return mLeAudioService.getLocalBroadcastReceivers().contains(device) + && !mLeAudioService.isPrimaryDevice(device); + } + /** * Dump status of TBS service along with related objects * diff --git a/android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java b/android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java new file mode 100644 index 0000000000..4fd324dcf9 --- /dev/null +++ b/android/app/src/com/com/android/bluetooth/le_scan/BatchScanThrottler.java @@ -0,0 +1,112 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.le_scan; + +import static com.android.bluetooth.le_scan.ScanController.DEFAULT_REPORT_DELAY_FLOOR; + +import android.provider.DeviceConfig; + +import com.android.bluetooth.Utils.TimeProvider; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Set; + +/** + * Throttler to reduce the number of times the Bluetooth process wakes up to check for pending batch + * scan results. The wake-up intervals are increased when no matching results are found and are + * longer when the screen is off. + */ +class BatchScanThrottler { + // Minimum batch trigger interval to check for batched results when the screen is off + @VisibleForTesting static final long SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS = 20000L; + // Adjusted minimum report delay for unfiltered batch scan clients + @VisibleForTesting static final long UNFILTERED_DELAY_FLOOR_MS = 20000L; + // Adjusted minimum report delay for unfiltered batch scan clients when the screen is off + @VisibleForTesting static final long UNFILTERED_SCREEN_OFF_DELAY_FLOOR_MS = 60000L; + // Backoff stages used as multipliers for the minimum delay floor (standard or screen-off) + @VisibleForTesting static final int[] BACKOFF_MULTIPLIERS = {1, 1, 2, 2, 4}; + // Start screen-off trigger interval throttling after the screen has been off for this period + // of time. This allows the screen-on intervals to be used for a short period of time after the + // screen has gone off, and avoids too much flipping between screen-off and screen-on backoffs + // when the screen is off for a short period of time + @VisibleForTesting static final long SCREEN_OFF_DELAY_MS = 60000L; + private final TimeProvider mTimeProvider; + private final long mDelayFloor; + private final long mScreenOffDelayFloor; + private int mBackoffStage = 0; + private long mScreenOffTriggerTime = 0L; + private boolean mScreenOffThrottling = false; + + BatchScanThrottler(TimeProvider timeProvider, boolean screenOn) { + mTimeProvider = timeProvider; + mDelayFloor = + DeviceConfig.getLong( + DeviceConfig.NAMESPACE_BLUETOOTH, + "report_delay", + DEFAULT_REPORT_DELAY_FLOOR); + mScreenOffDelayFloor = Math.max(mDelayFloor, SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS); + onScreenOn(screenOn); + } + + void resetBackoff() { + mBackoffStage = 0; + } + + void onScreenOn(boolean screenOn) { + if (screenOn) { + mScreenOffTriggerTime = 0L; + mScreenOffThrottling = false; + resetBackoff(); + } else { + // Screen-off intervals to be used after the trigger time + mScreenOffTriggerTime = mTimeProvider.elapsedRealtime() + SCREEN_OFF_DELAY_MS; + } + } + + long getBatchTriggerIntervalMillis(Set<ScanClient> batchClients) { + // Check if we're past the screen-off time and should be using screen-off backoff values + if (!mScreenOffThrottling + && mScreenOffTriggerTime != 0 + && mTimeProvider.elapsedRealtime() >= mScreenOffTriggerTime) { + mScreenOffThrottling = true; + resetBackoff(); + } + long unfilteredFloor = + mScreenOffThrottling + ? UNFILTERED_SCREEN_OFF_DELAY_FLOOR_MS + : UNFILTERED_DELAY_FLOOR_MS; + long intervalMillis = Long.MAX_VALUE; + for (ScanClient client : batchClients) { + if (client.settings.getReportDelayMillis() > 0) { + long clientIntervalMillis = client.settings.getReportDelayMillis(); + if ((client.filters == null || client.filters.isEmpty()) + && clientIntervalMillis < unfilteredFloor) { + clientIntervalMillis = unfilteredFloor; + } + intervalMillis = Math.min(intervalMillis, clientIntervalMillis); + } + } + int backoffIndex = + mBackoffStage >= BACKOFF_MULTIPLIERS.length + ? BACKOFF_MULTIPLIERS.length - 1 + : mBackoffStage++; + return Math.max( + intervalMillis, + (mScreenOffThrottling ? mScreenOffDelayFloor : mDelayFloor) + * BACKOFF_MULTIPLIERS[backoffIndex]); + } +} diff --git a/android/app/tests/unit/Android.bp b/android/app/tests/unit/Android.bp index 346c57fffa..eae8e4a89f 100644 --- a/android/app/tests/unit/Android.bp +++ b/android/app/tests/unit/Android.bp @@ -22,6 +22,7 @@ java_defaults { static_libs: [ "PlatformProperties", + "TestParameterInjector", "android.media.audio-aconfig-exported-java", "androidx.media_media", "androidx.room_room-migration", diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java index cd9ad4dd9a..b3d61e33af 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/BrowserPlayerWrapperTest.java @@ -40,7 +40,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; @@ -75,7 +74,6 @@ public class BrowserPlayerWrapperTest { private HandlerThread mThread; @Mock Context mMockContext; - @Mock Resources mMockResources; private Context mTargetContext; private Resources mTestResources; private MockContentResolver mTestContentResolver; @@ -116,8 +114,7 @@ public class BrowserPlayerWrapperTest { }); when(mMockContext.getContentResolver()).thenReturn(mTestContentResolver); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); - when(mMockContext.getResources()).thenReturn(mMockResources); + Util.sUriImagesSupport = true; // Set up Looper thread for the timeout handler mThread = new HandlerThread("MediaPlayerWrapperTestThread"); @@ -138,6 +135,7 @@ public class BrowserPlayerWrapperTest { mTestBitmap = null; mTestResources = null; mTargetContext = null; + Util.sUriImagesSupport = false; } private Bitmap loadImage(int resId) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java index 60ca5a5aef..6c6e6e78fc 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/ImageTest.java @@ -36,7 +36,6 @@ import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; @@ -57,7 +56,6 @@ public class ImageTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private @Mock Context mMockContext; - private @Mock Resources mMockResources; private Resources mTestResources; private MockContentResolver mTestContentResolver; @@ -108,8 +106,7 @@ public class ImageTest { }); when(mMockContext.getContentResolver()).thenReturn(mTestContentResolver); - when(mMockContext.getResources()).thenReturn(mMockResources); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); + Util.sUriImagesSupport = true; } @After @@ -119,6 +116,7 @@ public class ImageTest { mTestResources = null; mTargetContext = null; mMockContext = null; + Util.sUriImagesSupport = false; } private Bitmap loadImage(int resId) { @@ -287,7 +285,7 @@ public class ImageTest { */ @Test public void testCreateImageFromMediaMetadataWithArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata metadata = getMediaMetadataWithUri(MediaMetadata.METADATA_KEY_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, metadata); @@ -302,7 +300,7 @@ public class ImageTest { */ @Test public void testCreateImageFromMediaMetadataWithAlbumArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata metadata = getMediaMetadataWithUri(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, metadata); @@ -317,7 +315,7 @@ public class ImageTest { */ @Test public void testCreateImageFromMediaMetadataWithDisplayIconUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata metadata = getMediaMetadataWithUri( MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, IMAGE_STRING_1); @@ -459,7 +457,7 @@ public class ImageTest { */ @Test public void testCreateImageFromBundleWithArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, bundle); assertThat(artwork.getImage()).isNull(); @@ -473,7 +471,7 @@ public class ImageTest { */ @Test public void testCreateImageFromBundleWithAlbumArtUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, bundle); assertThat(artwork.getImage()).isNull(); @@ -487,7 +485,7 @@ public class ImageTest { */ @Test public void testCreateImageFromBundleWithDisplayIconUriDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, IMAGE_STRING_1); Image artwork = new Image(mMockContext, bundle); diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java index 2b323d9810..794a1200b3 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MediaPlayerWrapperTest.java @@ -36,9 +36,9 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -75,7 +75,6 @@ public class MediaPlayerWrapperTest { @Mock MediaController mMockController; @Mock MediaPlayerWrapper.Callback mTestCbs; @Mock Context mMockContext; - @Mock Resources mMockResources; List<MediaSession.QueueItem> getQueueFromDescriptions( List<MediaDescription.Builder> descriptions) { @@ -98,8 +97,7 @@ public class MediaPlayerWrapperTest { InstrumentationRegistry.getInstrumentation().getTargetContext()); mTestBitmap = loadImage(com.android.bluetooth.tests.R.raw.image_200_200); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); - when(mMockContext.getResources()).thenReturn(mMockResources); + Util.sUriImagesSupport = true; // Set failure handler to capture Log.wtf messages Log.setWtfHandler(mFailHandler); @@ -160,6 +158,14 @@ public class MediaPlayerWrapperTest { MediaPlayerWrapper.sTesting = true; } + @After + public void tearDown() { + if (mThread != null) { + mThread.quitSafely(); + } + Util.sUriImagesSupport = false; + } + private Bitmap loadImage(int resId) { InputStream imageInputStream = mTestResources.openRawResource(resId); return BitmapFactory.decodeStream(imageInputStream); diff --git a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java index 5ba6e0f4e5..4cc12f763d 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/audio_util/MetadataTest.java @@ -38,7 +38,6 @@ import android.test.mock.MockContentResolver; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import org.junit.After; @@ -59,7 +58,6 @@ public class MetadataTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private @Mock Context mMockContext; - private @Mock Resources mMockResources; private Resources mTestResources; private MockContentResolver mTestContentResolver; @@ -111,8 +109,7 @@ public class MetadataTest { }); when(mMockContext.getContentResolver()).thenReturn(mTestContentResolver); - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(true); - when(mMockContext.getResources()).thenReturn(mMockResources); + Util.sUriImagesSupport = true; mSongImage = new Image(mMockContext, mTestBitmap); } @@ -125,6 +122,7 @@ public class MetadataTest { mTestResources = null; mTargetContext = null; mMockContext = null; + Util.sUriImagesSupport = false; } private Bitmap loadImage(int resId) { @@ -427,7 +425,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromMediaMetadataWithUriAndUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaMetadata m = getMediaMetadataWithUri(MediaMetadata.METADATA_KEY_ART_URI, IMAGE_URI_1); Metadata metadata = new Metadata.Builder().useContext(mMockContext).fromMediaMetadata(m).build(); @@ -737,7 +735,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromBundleWithUriAndUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; Bundle bundle = getBundleWithUri(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI, IMAGE_URI_1); Metadata metadata = new Metadata.Builder().useContext(mMockContext).fromBundle(bundle).build(); @@ -852,7 +850,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromMediaItemWithIconUriAndUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaDescription description = getMediaDescription(null, IMAGE_URI_1, null); MediaItem item = getMediaItem(description); Metadata metadata = @@ -980,7 +978,7 @@ public class MetadataTest { */ @Test public void testBuildMetadataFromQueueItemWithIconUriandUrisDisabled() { - when(mMockResources.getBoolean(R.bool.avrcp_target_cover_art_uri_images)).thenReturn(false); + Util.sUriImagesSupport = false; MediaDescription description = getMediaDescription(null, IMAGE_URI_1, null); QueueItem queueItem = getQueueItem(description); Metadata metadata = diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java index 2749dd7e09..c1c7734f2e 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceTest.java @@ -4472,6 +4472,8 @@ public class BassClientServiceTest { // Verify getSyncedBroadcastSinks returns empty device list if no broadcst ID assertThat(mBassClientService.getSyncedBroadcastSinks().isEmpty()).isTrue(); + assertThat(mBassClientService.getSyncedBroadcastSinks(TEST_BROADCAST_ID).isEmpty()) + .isTrue(); // Update receiver state with broadcast ID injectRemoteSourceStateChanged(meta, true, false); @@ -4487,6 +4489,16 @@ public class BassClientServiceTest { assertThat(mBassClientService.getSyncedBroadcastSinks().isEmpty()).isTrue(); } + activeSinks.clear(); + // Verify getSyncedBroadcastSinks by broadcast id + activeSinks = mBassClientService.getSyncedBroadcastSinks(TEST_BROADCAST_ID); + if (Flags.leaudioBigDependsOnAudioState()) { + // Verify getSyncedBroadcastSinks returns correct device list if no BIS synced + assertThat(activeSinks.size()).isEqualTo(2); + assertThat(activeSinks.contains(mCurrentDevice)).isTrue(); + assertThat(activeSinks.contains(mCurrentDevice1)).isTrue(); + } + // Update receiver state with BIS sync injectRemoteSourceStateChanged(meta, true, true); @@ -6348,7 +6360,8 @@ public class BassClientServiceTest { @Test @EnableFlags({ Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER, - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE + Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE, + Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR }) public void sinkUnintentional_handleUnicastSourceStreamStatusChange_withoutScanning() { sinkUnintentionalWithoutScanning(); @@ -6357,7 +6370,6 @@ public class BassClientServiceTest { mBassClientService.handleUnicastSourceStreamStatusChange( 0 /* STATUS_LOCAL_STREAM_REQUESTED */); verifyStopBigMonitoringWithUnsync(); - verifyRemoveMessageAndInjectSourceRemoval(); checkNoResumeSynchronizationByBig(); /* Unicast finished streaming */ @@ -6370,7 +6382,8 @@ public class BassClientServiceTest { @Test @EnableFlags({ Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER, - Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE + Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE, + Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR }) public void sinkUnintentional_handleUnicastSourceStreamStatusChange_duringScanning() { sinkUnintentionalDuringScanning(); @@ -6379,7 +6392,6 @@ public class BassClientServiceTest { mBassClientService.handleUnicastSourceStreamStatusChange( 0 /* STATUS_LOCAL_STREAM_REQUESTED */); verifyStopBigMonitoringWithoutUnsync(); - verifyRemoveMessageAndInjectSourceRemoval(); checkNoResumeSynchronizationByBig(); /* Unicast finished streaming */ @@ -6642,6 +6654,44 @@ public class BassClientServiceTest { @Test @EnableFlags({ Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER, + Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE, + Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT, + Flags.FLAG_LEAUDIO_MONITOR_UNICAST_SOURCE_WHEN_MANAGED_BY_BROADCAST_DELEGATOR + }) + public void hostIntentional_handleUnicastSourceStreamStatusChange_beforeResumeCompleted() { + prepareSynchronizedPairAndStopSearching(); + + /* Unicast would like to stream */ + mBassClientService.handleUnicastSourceStreamStatusChange( + 0 /* STATUS_LOCAL_STREAM_REQUESTED */); + checkNoSinkPause(); + + /* Unicast finished streaming */ + mBassClientService.handleUnicastSourceStreamStatusChange( + 2 /* STATUS_LOCAL_STREAM_SUSPENDED */); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), any(), anyInt(), anyInt(), any(), any()); + + /* Unicast would like to stream again before previous resume was complete*/ + mBassClientService.handleUnicastSourceStreamStatusChange( + 0 /* STATUS_LOCAL_STREAM_REQUESTED */); + + /* Unicast finished streaming */ + mBassClientService.handleUnicastSourceStreamStatusChange( + 2 /* STATUS_LOCAL_STREAM_SUSPENDED */); + mInOrderMethodProxy + .verify(mMethodProxy) + .periodicAdvertisingManagerRegisterSync( + any(), any(), anyInt(), anyInt(), any(), any()); + onSyncEstablished(mSourceDevice, TEST_SYNC_HANDLE); // In case of add source to inactive + verifyAllGroupMembersGettingUpdateOrAddSource(createBroadcastMetadata(TEST_BROADCAST_ID)); + } + + @Test + @EnableFlags({ + Flags.FLAG_LEAUDIO_BROADCAST_RESYNC_HELPER, Flags.FLAG_LEAUDIO_BROADCAST_EXTRACT_PERIODIC_SCANNER_FROM_STATE_MACHINE }) public void hostIntentional_handleUnicastSourceStreamStatusChangeNoContext_withoutScanning() { diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java index cc40c6af72..e274ac6eed 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/BondStateMachineTest.java @@ -218,7 +218,7 @@ public class BondStateMachineTest { RemoteDevices.DeviceProperties testDeviceProperties = mRemoteDevices.addDeviceProperties(TEST_BT_ADDR_BYTES); - testDeviceProperties.mUuids = TEST_UUIDS; + testDeviceProperties.mUuidsBrEdr = TEST_UUIDS; BluetoothDevice testDevice = testDeviceProperties.getDevice(); assertThat(testDevice).isNotNull(); @@ -228,7 +228,7 @@ public class BondStateMachineTest { bondingMsg.arg2 = AbstractionLayer.BT_STATUS_RMT_DEV_DOWN; mBondStateMachine.sendMessage(bondingMsg); - pendingDeviceProperties.mUuids = TEST_UUIDS; + pendingDeviceProperties.mUuidsBrEdr = TEST_UUIDS; Message uuidUpdateMsg = mBondStateMachine.obtainMessage(BondStateMachine.UUID_UPDATE); uuidUpdateMsg.obj = pendingDevice; @@ -634,7 +634,7 @@ public class BondStateMachineTest { } if (uuids != null) { // Add dummy UUID for the device. - mDeviceProperties.mUuids = TEST_UUIDS; + mDeviceProperties.mUuidsBrEdr = TEST_UUIDS; } testSendIntentCase( oldState, diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java index 3a0abb2870..1eca1e2483 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java @@ -16,6 +16,8 @@ package com.android.bluetooth.gatt; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -54,6 +56,13 @@ public class AdvertiseBinderTest { @Before public void setUp() { + doAnswer( + invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }) + .when(mAdvertiseManager) + .doOnAdvertiseThread(any()); mBinder = new AdvertiseBinder(mAdapterService, mAdvertiseManager); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java index 6621a43331..4cd5423ce9 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseManagerTest.java @@ -27,15 +27,16 @@ import android.bluetooth.le.AdvertisingSetParameters; import android.bluetooth.le.IAdvertisingSetCallback; import android.bluetooth.le.PeriodicAdvertisingParameters; import android.os.IBinder; +import android.os.test.TestLooper; +import android.platform.test.flag.junit.FlagsParameterization; +import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; -import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; +import com.android.bluetooth.flags.Flags; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -44,17 +45,21 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + +import java.util.List; + /** Test cases for {@link AdvertiseManager}. */ @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) public class AdvertiseManagerTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + @Rule public final SetFlagsRule mSetFlagsRule; @Mock private AdapterService mAdapterService; - @Mock private GattService mService; - @Mock private AdvertiserMap mAdvertiserMap; @Mock private AdvertiseManagerNativeInterface mNativeInterface; @@ -66,10 +71,23 @@ public class AdvertiseManagerTest { private AdvertiseManager mAdvertiseManager; private int mAdvertiserId; + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf(Flags.FLAG_ADVERTISE_THREAD); + } + + public AdvertiseManagerTest(FlagsParameterization flags) { + mSetFlagsRule = new SetFlagsRule(flags); + } + @Before public void setUp() throws Exception { - TestUtils.setAdapterService(mAdapterService); - mAdvertiseManager = new AdvertiseManager(mService, mNativeInterface, mAdvertiserMap); + mAdvertiseManager = + new AdvertiseManager( + mAdapterService, + new TestLooper().getLooper(), + mNativeInterface, + mAdvertiserMap); AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build(); AdvertiseData advertiseData = new AdvertiseData.Builder().build(); @@ -95,12 +113,7 @@ public class AdvertiseManagerTest { mCallback, InstrumentationRegistry.getTargetContext().getAttributionSource()); - mAdvertiserId = AdvertiseManager.sTempRegistrationId; - } - - @After - public void tearDown() throws Exception { - TestUtils.clearAdapterService(mAdapterService); + mAdvertiserId = mAdvertiseManager.mTempRegistrationId; } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java index f14884bcb8..b7a9a49116 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThrows; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -47,6 +48,7 @@ import android.media.AudioManager; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.SystemClock; +import android.platform.test.annotations.EnableFlags; import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; @@ -1296,6 +1298,54 @@ public class HeadsetServiceTest { assertThat(mHeadsetService.isInbandRingingEnabled()).isFalse(); } + @Test + @EnableFlags({Flags.FLAG_UPDATE_ACTIVE_DEVICE_IN_BAND_RINGTONE}) + public void testIncomingCallDeviceConnect_InbandRingStatus() { + when(mDatabaseManager.getProfileConnectionPolicy( + any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) + .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); + mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0); + connectDeviceHelper(mCurrentDevice); + + when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice); + when(mStateMachines.get(mCurrentDevice).getConnectionState()) + .thenReturn(BluetoothProfile.STATE_CONNECTED); + + when(mSystemInterface.isRinging()).thenReturn(true); + mHeadsetService.setActiveDevice(mCurrentDevice); + + verify(mNativeInterface).setActiveDevice(mCurrentDevice); + verify(mStateMachines.get(mCurrentDevice)) + .sendMessage(HeadsetStateMachine.CONNECT_AUDIO, mCurrentDevice); + verify(mStateMachines.get(mCurrentDevice)) + .sendMessage(eq(HeadsetStateMachine.SEND_BSIR), eq(1)); + } + + @Test + @EnableFlags({Flags.FLAG_UPDATE_ACTIVE_DEVICE_IN_BAND_RINGTONE}) + public void testIncomingCallWithDeviceAudioConnected() { + ArrayList<BluetoothDevice> connectedDevices = new ArrayList<>(); + when(mDatabaseManager.getProfileConnectionPolicy( + any(BluetoothDevice.class), eq(BluetoothProfile.HEADSET))) + .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN); + for (int i = 2; i >= 0; i--) { + mCurrentDevice = TestUtils.getTestDevice(mAdapter, i); + connectDeviceHelper(mCurrentDevice); + connectedDevices.add(mCurrentDevice); + } + + mHeadsetService.setActiveDevice(connectedDevices.get(1)); + when(mStateMachines.get(connectedDevices.get(1)).getAudioState()) + .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED); + + when(mSystemInterface.isRinging()).thenReturn(true); + mHeadsetService.setActiveDevice(connectedDevices.get(2)); + + verify(mNativeInterface).setActiveDevice(connectedDevices.get(2)); + verify(mStateMachines.get(connectedDevices.get(2)), atLeast(1)) + .sendMessage(eq(HeadsetStateMachine.SEND_BSIR), eq(0)); + } + private void addConnectedDeviceHelper(BluetoothDevice device) { mCurrentDevice = device; when(mDatabaseManager.getProfileConnectionPolicy( @@ -1329,4 +1379,19 @@ public class HeadsetServiceTest { .thenReturn(priority); assertThat(mHeadsetService.okToAcceptConnection(device, false)).isEqualTo(expected); } + + private void connectDeviceHelper(BluetoothDevice device) { + assertThat(mHeadsetService.connect(device)).isTrue(); + verify(mObjectsFactory) + .makeStateMachine( + device, + mHeadsetService.getStateMachinesThreadLooper(), + mHeadsetService, + mAdapterService, + mNativeInterface, + mSystemInterface); + when(mStateMachines.get(device).getDevice()).thenReturn(device); + when(mStateMachines.get(device).getConnectionState()) + .thenReturn(BluetoothProfile.STATE_CONNECTED); + } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java index e594c924a8..0b42a50d92 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetStateMachineTest.java @@ -1793,28 +1793,6 @@ public class HeadsetStateMachineTest { } @Test - @EnableFlags({Flags.FLAG_HFP_ALLOW_VOLUME_CHANGE_WITHOUT_SCO}) - public void testVolumeChangeEvent_fromIntentWhenConnected() { - setUpConnectedState(); - int originalVolume = mHeadsetStateMachine.mSpeakerVolume; - mHeadsetStateMachine.mSpeakerVolume = 0; - int vol = 10; - - // Send INTENT_SCO_VOLUME_CHANGED message - Intent volumeChange = new Intent(AudioManager.ACTION_VOLUME_CHANGED); - volumeChange.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, vol); - - mHeadsetStateMachine.sendMessage( - HeadsetStateMachine.INTENT_SCO_VOLUME_CHANGED, volumeChange); - TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper()); - - // verify volume processed - verify(mNativeInterface).setVolume(mTestDevice, HeadsetHalConstants.VOLUME_TYPE_SPK, vol); - - mHeadsetStateMachine.mSpeakerVolume = originalVolume; - } - - @Test public void testVolumeChangeEvent_fromIntentWhenAudioOn() { setUpAudioOnState(); int originalVolume = mHeadsetStateMachine.mSpeakerVolume; diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java index 4ac2e96ce7..8433d1398c 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBroadcastServiceTest.java @@ -1730,6 +1730,30 @@ public class LeAudioBroadcastServiceTest { } } + @Test + public void testGetLocalBroadcastReceivers() { + int broadcastId = 243; + byte[] code = {0x00, 0x01, 0x00, 0x02}; + + synchronized (mService.mBroadcastCallbacks) { + mService.mBroadcastCallbacks.register(mCallbacks); + } + + BluetoothLeAudioContentMetadata.Builder meta_builder = + new BluetoothLeAudioContentMetadata.Builder(); + meta_builder.setLanguage("deu"); + meta_builder.setProgramInfo("Subgroup broadcast info"); + BluetoothLeAudioContentMetadata meta = meta_builder.build(); + + verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 1)); + when(mBassClientService.getSyncedBroadcastSinks(broadcastId)).thenReturn(List.of(mDevice)); + assertThat(mService.getLocalBroadcastReceivers().size()).isEqualTo(1); + assertThat(mService.getLocalBroadcastReceivers()).containsExactly(mDevice); + + verifyBroadcastStopped(broadcastId); + assertThat(mService.getLocalBroadcastReceivers()).isEmpty(); + } + private class LeAudioIntentReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java index 312a031f82..4fea856b9b 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceTest.java @@ -1243,8 +1243,6 @@ public class LeAudioServiceTest { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; - int snkAudioLocation = 3; - int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; // Not connected device @@ -1254,15 +1252,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); // Add location support - LeAudioStackEvent audioConfChangedEvent = - new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); - audioConfChangedEvent.device = mSingleDevice; - audioConfChangedEvent.valueInt1 = direction; - audioConfChangedEvent.valueInt2 = groupId; - audioConfChangedEvent.valueInt3 = snkAudioLocation; - audioConfChangedEvent.valueInt4 = srcAudioLocation; - audioConfChangedEvent.valueInt5 = availableContexts; - mService.messageFromNative(audioConfChangedEvent); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -1293,8 +1283,6 @@ public class LeAudioServiceTest { int groupId = 1; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; - int snkAudioLocation = 3; - int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; // Not connected device @@ -1304,15 +1292,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); // Add location support - LeAudioStackEvent audioConfChangedEvent = - new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); - audioConfChangedEvent.device = mSingleDevice; - audioConfChangedEvent.valueInt1 = direction; - audioConfChangedEvent.valueInt2 = groupId; - audioConfChangedEvent.valueInt3 = snkAudioLocation; - audioConfChangedEvent.valueInt4 = srcAudioLocation; - audioConfChangedEvent.valueInt5 = availableContexts; - mService.messageFromNative(audioConfChangedEvent); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -1357,8 +1337,6 @@ public class LeAudioServiceTest { /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ /* AUDIO_DIRECTION_INPUT_BIT = 0x02 */ int direction = 3; - int snkAudioLocation = 3; - int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; ArgumentCaptor<BluetoothProfileConnectionInfo> connectionInfoArgumentCaptor = @@ -1372,15 +1350,7 @@ public class LeAudioServiceTest { connectTestDevice(mRightDevice, groupId); // Add location support - LeAudioStackEvent audioConfChangedEvent = - new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); - audioConfChangedEvent.device = mSingleDevice; - audioConfChangedEvent.valueInt1 = direction; - audioConfChangedEvent.valueInt2 = groupId; - audioConfChangedEvent.valueInt3 = snkAudioLocation; - audioConfChangedEvent.valueInt4 = srcAudioLocation; - audioConfChangedEvent.valueInt5 = availableContexts; - mService.messageFromNative(audioConfChangedEvent); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -1426,7 +1396,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); verify(mNativeInterface, times(0)).groupSetActive(groupId); @@ -1453,8 +1423,8 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice_2, groupId_2); // Add location support - injectAudioConfChanged(groupId_1, availableContexts, direction); - injectAudioConfChanged(groupId_2, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId_1, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId_2, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId_1); @@ -1530,7 +1500,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, groupId_1); // Add location support - injectAudioConfChanged(groupId_1, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId_1, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId_1); @@ -1577,7 +1547,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -1636,8 +1606,6 @@ public class LeAudioServiceTest { int groupId_2 = 2; /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */ int direction = 1; - int snkAudioLocation = 3; - int srcAudioLocation = 4; int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE; int broadcastId = 243; byte[] code = {0x00, 0x01, 0x00, 0x02}; @@ -1680,17 +1648,8 @@ public class LeAudioServiceTest { broadcastStateStreamingEvent.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_STREAMING; mService.messageFromNative(broadcastStateStreamingEvent); - injectAudioConfChanged(groupId, availableContexts, direction); - - LeAudioStackEvent audioConfChangedEvent = - new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED); - audioConfChangedEvent.device = mSingleDevice_2; - audioConfChangedEvent.valueInt1 = direction; - audioConfChangedEvent.valueInt2 = groupId_2; - audioConfChangedEvent.valueInt3 = snkAudioLocation; - audioConfChangedEvent.valueInt4 = srcAudioLocation; - audioConfChangedEvent.valueInt5 = availableContexts; - mService.messageFromNative(audioConfChangedEvent); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice_2, groupId_2, availableContexts, direction); // Verify only update the fallback group and not proceed to change active assertThat(mService.setActiveDevice(mSingleDevice_2)).isTrue(); @@ -1728,7 +1687,7 @@ public class LeAudioServiceTest { assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); @@ -1769,14 +1728,15 @@ public class LeAudioServiceTest { mService.messageFromNative(groupStreamStatusChangedEvent); } - private void injectAudioConfChanged(int groupId, Integer availableContexts, int direction) { + private void injectAudioConfChanged( + BluetoothDevice device, int groupId, Integer availableContexts, int direction) { int snkAudioLocation = 3; int srcAudioLocation = 4; int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED; // Add device to group LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(eventType); - audioConfChangedEvent.device = mSingleDevice; + audioConfChangedEvent.device = device; audioConfChangedEvent.valueInt1 = direction; audioConfChangedEvent.valueInt2 = groupId; audioConfChangedEvent.valueInt3 = snkAudioLocation; @@ -1798,6 +1758,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); @@ -1819,6 +1780,7 @@ public class LeAudioServiceTest { // Remove source direction injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 1); @@ -1840,6 +1802,7 @@ public class LeAudioServiceTest { // remove Sink and add Source back injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 2); @@ -1868,6 +1831,7 @@ public class LeAudioServiceTest { public void testMessageFromNativeAudioConfChangedActiveGroup() { connectTestDevice(mSingleDevice, testGroupId); injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); @@ -1891,7 +1855,7 @@ public class LeAudioServiceTest { Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL; - injectAudioConfChanged(testGroupId, contexts, 3); + injectAudioConfChanged(mSingleDevice, testGroupId, contexts, 3); TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); } @@ -1901,7 +1865,7 @@ public class LeAudioServiceTest { public void testMessageFromNativeAudioConfChangedNoGroupChanged() { connectTestDevice(mSingleDevice, testGroupId); - injectAudioConfChanged(testGroupId, 0, 3); + injectAudioConfChanged(mSingleDevice, testGroupId, 0, 3); TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice)); } @@ -1934,6 +1898,32 @@ public class LeAudioServiceTest { assertThat(mService.mLeAudioNativeIsInitialized).isTrue(); } + @Test + public void testHealthBasedGroupAction_recommendDisable() { + doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class)); + connectTestDevice(mLeftDevice, testGroupId); + connectTestDevice(mRightDevice, testGroupId); + + LeAudioStackEvent healthBasedGroupAction = + new LeAudioStackEvent( + LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION); + healthBasedGroupAction.valueInt1 = testGroupId; + healthBasedGroupAction.valueInt2 = LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_DISABLE; + mService.messageFromNative(healthBasedGroupAction); + assertThat(mService.mLeAudioNativeIsInitialized).isTrue(); + + verify(mDatabaseManager) + .setProfileConnectionPolicy( + mLeftDevice, + BluetoothProfile.LE_AUDIO, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + verify(mDatabaseManager) + .setProfileConnectionPolicy( + mRightDevice, + BluetoothProfile.LE_AUDIO, + BluetoothProfile.CONNECTION_POLICY_FORBIDDEN); + } + private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) { onGroupStatusCallbackCalled = false; @@ -1984,6 +1974,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); @@ -2044,6 +2035,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); @@ -2233,6 +2225,7 @@ public class LeAudioServiceTest { injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG); injectAudioConfChanged( + mSingleDevice, testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3); @@ -2369,7 +2362,7 @@ public class LeAudioServiceTest { assertThat(mService.setActiveDevice(leadDevice)).isFalse(); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(leadDevice)).isTrue(); @@ -2435,7 +2428,7 @@ public class LeAudioServiceTest { assertThat(mService.setActiveDevice(leadDevice)).isFalse(); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(leadDevice)).isTrue(); @@ -2496,7 +2489,7 @@ public class LeAudioServiceTest { ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class); // Add location support. - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); @@ -2555,7 +2548,7 @@ public class LeAudioServiceTest { ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class); // Add location support. - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper()); @@ -2845,7 +2838,7 @@ public class LeAudioServiceTest { assertThat(mService.setActiveDevice(mSingleDevice)).isFalse(); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); @@ -2879,7 +2872,7 @@ public class LeAudioServiceTest { assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -2896,12 +2889,12 @@ public class LeAudioServiceTest { reset(mNativeInterface); /* Don't expect any change. */ - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); verify(mNativeInterface, times(0)).groupSetActive(groupId); reset(mNativeInterface); /* Expect device to be incactive */ - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); verify(mNativeInterface).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); @@ -2916,7 +2909,7 @@ public class LeAudioServiceTest { reset(mAudioManager); /* Expect device to be incactive */ - injectAudioConfChanged(groupId, 1, direction); + injectAudioConfChanged(mSingleDevice, groupId, 1, direction); verify(mNativeInterface).groupSetActive(groupId); reset(mNativeInterface); @@ -2968,7 +2961,7 @@ public class LeAudioServiceTest { assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.isGroupAvailableForStream(groupId)).isTrue(); injectAndVerifyDeviceDisconnected(mLeftDevice); @@ -2997,7 +2990,7 @@ public class LeAudioServiceTest { assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.isGroupAvailableForStream(groupId)).isTrue(); @@ -3034,7 +3027,7 @@ public class LeAudioServiceTest { assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.isGroupAvailableForStream(groupId)).isTrue(); @@ -3045,7 +3038,7 @@ public class LeAudioServiceTest { assertThat(mService.isAutoActiveModeEnabled(groupId)).isFalse(); injectAndVerifyDeviceConnected(mLeftDevice); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); assertThat(mService.isAutoActiveModeEnabled(groupId)).isTrue(); @@ -3079,7 +3072,7 @@ public class LeAudioServiceTest { List<BluetoothDevice> groupDevicesById = mService.getGroupDevices(groupId); assertThat(groupDevicesById).containsExactly(mLeftDevice, mRightDevice); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.isGroupAvailableForStream(groupId)).isTrue(); @@ -3090,7 +3083,7 @@ public class LeAudioServiceTest { assertThat(mService.isAutoActiveModeEnabled(groupId)).isFalse(); injectAndVerifyDeviceConnected(mLeftDevice); - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); verify(mScanController).registerScannerInternal(scanCallbacks.capture(), any(), any()); @@ -3133,13 +3126,13 @@ public class LeAudioServiceTest { assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isFalse(); verify(mNativeInterface, times(0)).groupSetActive(groupId); // Expect device to be active - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); verify(mNativeInterface).groupSetActive(groupId); @@ -3155,7 +3148,7 @@ public class LeAudioServiceTest { reset(mNativeInterface); // Expect device to be inactive - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); verify(mNativeInterface).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); @@ -3170,7 +3163,7 @@ public class LeAudioServiceTest { reset(mAudioManager); // Expect device to be active - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); verify(mNativeInterface).groupSetActive(groupId); reset(mNativeInterface); @@ -3217,7 +3210,7 @@ public class LeAudioServiceTest { assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -3234,7 +3227,7 @@ public class LeAudioServiceTest { reset(mNativeInterface); // Expect device to be inactive - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); verify(mNativeInterface).groupSetActive(-1); injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE); @@ -3259,7 +3252,7 @@ public class LeAudioServiceTest { assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse(); // Expect device to be inactive - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); @@ -3268,7 +3261,7 @@ public class LeAudioServiceTest { assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); // Expect device to be inactive - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); generateConnectionMessageFromNative( mRightDevice, @@ -3279,7 +3272,7 @@ public class LeAudioServiceTest { assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); // Expect device to be active - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); verify(mNativeInterface).groupSetActive(groupId); @@ -3321,7 +3314,7 @@ public class LeAudioServiceTest { assertThat(groupDevicesById.contains(mRightDevice)).isTrue(); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mLeftDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -3347,7 +3340,7 @@ public class LeAudioServiceTest { reset(mAudioManager); // Expect device to be inactive - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); generateConnectionMessageFromNative( mLeftDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); @@ -3356,7 +3349,7 @@ public class LeAudioServiceTest { assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue(); // Expect device to be inactive - injectAudioConfChanged(groupId, 0, direction); + injectAudioConfChanged(mSingleDevice, groupId, 0, direction); generateConnectionMessageFromNative( mRightDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED); @@ -3365,7 +3358,7 @@ public class LeAudioServiceTest { assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue(); // Expect device to be active - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); verify(mNativeInterface).groupSetActive(groupId); @@ -3395,7 +3388,7 @@ public class LeAudioServiceTest { connectTestDevice(mSingleDevice, testGroupId); // Add location support - injectAudioConfChanged(groupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, groupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(groupId); @@ -3461,7 +3454,7 @@ public class LeAudioServiceTest { assertThat(mService.getBroadcastToUnicastFallbackGroup()).isEqualTo(firstGroupId); // Add location support - injectAudioConfChanged(firstGroupId, availableContexts, direction); + injectAudioConfChanged(mSingleDevice, firstGroupId, availableContexts, direction); assertThat(mService.setActiveDevice(mSingleDevice)).isTrue(); verify(mNativeInterface).groupSetActive(firstGroupId); diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java new file mode 100644 index 0000000000..67f32c4f0c --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/BatchScanThrottlerTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.le_scan; + +import static android.bluetooth.le.ScanSettings.SCAN_MODE_BALANCED; + +import static com.android.bluetooth.le_scan.ScanController.DEFAULT_REPORT_DELAY_FLOOR; + +import static com.google.common.truth.Truth.assertThat; + +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanSettings; +import android.platform.test.flag.junit.SetFlagsRule; + +import androidx.test.filters.SmallTest; + +import com.android.bluetooth.TestUtils.FakeTimeProvider; + +import com.google.testing.junit.testparameterinjector.TestParameter; +import com.google.testing.junit.testparameterinjector.TestParameterInjector; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.LongStream; + +/** Test cases for {@link BatchScanThrottler}. */ +@SmallTest +@RunWith(TestParameterInjector.class) +public class BatchScanThrottlerTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); + + private FakeTimeProvider mTimeProvider; + + @Before + public void setUp() { + mTimeProvider = new FakeTimeProvider(); + } + + private void advanceTime(long amountToAdvanceMillis) { + mTimeProvider.advanceTime(Duration.ofMillis(amountToAdvanceMillis)); + } + + @Test + public void basicThrottling( + @TestParameter boolean isFiltered, @TestParameter boolean isScreenOn) { + BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, isScreenOn); + if (!isScreenOn) { + advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS); + } + Set<ScanClient> clients = + Collections.singleton( + createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, isFiltered)); + long[] backoffIntervals = + getBackoffIntervals( + isScreenOn + ? DEFAULT_REPORT_DELAY_FLOOR + : BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS); + for (long x : backoffIntervals) { + long expected = adjustExpectedInterval(x, isFiltered, isScreenOn); + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(expected); + } + long expected = + adjustExpectedInterval( + backoffIntervals[backoffIntervals.length - 1], isFiltered, isScreenOn); + // Ensure that subsequent calls continue to return the final throttled interval + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(expected); + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(expected); + } + + @Test + public void screenOffDelayAndReset(@TestParameter boolean screenOnAtStart) { + BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, screenOnAtStart); + if (screenOnAtStart) { + throttler.onScreenOn(false); + } + Set<ScanClient> clients = + Collections.singleton(createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, true)); + long[] backoffIntervals = getBackoffIntervals(DEFAULT_REPORT_DELAY_FLOOR); + advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS - 1); + for (long x : backoffIntervals) { + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x); + } + + backoffIntervals = + getBackoffIntervals(BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS); + advanceTime(1); + for (long x : backoffIntervals) { + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x); + } + assertThat(throttler.getBatchTriggerIntervalMillis(clients)) + .isEqualTo(backoffIntervals[backoffIntervals.length - 1]); + } + + @Test + public void testScreenOnReset() { + BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, false); + advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS); + Set<ScanClient> clients = + Collections.singleton(createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, true)); + long[] backoffIntervals = + getBackoffIntervals(BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS); + for (long x : backoffIntervals) { + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x); + } + + throttler.onScreenOn(true); + backoffIntervals = getBackoffIntervals(DEFAULT_REPORT_DELAY_FLOOR); + for (long x : backoffIntervals) { + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x); + } + assertThat(throttler.getBatchTriggerIntervalMillis(clients)) + .isEqualTo(backoffIntervals[backoffIntervals.length - 1]); + } + + @Test + public void resetBackoff_restartsToFirstStage(@TestParameter boolean isScreenOn) { + BatchScanThrottler throttler = new BatchScanThrottler(mTimeProvider, isScreenOn); + if (!isScreenOn) { + // Advance the time before we start the test to when the screen-off intervals should be + // used + advanceTime(BatchScanThrottler.SCREEN_OFF_DELAY_MS); + } + Set<ScanClient> clients = + Collections.singleton(createBatchScanClient(DEFAULT_REPORT_DELAY_FLOOR, true)); + long[] backoffIntervals = + getBackoffIntervals( + isScreenOn + ? DEFAULT_REPORT_DELAY_FLOOR + : BatchScanThrottler.SCREEN_OFF_MINIMUM_DELAY_FLOOR_MS); + for (long x : backoffIntervals) { + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x); + } + assertThat(throttler.getBatchTriggerIntervalMillis(clients)) + .isEqualTo(backoffIntervals[backoffIntervals.length - 1]); + + throttler.resetBackoff(); + for (long x : backoffIntervals) { + assertThat(throttler.getBatchTriggerIntervalMillis(clients)).isEqualTo(x); + } + assertThat(throttler.getBatchTriggerIntervalMillis(clients)) + .isEqualTo(backoffIntervals[backoffIntervals.length - 1]); + } + + private long adjustExpectedInterval(long interval, boolean isFiltered, boolean isScreenOn) { + if (isFiltered) { + return interval; + } + long threshold = + isScreenOn + ? BatchScanThrottler.UNFILTERED_DELAY_FLOOR_MS + : BatchScanThrottler.UNFILTERED_SCREEN_OFF_DELAY_FLOOR_MS; + return Math.max(interval, threshold); + } + + private long[] getBackoffIntervals(long baseInterval) { + return LongStream.range(0, BatchScanThrottler.BACKOFF_MULTIPLIERS.length) + .map(x -> BatchScanThrottler.BACKOFF_MULTIPLIERS[(int) x] * baseInterval) + .toArray(); + } + + private ScanClient createBatchScanClient(long reportDelayMillis, boolean isFiltered) { + ScanSettings scanSettings = + new ScanSettings.Builder() + .setScanMode(SCAN_MODE_BALANCED) + .setReportDelay(reportDelayMillis) + .build(); + + return new ScanClient(1, scanSettings, createScanFilterList(isFiltered), 1); + } + + private List<ScanFilter> createScanFilterList(boolean isFiltered) { + List<ScanFilter> scanFilterList = null; + if (isFiltered) { + scanFilterList = List.of(new ScanFilter.Builder().setDeviceName("TestName").build()); + } + return scanFilterList; + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java index 2b19c1aa0b..ea051bc528 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanControllerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothAdapter; @@ -47,7 +48,6 @@ import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; @@ -55,6 +55,9 @@ import com.android.bluetooth.btservice.CompanionManager; import com.android.bluetooth.gatt.GattNativeInterface; import com.android.bluetooth.gatt.GattObjectsFactory; +import com.google.testing.junit.testparameterinjector.TestParameter; +import com.google.testing.junit.testparameterinjector.TestParameterInjector; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -73,7 +76,7 @@ import java.util.Set; /** Test cases for {@link ScanController}. */ @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(TestParameterInjector.class) public class ScanControllerTest { @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -187,7 +190,8 @@ public class ScanControllerTest { } @Test - public void onBatchScanReportsInternal_deliverBatchScan() throws RemoteException { + public void onBatchScanReportsInternal_deliverBatchScan_full( + @TestParameter boolean expectResults) throws RemoteException { int status = 1; int scannerId = 2; int reportType = ScanManager.SCAN_RESULT_TYPE_FULL; @@ -200,28 +204,59 @@ public class ScanControllerTest { Set<ScanClient> scanClientSet = new HashSet<>(); ScanClient scanClient = new ScanClient(scannerId); scanClient.associatedDevices = new ArrayList<>(); - scanClient.associatedDevices.add("02:00:00:00:00:00"); scanClient.scannerId = scannerId; + if (expectResults) { + scanClient.hasScanWithoutLocationPermission = true; + } scanClientSet.add(scanClient); doReturn(scanClientSet).when(mScanManager).getFullBatchScanQueue(); doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId); + IScannerCallback callback = mock(IScannerCallback.class); + mApp.mCallback = callback; mScanController.onBatchScanReportsInternal( status, scannerId, reportType, numRecords, recordData); verify(mScanManager).callbackDone(scannerId, status); + if (expectResults) { + verify(callback).onBatchScanResults(any()); + } else { + verify(callback, never()).onBatchScanResults(any()); + } + } - reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED; - recordData = + @Test + public void onBatchScanReportsInternal_deliverBatchScan_truncated( + @TestParameter boolean expectResults) throws RemoteException { + int status = 1; + int scannerId = 2; + int reportType = ScanManager.SCAN_RESULT_TYPE_TRUNCATED; + int numRecords = 1; + byte[] recordData = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04, 0x02, 0x02, 0x00, 0x00, 0x02 }; + + Set<ScanClient> scanClientSet = new HashSet<>(); + ScanClient scanClient = new ScanClient(scannerId); + scanClient.associatedDevices = new ArrayList<>(); + if (expectResults) { + scanClient.associatedDevices.add("02:00:00:00:00:00"); + } + scanClient.scannerId = scannerId; + scanClientSet.add(scanClient); doReturn(scanClientSet).when(mScanManager).getBatchScanQueue(); + doReturn(mApp).when(mScannerMap).getById(scanClient.scannerId); IScannerCallback callback = mock(IScannerCallback.class); mApp.mCallback = callback; mScanController.onBatchScanReportsInternal( status, scannerId, reportType, numRecords, recordData); - verify(callback).onBatchScanResults(any()); + verify(mScanManager).callbackDone(scannerId, status); + if (expectResults) { + verify(callback).onBatchScanResults(any()); + } else { + verify(callback, never()).onBatchScanResults(any()); + } } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java index 25a27ce876..041a982a9b 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java @@ -1045,7 +1045,7 @@ public class ScanManagerTest { assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); assertThat(mScanManager.getBatchScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode); + assertThat(mScanManager.getBatchScanParams().mScanMode).isEqualTo(expectedScanMode); } } @@ -1075,13 +1075,13 @@ public class ScanManagerTest { sendMessageWaitForProcessed(createStartStopScanMessage(true, client)); assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); - assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode); + assertThat(mScanManager.getBatchScanParams().mScanMode).isEqualTo(expectedScanMode); // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); assertThat(mScanManager.getBatchScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanParams().scanMode).isEqualTo(expectedScanMode); + assertThat(mScanManager.getBatchScanParams().mScanMode).isEqualTo(expectedScanMode); } } @@ -1152,7 +1152,7 @@ public class ScanManagerTest { assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); assertThat(mScanManager.getBatchScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanParams().scanMode) + assertThat(mScanManager.getBatchScanParams().mScanMode) .isEqualTo(expectedScanMode); // Turn on screen sendMessageWaitForProcessed(createScreenOnOffMessage(true)); @@ -1166,7 +1166,7 @@ public class ScanManagerTest { assertThat(mScanManager.getRegularScanQueue()).doesNotContain(client); assertThat(mScanManager.getSuspendedScanQueue()).doesNotContain(client); assertThat(mScanManager.getBatchScanQueue()).contains(client); - assertThat(mScanManager.getBatchScanParams().scanMode) + assertThat(mScanManager.getBatchScanParams().mScanMode) .isEqualTo(expectedScanMode); }); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java index ddd76bb793..345dd8a5ee 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsGenericTest.java @@ -45,6 +45,7 @@ import org.mockito.junit.MockitoRule; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.UUID; @@ -683,4 +684,80 @@ public class TbsGenericTest { anyInt(), eq(BluetoothLeCallControl.RESULT_SUCCESS)); } + + @Test + public void testCallOperationsBlockedForBroadcastReceiver() { + Integer ccid = prepareTestBearer(); + reset(mTbsGatt); + + LeAudioService leAudioService = mock(LeAudioService.class); + mTbsGeneric.setLeAudioServiceForTesting(leAudioService); + + // Prepare the incoming call + UUID callUuid = UUID.randomUUID(); + List<BluetoothLeCall> tbsCalls = new ArrayList<>(); + tbsCalls.add( + new BluetoothLeCall( + callUuid, + "tel:987654321", + "aFriendlyCaller", + BluetoothLeCall.STATE_INCOMING, + 0)); + mTbsGeneric.currentCallsList(ccid, tbsCalls); + + ArgumentCaptor<Map> currentCallsCaptor = ArgumentCaptor.forClass(Map.class); + verify(mTbsGatt).setCallState(currentCallsCaptor.capture()); + Map<Integer, TbsCall> capturedCurrentCalls = currentCallsCaptor.getValue(); + assertThat(capturedCurrentCalls.size()).isEqualTo(1); + Integer callIndex = capturedCurrentCalls.entrySet().iterator().next().getKey(); + reset(mTbsGatt); + + doReturn(new HashSet<>(Arrays.asList(mDevice))) + .when(leAudioService) + .getLocalBroadcastReceivers(); + + doReturn(false).when(leAudioService).isPrimaryDevice(mDevice); + + // Verify call accept + byte args[] = new byte[1]; + args[0] = (byte) (callIndex & 0xFF); + mTbsGattCallback + .getValue() + .onCallControlPointRequest( + mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT, args); + + // Active device should not be changed + verify(leAudioService, never()).setActiveDevice(mDevice); + // Verify if GTBS control point is updated to notify the peer about the result + verify(mTbsGatt) + .setCallControlPointResult( + eq(mDevice), + eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_ACCEPT), + eq(0), + eq(TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE)); + + // Verify call terminate + tbsCalls.clear(); + tbsCalls.add( + new BluetoothLeCall( + callUuid, + "tel:987654321", + "aFriendlyCaller", + BluetoothLeCall.STATE_ACTIVE, + 0)); + mTbsGeneric.currentCallsList(ccid, tbsCalls); + + mTbsGattCallback + .getValue() + .onCallControlPointRequest( + mDevice, TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE, args); + + // Verify if GTBS control point is updated to notify the peer about the result + verify(mTbsGatt) + .setCallControlPointResult( + eq(mDevice), + eq(TbsGatt.CALL_CONTROL_POINT_OPCODE_TERMINATE), + eq(0), + eq(TbsGatt.CALL_CONTROL_POINT_RESULT_OPERATION_NOT_POSSIBLE)); + } } diff --git a/android/pandora/mmi2grpc/mmi2grpc/_rootcanal.py b/android/pandora/mmi2grpc/mmi2grpc/_rootcanal.py index 42ef33f68b..5f68da54f7 100644 --- a/android/pandora/mmi2grpc/mmi2grpc/_rootcanal.py +++ b/android/pandora/mmi2grpc/mmi2grpc/_rootcanal.py @@ -92,6 +92,7 @@ class Dongle(enum.Enum): DEFAULT = "default" LAIRD_BL654 = "laird_bl654" CSR_RCK_PTS_DONGLE = "csr_rck_pts_dongle" + INTEL_BE200 = "intel_be200" class RootCanal: diff --git a/android/pandora/mmi2grpc/mmi2grpc/avrcp.py b/android/pandora/mmi2grpc/mmi2grpc/avrcp.py index 9fbc55d56c..c64eb2113f 100644 --- a/android/pandora/mmi2grpc/mmi2grpc/avrcp.py +++ b/android/pandora/mmi2grpc/mmi2grpc/avrcp.py @@ -117,6 +117,7 @@ class AVRCPProxy(ProfileProxy): Action: Make sure the IUT is in a connectable state. """ + self.mediaplayer.ResetQueue() return "OK" @assert_description @@ -1080,3 +1081,73 @@ class AVRCPProxy(ProfileProxy): """ return "OK" + + @assert_description + def TSC_OBEX_MMI_iut_accept_slc_connect_l2cap(self, **kwargs): + """ + Please accept the l2cap channel connection for an OBEX connection. + """ + + return "OK" + + @assert_description + def TSC_OBEX_MMI_iut_accept_connect(self, **kwargs): + """ + Please accept the OBEX CONNECT REQ. + """ + + return "OK" + + + @assert_description + def TSC_AVRCP_mmi_user_queue_cover_art_element(self, **kwargs): + """ + Take action to play a media element with cover art. Press 'Ok' when + ready. + """ + self.mediaplayer.Play() + + return "OK" + + @assert_description + def TSC_AVRCP_mmi_iut_reject_invalid_get_img(self, **kwargs): + """ + Take action to reject the invalid 'get-img' request sent by the tester. + """ + + return "OK" + + @assert_description + def TSC_BIP_MMI_iut_accept_get_img_properties(self, **kwargs): + """ + Take action to accept the GetImgProperties operation from the tester. + """ + + return "OK" + + @assert_description + def TSC_BIP_MMI_iut_accept_get_img(self, **kwargs): + """ + Take action to accept the GetImg operation from the tester. + """ + + return "OK" + + @assert_description + def TSC_OBEX_MMI_tester_verify_sent_file_or_folder(self, **kwargs): + """ + Was the currently displayed file or folder sent by the IUT? + """ + + return "OK" + + @assert_description + def TSC_AVRCP_mmi_user_queue_no_cover_art_element(self, **kwargs): + """ + Take action to play a media element that does not have any cover art + with it. Press 'Ok' when ready. + """ + self.mediaplayer.UpdateQueue() + self.mediaplayer.PlayUpdated() + + return "OK" diff --git a/android/pandora/server/configs/pts_bot_tests_config.json b/android/pandora/server/configs/pts_bot_tests_config.json index 7b44a66f18..9a80a7471e 100644 --- a/android/pandora/server/configs/pts_bot_tests_config.json +++ b/android/pandora/server/configs/pts_bot_tests_config.json @@ -129,6 +129,11 @@ "AVDTP/SRC/INT/SIG/SMG/BV-31-C", "AVDTP/SRC/INT/SIG/SYN/BV-05-C", "AVDTP/SRC/INT/TRA/BTR/BV-01-C", + "AVRCP/TG/CA/BI-03-C", + "AVRCP/TG/CA/BI-05-C", + "AVRCP/TG/CA/BI-07-C", + "AVRCP/TG/CA/BI-10-C", + "AVRCP/TG/CA/BV-16-C", "AVRCP/CT/CEC/BV-02-I", "AVRCP/CT/CRC/BV-02-I", "AVRCP/TG/CEC/BV-01-I", @@ -769,6 +774,21 @@ "AVDTP/SRC/INT/SIG/SMG/BV-23-C", "AVDTP/SRC/INT/SIG/SMG/BV-33-C", "AVDTP/SRC/INT/SIG/SMG/ESR05/BV-13-C", + "AVRCP/TG/CA/BI-01-C", + "AVRCP/TG/CA/BI-04-C", + "AVRCP/TG/CA/BI-06-C", + "AVRCP/TG/CA/BI-08-C", + "AVRCP/TG/CA/BI-09-C", + "AVRCP/TG/CA/BV-01-I", + "AVRCP/TG/CA/BV-02-C", + "AVRCP/TG/CA/BV-02-I", + "AVRCP/TG/CA/BV-03-I", + "AVRCP/TG/CA/BV-04-C", + "AVRCP/TG/CA/BV-06-C", + "AVRCP/TG/CA/BV-08-C", + "AVRCP/TG/CA/BV-10-C", + "AVRCP/TG/CA/BV-12-C", + "AVRCP/TG/CA/BV-14-C", "AVRCP/CT/CEC/BV-01-I", "AVRCP/CT/CRC/BV-01-I", "AVRCP/CT/PTH/BV-01-C", @@ -1566,6 +1586,7 @@ "TSPC_AVRCP_7_64": true, "TSPC_AVRCP_7_65": true, "TSPC_AVRCP_7_66": true, + "TSPC_AVRCP_7_67": true, "TSPC_AVRCP_7b_4": true, "TSPC_AVRCP_8_19": true, "TSPC_AVRCP_8_20": true, @@ -3215,6 +3236,20 @@ "flags": [ { "flags": [ + "set_addressed_player", + "browsing_refactor", + "avrcp_16_default" + ], + "tests": [ + "AVRCP/TG/CA/BI-03-C", + "AVRCP/TG/CA/BI-05-C", + "AVRCP/TG/CA/BI-07-C", + "AVRCP/TG/CA/BI-10-C", + "AVRCP/TG/CA/BV-16-C" + ] + }, + { + "flags": [ "leaudio_allow_leaudio_only_devices", "enable_hap_by_default" ], diff --git a/android/pandora/server/src/A2dp.kt b/android/pandora/server/src/A2dp.kt index 6708dd1c46..0b5a0d6640 100644 --- a/android/pandora/server/src/A2dp.kt +++ b/android/pandora/server/src/A2dp.kt @@ -41,7 +41,6 @@ import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.filter @@ -110,10 +109,6 @@ class A2dp(val context: Context) : A2DPImplBase(), Closeable { } } - // TODO: b/234891800, AVDTP start request sometimes never sent if playback starts too - // early. - delay(2000L) - val source = Source.newBuilder().setCookie(ByteString.copyFrom(device.getAddress(), "UTF-8")) OpenSourceResponse.newBuilder().setSource(source).build() @@ -147,10 +142,6 @@ class A2dp(val context: Context) : A2DPImplBase(), Closeable { } } - // TODO: b/234891800, AVDTP start request sometimes never sent if playback starts too - // early. - delay(2000L) - val source = Source.newBuilder().setCookie(ByteString.copyFrom(device.getAddress(), "UTF-8")) WaitSourceResponse.newBuilder().setSource(source).build() diff --git a/android/pandora/server/src/MediaPlayer.kt b/android/pandora/server/src/MediaPlayer.kt index 81d6a8a78a..a2c981f881 100644 --- a/android/pandora/server/src/MediaPlayer.kt +++ b/android/pandora/server/src/MediaPlayer.kt @@ -55,6 +55,13 @@ class MediaPlayer(val context: Context) : MediaPlayerImplBase(), Closeable { } } + override fun playUpdated(request: Empty, responseObserver: StreamObserver<Empty>) { + grpcUnary<Empty>(scope, responseObserver) { + MediaPlayerBrowserService.instance.playUpdated() + Empty.getDefaultInstance() + } + } + override fun stop(request: Empty, responseObserver: StreamObserver<Empty>) { grpcUnary<Empty>(scope, responseObserver) { MediaPlayerBrowserService.instance.stop() @@ -111,6 +118,13 @@ class MediaPlayer(val context: Context) : MediaPlayerImplBase(), Closeable { } } + override fun resetQueue(request: Empty, responseObserver: StreamObserver<Empty>) { + grpcUnary<Empty>(scope, responseObserver) { + MediaPlayerBrowserService.instance.resetQueue() + Empty.getDefaultInstance() + } + } + override fun getShuffleMode( request: Empty, responseObserver: StreamObserver<GetShuffleModeResponse> diff --git a/android/pandora/server/src/MediaPlayerBrowserService.kt b/android/pandora/server/src/MediaPlayerBrowserService.kt index b7a358f79c..eaab8a9bbe 100644 --- a/android/pandora/server/src/MediaPlayerBrowserService.kt +++ b/android/pandora/server/src/MediaPlayerBrowserService.kt @@ -17,6 +17,7 @@ package com.android.pandora import android.content.Intent +import android.graphics.Bitmap import android.media.MediaPlayer import android.os.Bundle import android.support.v4.media.* @@ -43,6 +44,7 @@ class MediaPlayerBrowserService : MediaBrowserServiceCompat() { private var metadataItems = mutableMapOf<String, MediaMetadataCompat>() private var queue = mutableListOf<MediaSessionCompat.QueueItem>() private var currentTrack = -1 + private val testIcon = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888) override fun onCreate() { super.onCreate() @@ -114,6 +116,12 @@ class MediaPlayerBrowserService : MediaBrowserServiceCompat() { mediaSession.setMetadata(metadataItems.get("" + currentTrack)) } + fun playUpdated() { + currentTrack = NEW_QUEUE_ITEM_INDEX + setPlaybackState(PlaybackStateCompat.STATE_PLAYING) + mediaSession.setMetadata(metadataItems.get("" + currentTrack)) + } + fun stop() { setPlaybackState(PlaybackStateCompat.STATE_STOPPED) mediaSession.setMetadata(null) @@ -170,12 +178,23 @@ class MediaPlayerBrowserService : MediaBrowserServiceCompat() { .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, NEW_QUEUE_ITEM_INDEX.toLong()) .build() val mediaItem = MediaItem(metaData.description, MediaItem.FLAG_PLAYABLE) + metadataItems.put("" + NEW_QUEUE_ITEM_INDEX, metaData) queue.add( MediaSessionCompat.QueueItem(mediaItem.description, NEW_QUEUE_ITEM_INDEX.toLong()) ) mediaSession.setQueue(queue) } + fun resetQueue() { + if (metadataItems.contains("" + NEW_QUEUE_ITEM_INDEX)) { + metadataItems.remove("" + NEW_QUEUE_ITEM_INDEX) + queue.removeLast() + mediaSession.setQueue(queue) + stop() + currentTrack = QUEUE_START_INDEX + } + } + fun getShuffleMode(): Int { val controller = mediaSession.getController() return controller.getShuffleMode() @@ -261,6 +280,7 @@ class MediaPlayerBrowserService : MediaBrowserServiceCompat() { ) .putLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER, item.toLong()) .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, QUEUE_SIZE.toLong()) + .putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, testIcon) .build() val mediaItem = MediaItem(metaData.description, MediaItem.FLAG_PLAYABLE) mediaItems.add(mediaItem) diff --git a/flags/Android.bp b/flags/Android.bp index 8a1dc66552..80a57891a5 100644 --- a/flags/Android.bp +++ b/flags/Android.bp @@ -31,6 +31,7 @@ aconfig_declarations { "hid.aconfig", "l2cap.aconfig", "le_advertising.aconfig", + "le_scanning.aconfig", "leaudio.aconfig", "mapclient.aconfig", "mcp.aconfig", diff --git a/flags/BUILD.gn b/flags/BUILD.gn index 9f96d85933..da5aa8621b 100644 --- a/flags/BUILD.gn +++ b/flags/BUILD.gn @@ -24,6 +24,7 @@ aconfig("bluetooth_flags_c_lib") { "hid.aconfig", "l2cap.aconfig", "le_advertising.aconfig", + "le_scanning.aconfig", "leaudio.aconfig", "mapclient.aconfig", "mcp.aconfig", diff --git a/flags/bta_dm.aconfig b/flags/bta_dm.aconfig index d91f3168b7..287a0f0187 100644 --- a/flags/bta_dm.aconfig +++ b/flags/bta_dm.aconfig @@ -9,13 +9,6 @@ flag { } flag { - name: "bta_dm_discover_both" - namespace: "bluetooth" - description: "perform both LE and Classic service discovery simulteanously on capable devices" - bug: "339217881" -} - -flag { name: "cancel_open_discovery_client" namespace: "bluetooth" description: "Cancel connection from discovery client correctly" diff --git a/flags/gap.aconfig b/flags/gap.aconfig index 9b139eead2..5da5144aa1 100644 --- a/flags/gap.aconfig +++ b/flags/gap.aconfig @@ -271,3 +271,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "fix_bluetooth_gatt_getting_duplicate_services" + namespace: "bluetooth" + description: "Fixes BluetoothGatt getting duplicate GATT services" + bug: "391773937" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "batch_scan_optimization" + namespace: "bluetooth" + description: "Optimized batch scan for less wakeups" + bug: "392132489" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/hfp.aconfig b/flags/hfp.aconfig index 7b81b483b4..026c3b22b3 100644 --- a/flags/hfp.aconfig +++ b/flags/hfp.aconfig @@ -100,16 +100,6 @@ flag { } flag { - name: "hfp_allow_volume_change_without_sco" - namespace: "bluetooth" - description: "Allow Audio Fwk to change SCO volume when HFP profile is connected and SCO not connected" - bug: "362313390" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "choose_wrong_hfp_codec_in_specific_config" namespace: "bluetooth" description: "Flag to fix codec selection in nego when the peer device only support NB and SWB." diff --git a/flags/le_scanning.aconfig b/flags/le_scanning.aconfig new file mode 100644 index 0000000000..0b4985e45e --- /dev/null +++ b/flags/le_scanning.aconfig @@ -0,0 +1,12 @@ +package: "com.android.bluetooth.flags" +container: "com.android.bt" + +flag { + name: "scan_results_in_main_thread" + namespace: "bluetooth" + description: "Use main thread for handling scan results" + bug: "392693506" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig index e7b0b2abcf..03cc9a4c02 100644 --- a/flags/leaudio.aconfig +++ b/flags/leaudio.aconfig @@ -451,3 +451,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "leaudio_disable_broadcast_for_hap_device" + namespace: "bluetooth" + description: "Disable broadcast feature for HAP device" + bug: "391702876" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/security.aconfig b/flags/security.aconfig index ddfd78611c..006c51f307 100644 --- a/flags/security.aconfig +++ b/flags/security.aconfig @@ -9,6 +9,16 @@ flag { } flag { + name: "key_missing_ble_peripheral" + namespace: "bluetooth" + description: "Key missing broadcast for LE devices in peripheral role" + bug: "392895615" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "key_missing_as_ordered_broadcast" namespace: "bluetooth" description: "Key missing broadcast would be send as ordered broadcast" diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index 5a506f21fb..60657262e3 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -53,7 +53,7 @@ package android.bluetooth { method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void disableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void enableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice); method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BufferConstraints getBufferConstraints(); - method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice); + method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}, conditional=true) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int getDynamicBufferSupport(); method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int isOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice); diff --git a/framework/java/android/bluetooth/BluetoothA2dp.java b/framework/java/android/bluetooth/BluetoothA2dp.java index 5bcd0789ab..54f2c702aa 100644 --- a/framework/java/android/bluetooth/BluetoothA2dp.java +++ b/framework/java/android/bluetooth/BluetoothA2dp.java @@ -724,20 +724,23 @@ public final class BluetoothA2dp implements BluetoothProfile { /** * Gets the current codec status (configuration and capability). * + * <p>This method requires the calling app to have the {@link + * android.Manifest.permission#BLUETOOTH_CONNECT} permission. Additionally, an app must either + * have the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} or be associated with the + * Companion Device manager (see {@link android.companion.CompanionDeviceManager#associate( + * AssociationRequest, android.companion.CompanionDeviceManager.Callback, Handler)}) + * * @param device the remote Bluetooth device. * @return the current codec status * @hide */ @SystemApi - @Nullable @RequiresLegacyBluetoothPermission @RequiresBluetoothConnectPermission @RequiresPermission( - allOf = { - BLUETOOTH_CONNECT, - BLUETOOTH_PRIVILEGED, - }) - public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { + allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, + conditional = true) + public @Nullable BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")"); verifyDeviceNotNull(device, "getCodecStatus"); final IBluetoothA2dp service = getService(); diff --git a/framework/java/android/bluetooth/BluetoothGatt.java b/framework/java/android/bluetooth/BluetoothGatt.java index e67d823c7e..a69c8ba792 100644 --- a/framework/java/android/bluetooth/BluetoothGatt.java +++ b/framework/java/android/bluetooth/BluetoothGatt.java @@ -37,6 +37,8 @@ import android.os.ParcelUuid; import android.os.RemoteException; import android.util.Log; +import com.android.bluetooth.flags.Flags; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -243,6 +245,9 @@ public final class BluetoothGatt implements BluetoothProfile { + " unregistering"); } unregisterApp(); + if (Flags.unregisterGattClientDisconnected()) { + mCallback = null; + } return; } if (VDBG) { @@ -274,7 +279,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { // autoConnect is inverse of "isDirect" mService.clientConnect( - mClientIf, + clientIf, mDevice.getAddress(), mDevice.getAddressType(), !mAutoConnect, @@ -361,6 +366,8 @@ public final class BluetoothGatt implements BluetoothProfile { * @hide */ @Override + @RequiresBluetoothConnectPermission + @RequiresPermission(BLUETOOTH_CONNECT) public void onClientConnectionState( int status, int clientIf, boolean connected, String address) { if (DBG) { @@ -381,6 +388,10 @@ public final class BluetoothGatt implements BluetoothProfile { ? BluetoothProfile.STATE_CONNECTED : BluetoothProfile.STATE_DISCONNECTED; + if (Flags.unregisterGattClientDisconnected() && !connected && !mAutoConnect) { + unregisterApp(); + } + runOrQueueCallback( new Runnable() { @Override @@ -433,6 +444,10 @@ public final class BluetoothGatt implements BluetoothProfile { s.setDevice(mDevice); } + if (Flags.fixBluetoothGattGettingDuplicateServices()) { + mServices.clear(); + } + mServices.addAll(services); // Fix references to included services, as they doesn't point to right objects. @@ -493,16 +508,18 @@ public final class BluetoothGatt implements BluetoothProfile { mDeviceBusy = false; } + int clientIf = mClientIf; if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM) + && (clientIf > 0)) { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.readCharacteristic( - mClientIf, address, handle, authReq, mAttributionSource); + clientIf, address, handle, authReq, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -573,10 +590,13 @@ public final class BluetoothGatt implements BluetoothProfile { ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN; - for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { + int clientIf = mClientIf; + for (int i = 0; + (i < WRITE_CHARACTERISTIC_MAX_RETRIES) && (clientIf > 0); + i++) { requestStatus = mService.writeCharacteristic( - mClientIf, + clientIf, address, handle, characteristic.getWriteType(), @@ -679,16 +699,18 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; + int clientIf = mClientIf; if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM) + && (clientIf > 0)) { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.readDescriptor( - mClientIf, address, handle, authReq, mAttributionSource); + clientIf, address, handle, authReq, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -741,16 +763,18 @@ public final class BluetoothGatt implements BluetoothProfile { BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle); if (descriptor == null) return; + int clientIf = mClientIf; if ((status == GATT_INSUFFICIENT_AUTHENTICATION || status == GATT_INSUFFICIENT_ENCRYPTION) - && (mAuthRetryState != AUTH_RETRY_STATE_MITM)) { + && (mAuthRetryState != AUTH_RETRY_STATE_MITM) + && (clientIf > 0)) { try { final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE) ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM; mService.writeDescriptor( - mClientIf, address, handle, authReq, value, mAttributionSource); + clientIf, address, handle, authReq, value, mAttributionSource); mAuthRetryState++; return; } catch (RemoteException e) { @@ -1033,6 +1057,10 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "close()"); unregisterApp(); + if (Flags.unregisterGattClientDisconnected()) { + mCallback = null; + } + mConnState = CONN_STATE_CLOSED; mAuthRetryState = AUTH_RETRY_STATE_IDLE; } @@ -1166,7 +1194,9 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf); try { - mCallback = null; + if (!Flags.unregisterGattClientDisconnected()) { + mCallback = null; + } mService.unregisterClient(mClientIf, mAttributionSource); mClientIf = 0; } catch (RemoteException e) { @@ -1229,10 +1259,11 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public void disconnect() { if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice); - if (mService == null || mClientIf == 0) return; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return; try { - mService.clientDisconnect(mClientIf, mDevice.getAddress(), mAttributionSource); + mService.clientDisconnect(clientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1250,6 +1281,40 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(BLUETOOTH_CONNECT) public boolean connect() { + int clientIf = mClientIf; + if (mService == null) return false; + if (clientIf == 0) { + if (!Flags.unregisterGattClientDisconnected()) { + return false; + } + synchronized (mStateLock) { + if (mConnState != CONN_STATE_IDLE) { + return false; + } + mConnState = CONN_STATE_CONNECTING; + } + + UUID uuid = UUID.randomUUID(); + if (DBG) Log.d(TAG, "reconnect from connect(), UUID=" + uuid); + + try { + mService.registerClient( + new ParcelUuid(uuid), + mBluetoothGattCallback, + /* eatt_support= */ false, + mAttributionSource); + } catch (RemoteException e) { + Log.e(TAG, "", e); + synchronized (mStateLock) { + mConnState = CONN_STATE_IDLE; + } + Log.e(TAG, "Failed to register callback"); + return false; + } + + return true; + } + try { if (DBG) { Log.d(TAG, "connect(void) - device: " + mDevice + ", auto=" + mAutoConnect); @@ -1257,7 +1322,7 @@ public final class BluetoothGatt implements BluetoothProfile { // autoConnect is inverse of "isDirect" mService.clientConnect( - mClientIf, + clientIf, mDevice.getAddress(), mDevice.getAddressType(), !mAutoConnect, @@ -1293,9 +1358,12 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(BLUETOOTH_CONNECT) public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) { + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return; + try { mService.clientSetPreferredPhy( - mClientIf, mDevice.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource); + clientIf, mDevice.getAddress(), txPhy, rxPhy, phyOptions, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1308,8 +1376,11 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresBluetoothConnectPermission @RequiresPermission(BLUETOOTH_CONNECT) public void readPhy() { + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return; + try { - mService.clientReadPhy(mClientIf, mDevice.getAddress(), mAttributionSource); + mService.clientReadPhy(clientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1340,12 +1411,16 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean discoverServices() { if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice); - if (mService == null || mClientIf == 0) return false; - mServices.clear(); + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; + + if (!Flags.fixBluetoothGattGettingDuplicateServices()) { + mServices.clear(); + } try { - mService.discoverServices(mClientIf, mDevice.getAddress(), mAttributionSource); + mService.discoverServices(clientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1367,13 +1442,16 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean discoverServiceByUuid(UUID uuid) { if (DBG) Log.d(TAG, "discoverServiceByUuid() - device: " + mDevice); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; - mServices.clear(); + if (!Flags.fixBluetoothGattGettingDuplicateServices()) { + mServices.clear(); + } try { mService.discoverServiceByUuid( - mClientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); + clientIf, mDevice.getAddress(), new ParcelUuid(uuid), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1447,7 +1525,8 @@ public final class BluetoothGatt implements BluetoothProfile { } if (VDBG) Log.d(TAG, "readCharacteristic() - uuid: " + characteristic.getUuid()); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; BluetoothGattService service = characteristic.getService(); if (service == null) return false; @@ -1462,7 +1541,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readCharacteristic( - mClientIf, + clientIf, device.getAddress(), characteristic.getInstanceId(), AUTHENTICATION_NONE, @@ -1494,7 +1573,8 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean readUsingCharacteristicUuid(UUID uuid, int startHandle, int endHandle) { if (VDBG) Log.d(TAG, "readUsingCharacteristicUuid() - uuid: " + uuid); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; @@ -1503,7 +1583,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readUsingCharacteristicUuid( - mClientIf, + clientIf, mDevice.getAddress(), new ParcelUuid(uuid), startHandle, @@ -1601,7 +1681,8 @@ public final class BluetoothGatt implements BluetoothProfile { == 0) { return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED; } - if (mService == null || mClientIf == 0) { + int clientIf = mClientIf; + if (mService == null || clientIf == 0) { return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; } @@ -1627,7 +1708,7 @@ public final class BluetoothGatt implements BluetoothProfile { for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) { requestStatus = mService.writeCharacteristic( - mClientIf, + clientIf, device.getAddress(), characteristic.getInstanceId(), writeType, @@ -1674,7 +1755,8 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean readDescriptor(BluetoothGattDescriptor descriptor) { if (VDBG) Log.d(TAG, "readDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic(); if (characteristic == null) return false; @@ -1692,7 +1774,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.readDescriptor( - mClientIf, + clientIf, device.getAddress(), descriptor.getInstanceId(), AUTHENTICATION_NONE, @@ -1755,7 +1837,8 @@ public final class BluetoothGatt implements BluetoothProfile { throw new IllegalArgumentException("value must not be null"); } if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid()); - if (mService == null || mClientIf == 0) { + int clientIf = mClientIf; + if (mService == null || clientIf == 0) { return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; } @@ -1781,7 +1864,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { return mService.writeDescriptor( - mClientIf, + clientIf, device.getAddress(), descriptor.getInstanceId(), AUTHENTICATION_NONE, @@ -1818,10 +1901,11 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean beginReliableWrite() { if (VDBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; try { - mService.beginReliableWrite(mClientIf, mDevice.getAddress(), mAttributionSource); + mService.beginReliableWrite(clientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1846,7 +1930,8 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean executeReliableWrite() { if (VDBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; synchronized (mDeviceBusyLock) { if (mDeviceBusy) return false; @@ -1854,7 +1939,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), true, mAttributionSource); + mService.endReliableWrite(clientIf, mDevice.getAddress(), true, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); synchronized (mDeviceBusyLock) { @@ -1877,10 +1962,11 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public void abortReliableWrite() { if (VDBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice); - if (mService == null || mClientIf == 0) return; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return; try { - mService.endReliableWrite(mClientIf, mDevice.getAddress(), false, mAttributionSource); + mService.endReliableWrite(clientIf, mDevice.getAddress(), false, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); } @@ -1921,7 +2007,8 @@ public final class BluetoothGatt implements BluetoothProfile { + " enable: " + enable); } - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; BluetoothGattService service = characteristic.getService(); if (service == null) return false; @@ -1931,7 +2018,7 @@ public final class BluetoothGatt implements BluetoothProfile { try { mService.registerForNotification( - mClientIf, + clientIf, device.getAddress(), characteristic.getInstanceId(), enable, @@ -1954,10 +2041,11 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean refresh() { if (DBG) Log.d(TAG, "refresh() - device: " + mDevice); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; try { - mService.refreshDevice(mClientIf, mDevice.getAddress(), mAttributionSource); + mService.refreshDevice(clientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -1979,10 +2067,11 @@ public final class BluetoothGatt implements BluetoothProfile { @RequiresPermission(BLUETOOTH_CONNECT) public boolean readRemoteRssi() { if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; try { - mService.readRemoteRssi(mClientIf, mDevice.getAddress(), mAttributionSource); + mService.readRemoteRssi(clientIf, mDevice.getAddress(), mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -2014,10 +2103,11 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) { Log.d(TAG, "configureMTU() - device: " + mDevice + " mtu: " + mtu); } - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; try { - mService.configureMTU(mClientIf, mDevice.getAddress(), mtu, mAttributionSource); + mService.configureMTU(clientIf, mDevice.getAddress(), mtu, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -2047,11 +2137,12 @@ public final class BluetoothGatt implements BluetoothProfile { } if (DBG) Log.d(TAG, "requestConnectionPriority() - params: " + connectionPriority); - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; try { mService.connectionParameterUpdate( - mClientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); + clientIf, mDevice.getAddress(), connectionPriority, mAttributionSource); } catch (RemoteException e) { Log.e(TAG, "", e); return false; @@ -2098,11 +2189,12 @@ public final class BluetoothGatt implements BluetoothProfile { + ", max_ce=" + maxConnectionEventLen); } - if (mService == null || mClientIf == 0) return false; + int clientIf = mClientIf; + if (mService == null || clientIf == 0) return false; try { mService.leConnectionUpdate( - mClientIf, + clientIf, mDevice.getAddress(), minConnectionInterval, maxConnectionInterval, @@ -2148,12 +2240,13 @@ public final class BluetoothGatt implements BluetoothProfile { if (DBG) { Log.d(TAG, "requestsubrateMode(" + subrateMode + ")"); } - if (mService == null || mClientIf == 0) { + int clientIf = mClientIf; + if (mService == null || clientIf == 0) { return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; } try { - return mService.subrateModeRequest(mClientIf, mDevice, subrateMode, mAttributionSource); + return mService.subrateModeRequest(clientIf, mDevice, subrateMode, mAttributionSource); } catch (RemoteException e) { logRemoteException(TAG, e); return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; diff --git a/framework/java/android/bluetooth/BluetoothSocket.java b/framework/java/android/bluetooth/BluetoothSocket.java index 97bdf595a0..cde23baf4d 100644 --- a/framework/java/android/bluetooth/BluetoothSocket.java +++ b/framework/java/android/bluetooth/BluetoothSocket.java @@ -978,6 +978,8 @@ public final class BluetoothSocket implements Closeable { if (mL2capBuffer.remaining() == 0) { if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling..."); if (fillL2capRxBuffer() == -1) { + Log.d(TAG, "socket EOF, returning -1"); + mSocketState = SocketState.CLOSED; return -1; } } @@ -994,6 +996,7 @@ public final class BluetoothSocket implements Closeable { ret = mSocketIS.read(b, offset, length); } if (ret < 0) { + mSocketState = SocketState.CLOSED; throw new IOException("bt socket closed, read return: " + ret); } if (VDBG) Log.d(TAG, "read out: " + mSocketIS + " ret: " + ret); diff --git a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt index e658e8c645..ea41c55ee0 100644 --- a/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt +++ b/framework/tests/bumble/src/android/bluetooth/DckL2capTest.kt @@ -226,6 +226,51 @@ public class DckL2capTest() : Closeable { Log.d(TAG, "testReceive: done") } + @Test + @VirtualOnly + fun testReadReturnOnRemoteSocketDisconnect() { + Log.d(TAG, "testReadReturnonSocketDisconnect: Connect L2CAP") + var bluetoothSocket: BluetoothSocket? + val l2capServer = bluetoothAdapter.listenUsingInsecureL2capChannel() + val socketFlow = flow { emit(l2capServer.accept()) } + val connectResponse = createAndConnectL2capChannelWithBumble(l2capServer.psm) + runBlocking { + bluetoothSocket = socketFlow.first() + assertThat(connectResponse.hasChannel()).isTrue() + } + + val inputStream = bluetoothSocket!!.inputStream + + // block on read() on server thread + val readThread = Thread { + Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: Receive data on Android") + val ret = inputStream.read() + Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: read returns : " + ret) + Log.d( + TAG, + "testReadReturnOnRemoteSocketDisconnect: isConnected() : " + + bluetoothSocket!!.isConnected(), + ) + assertThat(ret).isEqualTo(-1) + assertThat(bluetoothSocket!!.isConnected()).isFalse() + } + readThread.start() + // check that socket is still connected + assertThat(bluetoothSocket!!.isConnected()).isTrue() + + // read() would be blocking till underlying l2cap is disconnected + Thread.sleep(1000 * 10) + Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: disconnect after 10 secs") + val disconnectRequest = + DisconnectRequest.newBuilder().setChannel(connectResponse.channel).build() + val disconnectResponse = mBumble.l2capBlocking().disconnect(disconnectRequest) + assertThat(disconnectResponse.hasSuccess()).isTrue() + inputStream.close() + bluetoothSocket?.close() + l2capServer.close() + Log.d(TAG, "testReadReturnOnRemoteSocketDisconnect: done") + } + private fun createAndConnectL2capChannelWithBumble(psm: Int): ConnectResponse { Log.d(TAG, "createAndConnectL2capChannelWithBumble") val remoteDevice = diff --git a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java index c9fa50bbd9..64ada411d6 100644 --- a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java +++ b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java @@ -720,6 +720,44 @@ public class GattClientTest { } } + @Test + @RequiresFlagsEnabled(Flags.FLAG_UNREGISTER_GATT_CLIENT_DISCONNECTED) + public void connectAndDisconnectManyClientsWithoutClose() throws Exception { + advertiseWithBumble(); + + List<BluetoothGatt> gatts = new ArrayList<>(); + try { + for (int i = 0; i < 100; i++) { + BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); + InOrder inOrder = inOrder(gattCallback); + + BluetoothGatt gatt = mRemoteLeDevice.connectGatt(mContext, false, gattCallback); + gatts.add(gatt); + + inOrder.verify(gattCallback, timeout(1000)) + .onConnectionStateChange(any(), anyInt(), eq(STATE_CONNECTED)); + + gatt.disconnect(); + inOrder.verify(gattCallback, timeout(1000)) + .onConnectionStateChange( + any(), anyInt(), eq(BluetoothProfile.STATE_DISCONNECTED)); + + gatt.connect(); + inOrder.verify(gattCallback, timeout(1000)) + .onConnectionStateChange(any(), anyInt(), eq(STATE_CONNECTED)); + + gatt.disconnect(); + inOrder.verify(gattCallback, timeout(1000)) + .onConnectionStateChange( + any(), anyInt(), eq(BluetoothProfile.STATE_DISCONNECTED)); + } + } finally { + for (BluetoothGatt gatt : gatts) { + gatt.close(); + } + } + } + private void createLeBondAndWaitBonding(BluetoothDevice device) { advertiseWithBumble(); mHost.createBondAndVerify(device); diff --git a/offload/hal/service.rs b/offload/hal/service.rs index 779be6da8c..eb8c679b09 100644 --- a/offload/hal/service.rs +++ b/offload/hal/service.rs @@ -16,7 +16,7 @@ use crate::ffi::{CInterface, CStatus, Callbacks, DataCallbacks, Ffi}; use android_hardware_bluetooth::aidl::android::hardware::bluetooth::{ IBluetoothHci::IBluetoothHci, IBluetoothHciCallbacks::IBluetoothHciCallbacks, Status::Status, }; -use binder::{DeathRecipient, ExceptionCode, Interface, Result as BinderResult, Strong}; +use binder::{DeathRecipient, ExceptionCode, IBinder, Interface, Result as BinderResult, Strong}; use bluetooth_offload_hci::{Module, ModuleBuilder}; use std::sync::{Arc, RwLock}; @@ -41,7 +41,6 @@ struct SinkModule<T: Callbacks> { enum State { Closed, - Opening { ffi: Arc<Ffi<FfiCallbacks>>, proxy: Arc<dyn Module> }, Opened { proxy: Arc<dyn Module>, _death_recipient: DeathRecipient }, } @@ -73,10 +72,25 @@ impl IBluetoothHci for HciHalProxy { for m in self.modules.iter().rev() { proxy = m.build(proxy); } - let callbacks = FfiCallbacks::new(callbacks.clone(), proxy.clone(), self.state.clone()); - *state = State::Opening { ffi: self.ffi.clone(), proxy: proxy.clone() }; - (self.ffi.clone(), callbacks) + let mut death_recipient = { + let (ffi, state) = (self.ffi.clone(), self.state.clone()); + DeathRecipient::new(move || { + log::info!("Bluetooth stack has died"); + let mut state = state.write().unwrap(); + if !matches!(*state, State::Closed) { + ffi.close(); + } + *state = State::Closed; + }) + }; + callbacks.as_binder().link_to_death(&mut death_recipient)?; + + *state = State::Opened { proxy: proxy.clone(), _death_recipient: death_recipient }; + ( + self.ffi.clone(), + FfiCallbacks::new(callbacks.clone(), proxy.clone(), self.state.clone()), + ) }; ffi.initialize(callbacks); @@ -185,31 +199,12 @@ impl FfiCallbacks { impl Callbacks for FfiCallbacks { fn initialization_complete(&self, status: CStatus) { let mut state = self.state.write().unwrap(); - match status { - CStatus::Success => { - let State::Opening { ref ffi, ref proxy } = *state else { - panic!("Initialization completed called in bad state"); - }; - - *state = State::Opened { - proxy: proxy.clone(), - _death_recipient: { - let (ffi, state) = (ffi.clone(), self.state.clone()); - DeathRecipient::new(move || { - log::info!("Bluetooth stack has died"); - *state.write().unwrap() = State::Closed; - ffi.close(); - }) - }, - }; - } - - CStatus::AlreadyInitialized => panic!("Initialization completed called in bad state"), - _ => *state = State::Closed, - }; - + if status != CStatus::Success { + *state = State::Closed; + } if let Err(e) = self.callbacks.initializationComplete(status.into()) { log::error!("Cannot call-back client: {:?}", e); + *state = State::Closed; } } } diff --git a/offload/hci/data.rs b/offload/hci/data.rs index 0e20e82029..bb4a452a59 100644 --- a/offload/hci/data.rs +++ b/offload/hci/data.rs @@ -120,7 +120,7 @@ impl<'a> IsoData<'a> { } } -impl<'a> Write for IsoData<'a> { +impl Write for IsoData<'_> { fn write(&self, w: &mut Writer) { let (pb_flag, hdr) = match self.sdu_fragment { IsoSduFragment::First { ref hdr, is_last: false } => (0b00, Some(hdr)), diff --git a/offload/leaudio/hci/proxy.rs b/offload/leaudio/hci/proxy.rs index 413a7f84b5..79e45f06e1 100644 --- a/offload/leaudio/hci/proxy.rs +++ b/offload/leaudio/hci/proxy.rs @@ -29,7 +29,6 @@ pub struct LeAudioModuleBuilder {} pub(crate) struct LeAudioModule { next_module: Arc<dyn Module>, state: Mutex<State>, - service: Service, } #[derive(Default)] @@ -145,13 +144,14 @@ impl Stream { impl ModuleBuilder for LeAudioModuleBuilder { /// Build the HCI-Proxy module from the next module in the chain fn build(&self, next_module: Arc<dyn Module>) -> Arc<dyn Module> { + Service::register(); Arc::new(LeAudioModule::new(next_module)) } } impl LeAudioModule { pub(crate) fn new(next_module: Arc<dyn Module>) -> Self { - Self { next_module, state: Mutex::new(Default::default()), service: Service::new() } + Self { next_module, state: Mutex::new(Default::default()) } } #[cfg(test)] @@ -216,7 +216,7 @@ impl Module for LeAudioModule { ret.iso_data_packet_length.into(), ret.total_num_iso_data_packets.into(), ))); - self.service.reset(Arc::downgrade(state.arbiter.as_ref().unwrap())); + Service::reset(Arc::downgrade(state.arbiter.as_ref().unwrap())); } ReturnParameters::LeSetCigParameters(ref ret) if ret.status == Status::Success => { @@ -249,7 +249,7 @@ impl Module for LeAudioModule { IsoType::Bis { ref c_to_p } => c_to_p, }; - self.service.start_stream( + Service::start_stream( ret.connection_handle, StreamConfiguration { isoIntervalUs: stream.iso_interval_us as i32, @@ -265,7 +265,7 @@ impl Module for LeAudioModule { let mut state = self.state.lock().unwrap(); let stream = state.stream.get_mut(&ret.connection_handle).unwrap(); if stream.state == StreamState::Enabled { - self.service.stop_stream(ret.connection_handle); + Service::stop_stream(ret.connection_handle); } stream.state = StreamState::Disabled; } diff --git a/offload/leaudio/hci/service.rs b/offload/leaudio/hci/service.rs index 85fac7168c..05bc377287 100644 --- a/offload/leaudio/hci/service.rs +++ b/offload/leaudio/hci/service.rs @@ -19,12 +19,10 @@ use aidl::android::hardware::bluetooth::offload::leaudio::{ IHciProxy::{BnHciProxy, BpHciProxy, IHciProxy}, IHciProxyCallbacks::IHciProxyCallbacks, }; -use binder::{ - BinderFeatures, DeathRecipient, ExceptionCode, Interface, Result as BinderResult, Strong, -}; +use binder::{BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Strong}; use bluetooth_offload_hci::IsoData; use std::collections::HashMap; -use std::sync::{Arc, Mutex, Weak}; +use std::sync::{Arc, LazyLock, Mutex, Weak}; pub(crate) use aidl::android::hardware::bluetooth::offload::leaudio::StreamConfiguration::StreamConfiguration; @@ -32,27 +30,31 @@ pub(crate) struct Service { state: Arc<Mutex<State>>, } +static SERVICE: LazyLock<Service> = LazyLock::new(|| { + let state = Arc::new(Mutex::new(State::default())); + HciProxy::register(state.clone()); + Service { state } +}); + #[derive(Default)] struct State { arbiter: Weak<Arbiter>, - callbacks: Option<Strong<dyn IHciProxyCallbacks>>, streams: HashMap<u16, StreamConfiguration>, + callbacks: Option<Strong<dyn IHciProxyCallbacks>>, } impl Service { - pub(crate) fn new() -> Self { - let state = Arc::new(Mutex::new(State::default())); - HciProxy::register(state.clone()); - Self { state } + pub(crate) fn register() { + LazyLock::force(&SERVICE); } - pub(crate) fn reset(&self, arbiter: Weak<Arbiter>) { - let mut state = self.state.lock().unwrap(); + pub(crate) fn reset(arbiter: Weak<Arbiter>) { + let mut state = SERVICE.state.lock().unwrap(); *state = State { arbiter, ..Default::default() } } - pub(crate) fn start_stream(&self, handle: u16, config: StreamConfiguration) { - let mut state = self.state.lock().unwrap(); + pub(crate) fn start_stream(handle: u16, config: StreamConfiguration) { + let mut state = SERVICE.state.lock().unwrap(); if let Some(callbacks) = &state.callbacks { let _ = callbacks.startStream(handle.into(), &config); } else { @@ -61,8 +63,8 @@ impl Service { state.streams.insert(handle, config); } - pub(crate) fn stop_stream(&self, handle: u16) { - let mut state = self.state.lock().unwrap(); + pub(crate) fn stop_stream(handle: u16) { + let mut state = SERVICE.state.lock().unwrap(); state.streams.remove(&handle); if let Some(callbacks) = &state.callbacks { let _ = callbacks.stopStream(handle.into()); @@ -72,28 +74,15 @@ impl Service { struct HciProxy { state: Arc<Mutex<State>>, - _death_recipient: DeathRecipient, } impl Interface for HciProxy {} impl HciProxy { fn register(state: Arc<Mutex<State>>) { - let death_recipient = { - let state = state.clone(); - DeathRecipient::new(move || { - log::info!("Client has died"); - state.lock().unwrap().callbacks = None; - }) - }; - binder::add_service( &format!("{}/default", BpHciProxy::get_descriptor()), - BnHciProxy::new_binder( - Self { state, _death_recipient: death_recipient }, - BinderFeatures::default(), - ) - .as_binder(), + BnHciProxy::new_binder(Self { state }, BinderFeatures::default()).as_binder(), ) .expect("Failed to register service"); } @@ -106,7 +95,6 @@ impl IHciProxy for HciProxy { for (handle, config) in &state.streams { let _ = callbacks.startStream((*handle).into(), config); } - Ok(()) } diff --git a/pandora/interfaces/pandora_experimental/mediaplayer.proto b/pandora/interfaces/pandora_experimental/mediaplayer.proto index 98ac7b674d..69ead553d8 100644 --- a/pandora/interfaces/pandora_experimental/mediaplayer.proto +++ b/pandora/interfaces/pandora_experimental/mediaplayer.proto @@ -9,6 +9,7 @@ import "google/protobuf/empty.proto"; service MediaPlayer { rpc Play(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc PlayUpdated(google.protobuf.Empty) returns (google.protobuf.Empty); rpc Stop(google.protobuf.Empty) returns (google.protobuf.Empty); rpc Pause(google.protobuf.Empty) returns (google.protobuf.Empty); rpc Rewind(google.protobuf.Empty) returns (google.protobuf.Empty); @@ -17,6 +18,7 @@ service MediaPlayer { rpc Backward(google.protobuf.Empty) returns (google.protobuf.Empty); rpc SetLargeMetadata(google.protobuf.Empty) returns (google.protobuf.Empty); rpc UpdateQueue(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc ResetQueue(google.protobuf.Empty) returns (google.protobuf.Empty); rpc GetShuffleMode(google.protobuf.Empty) returns (GetShuffleModeResponse); rpc SetShuffleMode(SetShuffleModeRequest) returns (google.protobuf.Empty); rpc StartTestPlayback(google.protobuf.Empty) returns (google.protobuf.Empty); diff --git a/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info.cc b/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info.cc index ccd0bf04fe..8ab27d06d2 100644 --- a/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info.cc +++ b/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info.cc @@ -171,9 +171,6 @@ static std::optional<btav_a2dp_codec_index_t> assignSinkCodecIndex( int codec_id = codec.id.get<CodecId::vendor>().codecId; /* match know vendor codecs */ - if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { - return BTAV_A2DP_CODEC_INDEX_SINK_LDAC; - } if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { return BTAV_A2DP_CODEC_INDEX_SINK_OPUS; } diff --git a/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info_unittest.cc b/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info_unittest.cc index b3731e5627..6ac1bee5ed 100644 --- a/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info_unittest.cc +++ b/system/audio_hal_interface/aidl/a2dp/a2dp_provider_info_unittest.cc @@ -269,7 +269,7 @@ TEST_F(ProviderInfoTest, TestGetCodecNotSupported) { GetProviderInfoForTesting(true, false); auto received_codec_info_not_supported_codec = - provider_info->GetCodec(BTAV_A2DP_CODEC_INDEX_SINK_LDAC); + provider_info->GetCodec(BTAV_A2DP_CODEC_INDEX_SINK_MAX); ASSERT_FALSE(received_codec_info_not_supported_codec.has_value()); } diff --git a/system/audio_hal_interface/aidl/a2dp/codec_status_aidl.cc b/system/audio_hal_interface/aidl/a2dp/codec_status_aidl.cc index cb4a601f0a..dfdebd2071 100644 --- a/system/audio_hal_interface/aidl/a2dp/codec_status_aidl.cc +++ b/system/audio_hal_interface/aidl/a2dp/codec_status_aidl.cc @@ -502,16 +502,11 @@ bool UpdateOffloadingCapabilities( codec_type_set.insert(CodecType::OPUS); break; case BTAV_A2DP_CODEC_INDEX_SINK_SBC: - [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_AAC: - [[fallthrough]]; - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: log::warn("Ignore sink codec_type={}", preference.codec_type); break; case BTAV_A2DP_CODEC_INDEX_MAX: - [[fallthrough]]; default: log::error("Unknown codec_type={}", preference.codec_type); return false; diff --git a/system/audio_hal_interface/fuzzer/README.md b/system/audio_hal_interface/fuzzer/README.md index 6d92d8f81f..a54c5cb9d7 100644 --- a/system/audio_hal_interface/fuzzer/README.md +++ b/system/audio_hal_interface/fuzzer/README.md @@ -34,7 +34,7 @@ Fuzzers assigns values to the following parameters to pass on to libbt-audio-hal |------------- |-------------| ----- | | `status` | 0.`UNKNOWN` 1.`SUCCESS` 2.`UNSUPPORTED_CODEC_CONFIGURATION` 3.`FAILURE` 4.`PENDING` | Value obtained from FuzzedDataProvider | -| `index` | 0.`BTAV_A2DP_CODEC_INDEX_SOURCE_SBC` 1.`BTAV_A2DP_CODEC_INDEX_SOURCE_AAC` 2.`BTAV_A2DP_CODEC_INDEX_SOURCE_APTX` 3.`BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD` 4.`BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC` 5.`BTAV_A2DP_CODEC_INDEX_SINK_SBC` 6.`BTAV_A2DP_CODEC_INDEX_SINK_AAC` 7.`BTAV_A2DP_CODEC_INDEX_SINK_LDAC` | Value obtained from FuzzedDataProvider | +| `index` | 0.`BTAV_A2DP_CODEC_INDEX_SOURCE_SBC` 1.`BTAV_A2DP_CODEC_INDEX_SOURCE_AAC` 2.`BTAV_A2DP_CODEC_INDEX_SOURCE_APTX` 3.`BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD` 4.`BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC` 5.`BTAV_A2DP_CODEC_INDEX_SINK_SBC` 6.`BTAV_A2DP_CODEC_INDEX_SINK_AAC` | Value obtained from FuzzedDataProvider | | `sessionType` | 0.`SessionType::UNKNOWN` 1.`SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH` 2.`SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH` 3.`SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH` | Value obtained from FuzzedDataProvider | | `sessionType_2_1` | 0.`SessionType_2_1::UNKNOWN` 1.`SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH` 2.`SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH` 3.`SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH` 4.`SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH` 5.`SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH` 6.`SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH` 7.`SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH` | Value obtained from FuzzedDataProvider | | `param.sampleRate` | 0.`SampleRate::RATE_UNKNOWN` 1.`SampleRate::RATE_8000` 2.`SampleRate::RATE_16000` 3.`SampleRate::RATE_24000` 4.`SampleRate::RATE_32000` 5.`SampleRate::RATE_44100` 6.`SampleRate::RATE_48000` | Value obtained from FuzzedDataProvider | diff --git a/system/audio_hal_interface/fuzzer/libbt_audio_hal_a2dp_encoding_fuzzer.cpp b/system/audio_hal_interface/fuzzer/libbt_audio_hal_a2dp_encoding_fuzzer.cpp index 7613b7efe6..64a85ddfe4 100644 --- a/system/audio_hal_interface/fuzzer/libbt_audio_hal_a2dp_encoding_fuzzer.cpp +++ b/system/audio_hal_interface/fuzzer/libbt_audio_hal_a2dp_encoding_fuzzer.cpp @@ -42,7 +42,7 @@ constexpr btav_a2dp_codec_index_t kCodecIndices[] = { BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, BTAV_A2DP_CODEC_INDEX_SOURCE_AAC, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_SBC, - BTAV_A2DP_CODEC_INDEX_SINK_AAC, BTAV_A2DP_CODEC_INDEX_SINK_LDAC}; + BTAV_A2DP_CODEC_INDEX_SINK_AAC}; std::vector<std::vector<btav_a2dp_codec_config_t>> CodecOffloadingPreferenceGenerator() { std::vector<std::vector<btav_a2dp_codec_config_t>> offloadingPreferences = { diff --git a/system/audio_hal_interface/fuzzer/libbt_audio_hal_client_interface_fuzzer.cpp b/system/audio_hal_interface/fuzzer/libbt_audio_hal_client_interface_fuzzer.cpp index c309d44b34..7f5bbfc0c6 100644 --- a/system/audio_hal_interface/fuzzer/libbt_audio_hal_client_interface_fuzzer.cpp +++ b/system/audio_hal_interface/fuzzer/libbt_audio_hal_client_interface_fuzzer.cpp @@ -131,7 +131,7 @@ constexpr btav_a2dp_codec_index_t kCodecIndices[] = { BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, BTAV_A2DP_CODEC_INDEX_SOURCE_AAC, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_SBC, - BTAV_A2DP_CODEC_INDEX_SINK_AAC, BTAV_A2DP_CODEC_INDEX_SINK_LDAC}; + BTAV_A2DP_CODEC_INDEX_SINK_AAC}; class TestSinkTransport : public bluetooth::audio::hidl::IBluetoothSinkTransportInstance { private: diff --git a/system/audio_hal_interface/hidl/client_interface_hidl_unittest.cc b/system/audio_hal_interface/hidl/client_interface_hidl_unittest.cc index 13ed21c06f..b9ac810b17 100644 --- a/system/audio_hal_interface/hidl/client_interface_hidl_unittest.cc +++ b/system/audio_hal_interface/hidl/client_interface_hidl_unittest.cc @@ -126,7 +126,7 @@ constexpr btav_a2dp_codec_index_t codec_indexes[] = { BTAV_A2DP_CODEC_INDEX_SOURCE_SBC, BTAV_A2DP_CODEC_INDEX_SOURCE_AAC, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX, BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD, BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_SBC, - BTAV_A2DP_CODEC_INDEX_SINK_AAC, BTAV_A2DP_CODEC_INDEX_SINK_LDAC}; + BTAV_A2DP_CODEC_INDEX_SINK_AAC}; constexpr uint16_t kPeerMtus[5] = {660, 663, 883, 1005, 1500}; class TestSinkTransport : public bluetooth::audio::hidl::IBluetoothSinkTransportInstance { diff --git a/system/audio_hal_interface/hidl/codec_status_hidl.cc b/system/audio_hal_interface/hidl/codec_status_hidl.cc index fe54dbdb1e..a74592a60c 100644 --- a/system/audio_hal_interface/hidl/codec_status_hidl.cc +++ b/system/audio_hal_interface/hidl/codec_status_hidl.cc @@ -463,16 +463,11 @@ bool UpdateOffloadingCapabilities( log::warn("Ignore source codec_type={}, not supported on HIDL", preference.codec_type); break; case BTAV_A2DP_CODEC_INDEX_SINK_SBC: - [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_AAC: - [[fallthrough]]; - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - [[fallthrough]]; case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: log::warn("Ignore sink codec_type={}", preference.codec_type); break; case BTAV_A2DP_CODEC_INDEX_MAX: - [[fallthrough]]; default: log::error("Unknown codec_type={}", preference.codec_type); return false; diff --git a/system/bta/av/bta_av_aact.cc b/system/bta/av/bta_av_aact.cc index 5854c8aa6c..769cefc4d9 100644 --- a/system/bta/av/bta_av_aact.cc +++ b/system/bta/av/bta_av_aact.cc @@ -878,17 +878,6 @@ void bta_av_cleanup(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /* p_data */) { alarm_cancel(p_scb->accept_open_timer); } - /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION - vendor_get_interface()->send_command( - (vendor_opcode_t)BT_VND_OP_A2DP_OFFLOAD_STOP, (void*)&p_scb->l2c_cid); - if (p_scb->offload_start_pending) { - tBTA_AV_STATUS status = BTA_AV_FAIL_STREAM; - tBTA_AV bta_av_data; - bta_av_data.status = status; - (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data); - } - */ - if (p_scb->deregistering) { /* remove stream */ for (int i = 0; i < BTAV_A2DP_CODEC_INDEX_MAX; i++) { @@ -3185,67 +3174,32 @@ void bta_av_vendor_offload_stop() { * ******************************************************************************/ void bta_av_offload_req(tBTA_AV_SCB* p_scb, tBTA_AV_DATA* /*p_data*/) { - tBTA_AV_STATUS status = BTA_AV_FAIL_RESOURCES; - + tBTA_AV bta_av_data = {}; tBT_A2DP_OFFLOAD offload_start; log::verbose("stream {}, audio channels open {}", p_scb->started ? "STARTED" : "STOPPED", bta_av_cb.audio_open_cnt); - A2dpCodecConfig* codec_config = bta_av_get_a2dp_current_codec(); - log::assert_that(codec_config != nullptr, "assert failed: codec_config != nullptr"); + if (!p_scb->started) { + log::warn("stream not started, start offload failed."); + bta_av_data.status = BTA_AV_FAIL_STREAM; + (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data); + return; + } - /* Check if stream has already been started. */ - /* Support offload if only one audio source stream is open. */ - if (p_scb->started != true) { - status = BTA_AV_FAIL_STREAM; - } else if (bta_av_cb.offload_start_pending_hndl || bta_av_cb.offload_started_hndl) { + if (bta_av_cb.offload_start_pending_hndl || bta_av_cb.offload_started_hndl) { log::warn("offload already started, ignore request"); return; - } else if (::bluetooth::audio::a2dp::provider::supports_codec(codec_config->codecIndex())) { + } + + A2dpCodecConfig* codec_config = bta_av_get_a2dp_current_codec(); + log::assert_that(codec_config != nullptr, "assert failed: codec_config != nullptr"); + + if (::bluetooth::audio::a2dp::provider::supports_codec(codec_config->codecIndex())) { bta_av_vendor_offload_start_v2(p_scb, static_cast<A2dpCodecConfigExt*>(codec_config)); } else { bta_av_offload_codec_builder(p_scb, &offload_start); bta_av_vendor_offload_start(p_scb, &offload_start); - return; - } - if (status != BTA_AV_SUCCESS) { - tBTA_AV bta_av_data; - bta_av_data.status = status; - (*bta_av_cb.p_cback)(BTA_AV_OFFLOAD_START_RSP_EVT, &bta_av_data); - } - /* TODO(eisenbach): RE-IMPLEMENT USING VSC OR HAL EXTENSION - else if (bta_av_cb.audio_open_cnt == 1 && - p_scb->seps[p_scb->sep_idx].tsep == AVDT_TSEP_SRC && - p_scb->chnl == BTA_AV_CHNL_AUDIO) { - bt_vendor_op_a2dp_offload_t a2dp_offload_start; - - if (L2CA_GetConnectionConfig( - p_scb->l2c_cid, &a2dp_offload_start.acl_data_size, - &a2dp_offload_start.remote_cid, &a2dp_offload_start.lm_handle)) { - log::verbose("l2cmtu {} lcid 0x{:02X} rcid 0x{:02X} lm_handle 0x{:02X}", - a2dp_offload_start.acl_data_size, p_scb->l2c_cid, - a2dp_offload_start.remote_cid, a2dp_offload_start.lm_handle); - - a2dp_offload_start.bta_av_handle = p_scb->hndl; - a2dp_offload_start.xmit_quota = BTA_AV_A2DP_OFFLOAD_XMIT_QUOTA; - a2dp_offload_start.stream_mtu = p_scb->stream_mtu; - a2dp_offload_start.local_cid = p_scb->l2c_cid; - a2dp_offload_start.is_flushable = true; - a2dp_offload_start.stream_source = - ((uint32_t)(p_scb->cfg.codec_info[1] | p_scb->cfg.codec_info[2])); - - memcpy(a2dp_offload_start.codec_info, p_scb->cfg.codec_info, - sizeof(a2dp_offload_start.codec_info)); - - if (!vendor_get_interface()->send_command( - (vendor_opcode_t)BT_VND_OP_A2DP_OFFLOAD_START, - &a2dp_offload_start)) { - status = BTA_AV_SUCCESS; - p_scb->offload_start_pending = true; - } - } } - */ } /******************************************************************************* diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc index e9c6ba5e77..c3977454f5 100644 --- a/system/bta/dm/bta_dm_disc.cc +++ b/system/bta/dm/bta_dm_disc.cc @@ -511,7 +511,7 @@ static void bta_dm_gatt_disc_complete(tCONN_ID conn_id, tGATT_STATUS status) { log::verbose("conn_id = {}, status = {}, sdp_pending = {}, le_pending = {}", conn_id, status, sdp_pending, le_pending); - if (com::android::bluetooth::flags::bta_dm_discover_both() && sdp_pending && !le_pending) { + if (sdp_pending && !le_pending) { /* LE Service discovery finished, and services were reported, but SDP is not * finished yet. gatt_close_timer closed the connection, and we received * this callback because of disconnection */ @@ -784,8 +784,7 @@ static void bta_dm_disc_sm_execute(tBTA_DM_DISC_EVT event, std::unique_ptr<tBTA_ "bad message type: {}", msg->index()); auto req = std::get<tBTA_DM_API_DISCOVER>(*msg); - if (com::android::bluetooth::flags::bta_dm_discover_both() && - is_same_device(req.bd_addr, bta_dm_discovery_cb.peer_bdaddr)) { + if (is_same_device(req.bd_addr, bta_dm_discovery_cb.peer_bdaddr)) { bta_dm_discover_services(std::get<tBTA_DM_API_DISCOVER>(*msg)); } else { bta_dm_queue_disc(std::get<tBTA_DM_API_DISCOVER>(*msg)); diff --git a/system/bta/hh/bta_hh_headtracker.cc b/system/bta/hh/bta_hh_headtracker.cc index c8b3e20f05..0282cb59d0 100644 --- a/system/bta/hh/bta_hh_headtracker.cc +++ b/system/bta/hh/bta_hh_headtracker.cc @@ -140,7 +140,10 @@ void bta_hh_headtracker_parse_service(tBTA_HH_DEV_CB* p_dev_cb, const gatt::Serv bool bta_hh_headtracker_supported(tBTA_HH_DEV_CB* p_dev_cb) { if (p_dev_cb->hid_srvc.headtracker_support == BTA_HH_UNKNOWN) { bluetooth::Uuid remote_uuids[BT_MAX_NUM_UUIDS] = {}; - bt_property_t remote_properties = {BT_PROPERTY_UUIDS, sizeof(remote_uuids), &remote_uuids}; + bt_property_t remote_properties = {com::android::bluetooth::flags::separate_service_storage() + ? BT_PROPERTY_UUIDS_LE + : BT_PROPERTY_UUIDS, + sizeof(remote_uuids), &remote_uuids}; const RawAddress& bd_addr = p_dev_cb->link_spec.addrt.bda; p_dev_cb->hid_srvc.headtracker_support = BTA_HH_UNAVAILABLE; diff --git a/system/bta/le_audio/client.cc b/system/bta/le_audio/client.cc index 6dbb4ace85..03d3bc9652 100644 --- a/system/bta/le_audio/client.cc +++ b/system/bta/le_audio/client.cc @@ -1558,7 +1558,7 @@ public: active_group_id_ = bluetooth::groups::kGroupUnknown; } - void ConfigureStream(LeAudioDeviceGroup* group, bool up_to_qos_configured) { + bool ConfigureStream(LeAudioDeviceGroup* group, bool up_to_qos_configured) { log::debug("group_id: {}", group->group_id_); BidirectionalPair<std::vector<uint8_t>> ccids = { @@ -1566,11 +1566,16 @@ public: local_metadata_context_types_.sink), .source = ContentControlIdKeeper::GetInstance()->GetAllCcids( local_metadata_context_types_.source)}; + + group->SetPendingConfiguration(); if (!groupStateMachine_->ConfigureStream(group, configuration_context_type_, local_metadata_context_types_, ccids, up_to_qos_configured)) { - log::info("Could not configure group {}", group->group_id_); + group->ClearPendingConfiguration(); + return false; } + + return true; } void PrepareStreamForAConversational(LeAudioDeviceGroup* group) { @@ -1590,13 +1595,7 @@ public: return; } - BidirectionalPair<std::vector<uint8_t>> ccids = { - .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids( - local_metadata_context_types_.sink), - .source = ContentControlIdKeeper::GetInstance()->GetAllCcids( - local_metadata_context_types_.source)}; - if (!groupStateMachine_->ConfigureStream(group, configuration_context_type_, - local_metadata_context_types_, ccids, true)) { + if (!ConfigureStream(group, true)) { log::info("Reconfiguration is needed for group {}", group->group_id_); initReconfiguration(group, LeAudioContextType::UNSPECIFIED); } @@ -1724,7 +1723,10 @@ public: * only Enable will left. * Otherwise, if there is group switch, let's move ASEs to Configured state. */ - ConfigureStream(group, prepare_for_a_call); + + if (!ConfigureStream(group, prepare_for_a_call)) { + log::info("Could not configure group {}", group->group_id_); + } } /* Reset sink and source listener notified status */ @@ -6194,15 +6196,17 @@ public: return; } - if (is_active_group_operation) { - if (audio_sender_state_ != AudioState::IDLE) { - audio_sender_state_ = AudioState::RELEASING; - } + /* Releasing state shall be always set here, because we do support only single group + * streaming at the time. */ + if (audio_sender_state_ != AudioState::IDLE) { + audio_sender_state_ = AudioState::RELEASING; + } - if (audio_receiver_state_ != AudioState::IDLE) { - audio_receiver_state_ = AudioState::RELEASING; - } + if (audio_receiver_state_ != AudioState::IDLE) { + audio_receiver_state_ = AudioState::RELEASING; + } + if (is_active_group_operation) { if (group && group->IsPendingConfiguration()) { log::info("Releasing for reconfiguration, don't send anything on CISes"); SuspendedForReconfiguration(); diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index 693494f73c..98ed3d3c2e 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -931,14 +931,16 @@ protected: // Inject the state group->SetTargetState(config_state); group->SetState(group->GetTargetState()); - group->ClearPendingConfiguration(); - do_in_main_thread(base::BindOnce( - [](int group_id, bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* - state_machine_callbacks) { - state_machine_callbacks->StatusReportCb( - group_id, GroupStreamStatus::CONFIGURED_BY_USER); - }, - group->group_id_, base::Unretained(this->state_machine_callbacks_))); + if (group->IsPendingConfiguration()) { + group->ClearPendingConfiguration(); + do_in_main_thread(base::BindOnce( + [](int group_id, bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* + state_machine_callbacks) { + state_machine_callbacks->StatusReportCb( + group_id, GroupStreamStatus::CONFIGURED_BY_USER); + }, + group->group_id_, base::Unretained(this->state_machine_callbacks_))); + } return true; }); @@ -5081,6 +5083,9 @@ TEST_F(UnicastTest, AnotherGroupSetActive_DuringMediaStream) { SyncOnMainLoop(); Mock::VerifyAndClearExpectations(&mock_state_machine_); + + LocalAudioSourceResume(); + SyncOnMainLoop(); } TEST_F(UnicastTest, AnotherGroupSetActive_DuringVoip) { diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc index 8d8ca50ab0..b4363d8253 100644 --- a/system/bta/le_audio/state_machine.cc +++ b/system/bta/le_audio/state_machine.cc @@ -1084,7 +1084,7 @@ public: /* Note, that this type is actually LONG WRITE. * Meaning all the Prepare Writes plus Execute is handled in the stack */ - write_type = GATT_WRITE_PREPARE; + write_type = GATT_WRITE; } BtaGattQueue::WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, @@ -2293,6 +2293,7 @@ private: if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) { cancel_watchdog_if_needed(group->group_id_); + group->ClearPendingConfiguration(); state_machine_callbacks_->StatusReportCb(group->group_id_, GroupStreamStatus::CONFIGURED_BY_USER); return; diff --git a/system/bta/test/bta_disc_test.cc b/system/bta/test/bta_disc_test.cc index 58421f2962..3a1dbc9882 100644 --- a/system/bta/test/bta_disc_test.cc +++ b/system/bta/test/bta_disc_test.cc @@ -219,61 +219,7 @@ int gatt_service_cb_both_call_cnt = 0; /* This test exercises the usual service discovery flow when bonding to * dual-mode, CTKD capable device on LE transport. */ -TEST_F_WITH_FLAGS(BtaInitializedTest, bta_dm_disc_both_transports_flag_disabled, - REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(TEST_BT, bta_dm_discover_both))) { - bta_dm_disc_start(true); - - std::promise<void> gatt_triggered; - int gatt_call_cnt = 0; - base::RepeatingCallback<void(const RawAddress&)> gatt_performer = - base::BindLambdaForTesting([&](const RawAddress& /*bd_addr*/) { - gatt_call_cnt++; - gatt_triggered.set_value(); - }); - bta_dm_disc_override_gatt_performer_for_testing(gatt_performer); - - int sdp_call_cnt = 0; - base::RepeatingCallback<void(tBTA_DM_SDP_STATE*)> sdp_performer = - base::BindLambdaForTesting([&](tBTA_DM_SDP_STATE* /*sdp_state*/) { sdp_call_cnt++; }); - bta_dm_disc_override_sdp_performer_for_testing(sdp_performer); - - gatt_service_cb_both_call_cnt = 0; - service_cb_both_call_cnt = 0; - - bta_dm_disc_start_service_discovery( - {[](RawAddress, std::vector<bluetooth::Uuid>&, bool) {}, nullptr, - [](RawAddress /*addr*/, const std::vector<bluetooth::Uuid>&, tBTA_STATUS) { - service_cb_both_call_cnt++; - }}, - kRawAddress, BT_TRANSPORT_BR_EDR); - EXPECT_EQ(sdp_call_cnt, 1); - - bta_dm_disc_start_service_discovery( - {[](RawAddress, std::vector<bluetooth::Uuid>&, bool) { gatt_service_cb_both_call_cnt++; }, - nullptr, [](RawAddress /*addr*/, const std::vector<bluetooth::Uuid>&, tBTA_STATUS) {}}, - kRawAddress, BT_TRANSPORT_LE); - - // GATT discovery is queued, until SDP finishes - EXPECT_EQ(gatt_call_cnt, 0); - - bta_dm_sdp_finished(kRawAddress, BTA_SUCCESS, {}, {}); - EXPECT_EQ(service_cb_both_call_cnt, 1); - - // SDP finished, wait until GATT is triggered. - EXPECT_EQ(std::future_status::ready, - gatt_triggered.get_future().wait_for(std::chrono::seconds(1))); - bta_dm_gatt_finished(kRawAddress, BTA_SUCCESS); - EXPECT_EQ(gatt_service_cb_both_call_cnt, 1); - - bta_dm_disc_override_sdp_performer_for_testing({}); - bta_dm_disc_override_gatt_performer_for_testing({}); -} - -/* This test exercises the usual service discovery flow when bonding to - * dual-mode, CTKD capable device on LE transport. - */ -TEST_F_WITH_FLAGS(BtaInitializedTest, bta_dm_disc_both_transports_flag_enabled, - REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(TEST_BT, bta_dm_discover_both))) { +TEST_F(BtaInitializedTest, bta_dm_disc_both_transports) { bta_dm_disc_start(true); int gatt_call_cnt = 0; diff --git a/system/btif/include/btif_dm.h b/system/btif/include/btif_dm.h index b165baa441..9069c97b76 100644 --- a/system/btif/include/btif_dm.h +++ b/system/btif/include/btif_dm.h @@ -151,6 +151,7 @@ void btif_dm_get_ble_local_keys(tBTA_DM_BLE_LOCAL_KEY_MASK* p_key_mask, Octet16* tBTA_BLE_LOCAL_ID_KEYS* p_id_keys); void btif_update_remote_properties(const RawAddress& bd_addr, BD_NAME bd_name, DEV_CLASS dev_class, tBT_DEVICE_TYPE dev_type); +bool btif_is_interesting_le_service(const bluetooth::Uuid& uuid); bool check_cod_hid(const RawAddress& bd_addr); bool check_cod_hid_major(const RawAddress& bd_addr, uint32_t cod); diff --git a/system/btif/include/btif_storage.h b/system/btif/include/btif_storage.h index 0c1043faae..01432bade6 100644 --- a/system/btif/include/btif_storage.h +++ b/system/btif/include/btif_storage.h @@ -446,6 +446,7 @@ bt_status_t btif_storage_set_hid_connection_policy(const tAclLinkSpec& link_spec bt_status_t btif_storage_get_hid_connection_policy(const tAclLinkSpec& link_spec, bool* reconnect_allowed); +void btif_storage_migrate_services(); /****************************************************************************** * Exported for unit tests *****************************************************************************/ diff --git a/system/btif/src/btif_core.cc b/system/btif/src/btif_core.cc index f0d06373b6..8e481daed0 100644 --- a/system/btif/src/btif_core.cc +++ b/system/btif/src/btif_core.cc @@ -134,7 +134,12 @@ int btif_is_enabled(void) { return (!btif_is_dut_mode()) && (stack_manager_get_interface()->get_stack_is_running()); } -void btif_init_ok() { btif_dm_load_ble_local_keys(); } +void btif_init_ok() { + btif_dm_load_ble_local_keys(); + if (com::android::bluetooth::flags::separate_service_storage()) { + btif_storage_migrate_services(); + } +} /******************************************************************************* * @@ -290,7 +295,7 @@ void btif_dut_mode_send(uint16_t opcode, uint8_t* buf, uint8_t len) { ****************************************************************************/ static bt_status_t btif_in_get_adapter_properties(void) { - const static uint32_t NUM_ADAPTER_PROPERTIES = 5; + static const uint32_t NUM_ADAPTER_PROPERTIES = 5; bt_property_t properties[NUM_ADAPTER_PROPERTIES]; uint32_t num_props = 0; @@ -340,12 +345,13 @@ static bt_status_t btif_in_get_adapter_properties(void) { } static bt_status_t btif_in_get_remote_device_properties(RawAddress* bd_addr) { - bt_property_t remote_properties[8]; + bt_property_t remote_properties[9]; uint32_t num_props = 0; bt_bdname_t name, alias; uint32_t cod, devtype; Uuid remote_uuids[BT_MAX_NUM_UUIDS]; + Uuid remote_uuids_le[BT_MAX_NUM_UUIDS]; memset(remote_properties, 0, sizeof(remote_properties)); BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_BDNAME, sizeof(name), @@ -369,10 +375,17 @@ static bt_status_t btif_in_get_remote_device_properties(RawAddress* bd_addr) { num_props++; BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_UUIDS, sizeof(remote_uuids), - remote_uuids); + &remote_uuids); btif_storage_get_remote_device_property(bd_addr, &remote_properties[num_props]); num_props++; + if (com::android::bluetooth::flags::separate_service_storage()) { + BTIF_STORAGE_FILL_PROPERTY(&remote_properties[num_props], BT_PROPERTY_UUIDS_LE, + sizeof(remote_uuids_le), &remote_uuids_le); + btif_storage_get_remote_device_property(bd_addr, &remote_properties[num_props]); + num_props++; + } + GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb( BT_STATUS_SUCCESS, *bd_addr, num_props, remote_properties); diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc index eda581d81b..6f83aee781 100644 --- a/system/btif/src/btif_dm.cc +++ b/system/btif/src/btif_dm.cc @@ -1517,15 +1517,16 @@ static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* } /* Returns true if |uuid| should be passed as device property */ -static bool btif_is_interesting_le_service(bluetooth::Uuid uuid) { +bool btif_is_interesting_le_service(const bluetooth::Uuid& uuid) { return uuid.As16Bit() == UUID_SERVCLASS_LE_HID || uuid == UUID_HEARING_AID || uuid == UUID_VC || uuid == UUID_CSIS || uuid == UUID_LE_AUDIO || uuid == UUID_LE_MIDI || uuid == UUID_HAS || uuid == UUID_BASS || uuid == UUID_BATTERY || uuid == ANDROID_HEADTRACKER_SERVICE_UUID; } -static bt_status_t btif_get_existing_uuids(RawAddress* bd_addr, Uuid* existing_uuids) { +static bt_status_t btif_get_existing_uuids(RawAddress* bd_addr, Uuid* existing_uuids, + bt_property_type_t property_type = BT_PROPERTY_UUIDS) { bt_property_t tmp_prop; - BTIF_STORAGE_FILL_PROPERTY(&tmp_prop, BT_PROPERTY_UUIDS, sizeof(*existing_uuids), existing_uuids); + BTIF_STORAGE_FILL_PROPERTY(&tmp_prop, property_type, sizeof(*existing_uuids), existing_uuids); return btif_storage_get_remote_device_property(bd_addr, &tmp_prop); } @@ -1537,9 +1538,10 @@ static bool btif_is_gatt_service_discovery_post_pairing(const RawAddress bd_addr (pairing_cb.gatt_over_le == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED); } -static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids) { +static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids, + bt_property_type_t property_type = BT_PROPERTY_UUIDS) { Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {}; - bt_status_t lookup_result = btif_get_existing_uuids(&addr, existing_uuids); + bt_status_t lookup_result = btif_get_existing_uuids(&addr, existing_uuids, property_type); if (lookup_result == BT_STATUS_FAIL) { return; @@ -1550,18 +1552,14 @@ static void btif_merge_existing_uuids(RawAddress& addr, std::set<Uuid>* uuids) { if (btif_should_ignore_uuid(uuid)) { continue; } - if (btif_is_interesting_le_service(uuid)) { - log::info("interesting le service {} insert", uuid.ToString()); - uuids->insert(uuid); - } + + uuids->insert(uuid); } } static void btif_on_service_discovery_results(RawAddress bd_addr, const std::vector<bluetooth::Uuid>& uuids_param, tBTA_STATUS result) { - bt_property_t prop; - std::vector<uint8_t> property_value; std::set<Uuid> uuids; bool a2dp_sink_capable = false; @@ -1589,8 +1587,12 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, pairing_cb.sdp_over_classic = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED; } - prop.type = BT_PROPERTY_UUIDS; - prop.len = 0; + std::vector<uint8_t> bredr_property_value; + std::vector<uint8_t> le_property_value; + bt_property_t uuid_props[2] = {}; + bt_property_t& bredr_prop = uuid_props[0]; + bt_property_t& le_prop = uuid_props[1]; + if ((result == BTA_SUCCESS) && !uuids_param.empty()) { log::info("New UUIDs for {}:", bd_addr); for (const auto& uuid : uuids_param) { @@ -1610,13 +1612,35 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, for (auto& uuid : uuids) { auto uuid_128bit = uuid.To128BitBE(); - property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end()); + bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(), + uuid_128bit.end()); if (uuid == UUID_A2DP_SINK) { a2dp_sink_capable = true; } } - prop.val = (void*)property_value.data(); - prop.len = Uuid::kNumBytes128 * uuids.size(); + + bredr_prop = {BT_PROPERTY_UUIDS, static_cast<int>(Uuid::kNumBytes128 * uuids.size()), + (void*)bredr_property_value.data()}; + + if (com::android::bluetooth::flags::separate_service_storage()) { + bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &bredr_prop); + ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote classic services failed", ret); + + std::set<Uuid> le_uuids; + if (results_for_bonding_device) { + btif_merge_existing_uuids(pairing_cb.static_bdaddr, &le_uuids, BT_PROPERTY_UUIDS_LE); + btif_merge_existing_uuids(pairing_cb.bd_addr, &le_uuids, BT_PROPERTY_UUIDS_LE); + } else { + btif_merge_existing_uuids(bd_addr, &le_uuids, BT_PROPERTY_UUIDS_LE); + } + + for (auto& uuid : le_uuids) { + auto uuid_128bit = uuid.To128BitBE(); + le_property_value.insert(le_property_value.end(), uuid_128bit.begin(), uuid_128bit.end()); + } + le_prop = {BT_PROPERTY_UUIDS_LE, static_cast<int>(Uuid::kNumBytes128 * le_uuids.size()), + (void*)le_property_value.data()}; + } } bool skip_reporting_wait_for_le = false; @@ -1649,17 +1673,18 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, log::info("SDP failed, send {} EIR UUIDs to unblock bonding {}", num_eir_uuids, bd_addr); for (auto eir_uuid : uuids_iter->second) { auto uuid_128bit = eir_uuid.To128BitBE(); - property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end()); + bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(), + uuid_128bit.end()); } eir_uuids_cache.erase(uuids_iter); } if (num_eir_uuids > 0) { - prop.val = (void*)property_value.data(); - prop.len = num_eir_uuids * Uuid::kNumBytes128; + bredr_prop.val = (void*)bredr_property_value.data(); + bredr_prop.len = num_eir_uuids * Uuid::kNumBytes128; } else { log::warn("SDP failed and we have no EIR UUIDs to report either"); - prop.val = &uuid; - prop.len = Uuid::kNumBytes128; + bredr_prop.val = &uuid; + bredr_prop.len = Uuid::kNumBytes128; } } @@ -1677,9 +1702,10 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, uuids_param.size(), num_eir_uuids)); if (!uuids_param.empty() || num_eir_uuids != 0) { - /* Also write this to the NVRAM */ - const bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &prop); - ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret); + if (!com::android::bluetooth::flags::separate_service_storage()) { + const bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &bredr_prop); + ASSERTC(ret == BT_STATUS_SUCCESS, "storing remote services failed", ret); + } if (skip_reporting_wait_for_le) { log::info( @@ -1694,16 +1720,13 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, } /* Send the event to the BTIF */ - GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr, - 1, &prop); + GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb( + BT_STATUS_SUCCESS, bd_addr, ARRAY_SIZE(uuid_props), uuid_props); } } static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid>& services, bool is_transport_le) { - std::vector<bt_property_t> prop; - std::vector<uint8_t> property_value; - std::set<Uuid> uuids; RawAddress static_addr_copy = pairing_cb.static_bdaddr; bool lea_supported = is_le_audio_capable_during_service_discovery(bd_addr); @@ -1739,6 +1762,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using SDP transport"); } + std::set<Uuid> uuids; for (Uuid uuid : services) { if (btif_is_interesting_le_service(uuid)) { if (btif_should_ignore_uuid(uuid)) { @@ -1767,46 +1791,28 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid log::info("Will return Classic SDP results, if done, to unblock bonding"); } - Uuid existing_uuids[BT_MAX_NUM_UUIDS] = {}; - - // Look up UUIDs using pseudo address (either RPA or static address) - bt_status_t existing_lookup_result = btif_get_existing_uuids(&bd_addr, existing_uuids); - - if (existing_lookup_result != BT_STATUS_FAIL) { - log::info("Got some existing UUIDs by address {}", bd_addr); - - for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) { - Uuid uuid = existing_uuids[i]; - if (uuid.IsEmpty()) { - continue; - } - uuids.insert(uuid); + if (!com::android::bluetooth::flags::separate_service_storage()) { + // Look up UUIDs using pseudo address (either RPA or static address) + btif_merge_existing_uuids(bd_addr, &uuids); + if (bd_addr != static_addr_copy) { + // Look up UUID using static address, if different than sudo address + btif_merge_existing_uuids(static_addr_copy, &uuids); } } - if (bd_addr != static_addr_copy) { - // Look up UUID using static address, if different than sudo address - existing_lookup_result = btif_get_existing_uuids(&static_addr_copy, existing_uuids); - if (existing_lookup_result != BT_STATUS_FAIL) { - log::info("Got some existing UUIDs by static address {}", static_addr_copy); - for (int i = 0; i < BT_MAX_NUM_UUIDS; i++) { - Uuid uuid = existing_uuids[i]; - if (uuid.IsEmpty()) { - continue; - } - uuids.insert(uuid); - } - } - } + std::vector<bt_property_t> prop; + std::vector<uint8_t> property_value; for (auto& uuid : uuids) { auto uuid_128bit = uuid.To128BitBE(); property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end()); } - prop.push_back(bt_property_t{BT_PROPERTY_UUIDS, - static_cast<int>(Uuid::kNumBytes128 * uuids.size()), - (void*)property_value.data()}); + prop.push_back(bt_property_t{ + (com::android::bluetooth::flags::separate_service_storage() && is_transport_le) + ? BT_PROPERTY_UUIDS_LE + : BT_PROPERTY_UUIDS, + static_cast<int>(Uuid::kNumBytes128 * uuids.size()), (void*)property_value.data()}); /* Also write this to the NVRAM */ bt_status_t ret = btif_storage_set_remote_device_property(&bd_addr, &prop[0]); @@ -1817,8 +1823,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid * send them with rest of SDP results in on_service_discovery_results */ return; } else { - if (pairing_cb.sdp_over_classic == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED && - com::android::bluetooth::flags::bta_dm_discover_both()) { + if (pairing_cb.sdp_over_classic == btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED) { /* Don't report services yet, they will be reported together once SDP * finishes. */ log::info("will report services later, with SDP results {}", bd_addr); @@ -1826,6 +1831,32 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid } } + if (!com::android::bluetooth::flags::separate_service_storage()) { + /* Send the event to the BTIF */ + GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr, + prop.size(), prop.data()); + return; + } + + std::set<Uuid> bredr_uuids; + // Look up UUIDs using pseudo address (either RPA or static address) + btif_merge_existing_uuids(bd_addr, &bredr_uuids); + if (bd_addr != static_addr_copy) { + // Look up UUID using static address, if different than sudo address + btif_merge_existing_uuids(static_addr_copy, &bredr_uuids); + } + + std::vector<uint8_t> bredr_property_value; + + for (auto& uuid : bredr_uuids) { + auto uuid_128bit = uuid.To128BitBE(); + bredr_property_value.insert(bredr_property_value.end(), uuid_128bit.begin(), uuid_128bit.end()); + } + + prop.push_back(bt_property_t{BT_PROPERTY_UUIDS, + static_cast<int>(Uuid::kNumBytes128 * bredr_uuids.size()), + (void*)bredr_property_value.data()}); + /* Send the event to the BTIF */ GetInterfaceToProfiles()->events->invoke_remote_device_properties_cb(BT_STATUS_SUCCESS, bd_addr, prop.size(), prop.data()); @@ -2517,6 +2548,9 @@ void btif_dm_cancel_bond(const RawAddress bd_addr) { ** 2. special handling for HID devices */ if (is_bonding_or_sdp()) { + // clear sdp_attempts + pairing_cb.sdp_attempts = 0; + if (com::android::bluetooth::flags::ignore_unrelated_cancel_bond() && (pairing_cb.bd_addr != bd_addr)) { log::warn("Ignoring bond cancel for unrelated device: {} pairing: {}", bd_addr, diff --git a/system/btif/src/btif_storage.cc b/system/btif/src/btif_storage.cc index e51756483c..4704362f2c 100644 --- a/system/btif/src/btif_storage.cc +++ b/system/btif/src/btif_storage.cc @@ -47,6 +47,7 @@ #include "btif/include/btif_api.h" #include "btif/include/btif_config.h" +#include "btif/include/btif_dm.h" #include "btif/include/btif_util.h" #include "btif/include/core_callbacks.h" #include "btif/include/stack_manager_t.h" @@ -179,15 +180,18 @@ static bool prop2cfg(const RawAddress* remote_bd_addr, bt_property_t* prop) { case BT_PROPERTY_TYPE_OF_DEVICE: btif_config_set_int(bdstr, BTIF_STORAGE_KEY_DEV_TYPE, *reinterpret_cast<int*>(prop->val)); break; - case BT_PROPERTY_UUIDS: { + case BT_PROPERTY_UUIDS: + case BT_PROPERTY_UUIDS_LE: { std::string val; size_t cnt = (prop->len) / sizeof(Uuid); for (size_t i = 0; i < cnt; i++) { val += (reinterpret_cast<Uuid*>(prop->val) + i)->ToString() + " "; } - btif_config_set_str(bdstr, BTIF_STORAGE_KEY_REMOTE_SERVICE, val); - break; - } + std::string key = (prop->type == BT_PROPERTY_UUIDS_LE) ? BTIF_STORAGE_KEY_REMOTE_SERVICE_LE + : BTIF_STORAGE_KEY_REMOTE_SERVICE; + btif_config_set_str(bdstr, key, val); + } break; + case BT_PROPERTY_REMOTE_VERSION_INFO: { bt_remote_version_t* info = reinterpret_cast<bt_remote_version_t*>(prop->val); @@ -300,10 +304,15 @@ static bool cfg2prop(const RawAddress* remote_bd_addr, bt_property_t* prop) { reinterpret_cast<int*>(prop->val)); } break; - case BT_PROPERTY_UUIDS: { + case BT_PROPERTY_UUIDS: + case BT_PROPERTY_UUIDS_LE: { char value[1280]; int size = sizeof(value); - if (btif_config_get_str(bdstr, BTIF_STORAGE_KEY_REMOTE_SERVICE, value, &size)) { + + std::string key = (prop->type == BT_PROPERTY_UUIDS_LE) ? BTIF_STORAGE_KEY_REMOTE_SERVICE_LE + : BTIF_STORAGE_KEY_REMOTE_SERVICE; + + if (btif_config_get_str(bdstr, key, value, &size)) { Uuid* p_uuid = reinterpret_cast<Uuid*>(prop->val); size_t num_uuids = btif_split_uuids_string(value, p_uuid, BT_MAX_NUM_UUIDS); prop->len = num_uuids * sizeof(Uuid); @@ -938,13 +947,14 @@ bt_status_t btif_storage_load_bonded_devices(void) { uint32_t i = 0; bt_property_t adapter_props[6]; uint32_t num_props = 0; - bt_property_t remote_properties[10]; + bt_property_t remote_properties[11]; RawAddress addr; bt_bdname_t name, alias, model_name; bt_scan_mode_t mode; uint32_t disc_timeout; Uuid local_uuids[BT_MAX_NUM_UUIDS]; Uuid remote_uuids[BT_MAX_NUM_UUIDS]; + Uuid remote_uuids_le[BT_MAX_NUM_UUIDS]; bt_status_t status; remove_devices_with_sample_ltk(); @@ -1026,10 +1036,16 @@ bt_status_t btif_storage_load_bonded_devices(void) { sizeof(devtype), &remote_properties[num_props]); num_props++; - btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS, remote_uuids, + btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS, &remote_uuids, sizeof(remote_uuids), &remote_properties[num_props]); num_props++; + if (com::android::bluetooth::flags::separate_service_storage()) { + btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_UUIDS_LE, &remote_uuids_le, + sizeof(remote_uuids_le), &remote_properties[num_props]); + num_props++; + } + // Floss needs appearance for metrics purposes uint16_t appearance = 0; if (btif_storage_get_remote_prop(p_remote_addr, BT_PROPERTY_APPEARANCE, &appearance, @@ -1438,6 +1454,48 @@ void btif_storage_remove_gatt_cl_db_hash(const RawAddress& bd_addr) { bd_addr)); } +// TODO(b/369381361) Remove this function after all devices are migrated +void btif_storage_migrate_services() { + for (const auto& mac_address : btif_config_get_paired_devices()) { + auto addr_str = mac_address.ToString(); + + int device_type = BT_DEVICE_TYPE_UNKNOWN; + btif_config_get_int(addr_str, BTIF_STORAGE_KEY_DEV_TYPE, &device_type); + + if ((device_type == BT_DEVICE_TYPE_BREDR) || + btif_config_exist(addr_str, BTIF_STORAGE_KEY_REMOTE_SERVICE_LE)) { + /* Classic only, or already migrated entries don't need migration */ + continue; + } + + bt_property_t remote_uuids_prop; + Uuid remote_uuids[BT_MAX_NUM_UUIDS]; + BTIF_STORAGE_FILL_PROPERTY(&remote_uuids_prop, BT_PROPERTY_UUIDS, sizeof(remote_uuids), + remote_uuids); + btif_storage_get_remote_device_property(&mac_address, &remote_uuids_prop); + + log::info("Will migrate Services => ServicesLe for {}", mac_address.ToStringForLogging()); + + std::vector<uint8_t> property_value; + for (auto& uuid : remote_uuids) { + if (!btif_is_interesting_le_service(uuid)) { + continue; + } + + log::info("interesting LE service: {}", uuid); + auto uuid_128bit = uuid.To128BitBE(); + property_value.insert(property_value.end(), uuid_128bit.begin(), uuid_128bit.end()); + } + + bt_property_t le_uuids_prop{BT_PROPERTY_UUIDS_LE, static_cast<int>(property_value.size()), + (void*)property_value.data()}; + + /* Write LE services to storage */ + btif_storage_set_remote_device_property(&mac_address, &le_uuids_prop); + log::info("Migration finished for {}", mac_address.ToStringForLogging()); + } +} + void btif_debug_linkkey_type_dump(int fd) { dprintf(fd, "\nLink Key Types:\n"); for (const auto& bd_addr : btif_config_get_paired_devices()) { diff --git a/system/btif/src/btif_util.cc b/system/btif/src/btif_util.cc index 85c0069eaa..c6cf544d4a 100644 --- a/system/btif/src/btif_util.cc +++ b/system/btif/src/btif_util.cc @@ -129,6 +129,7 @@ std::string dump_property_type(bt_property_type_t type) { CASE_RETURN_STRING(BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT); CASE_RETURN_STRING(BT_PROPERTY_ADAPTER_BONDED_DEVICES); CASE_RETURN_STRING(BT_PROPERTY_REMOTE_FRIENDLY_NAME); + CASE_RETURN_STRING(BT_PROPERTY_UUIDS_LE); default: RETURN_UNKNOWN_TYPE_STRING(bt_property_type_t, type); } diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc index 66881a92d8..1bffc9ce3a 100644 --- a/system/btif/test/btif_core_test.cc +++ b/system/btif/test/btif_core_test.cc @@ -320,6 +320,7 @@ TEST_F(BtifUtilsTest, dump_property_type) { "BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT"), std::make_pair(BT_PROPERTY_ADAPTER_BONDED_DEVICES, "BT_PROPERTY_ADAPTER_BONDED_DEVICES"), std::make_pair(BT_PROPERTY_REMOTE_FRIENDLY_NAME, "BT_PROPERTY_REMOTE_FRIENDLY_NAME"), + std::make_pair(BT_PROPERTY_UUIDS_LE, "BT_PROPERTY_UUIDS_LE"), }; for (const auto& type : types) { EXPECT_TRUE(dump_property_type(type.first).starts_with(type.second)); diff --git a/system/conf/interop_database.conf b/system/conf/interop_database.conf index f798e328c4..94ef06e885 100644 --- a/system/conf/interop_database.conf +++ b/system/conf/interop_database.conf @@ -105,6 +105,16 @@ C8:FD:19 = Address_Based 0C:A6:94 = Address_Based 00:0f:59:50:00:00-00:0f:59:6f:ff:ff = Address_Range_Based +# Some devices only respond to read SIRK request via GATT_READ_CHAR_VALUE +# disabling GATT_READ_BY_TYPE for SIRK read for those devices. +# 48:73:CB ==> Name: Redmi buds +# 00:02:3C ==> Name: Zen Air Pro +# 14:0A:29 ==> Name: Redmi Buds 5 Pro +[INTEROP_DISABLE_SIRK_READ_BY_TYPE] +48:73:CB = Address_Based +00:02:3C = Address_Based +14:0A:29 = Address_Based + # Disable automatic pairing with headsets/car-kits # Some car kits do not react kindly to a failed pairing attempt and # do not allow immediate re-pairing. Denylist these so that the initial @@ -883,3 +893,8 @@ BSK10 = Name_Based [INTEROP_DISABLE_HF_PROFILE] JBL Flip 5 = Name_Based JBL Flip 6 = Name_Based + +# Some carkits don't support rejecting notifications when addressed player changed. +[INTEROP_ADDRESSED_PLAYER_CHANGE_REJECT] +4c:3f:d3 = Address_Based +e8:d5:2b = Address_Based diff --git a/system/device/include/interop.h b/system/device/include/interop.h index b23900888b..a19c5fbaae 100644 --- a/system/device/include/interop.h +++ b/system/device/include/interop.h @@ -48,6 +48,9 @@ typedef enum { // levels or general lack of controlability. INTEROP_DISABLE_ABSOLUTE_VOLUME, + // Devices requiring this read characteristics via GATT_READ_CHAR_VALUE + INTEROP_DISABLE_SIRK_READ_BY_TYPE, + // Disable automatic pairing with headsets/car-kits // Some car kits do not react kindly to a failed pairing attempt and // do not allow immediate re-pairing. Rejectlist these so that the initial @@ -365,6 +368,9 @@ typedef enum { // Some devices claim to support HFP in EIR but does not actually support it. INTEROP_DISABLE_HF_PROFILE, + // Some carkits don't support rejecting notifications when addressed player changed. + INTEROP_ADDRESSED_PLAYER_CHANGE_REJECT, + END_OF_INTEROP_LIST } interop_feature_t; diff --git a/system/device/src/interop.cc b/system/device/src/interop.cc index 354a9e1a65..614e3c8981 100644 --- a/system/device/src/interop.cc +++ b/system/device/src/interop.cc @@ -327,6 +327,7 @@ static const char* interop_feature_string_(const interop_feature_t feature) { CASE_RETURN_STR(INTEROP_DISABLE_LE_SECURE_CONNECTIONS) CASE_RETURN_STR(INTEROP_AUTO_RETRY_PAIRING) CASE_RETURN_STR(INTEROP_DISABLE_ABSOLUTE_VOLUME) + CASE_RETURN_STR(INTEROP_DISABLE_SIRK_READ_BY_TYPE) CASE_RETURN_STR(INTEROP_DISABLE_AUTO_PAIRING) CASE_RETURN_STR(INTEROP_KEYBOARD_REQUIRES_FIXED_PIN) CASE_RETURN_STR(INTEROP_2MBPS_LINK_ONLY) @@ -394,6 +395,7 @@ static const char* interop_feature_string_(const interop_feature_t feature) { CASE_RETURN_STR(INTEROP_HOGP_LONG_REPORT); CASE_RETURN_STR(INTEROP_HOGP_FORCE_MTU_EXCHANGE); CASE_RETURN_STR(INTEROP_DISABLE_HF_PROFILE); + CASE_RETURN_STR(INTEROP_ADDRESSED_PLAYER_CHANGE_REJECT); } return UNKNOWN_INTEROP_FEATURE; } diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc index 696f33e621..f5ef4fbec5 100644 --- a/system/gd/hci/distance_measurement_manager.cc +++ b/system/gd/hci/distance_measurement_manager.cc @@ -32,6 +32,7 @@ #include "hci/event_checkers.h" #include "hci/hci_layer.h" #include "module.h" +#include "os/alarm.h" #include "os/handler.h" #include "os/repeating_alarm.h" #include "packet/packet_view.h" @@ -84,6 +85,7 @@ static constexpr uint16_t kInvalidConnInterval = 0; // valid value is from 0x00 static constexpr uint16_t kDefaultRasMtu = 247; // Section 3.1.2 of RAP 1.0 static constexpr uint8_t kAttHeaderSize = 5; // Section 3.2.2.1 of RAS 1.0 static constexpr uint8_t kRasSegmentHeaderSize = 1; +static constexpr uint16_t kEnableSecurityTimeoutMs = 10000; // 10s struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { struct CsProcedureData { @@ -216,6 +218,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { PacketViewForRecombination segment_data_; uint16_t conn_interval_ = kInvalidConnInterval; uint8_t procedure_sequence_after_enable = -1; + std::unique_ptr<os::Alarm> enable_security_timeout_alarm = nullptr; }; bool get_free_config_id(uint16_t connection_handle, uint8_t& config_id) { @@ -683,10 +686,12 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { return; } switch (event.GetSubeventCode()) { - case hci::SubeventCode::LE_CS_TEST_END_COMPLETE: - case hci::SubeventCode::LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE: { + case hci::SubeventCode::LE_CS_TEST_END_COMPLETE: { log::warn("Unhandled subevent {}", hci::SubeventCodeText(event.GetSubeventCode())); } break; + case hci::SubeventCode::LE_CS_READ_REMOTE_FAE_TABLE_COMPLETE: { + on_cs_read_remote_fae_table_complete(LeCsReadRemoteFaeTableCompleteView::Create(event)); + } break; case hci::SubeventCode::LE_CS_SUBEVENT_RESULT_CONTINUE: case hci::SubeventCode::LE_CS_SUBEVENT_RESULT: { on_cs_subevent(event); @@ -747,6 +752,11 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { handler_->BindOnceOn(this, &impl::on_cs_set_default_settings_complete)); } + void send_le_cs_read_remote_fae_table(uint16_t connection_handle) const { + hci_layer_->EnqueueCommand(LeCsReadRemoteFaeTableBuilder::Create(connection_handle), + handler_->BindOnce(check_status<LeCsReadRemoteFaeTableStatusView>)); + } + void send_le_cs_create_config(uint16_t connection_handle, uint8_t config_id) { if (cs_requester_trackers_.find(connection_handle) == cs_requester_trackers_.end()) { log::warn("no cs tracker found for {}", connection_handle); @@ -938,6 +948,12 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { req_it->second.remote_num_antennas_supported_ = event_view.GetNumAntennasSupported(); req_it->second.retry_counter_for_create_config = 0; req_it->second.remote_supported_sw_time_ = event_view.GetTSwTimeSupported(); + + if (event_view.GetOptionalSubfeaturesSupported().no_frequency_actuation_error_ == 0) { + log::debug("read remote fae as the no_fae is false."); + send_le_cs_read_remote_fae_table(connection_handle); + } + send_le_cs_create_config(connection_handle, req_it->second.requesting_config_id); } log::info( @@ -963,19 +979,39 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { } } + static void on_cs_read_remote_fae_table_complete(LeCsReadRemoteFaeTableCompleteView event_view) { + if (!event_view.IsValid()) { + log::warn("Get invalid LeCsReadRemoteFaeTableCompleteView"); + return; + } + if (event_view.GetStatus() != ErrorCode::SUCCESS) { + log::warn("Received LeCsReadRemoteFaeTableCompleteView with error code {}", + ErrorCodeText(event_view.GetStatus())); + // not critical, do nothing here. + } + } + void on_cs_security_enable_complete(LeCsSecurityEnableCompleteView event_view) { if (!event_view.IsValid()) { log::warn("Get invalid LeCsSecurityEnableCompleteView"); return; } uint16_t connection_handle = event_view.GetConnectionHandle(); + auto req_it = cs_requester_trackers_.find(connection_handle); + if (req_it != cs_requester_trackers_.end() && + req_it->second.state == CsTrackerState::WAIT_FOR_SECURITY_ENABLED && + req_it->second.enable_security_timeout_alarm != nullptr) { + log::debug("cancel alarm for security enable cmd."); + req_it->second.enable_security_timeout_alarm->Cancel(); + req_it->second.enable_security_timeout_alarm = nullptr; + } if (event_view.GetStatus() != ErrorCode::SUCCESS) { std::string error_code = ErrorCodeText(event_view.GetStatus()); log::warn("Received LeCsSecurityEnableCompleteView with error code {}", error_code); handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); return; } - auto req_it = cs_requester_trackers_.find(connection_handle); + if (req_it != cs_requester_trackers_.end() && req_it->second.state == CsTrackerState::WAIT_FOR_SECURITY_ENABLED) { send_le_cs_set_procedure_parameters(event_view.GetConnectionHandle(), @@ -1055,8 +1091,25 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { // send the cmd from the BLE central only. send_le_cs_security_enable(connection_handle, live_tracker->local_start); } else { - // TODO: else set a timeout alarm to make sure the remote would trigger the cmd. live_tracker->state = CsTrackerState::WAIT_FOR_SECURITY_ENABLED; + if (live_tracker->local_start) { + if (live_tracker->enable_security_timeout_alarm == nullptr) { + live_tracker->enable_security_timeout_alarm = std::make_unique<os::Alarm>(handler_); + } + live_tracker->enable_security_timeout_alarm->Schedule( + common::Bind(&impl::le_cs_enable_security_timeout, common::Unretained(this), + connection_handle), + std::chrono::milliseconds(kEnableSecurityTimeoutMs)); + } + } + } + + void le_cs_enable_security_timeout(uint16_t connection_handle) { + auto req_it = cs_requester_trackers_.find(connection_handle); + if (req_it != cs_requester_trackers_.end()) { + log::info("security enable cmd is timeout, stop current session."); + handle_cs_setup_failure(connection_handle, + DistanceMeasurementErrorCode::REASON_INTERNAL_ERROR); } } diff --git a/system/gd/hci/le_advertising_manager.h b/system/gd/hci/le_advertising_manager.h index 55245a5cfa..fe614861e2 100644 --- a/system/gd/hci/le_advertising_manager.h +++ b/system/gd/hci/le_advertising_manager.h @@ -47,8 +47,8 @@ class AdvertisingConfig { public: std::vector<GapData> advertisement; std::vector<GapData> scan_response; - uint16_t interval_min; - uint16_t interval_max; + uint32_t interval_min; + uint32_t interval_max; AdvertisingType advertising_type; AdvertiserAddressType requested_advertiser_address_type; PeerAddressType peer_address_type; diff --git a/system/gd/rust/linux/stack/src/bluetooth.rs b/system/gd/rust/linux/stack/src/bluetooth.rs index 761f9483f3..b0b74193f4 100644 --- a/system/gd/rust/linux/stack/src/bluetooth.rs +++ b/system/gd/rust/linux/stack/src/bluetooth.rs @@ -374,7 +374,7 @@ struct BluetoothDeviceContext { pub info: BluetoothDevice, pub last_seen: Instant, pub properties: HashMap<BtPropertyType, BluetoothProperty>, - pub is_hh_connected: bool, + pub is_initiated_hh_connection: bool, /// If user wants to connect to all profiles, when new profiles are discovered we will also try /// to connect them. @@ -398,7 +398,7 @@ impl BluetoothDeviceContext { info, last_seen, properties: HashMap::new(), - is_hh_connected: false, + is_initiated_hh_connection: false, connect_to_new_profiles: false, }; device.update_properties(&properties); @@ -1302,8 +1302,11 @@ impl Bluetooth { || self.pending_create_bond.is_some() } - pub fn is_hh_connected(&self, device_address: &RawAddress) -> bool { - self.remote_devices.get(&device_address).map_or(false, |context| context.is_hh_connected) + /// Checks whether a Hid/Hog connection is being established or active. + pub fn is_initiated_hh_connection(&self, device_address: &RawAddress) -> bool { + self.remote_devices + .get(&device_address) + .map_or(false, |context| context.is_initiated_hh_connection) } /// Checks whether the list of device properties contains some UUID we should connect now @@ -3018,12 +3021,16 @@ impl BtifHHCallbacks for Bluetooth { let tx = self.tx.clone(); self.remote_devices.entry(address).and_modify(|context| { - if context.is_hh_connected && state != BthhConnectionState::Connected { + if context.is_initiated_hh_connection + && (state != BthhConnectionState::Connected + && state != BthhConnectionState::Connecting) + { tokio::spawn(async move { let _ = tx.send(Message::ProfileDisconnected(address)).await; }); } - context.is_hh_connected = state == BthhConnectionState::Connected; + context.is_initiated_hh_connection = + state == BthhConnectionState::Connected || state == BthhConnectionState::Connecting; }); if BtBondState::Bonded != self.get_bond_state_by_addr(&address) diff --git a/system/gd/rust/linux/stack/src/lib.rs b/system/gd/rust/linux/stack/src/lib.rs index 8027c1c61f..6cccf41219 100644 --- a/system/gd/rust/linux/stack/src/lib.rs +++ b/system/gd/rust/linux/stack/src/lib.rs @@ -619,7 +619,7 @@ impl Stack { // to know whether a socket is open or not. if bluetooth_gatt.lock().unwrap().get_connected_applications(&addr) == vec![bas_app_uuid] - && !bluetooth.lock().unwrap().is_hh_connected(&addr) + && !bluetooth.lock().unwrap().is_initiated_hh_connection(&addr) && bluetooth_media.lock().unwrap().get_connected_profiles(&addr).is_empty() { info!( diff --git a/system/gd/rust/topshim/gatt/gatt_ble_scanner_shim.cc b/system/gd/rust/topshim/gatt/gatt_ble_scanner_shim.cc index 2bfa893d80..0ae488cd9e 100644 --- a/system/gd/rust/topshim/gatt/gatt_ble_scanner_shim.cc +++ b/system/gd/rust/topshim/gatt/gatt_ble_scanner_shim.cc @@ -248,9 +248,7 @@ void BleScannerIntf::MsftAdvMonitorEnable(bool enable) { void BleScannerIntf::SetScanParameters(uint8_t scanner_id, uint8_t scan_type, uint16_t scan_interval, uint16_t scan_window, uint8_t scan_phy) { - scanner_intf_->SetScanParameters( - scanner_id, scan_type, scan_interval, scan_window, scan_phy, - base::Bind(&BleScannerIntf::OnStatusCallback, base::Unretained(this), scanner_id)); + scanner_intf_->SetScanParameters(scanner_id, scan_type, scan_interval, scan_window, scan_phy); } void BleScannerIntf::BatchscanConfigStorage(uint8_t scanner_id, int32_t batch_scan_full_max, diff --git a/system/gd/storage/config_keys.h b/system/gd/storage/config_keys.h index 4629a494ba..d3659b6a96 100644 --- a/system/gd/storage/config_keys.h +++ b/system/gd/storage/config_keys.h @@ -111,6 +111,7 @@ #define BTIF_STORAGE_KEY_PIN_LENGTH "PinLength" #define BTIF_STORAGE_KEY_PRODUCT_ID "ProductId" #define BTIF_STORAGE_KEY_REMOTE_SERVICE "Service" +#define BTIF_STORAGE_KEY_REMOTE_SERVICE_LE "ServiceLe" #define BTIF_STORAGE_KEY_REMOTE_VER_MFCT "Manufacturer" #define BTIF_STORAGE_KEY_REMOTE_VER_SUBVER "LmpSubVer" #define BTIF_STORAGE_KEY_REMOTE_VER_VER "LmpVer" diff --git a/system/include/hardware/ble_scanner.h b/system/include/hardware/ble_scanner.h index 6adb09d26c..6cf69bbab7 100644 --- a/system/include/hardware/ble_scanner.h +++ b/system/include/hardware/ble_scanner.h @@ -137,7 +137,7 @@ public: /** Sets the LE scan interval and window in units of N*0.625 msec */ virtual void SetScanParameters(int scanner_id, uint8_t scan_type, int scan_interval, - int scan_window, int scan_phy, Callback cb) = 0; + int scan_window, int scan_phy) = 0; /* Configure the batchscan storage */ virtual void BatchscanConfigStorage(int client_if, int batch_scan_full_max, diff --git a/system/include/hardware/bluetooth.h b/system/include/hardware/bluetooth.h index a7a3bb0aa2..fa15353d9f 100644 --- a/system/include/hardware/bluetooth.h +++ b/system/include/hardware/bluetooth.h @@ -299,7 +299,7 @@ typedef enum { */ BT_PROPERTY_TYPE_OF_DEVICE, /** - * Description - Bluetooth Service Record + * Description - Bluetooth Service Record, UUIDs on BREDR transport * Access mode - Only GET. * Data type - bt_service_record_t */ @@ -427,6 +427,14 @@ typedef enum { */ BT_PROPERTY_LPP_OFFLOAD_FEATURES, + /** + * Description - Bluetooth Service 128-bit UUIDs on LE transport + * Access mode - Only GET. + * Data type - Array of bluetooth::Uuid (Array size inferred from property + * length). + */ + BT_PROPERTY_UUIDS_LE, + BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP = 0xFF, } bt_property_type_t; diff --git a/system/include/hardware/bt_av.h b/system/include/hardware/bt_av.h index b839cb048f..f7d367bc6d 100644 --- a/system/include/hardware/bt_av.h +++ b/system/include/hardware/bt_av.h @@ -75,7 +75,6 @@ typedef enum { // Add an entry for each sink codec here BTAV_A2DP_CODEC_INDEX_SINK_SBC = BTAV_A2DP_CODEC_INDEX_SINK_MIN, BTAV_A2DP_CODEC_INDEX_SINK_AAC, - BTAV_A2DP_CODEC_INDEX_SINK_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_OPUS, BTAV_A2DP_CODEC_INDEX_SINK_MAX, @@ -184,8 +183,6 @@ struct btav_a2dp_codec_config_t { return "SBC (Sink)"; case BTAV_A2DP_CODEC_INDEX_SINK_AAC: return "AAC (Sink)"; - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - return "LDAC (Sink)"; case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: return "LC3"; case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: diff --git a/system/main/shim/ble_scanner_interface_impl.h b/system/main/shim/ble_scanner_interface_impl.h index 53c5f0df94..9c57c9b3cc 100644 --- a/system/main/shim/ble_scanner_interface_impl.h +++ b/system/main/shim/ble_scanner_interface_impl.h @@ -69,7 +69,7 @@ public: void MsftAdvMonitorRemove(uint8_t monitor_handle, MsftAdvMonitorRemoveCallback cb) override; void MsftAdvMonitorEnable(bool enable, MsftAdvMonitorEnableCallback cb) override; void SetScanParameters(int scanner_id, uint8_t scan_type, int scan_interval, int scan_window, - int scan_phy, Callback cb) override; + int scan_phy) override; void BatchscanConfigStorage(int client_if, int batch_scan_full_max, int batch_scan_trunc_max, int batch_scan_notify_threshold, Callback cb) override; void BatchscanEnable(int scan_mode, int scan_interval, int scan_window, int addr_type, diff --git a/system/main/shim/le_scanning_manager.cc b/system/main/shim/le_scanning_manager.cc index 8214dea25c..6aac1add2a 100644 --- a/system/main/shim/le_scanning_manager.cc +++ b/system/main/shim/le_scanning_manager.cc @@ -24,6 +24,7 @@ #include <hardware/bluetooth.h> #include "btif/include/btif_common.h" +#include "btif/include/btif_dm.h" #include "hci/address.h" #include "hci/le_scanning_manager.h" #include "hci/msft.h" @@ -35,6 +36,7 @@ #include "main/shim/shim.h" #include "stack/btm/btm_int_types.h" #include "stack/include/advertise_data_parser.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/bt_dev_class.h" #include "stack/include/btm_log_history.h" #include "stack/include/btm_status.h" @@ -111,20 +113,6 @@ private: static_cast<::ScanningCallbacks*>(&default_scanning_callback_); extern ::ScanningCallbacks* bluetooth::shim::default_scanning_callback; -void btm_ble_process_adv_pkt_cont_for_inquiry(uint16_t event_type, tBLE_ADDR_TYPE address_type, - const RawAddress& raw_address, uint8_t primary_phy, - uint8_t secondary_phy, uint8_t advertising_sid, - int8_t tx_power, int8_t rssi, - uint16_t periodic_adv_int, - std::vector<uint8_t> advertising_data); - -extern void btif_update_remote_properties(const RawAddress& bd_addr, BD_NAME bd_name, - DEV_CLASS dev_class, tBT_DEVICE_TYPE dev_type); - -void btm_ble_process_adv_addr(RawAddress& raw_address, tBLE_ADDR_TYPE* address_type); - -extern DEV_CLASS btm_ble_get_appearance_as_cod(std::vector<uint8_t> const& data); - using bluetooth::shim::BleScannerInterfaceImpl; void BleScannerInterfaceImpl::Init() { @@ -312,8 +300,7 @@ void BleScannerInterfaceImpl::OnMsftAdvMonitorEnable(bool enable, /** Sets the LE scan interval and window in units of N*0.625 msec */ void BleScannerInterfaceImpl::SetScanParameters(int scanner_id, uint8_t scan_type, - int scan_interval, int scan_window, int scan_phy, - Callback /* cb */) { + int scan_interval, int scan_window, int scan_phy) { log::info("in shim layer, scannerId={}", scanner_id); if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_EXT_SCAN_INT_MAX) && BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_EXT_SCAN_WIN_MAX)) { diff --git a/system/pdl/hci/hci_packets.pdl b/system/pdl/hci/hci_packets.pdl index 3810ffefd0..fcdd255b99 100644 --- a/system/pdl/hci/hci_packets.pdl +++ b/system/pdl/hci/hci_packets.pdl @@ -4954,7 +4954,7 @@ struct CsOptionalCsSyncPhysSupported { struct CsOptionalSubfeaturesSupported { _reserved_ : 1, - frequency_actuation_error : 1, + no_frequency_actuation_error : 1, channel_selection_algorithm : 1, phase_based_ranging : 1, _reserved_ : 12, diff --git a/system/profile/avrcp/Android.bp b/system/profile/avrcp/Android.bp index 6ea0ea1abb..3b3debea5f 100644 --- a/system/profile/avrcp/Android.bp +++ b/system/profile/avrcp/Android.bp @@ -121,10 +121,12 @@ cc_fuzz { "libbluetooth-types", "libbluetooth_gd", "libbluetooth_log", + "libbtdevice", "libchrome", "libcutils", "libevent", "liblog", + "libosi", "libstatslog_bt", "libutils", ], diff --git a/system/profile/avrcp/device.cc b/system/profile/avrcp/device.cc index d094108d8a..beaaf6b755 100644 --- a/system/profile/avrcp/device.cc +++ b/system/profile/avrcp/device.cc @@ -23,6 +23,7 @@ #include "abstract_message_loop.h" #include "avrcp_common.h" +#include "device/include/interop.h" #include "internal_include/stack_config.h" #include "packet/avrcp/avrcp_reject_packet.h" #include "packet/avrcp/general_reject_packet.h" @@ -736,7 +737,9 @@ void Device::AddressedPlayerNotificationResponse(uint8_t label, bool interim, if (!interim) { active_labels_.erase(label); addr_player_changed_ = Notification(false, 0); - RejectNotification(); + if (!interop_match_addr(INTEROP_ADDRESSED_PLAYER_CHANGE_REJECT, &address_)) { + RejectNotification(); + } } } diff --git a/system/stack/Android.bp b/system/stack/Android.bp index 841e7a93ef..35aa07b616 100644 --- a/system/stack/Android.bp +++ b/system/stack/Android.bp @@ -87,7 +87,6 @@ cc_library_static { "a2dp/a2dp_vendor_aptx_hd.cc", "a2dp/a2dp_vendor_aptx_hd_encoder.cc", "a2dp/a2dp_vendor_ldac.cc", - "a2dp/a2dp_vendor_ldac_decoder.cc", "a2dp/a2dp_vendor_ldac_encoder.cc", "a2dp/a2dp_vendor_opus.cc", "a2dp/a2dp_vendor_opus_decoder.cc", @@ -495,6 +494,7 @@ cc_fuzz { "libbluetooth_l2cap_pdl", "libbluetooth_log", "libbluetooth_smp_pdl", + "libbt-common", "libbt-platform-protos-lite", ], } @@ -567,6 +567,7 @@ cc_fuzz { ":TestCommonMockFunctions", ":TestCommonStackConfig", ":TestFakeOsi", + ":TestMockBtaDm", ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", @@ -949,6 +950,7 @@ cc_test { srcs: [ ":TestCommonMainHandler", ":TestCommonMockFunctions", + ":TestMockBtaDm", ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", @@ -1355,7 +1357,6 @@ cc_test { "a2dp/a2dp_vendor_aptx_hd.cc", "a2dp/a2dp_vendor_aptx_hd_encoder.cc", "a2dp/a2dp_vendor_ldac.cc", - "a2dp/a2dp_vendor_ldac_decoder.cc", "a2dp/a2dp_vendor_ldac_encoder.cc", "a2dp/a2dp_vendor_opus.cc", "a2dp/a2dp_vendor_opus_decoder.cc", @@ -1412,54 +1413,6 @@ cc_test { header_libs: ["libbluetooth_headers"], } -cc_test { - name: "net_test_stack_a2dp_native", - defaults: [ - "fluoride_defaults", - "mts_defaults", - ], - test_suites: ["general-tests"], - host_supported: true, - test_options: { - unit_test: true, - }, - include_dirs: [ - "external/libldac/inc", - "packages/modules/Bluetooth/system", - "packages/modules/Bluetooth/system/gd", - "packages/modules/Bluetooth/system/stack/include", - ], - srcs: [ - "a2dp/a2dp_vendor_ldac_decoder.cc", - "test/a2dp/a2dp_vendor_ldac_decoder_test.cc", - "test/a2dp/misc_fake.cc", - ], - aidl: { - libs: ["bluetooth_constants"], - }, - shared_libs: [ - "libbinder", - "libcrypto", - "libcutils", - ], - static_libs: [ - "libbase", - "libbluetooth_crypto_toolbox", - "libbluetooth_log", - "libbt-common", - "libchrome", - "libcom.android.sysprop.bluetooth.wrapped", - "liblog", - "libosi", - ], - sanitize: { - address: true, - cfi: true, - misc_undefined: ["bounds"], - }, - header_libs: ["libbluetooth_headers"], -} - // gatt sr hash test cc_test { name: "net_test_stack_gatt_sr_hash_native", diff --git a/system/stack/a2dp/a2dp_codec_config.cc b/system/stack/a2dp/a2dp_codec_config.cc index 9155dabf52..47cfc81030 100644 --- a/system/stack/a2dp/a2dp_codec_config.cc +++ b/system/stack/a2dp/a2dp_codec_config.cc @@ -192,9 +192,6 @@ A2dpCodecConfig* A2dpCodecConfig::createCodec(btav_a2dp_codec_index_t codec_inde case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: codec_config = new A2dpCodecConfigLdacSource(codec_priority); break; - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - codec_config = new A2dpCodecConfigLdacSink(codec_priority); - break; case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: codec_config = new A2dpCodecConfigOpusSource(codec_priority); break; diff --git a/system/stack/a2dp/a2dp_vendor.cc b/system/stack/a2dp/a2dp_vendor.cc index 28a27c7cb6..febfabb48d 100644 --- a/system/stack/a2dp/a2dp_vendor.cc +++ b/system/stack/a2dp/a2dp_vendor.cc @@ -129,11 +129,6 @@ tA2DP_STATUS A2DP_IsVendorSinkCodecSupported(const uint8_t* p_codec_info) { // Add checks based on <vendor_id, codec_id> // NOTE: Should be done only for local Sink codecs. - // Check for LDAC - if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { - return A2DP_IsVendorSinkCodecSupportedLdac(p_codec_info); - } - // Check for Opus if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { return A2DP_IsVendorSinkCodecSupportedOpus(p_codec_info); @@ -279,11 +274,6 @@ int A2DP_VendorGetSinkTrackChannelType(const uint8_t* p_codec_info) { // Add checks based on <vendor_id, codec_id> // NOTE: Should be done only for local Sink codecs. - // Check for LDAC - if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { - return A2DP_VendorGetSinkTrackChannelTypeLdac(p_codec_info); - } - // Check for Opus if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { return A2DP_VendorGetSinkTrackChannelTypeOpus(p_codec_info); @@ -358,11 +348,6 @@ const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterface(const uint8_t* p_c // Add checks based on <vendor_id, codec_id> // NOTE: Should be done only for local Sink codecs. - // Check for LDAC - if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { - return A2DP_VendorGetDecoderInterfaceLdac(p_codec_info); - } - // Check for Opus if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { return A2DP_VendorGetDecoderInterfaceOpus(p_codec_info); @@ -436,11 +421,6 @@ btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndex(const uint8_t* p_codec_info) { // Add checks based on <vendor_id, codec_id> // NOTE: Should be done only for local Sink codecs. - // Check for LDAC - if (vendor_id == A2DP_LDAC_VENDOR_ID && codec_id == A2DP_LDAC_CODEC_ID) { - return A2DP_VendorSinkCodecIndexLdac(p_codec_info); - } - // Check for Opus if (vendor_id == A2DP_OPUS_VENDOR_ID && codec_id == A2DP_OPUS_CODEC_ID) { return A2DP_VendorSinkCodecIndexOpus(p_codec_info); @@ -463,8 +443,6 @@ const char* A2DP_VendorCodecIndexStr(btav_a2dp_codec_index_t codec_index) { return A2DP_VendorCodecIndexStrAptxHd(); case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: return A2DP_VendorCodecIndexStrLdac(); - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - return A2DP_VendorCodecIndexStrLdacSink(); case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: return "LC3 not implemented"; case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: @@ -496,8 +474,6 @@ bool A2DP_VendorInitCodecConfig(btav_a2dp_codec_index_t codec_index, AvdtpSepCon return A2DP_VendorInitCodecConfigAptxHd(p_cfg); case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: return A2DP_VendorInitCodecConfigLdac(p_cfg); - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - return A2DP_VendorInitCodecConfigLdacSink(p_cfg); case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: break; // not implemented case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: diff --git a/system/stack/a2dp/a2dp_vendor_ldac.cc b/system/stack/a2dp/a2dp_vendor_ldac.cc index 951be4c35b..ef7fea0be8 100644 --- a/system/stack/a2dp/a2dp_vendor_ldac.cc +++ b/system/stack/a2dp/a2dp_vendor_ldac.cc @@ -37,7 +37,6 @@ #include "a2dp_codec_api.h" #include "a2dp_constants.h" #include "a2dp_vendor_ldac_constants.h" -#include "a2dp_vendor_ldac_decoder.h" #include "a2dp_vendor_ldac_encoder.h" #include "avdt_api.h" #include "btif/include/btif_av_co.h" @@ -102,16 +101,6 @@ static const tA2DP_ENCODER_INTERFACE a2dp_encoder_interface_ldac = { a2dp_vendor_ldac_send_frames, a2dp_vendor_ldac_set_transmit_queue_length}; -static const tA2DP_DECODER_INTERFACE a2dp_decoder_interface_ldac = { - a2dp_vendor_ldac_decoder_init, a2dp_vendor_ldac_decoder_cleanup, - a2dp_vendor_ldac_decoder_decode_packet, a2dp_vendor_ldac_decoder_start, - a2dp_vendor_ldac_decoder_suspend, a2dp_vendor_ldac_decoder_configure, -}; - -static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityLdac(const tA2DP_LDAC_CIE* p_cap, - const uint8_t* p_codec_info, - bool is_peer_codec_info); - // Builds the LDAC Media Codec Capabilities byte sequence beginning from the // LOSC octet. |media_type| is the media type |AVDT_MEDIA_TYPE_*|. // |p_ie| is a pointer to the LDAC Codec Information Element information. @@ -250,50 +239,6 @@ bool A2DP_IsCodecValidLdac(const uint8_t* p_codec_info) { (A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, true) == A2DP_SUCCESS); } -tA2DP_STATUS A2DP_IsVendorSinkCodecSupportedLdac(const uint8_t* p_codec_info) { - return A2DP_CodecInfoMatchesCapabilityLdac(&a2dp_ldac_sink_caps, p_codec_info, false); -} - -// Checks whether A2DP LDAC codec configuration matches with a device's codec -// capabilities. |p_cap| is the LDAC codec configuration. |p_codec_info| is -// the device's codec capabilities. -// If |is_capability| is true, the byte sequence is codec capabilities, -// otherwise is codec configuration. -// |p_codec_info| contains the codec capabilities for a peer device that -// is acting as an A2DP source. -// Returns A2DP_SUCCESS if the codec configuration matches with capabilities, -// otherwise the corresponding A2DP error status code. -static tA2DP_STATUS A2DP_CodecInfoMatchesCapabilityLdac(const tA2DP_LDAC_CIE* p_cap, - const uint8_t* p_codec_info, - bool is_capability) { - tA2DP_STATUS status; - tA2DP_LDAC_CIE cfg_cie; - - /* parse configuration */ - status = A2DP_ParseInfoLdac(&cfg_cie, p_codec_info, is_capability); - if (status != A2DP_SUCCESS) { - log::error("parsing failed {}", status); - return status; - } - - /* verify that each parameter is in range */ - - log::verbose("FREQ peer: 0x{:x}, capability 0x{:x}", cfg_cie.sampleRate, p_cap->sampleRate); - log::verbose("CH_MODE peer: 0x{:x}, capability 0x{:x}", cfg_cie.channelMode, p_cap->channelMode); - - /* sampling frequency */ - if ((cfg_cie.sampleRate & p_cap->sampleRate) == 0) { - return A2DP_NOT_SUPPORTED_SAMPLING_FREQUENCY; - } - - /* channel mode */ - if ((cfg_cie.channelMode & p_cap->channelMode) == 0) { - return A2DP_NOT_SUPPORTED_CHANNEL_MODE; - } - - return A2DP_SUCCESS; -} - bool A2DP_VendorUsesRtpHeaderLdac(bool /* content_protection_enabled */, const uint8_t* /* p_codec_info */) { // TODO: Is this correct? The RTP header is always included? @@ -566,15 +511,6 @@ const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceLdac( return &a2dp_encoder_interface_ldac; } -const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceLdac( - const uint8_t* p_codec_info) { - if (!A2DP_IsCodecValidLdac(p_codec_info)) { - return NULL; - } - - return &a2dp_decoder_interface_ldac; -} - bool A2DP_VendorAdjustCodecLdac(uint8_t* p_codec_info) { tA2DP_LDAC_CIE cfg_cie; @@ -590,22 +526,12 @@ btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexLdac(const uint8_t* /* p_code return BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC; } -btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexLdac(const uint8_t* /* p_codec_info */) { - return BTAV_A2DP_CODEC_INDEX_SINK_LDAC; -} - const char* A2DP_VendorCodecIndexStrLdac(void) { return "LDAC"; } -const char* A2DP_VendorCodecIndexStrLdacSink(void) { return "LDAC SINK"; } - bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg) { return A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_source_caps, p_cfg->codec_info); } -bool A2DP_VendorInitCodecConfigLdacSink(AvdtpSepConfig* p_cfg) { - return A2DP_BuildInfoLdac(AVDT_MEDIA_TYPE_AUDIO, &a2dp_ldac_sink_caps, p_cfg->codec_info); -} - A2dpCodecConfigLdacSource::A2dpCodecConfigLdacSource(btav_a2dp_codec_priority_t codec_priority) : A2dpCodecConfigLdacBase(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, A2DP_VendorCodecIndexStrLdac(), codec_priority, true) { @@ -1320,24 +1246,3 @@ fail: sizeof(ota_codec_peer_capability_)); return false; } - -A2dpCodecConfigLdacSink::A2dpCodecConfigLdacSink(btav_a2dp_codec_priority_t codec_priority) - : A2dpCodecConfigLdacBase(BTAV_A2DP_CODEC_INDEX_SINK_LDAC, A2DP_VendorCodecIndexStrLdacSink(), - codec_priority, false) {} - -A2dpCodecConfigLdacSink::~A2dpCodecConfigLdacSink() {} - -bool A2dpCodecConfigLdacSink::init() { - // Load the decoder - if (!A2DP_VendorLoadDecoderLdac()) { - log::error("cannot load the decoder"); - return false; - } - - return true; -} - -bool A2dpCodecConfigLdacSink::useRtpHeaderMarkerBit() const { - // TODO: This method applies only to Source codecs - return false; -} diff --git a/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc b/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc deleted file mode 100644 index 9c13d50160..0000000000 --- a/system/stack/a2dp/a2dp_vendor_ldac_decoder.cc +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "bluetooth-a2dp" - -#include "a2dp_vendor_ldac_decoder.h" - -#include <bluetooth/log.h> -#include <dlfcn.h> -#include <ldacBT.h> -#include <ldacBT_bco_for_fluoride.h> -#include <pthread.h> -#include <string.h> - -#include <cstdint> - -#include "a2dp_vendor_ldac.h" -#include "a2dp_vendor_ldac_constants.h" -#include "avdt_api.h" -#include "stack/include/bt_hdr.h" - -using namespace bluetooth; - -namespace std { -template <> -struct formatter<LDACBT_SMPL_FMT_T> : enum_formatter<LDACBT_SMPL_FMT_T> {}; -} // namespace std - -// -// Decoder for LDAC Source Codec -// - -// -// The LDAC BCO shared library, and the functions to use -// -static const char* LDAC_BCO_LIB_NAME = "libldacBT_bco.so"; -static void* ldac_bco_lib_handle = NULL; - -static const char* LDAC_BCO_INIT_NAME = "ldac_BCO_init"; -typedef HANDLE_LDAC_BCO (*tLDAC_BCO_INIT)(decoded_data_callback_t decode_callback); - -static const char* LDAC_BCO_CLEANUP_NAME = "ldac_BCO_cleanup"; -typedef int32_t (*tLDAC_BCO_CLEANUP)(HANDLE_LDAC_BCO hLdacBco); - -static const char* LDAC_BCO_DECODE_PACKET_NAME = "ldac_BCO_decode_packet"; -typedef int32_t (*tLDAC_BCO_DECODE_PACKET)(HANDLE_LDAC_BCO hLdacBco, void* data, int32_t length); - -static const char* LDAC_BCO_START_NAME = "ldac_BCO_start"; -typedef int32_t (*tLDAC_BCO_START)(HANDLE_LDAC_BCO hLdacBco); - -static const char* LDAC_BCO_SUSPEND_NAME = "ldac_BCO_suspend"; -typedef int32_t (*tLDAC_BCO_SUSPEND)(HANDLE_LDAC_BCO hLdacBco); - -static const char* LDAC_BCO_CONFIGURE_NAME = "ldac_BCO_configure"; -typedef int32_t (*tLDAC_BCO_CONFIGURE)(HANDLE_LDAC_BCO hLdacBco, int32_t sample_rate, - int32_t bits_per_sample, int32_t channel_mode); - -static tLDAC_BCO_INIT ldac_BCO_init_func; -static tLDAC_BCO_CLEANUP ldac_BCO_cleanup_func; -static tLDAC_BCO_DECODE_PACKET ldac_BCO_decode_packet_func; -static tLDAC_BCO_START ldac_BCO_start_func; -static tLDAC_BCO_SUSPEND ldac_BCO_suspend_func; -static tLDAC_BCO_CONFIGURE ldac_BCO_configure_func; - -// offset -#define A2DP_LDAC_OFFSET (AVDT_MEDIA_OFFSET + A2DP_LDAC_MPL_HDR_LEN) - -typedef struct { - uint32_t sample_rate; - uint8_t channel_mode; - uint8_t bits_per_sample; - int pcm_wlength; - LDACBT_SMPL_FMT_T pcm_fmt; -} tA2DP_LDAC_DECODER_PARAMS; - -typedef struct { - pthread_mutex_t mutex; - bool use_SCMS_T; - bool is_peer_edr; // True if the peer device supports EDR - bool peer_supports_3mbps; // True if the peer device supports 3Mbps EDR - uint16_t peer_mtu; // MTU of the A2DP peer - uint32_t timestamp; // Timestamp for the A2DP frames - - HANDLE_LDAC_BCO ldac_handle_bco; - bool has_ldac_handle; // True if ldac_handle is valid - unsigned char* decode_buf; - decoded_data_callback_t decode_callback; -} tA2DP_LDAC_DECODER_CB; - -static tA2DP_LDAC_DECODER_CB a2dp_ldac_decoder_cb; - -static void* load_func(const char* func_name) { - void* func_ptr = dlsym(ldac_bco_lib_handle, func_name); - if (func_ptr == NULL) { - log::error("cannot find function '{}' in the decoder library: {}", func_name, dlerror()); - A2DP_VendorUnloadDecoderLdac(); - return NULL; - } - return func_ptr; -} - -bool A2DP_VendorLoadDecoderLdac(void) { - if (ldac_bco_lib_handle != NULL) { - return true; // Already loaded - } - - // Initialize the control block - memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb)); - - pthread_mutex_init(&(a2dp_ldac_decoder_cb.mutex), NULL); - - // Open the decoder library - ldac_bco_lib_handle = dlopen(LDAC_BCO_LIB_NAME, RTLD_NOW); - if (ldac_bco_lib_handle == NULL) { - log::info("cannot open LDAC decoder library {}: {}", LDAC_BCO_LIB_NAME, dlerror()); - return false; - } - - // Load all functions - ldac_BCO_init_func = (tLDAC_BCO_INIT)load_func(LDAC_BCO_INIT_NAME); - if (ldac_BCO_init_func == NULL) { - return false; - } - - ldac_BCO_cleanup_func = (tLDAC_BCO_CLEANUP)load_func(LDAC_BCO_CLEANUP_NAME); - if (ldac_BCO_cleanup_func == NULL) { - return false; - } - - ldac_BCO_decode_packet_func = (tLDAC_BCO_DECODE_PACKET)load_func(LDAC_BCO_DECODE_PACKET_NAME); - if (ldac_BCO_decode_packet_func == NULL) { - return false; - } - - ldac_BCO_start_func = (tLDAC_BCO_START)load_func(LDAC_BCO_START_NAME); - if (ldac_BCO_start_func == NULL) { - return false; - } - - ldac_BCO_suspend_func = (tLDAC_BCO_SUSPEND)load_func(LDAC_BCO_SUSPEND_NAME); - if (ldac_BCO_suspend_func == NULL) { - return false; - } - - ldac_BCO_configure_func = (tLDAC_BCO_CONFIGURE)load_func(LDAC_BCO_CONFIGURE_NAME); - if (ldac_BCO_configure_func == NULL) { - return false; - } - - return true; -} - -void A2DP_VendorUnloadDecoderLdac(void) { - // Cleanup any LDAC-related state - if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_BCO_cleanup_func != NULL) { - ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco); - } - pthread_mutex_destroy(&(a2dp_ldac_decoder_cb.mutex)); - memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb)); - - ldac_BCO_init_func = NULL; - ldac_BCO_cleanup_func = NULL; - ldac_BCO_decode_packet_func = NULL; - ldac_BCO_start_func = NULL; - ldac_BCO_suspend_func = NULL; - ldac_BCO_configure_func = NULL; - - if (ldac_bco_lib_handle != NULL) { - dlclose(ldac_bco_lib_handle); - ldac_bco_lib_handle = NULL; - } -} - -bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback) { - pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex)); - - if (a2dp_ldac_decoder_cb.has_ldac_handle) { - ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco); - } - - a2dp_ldac_decoder_cb.ldac_handle_bco = ldac_BCO_init_func(decode_callback); - a2dp_ldac_decoder_cb.has_ldac_handle = (a2dp_ldac_decoder_cb.ldac_handle_bco != NULL); - - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); - return true; -} - -void a2dp_vendor_ldac_decoder_cleanup(void) { - pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex)); - if (a2dp_ldac_decoder_cb.has_ldac_handle) { - ldac_BCO_cleanup_func(a2dp_ldac_decoder_cb.ldac_handle_bco); - } - a2dp_ldac_decoder_cb.ldac_handle_bco = NULL; - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); -} - -bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf) { - if (p_buf == nullptr) { - log::error("Dropping packet with nullptr"); - return false; - } - pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex)); - unsigned char* pBuffer = reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset); - // unsigned int bufferSize = p_buf->len; - unsigned int bytesValid = p_buf->len; - if (bytesValid == 0) { - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); - log::warn("Dropping packet with zero length"); - return false; - } - - int bs_bytes, frame_number; - - frame_number = (int)pBuffer[0]; - bs_bytes = (int)bytesValid; - bytesValid -= 1; - log::info("INPUT size : {}, frame : {}", bs_bytes, frame_number); - - if (a2dp_ldac_decoder_cb.has_ldac_handle) { - ldac_BCO_decode_packet_func(a2dp_ldac_decoder_cb.ldac_handle_bco, pBuffer, bs_bytes); - } - - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); - return true; -} - -void a2dp_vendor_ldac_decoder_start(void) { - pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex)); - log::info(""); - if (a2dp_ldac_decoder_cb.has_ldac_handle) { - ldac_BCO_start_func(a2dp_ldac_decoder_cb.ldac_handle_bco); - } - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); -} - -void a2dp_vendor_ldac_decoder_suspend(void) { - pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex)); - log::info(""); - if (a2dp_ldac_decoder_cb.has_ldac_handle) { - ldac_BCO_suspend_func(a2dp_ldac_decoder_cb.ldac_handle_bco); - } - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); -} - -void a2dp_vendor_ldac_decoder_configure(const uint8_t* p_codec_info) { - int32_t sample_rate; - int32_t bits_per_sample; - int32_t channel_mode; - - if (p_codec_info == NULL) { - log::error("p_codec_info is NULL"); - return; - } - - pthread_mutex_lock(&(a2dp_ldac_decoder_cb.mutex)); - sample_rate = A2DP_VendorGetTrackSampleRateLdac(p_codec_info); - bits_per_sample = A2DP_VendorGetTrackBitsPerSampleLdac(p_codec_info); - channel_mode = A2DP_VendorGetChannelModeCodeLdac(p_codec_info); - - log::info(", sample_rate={}, bits_per_sample={}, channel_mode={}", sample_rate, bits_per_sample, - channel_mode); - - if (a2dp_ldac_decoder_cb.has_ldac_handle) { - ldac_BCO_configure_func(a2dp_ldac_decoder_cb.ldac_handle_bco, sample_rate, bits_per_sample, - channel_mode); - } - pthread_mutex_unlock(&(a2dp_ldac_decoder_cb.mutex)); -} diff --git a/system/stack/a2dp/a2dp_vendor_ldac_linux.cc b/system/stack/a2dp/a2dp_vendor_ldac_linux.cc index a777304c37..2401619c98 100644 --- a/system/stack/a2dp/a2dp_vendor_ldac_linux.cc +++ b/system/stack/a2dp/a2dp_vendor_ldac_linux.cc @@ -20,10 +20,6 @@ bool A2DP_IsCodecValidLdac(const uint8_t* p_codec_info) { return false; } -tA2DP_STATUS A2DP_IsVendorSinkCodecSupportedLdac(const uint8_t* p_codec_info) { - return A2DP_NOT_SUPPORTED_CODEC_TYPE; -} - bool A2DP_VendorUsesRtpHeaderLdac(bool content_protection_enabled, const uint8_t* p_codec_info) { return false; @@ -51,8 +47,6 @@ int A2DP_VendorGetTrackBitsPerSampleLdac(const uint8_t* p_codec_info) { return - int A2DP_VendorGetTrackChannelCountLdac(const uint8_t* p_codec_info) { return -1; } -int A2DP_VendorGetSinkTrackChannelTypeLdac(const uint8_t* p_codec_info) { return -1; } - int A2DP_VendorGetChannelModeCodeLdac(const uint8_t* p_codec_info) { return -1; } int A2DP_VendorGetFrameSizeLdac(const uint8_t* p_codec_info) { return -1; } @@ -75,28 +69,16 @@ const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceLdac(const uint8_t* return nullptr; } -const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceLdac(const uint8_t* p_codec_info) { - return nullptr; -} - bool A2DP_VendorAdjustCodecLdac(uint8_t* p_codec_info) { return false; } btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexLdac(const uint8_t* p_codec_info) { return BTAV_A2DP_CODEC_INDEX_MAX; } -btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexLdac(const uint8_t* p_codec_info) { - return BTAV_A2DP_CODEC_INDEX_MAX; -} - const char* A2DP_VendorCodecIndexStrLdac(void) { return "Ldac"; } -const char* A2DP_VendorCodecIndexStrLdacSink(void) { return "Ldac SINK"; } - bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg) { return false; } -bool A2DP_VendorInitCodecConfigLdacSink(AvdtpSepConfig* p_cfg) { return false; } - A2dpCodecConfigLdacSource::A2dpCodecConfigLdacSource(btav_a2dp_codec_priority_t codec_priority) : A2dpCodecConfigLdacBase(BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC, A2DP_VendorCodecIndexStrLdac(), codec_priority, true) {} @@ -118,13 +100,3 @@ tA2DP_STATUS A2dpCodecConfigLdacBase::setCodecConfig(const uint8_t* p_peer_codec bool A2dpCodecConfigLdacBase::setPeerCodecCapabilities(const uint8_t* p_peer_codec_capabilities) { return false; } - -A2dpCodecConfigLdacSink::A2dpCodecConfigLdacSink(btav_a2dp_codec_priority_t codec_priority) - : A2dpCodecConfigLdacBase(BTAV_A2DP_CODEC_INDEX_SINK_LDAC, A2DP_VendorCodecIndexStrLdacSink(), - codec_priority, false) {} - -A2dpCodecConfigLdacSink::~A2dpCodecConfigLdacSink() {} - -bool A2dpCodecConfigLdacSink::init() { return false; } - -bool A2dpCodecConfigLdacSink::useRtpHeaderMarkerBit() const { return false; } diff --git a/system/stack/acl/ble_acl.cc b/system/stack/acl/ble_acl.cc index 0aeb9ceb91..ab439987ed 100644 --- a/system/stack/acl/ble_acl.cc +++ b/system/stack/acl/ble_acl.cc @@ -38,8 +38,6 @@ using namespace bluetooth; extern tBTM_CB btm_cb; -void btm_ble_increment_link_topology_mask(uint8_t link_role); - static bool acl_ble_common_connection(const tBLE_BD_ADDR& address_with_type, uint16_t handle, tHCI_ROLE role, bool is_in_security_db, uint16_t conn_interval, uint16_t conn_latency, diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc index 16911e9cc5..2a55ee393d 100644 --- a/system/stack/acl/btm_acl.cc +++ b/system/stack/acl/btm_acl.cc @@ -61,6 +61,7 @@ #include "stack/btm/btm_int_types.h" #include "stack/btm/btm_sco.h" #include "stack/btm/btm_sec.h" +#include "stack/btm/internal/btm_api.h" #include "stack/btm/security_device_record.h" #include "stack/include/acl_api.h" #include "stack/include/acl_api_types.h" @@ -69,6 +70,7 @@ #include "stack/include/bt_types.h" #include "stack/include/btm_ble_api.h" #include "stack/include/btm_client_interface.h" +#include "stack/include/btm_iot_config.h" #include "stack/include/btm_iso_api.h" #include "stack/include/btm_status.h" #include "stack/include/hci_error_code.h" @@ -92,14 +94,7 @@ using namespace bluetooth; using bluetooth::legacy::hci::GetInterface; -void BTM_update_version_info(const RawAddress& bd_addr, - const remote_version_info& remote_version_info); - -void BTM_db_reset(void); - extern tBTM_CB btm_cb; -void btm_iot_save_remote_properties(tACL_CONN* p_acl_cb); -void btm_iot_save_remote_versions(tACL_CONN* p_acl_cb); struct StackAclBtmAcl { tACL_CONN* acl_allocate_connection(); @@ -157,8 +152,8 @@ static void btm_read_failed_contact_counter_timeout(void* data); static void btm_read_remote_ext_features(uint16_t handle, uint8_t page_number); static void btm_read_rssi_timeout(void* data); static void btm_read_tx_power_timeout(void* data); +static void btm_set_link_policy(tACL_CONN* conn, tLINK_POLICY policy); static void check_link_policy(tLINK_POLICY* settings); -void btm_set_link_policy(tACL_CONN* conn, tLINK_POLICY policy); namespace { void NotifyAclLinkUp(tACL_CONN& p_acl) { @@ -669,7 +664,7 @@ static void check_link_policy(tLINK_POLICY* settings) { } } -void btm_set_link_policy(tACL_CONN* conn, tLINK_POLICY policy) { +static void btm_set_link_policy(tACL_CONN* conn, tLINK_POLICY policy) { conn->link_policy = policy; check_link_policy(&conn->link_policy); if ((conn->link_policy & HCI_ENABLE_CENTRAL_PERIPHERAL_SWITCH) && diff --git a/system/stack/avrc/avrc_api.cc b/system/stack/avrc/avrc_api.cc index 13184ed058..6b3c1700cb 100644 --- a/system/stack/avrc/avrc_api.cc +++ b/system/stack/avrc/avrc_api.cc @@ -25,6 +25,7 @@ #include <android_bluetooth_sysprop.h> #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <string.h> #include <cstdint> @@ -1009,7 +1010,12 @@ uint16_t AVRC_GetControlProfileVersion() { uint16_t AVRC_GetProfileVersion() { uint16_t profile_version = AVRC_REV_1_4; char avrcp_version[PROPERTY_VALUE_MAX] = {0}; - osi_property_get(AVRC_VERSION_PROPERTY, avrcp_version, AVRC_DEFAULT_VERSION); + + if (!com::android::bluetooth::flags::avrcp_16_default()) { + osi_property_get(AVRC_VERSION_PROPERTY, avrcp_version, AVRC_1_5_STRING); + } else { + osi_property_get(AVRC_VERSION_PROPERTY, avrcp_version, AVRC_1_6_STRING); + } if (!strncmp(AVRC_1_6_STRING, avrcp_version, sizeof(AVRC_1_6_STRING))) { profile_version = AVRC_REV_1_6; diff --git a/system/stack/btm/btm_ble.cc b/system/stack/btm/btm_ble.cc index e71dfe67fa..b612b684b3 100644 --- a/system/stack/btm/btm_ble.cc +++ b/system/stack/btm/btm_ble.cc @@ -35,6 +35,7 @@ #include "stack/btm/btm_int_types.h" #include "stack/gatt/gatt_int.h" #include "stack/include/acl_api.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/bt_types.h" #include "stack/include/btm_ble_api.h" #include "stack/include/btm_client_interface.h" @@ -42,9 +43,6 @@ #include "stack/include/gatt_api.h" #include "stack/include/hcimsgs.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; extern tBTM_CB btm_cb; @@ -136,8 +134,8 @@ bool BTM_UseLeLink(const RawAddress& bd_addr) { return dev_type == BT_DEVICE_TYPE_BLE; } -void read_phy_cb(base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb, - uint8_t* data, uint16_t len) { +static void read_phy_cb(base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb, + uint8_t* data, uint16_t len) { uint8_t status, tx_phy, rx_phy; uint16_t handle; @@ -188,8 +186,6 @@ void BTM_BleReadPhy(const RawAddress& bd_addr, base::Bind(&read_phy_cb, std::move(cb))); } -void doNothing(uint8_t* /* data */, uint16_t /* len */) {} - void BTM_BleSetPhy(const RawAddress& bd_addr, uint8_t tx_phys, uint8_t rx_phys, uint16_t phy_options) { if (!get_btm_client_interface().peer.BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_LE)) { @@ -233,5 +229,6 @@ void BTM_BleSetPhy(const RawAddress& bd_addr, uint8_t tx_phys, uint8_t rx_phys, UINT8_TO_STREAM(pp, tx_phys); UINT8_TO_STREAM(pp, rx_phys); UINT16_TO_STREAM(pp, phy_options); - btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_SET_PHY, data, len, base::Bind(doNothing)); + btu_hcif_send_cmd_with_cb(FROM_HERE, HCI_BLE_SET_PHY, data, len, + base::Bind([](uint8_t*, uint16_t) {})); } diff --git a/system/stack/btm/btm_ble_gap.cc b/system/stack/btm/btm_ble_gap.cc index 462a93e394..808c9491d1 100644 --- a/system/stack/btm/btm_ble_gap.cc +++ b/system/stack/btm/btm_ble_gap.cc @@ -71,15 +71,10 @@ #include "types/ble_address_with_type.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; extern tBTM_CB btm_cb; -void btm_ble_adv_filter_init(void); - #define BTM_EXT_BLE_RMT_NAME_TIMEOUT_MS (30 * 1000) #define MIN_ADV_LENGTH 2 #define BTM_VSC_CHIP_CAPABILITY_RSP_LEN 9 @@ -197,7 +192,7 @@ AdvertisingCache cache; } // namespace -bool ble_vnd_is_included() { +static bool ble_vnd_is_included() { // replace build time config BLE_VND_INCLUDED with runtime return android::sysprop::bluetooth::Ble::vnd_included().value_or(true); } @@ -278,11 +273,6 @@ static void btm_ble_start_sync_timeout(void* data); * Local functions ******************************************************************************/ static void btm_ble_update_adv_flag(uint8_t flag); -void btm_ble_process_adv_pkt_cont(uint16_t evt_type, tBLE_ADDR_TYPE addr_type, - const RawAddress& bda, uint8_t primary_phy, uint8_t secondary_phy, - uint8_t advertising_sid, int8_t tx_power, int8_t rssi, - uint16_t periodic_adv_int, uint8_t data_len, const uint8_t* data, - const RawAddress& original_bda); static uint8_t btm_set_conn_mode_adv_init_addr(RawAddress& p_peer_addr_ptr, tBLE_ADDR_TYPE* p_peer_addr_type, tBLE_ADDR_TYPE* p_own_addr_type); @@ -488,7 +478,7 @@ void BTM_BleTargetAnnouncementObserve(bool enable, tBTM_INQ_RESULTS_CB* p_result } } -std::pair<uint16_t /* interval */, uint16_t /* window */> get_low_latency_scan_params() { +static std::pair<uint16_t /* interval */, uint16_t /* window */> get_low_latency_scan_params() { uint16_t scan_interval = osi_property_get_int32(kPropertyInquiryScanInterval, BTM_BLE_LOW_LATENCY_SCAN_INT); uint16_t scan_window = @@ -862,7 +852,8 @@ static void sync_queue_cleanup(remove_sync_node_t* p_param) { } } -void btm_ble_start_sync_request(uint8_t sid, RawAddress addr, uint16_t skip, uint16_t timeout) { +static void btm_ble_start_sync_request(uint8_t sid, RawAddress addr, uint16_t skip, + uint16_t timeout) { tBLE_ADDR_TYPE address_type = BLE_ADDR_RANDOM; tINQ_DB_ENT* p_i = btm_inq_db_find(addr); if (p_i) { @@ -1235,8 +1226,8 @@ static void btm_ble_select_adv_interval(uint8_t evt_type, uint16_t* p_adv_int_mi * Returns void * ******************************************************************************/ -void btm_ble_update_dmt_flag_bits(uint8_t* adv_flag_value, const uint16_t connect_mode, - const uint16_t disc_mode) { +static void btm_ble_update_dmt_flag_bits(uint8_t* adv_flag_value, const uint16_t connect_mode, + const uint16_t disc_mode) { /* BR/EDR non-discoverable , non-connectable */ if ((disc_mode & BTM_DISCOVERABLE_MASK) == 0 && (connect_mode & BTM_CONNECTABLE_MASK) == 0) { *adv_flag_value |= BTM_BLE_BREDR_NOT_SPT; @@ -1265,7 +1256,7 @@ void btm_ble_update_dmt_flag_bits(uint8_t* adv_flag_value, const uint16_t connec * Returns void * ******************************************************************************/ -void btm_ble_set_adv_flag(uint16_t connect_mode, uint16_t disc_mode) { +static void btm_ble_set_adv_flag(uint16_t connect_mode, uint16_t disc_mode) { uint8_t flag = 0, old_flag = 0; tBTM_BLE_LOCAL_ADV_DATA* p_adv_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; @@ -1594,8 +1585,8 @@ tBTM_STATUS btm_ble_start_inquiry(uint8_t duration) { * Returns void * ******************************************************************************/ -void btm_ble_read_remote_name_cmpl(bool status, const RawAddress& bda, uint16_t length, - char* p_name) { +static void btm_ble_read_remote_name_cmpl(bool status, const RawAddress& bda, uint16_t length, + char* p_name) { tHCI_STATUS hci_status = HCI_SUCCESS; BD_NAME bd_name; bd_name_from_char_pointer(bd_name, p_name); @@ -1918,7 +1909,7 @@ static DEV_CLASS btm_ble_appearance_to_cod(uint16_t appearance) { dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; dev_class[2] = BTM_COD_MINOR_DIGITAL_PAN; break; - case BTM_BLE_APPEARANCE_UKNOWN: + case BTM_BLE_APPEARANCE_UNKNOWN: case BTM_BLE_APPEARANCE_GENERIC_CLOCK: case BTM_BLE_APPEARANCE_GENERIC_TAG: case BTM_BLE_APPEARANCE_GENERIC_KEYRING: diff --git a/system/stack/btm/btm_ble_int.h b/system/stack/btm/btm_ble_int.h index eb8e88a2ce..bc2b4a896b 100644 --- a/system/stack/btm/btm_ble_int.h +++ b/system/stack/btm/btm_ble_int.h @@ -23,11 +23,11 @@ * ******************************************************************************/ -#ifndef BTM_BLE_INT_H -#define BTM_BLE_INT_H +#pragma once #include "stack/btm/btm_ble_int_types.h" #include "stack/btm/security_device_record.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/hci_error_code.h" #include "types/ble_address_with_type.h" #include "types/raw_address.h" @@ -53,8 +53,10 @@ void btm_ble_adv_filter_init(void); bool btm_ble_topology_check(tBTM_BLE_STATE_MASK request); bool btm_ble_clear_topology_mask(tBTM_BLE_STATE_MASK request_state); bool btm_ble_set_topology_mask(tBTM_BLE_STATE_MASK request_state); +tBTM_STATUS btm_ble_start_inquiry(uint8_t duration); +void btm_ble_stop_inquiry(void); +tBTM_STATUS btm_ble_set_connectability(uint16_t combined_mode); +tBTM_STATUS btm_ble_set_discoverability(uint16_t combined_mode); void btm_ble_scanner_init(void); void btm_ble_scanner_cleanup(void); - -#endif diff --git a/system/stack/btm/btm_ble_privacy.cc b/system/stack/btm/btm_ble_privacy.cc index bba53e0448..c0b83336b5 100644 --- a/system/stack/btm/btm_ble_privacy.cc +++ b/system/stack/btm/btm_ble_privacy.cc @@ -35,14 +35,12 @@ #include "main/shim/entry.h" #include "osi/include/allocator.h" #include "stack/btm/btm_int_types.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/bt_octets.h" #include "stack/include/bt_types.h" #include "stack/include/btm_client_interface.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; extern tBTM_CB btm_cb; diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc index e05091f656..9d6a604ee7 100644 --- a/system/stack/btm/btm_ble_sec.cc +++ b/system/stack/btm/btm_ble_sec.cc @@ -44,6 +44,7 @@ #include "stack/btm/security_device_record.h" #include "stack/eatt/eatt.h" #include "stack/include/acl_api.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/bt_name.h" #include "stack/include/bt_octets.h" #include "stack/include/bt_types.h" @@ -52,6 +53,7 @@ #include "stack/include/btm_ble_sec_api.h" #include "stack/include/btm_client_interface.h" #include "stack/include/btm_log_history.h" +#include "stack/include/btm_sec_api.h" #include "stack/include/btm_status.h" #include "stack/include/gatt_api.h" #include "stack/include/l2cap_security_interface.h" @@ -59,17 +61,10 @@ #include "stack/include/smp_api_types.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; extern tBTM_CB btm_cb; -bool btm_ble_init_pseudo_addr(tBTM_SEC_DEV_REC* p_dev_rec, const RawAddress& new_pseudo_addr); -tBTM_STATUS btm_ble_read_remote_name(const RawAddress& remote_bda, tBTM_NAME_CMPL_CB* p_cb); -tBTM_STATUS btm_ble_read_remote_cod(const RawAddress& remote_bda); - namespace { constexpr char kBtmLogTag[] = "SEC"; } diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc index 4840a6d36e..b7275d00d7 100644 --- a/system/stack/btm/btm_dev.cc +++ b/system/stack/btm/btm_dev.cc @@ -49,9 +49,6 @@ #include "stack/include/l2cap_interface.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; extern tBTM_CB btm_cb; @@ -825,11 +822,11 @@ void DumpsysRecord(int fd) { #undef DUMPSYS_TAG namespace bluetooth { -namespace testing { namespace legacy { +namespace testing { void wipe_secrets_and_remove(tBTM_SEC_DEV_REC* p_dev_rec) { ::wipe_secrets_and_remove(p_dev_rec); } -} // namespace legacy } // namespace testing +} // namespace legacy } // namespace bluetooth diff --git a/system/stack/btm/btm_dev.h b/system/stack/btm/btm_dev.h index ca9a38ad57..463a84c104 100644 --- a/system/stack/btm/btm_dev.h +++ b/system/stack/btm/btm_dev.h @@ -215,3 +215,7 @@ const tBLE_BD_ADDR BTM_Sec_GetAddressWithType(const RawAddress& bd_addr); * ******************************************************************************/ void DumpsysRecord(int fd); + +namespace bluetooth::legacy::testing { +void wipe_secrets_and_remove(tBTM_SEC_DEV_REC* p_dev_rec); +} // namespace bluetooth::legacy::testing diff --git a/system/stack/btm/btm_devctl.cc b/system/stack/btm/btm_devctl.cc index 953487b183..834834f61c 100644 --- a/system/stack/btm/btm_devctl.cc +++ b/system/stack/btm/btm_devctl.cc @@ -38,6 +38,7 @@ #include "main/shim/entry.h" #include "stack/btm/btm_int_types.h" #include "stack/btm/btm_sec.h" +#include "stack/btm/internal/btm_api.h" #include "stack/connection_manager/connection_manager.h" #include "stack/include/acl_api.h" #include "stack/include/acl_api_types.h" @@ -45,19 +46,17 @@ #include "stack/include/bt_types.h" #include "stack/include/btm_ble_privacy.h" #include "stack/include/btm_inq.h" +#include "stack/include/btm_sec_api.h" #include "stack/include/btm_status.h" +#include "stack/include/dev_hci_link_interface.h" #include "stack/include/hcidefs.h" #include "stack/include/l2cap_controller_interface.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace ::bluetooth; extern tBTM_CB btm_cb; -void btm_inq_db_reset(void); /******************************************************************************/ /* L O C A L D A T A D E F I N I T I O N S */ /******************************************************************************/ @@ -81,34 +80,6 @@ static void decode_controller_support(); /******************************************************************************* * - * Function btm_dev_init - * - * Description This function is on the BTM startup - * - * Returns void - * - ******************************************************************************/ -void btm_dev_init() { - /* Initialize nonzero defaults */ - memset(btm_sec_cb.cfg.bd_name, 0, sizeof(BD_NAME)); - - btm_cb.devcb.read_rssi_timer = alarm_new("btm.read_rssi_timer"); - btm_cb.devcb.read_failed_contact_counter_timer = - alarm_new("btm.read_failed_contact_counter_timer"); - btm_cb.devcb.read_automatic_flush_timeout_timer = - alarm_new("btm.read_automatic_flush_timeout_timer"); - btm_cb.devcb.read_tx_power_timer = alarm_new("btm.read_tx_power_timer"); -} - -void btm_dev_free() { - alarm_free(btm_cb.devcb.read_rssi_timer); - alarm_free(btm_cb.devcb.read_failed_contact_counter_timer); - alarm_free(btm_cb.devcb.read_automatic_flush_timeout_timer); - alarm_free(btm_cb.devcb.read_tx_power_timer); -} - -/******************************************************************************* - * * Function btm_db_reset * * Returns void diff --git a/system/stack/btm/btm_inq.cc b/system/stack/btm/btm_inq.cc index b1ed1c6d03..f478aff768 100644 --- a/system/stack/btm/btm_inq.cc +++ b/system/stack/btm/btm_inq.cc @@ -48,6 +48,7 @@ #include "osi/include/properties.h" #include "osi/include/stack_power_telemetry.h" #include "packet/bit_inserter.h" +#include "stack/btm/btm_ble_int.h" #include "stack/btm/btm_eir.h" #include "stack/btm/btm_int_types.h" #include "stack/btm/neighbor_inquiry.h" @@ -85,9 +86,6 @@ ((uint32_t)1 << (((uint32_t)(service)) % BTM_EIR_ARRAY_BITS))) >> \ (((uint32_t)(service)) % BTM_EIR_ARRAY_BITS)) -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - namespace { constexpr char kBtmLogTag[] = "SCAN"; @@ -118,12 +116,6 @@ uint16_t max_bd_entries_; /* Maximum number of entries that can be stored */ } // namespace extern tBTM_CB btm_cb; -void btm_inq_db_set_inq_by_rssi(void); -tBTM_STATUS btm_ble_set_discoverability(uint16_t combined_mode); -tBTM_STATUS btm_ble_set_connectability(uint16_t combined_mode); - -tBTM_STATUS btm_ble_start_inquiry(uint8_t duration); -void btm_ble_stop_inquiry(void); using namespace bluetooth; using bluetooth::Uuid; @@ -241,16 +233,15 @@ const uint16_t BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES] = { /******************************************************************************/ static void btm_clr_inq_db(const RawAddress* p_bda); static void btm_init_inq_result_flt(void); -void btm_clr_inq_result_flt(void); static uint8_t btm_convert_uuid_to_eir_service(uint16_t uuid16); -void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results); static const uint8_t* btm_eir_get_uuid_list(const uint8_t* p_eir, size_t eir_len, uint8_t uuid_size, uint8_t* p_num_uuid, uint8_t* p_uuid_list_type); - +static void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results); static void btm_process_cancel_complete(tHCI_STATUS status, uint8_t mode); static void on_incoming_hci_event(EventView event); static bool is_inquery_by_rssi() { return osi_property_get_bool(PROPERTY_INQ_BY_RSSI, false); } + /******************************************************************************* * * Function BTM_SetDiscoverability @@ -889,7 +880,7 @@ tBTM_STATUS BTM_ClearInqDb(const RawAddress* p_bda) { * Returns void * ******************************************************************************/ -void btm_clear_all_pending_le_entry(void) { +static void btm_clear_all_pending_le_entry(void) { uint16_t xx; std::lock_guard<std::mutex> lock(inq_db_lock_); tINQ_DB_ENT* p_ent = inq_db_; @@ -989,11 +980,11 @@ void btm_inq_db_reset(void) { * Returns void * ******************************************************************************/ -void btm_clr_inq_db(const RawAddress* p_bda) { +static void btm_clr_inq_db(const RawAddress* p_bda) { uint16_t xx; #if (BTM_INQ_DEBUG == TRUE) - log::verbose("btm_clr_inq_db: inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active, + log::verbose("inq_active:0x{:x} state:{}", btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); #endif std::lock_guard<std::mutex> lock(inq_db_lock_); @@ -1600,7 +1591,7 @@ static void btm_process_inq_results_extended(EventView event) { * Returns void * ******************************************************************************/ -void btm_sort_inq_result(void) { +static void btm_sort_inq_result(void) { uint8_t xx, yy, num_resp; std::lock_guard<std::mutex> lock(inq_db_lock_); tINQ_DB_ENT* p_ent = inq_db_; @@ -2073,7 +2064,7 @@ static uint16_t btm_convert_uuid_to_uuid16(const uint8_t* p_uuid, uint8_t uuid_s * Returns None * ******************************************************************************/ -void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results) { +static void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results) { const uint8_t* p_uuid_data; uint8_t num_uuid; uint16_t uuid16; @@ -2192,12 +2183,3 @@ void tBTM_INQUIRY_VAR_ST::Init() { } void tBTM_INQUIRY_VAR_ST::Free() { alarm_free(classic_inquiry_timer); } - -namespace bluetooth { -namespace legacy { -namespace testing { -void btm_clr_inq_db(const RawAddress* p_bda) { ::btm_clr_inq_db(p_bda); } -uint16_t btm_get_num_bd_entries() { return num_bd_entries_; } -} // namespace testing -} // namespace legacy -} // namespace bluetooth diff --git a/system/stack/btm/btm_iot_config.cc b/system/stack/btm/btm_iot_config.cc index 27990d3e86..56d6c90ef9 100644 --- a/system/stack/btm/btm_iot_config.cc +++ b/system/stack/btm/btm_iot_config.cc @@ -18,6 +18,8 @@ #define LOG_TAG "btm_iot" +#include "stack/include/btm_iot_config.h" + #include <bluetooth/log.h> #include "btif/include/btif_storage.h" @@ -26,9 +28,6 @@ #include "stack/acl/acl.h" #include "stack/include/btm_client_interface.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; /******************************************************************************* diff --git a/system/stack/btm/btm_main.cc b/system/stack/btm/btm_main.cc index 581b4d6445..8cf5328ad6 100644 --- a/system/stack/btm/btm_main.cc +++ b/system/stack/btm/btm_main.cc @@ -30,12 +30,12 @@ #include "common/strings.h" #include "main/shim/dumpsys.h" #include "stack/btm/btm_int_types.h" +#include "stack/btm/internal/btm_api.h" +#include "stack/include/btm_client_interface.h" +#include "stack/include/btm_log_history.h" #include "stack/include/security_client_callbacks.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - using namespace bluetooth; /* Global BTM control block structure diff --git a/system/stack/btm/btm_sco.cc b/system/stack/btm/btm_sco.cc index ecb0c4d734..c48b321448 100644 --- a/system/stack/btm/btm_sco.cc +++ b/system/stack/btm/btm_sco.cc @@ -58,13 +58,11 @@ #include "stack/include/hci_error_code.h" #include "stack/include/hcimsgs.h" #include "stack/include/main_thread.h" +#include "stack/include/sco_hci_link_interface.h" #include "stack/include/sdpdefs.h" #include "stack/include/stack_metrics_logging.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - extern tBTM_CB btm_cb; /* Default to allow enhanced connections where supported. */ @@ -92,11 +90,15 @@ constexpr char kBtmLogTag[] = "SCO"; using namespace bluetooth; using bluetooth::legacy::hci::GetInterface; -// forward declaration for dequeueing packets +/******************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/******************************************************************************/ +static tBTM_STATUS BTM_ChangeEScoLinkParms(uint16_t sco_inx, tBTM_CHG_ESCO_PARAMS* p_parms); + +static void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason); +static uint16_t btm_sco_voice_settings_to_legacy(enh_esco_params_t* p_parms); static void btm_route_sco_data(bluetooth::hci::ScoView valid_packet); -void btm_sco_conn_req(const RawAddress& bda, const DEV_CLASS& dev_class, uint8_t link_type); -void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason); -bool btm_sco_removed(uint16_t hci_handle, tHCI_REASON reason); +static bool btm_sco_removed(uint16_t hci_handle, tHCI_REASON reason); namespace cpp { bluetooth::common::BidiQueueEnd<bluetooth::hci::ScoBuilder, bluetooth::hci::ScoView>* @@ -214,7 +216,7 @@ enum btm_pcm_buf_state { DECODE_BUF_PARTIAL, }; -void incr_btm_pcm_buf_offset(size_t& offset, bool& mirror, size_t amount) { +static void incr_btm_pcm_buf_offset(size_t& offset, bool& mirror, size_t amount) { size_t bytes_remaining = BTM_SCO_DATA_SIZE_MAX - offset; if (bytes_remaining > amount) { offset += amount; @@ -225,7 +227,7 @@ void incr_btm_pcm_buf_offset(size_t& offset, bool& mirror, size_t amount) { offset = amount - bytes_remaining; } -btm_pcm_buf_state btm_pcm_buf_status() { +static btm_pcm_buf_state btm_pcm_buf_status() { if (btm_pcm_buf_read_offset == btm_pcm_buf_write_offset) { if (btm_pcm_buf_read_mirror == btm_pcm_buf_write_mirror) { return DECODE_BUF_EMPTY; @@ -235,7 +237,7 @@ btm_pcm_buf_state btm_pcm_buf_status() { return DECODE_BUF_PARTIAL; } -size_t btm_pcm_buf_data_len() { +static size_t btm_pcm_buf_data_len() { switch (btm_pcm_buf_status()) { case DECODE_BUF_EMPTY: return 0; @@ -250,9 +252,9 @@ size_t btm_pcm_buf_data_len() { }; } -size_t btm_pcm_buf_avail_len() { return BTM_SCO_DATA_SIZE_MAX - btm_pcm_buf_data_len(); } +static size_t btm_pcm_buf_avail_len() { return BTM_SCO_DATA_SIZE_MAX - btm_pcm_buf_data_len(); } -size_t write_btm_pcm_buf(uint8_t* source, size_t amount) { +static size_t write_btm_pcm_buf(uint8_t* source, size_t amount) { if (btm_pcm_buf_avail_len() < amount) { return 0; } @@ -269,13 +271,6 @@ size_t write_btm_pcm_buf(uint8_t* source, size_t amount) { return amount; } -/******************************************************************************/ -/* L O C A L F U N C T I O N P R O T O T Y P E S */ -/******************************************************************************/ -static tBTM_STATUS BTM_ChangeEScoLinkParms(uint16_t sco_inx, tBTM_CHG_ESCO_PARAMS* p_parms); - -static uint16_t btm_sco_voice_settings_to_legacy(enh_esco_params_t* p_parms); - /******************************************************************************* * * Function btm_esco_conn_rsp @@ -1226,7 +1221,7 @@ void BTM_RemoveScoByBdaddr(const RawAddress& bda) { * Returns true if the link is known about, else false * ******************************************************************************/ -bool btm_sco_removed(uint16_t hci_handle, tHCI_REASON reason) { +static bool btm_sco_removed(uint16_t hci_handle, tHCI_REASON reason) { tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; uint16_t xx; @@ -1255,7 +1250,7 @@ bool btm_sco_removed(uint16_t hci_handle, tHCI_REASON reason) { return false; } -void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason) { +static void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason) { tSCO_CONN* p_sco = btm_cb.sco_cb.get_sco_connection_from_handle(hci_handle); if (p_sco == nullptr) { log::debug("Unable to find sco connection"); diff --git a/system/stack/btm/btm_sco.h b/system/stack/btm/btm_sco.h index eea4ebe94a..ab55e78e44 100644 --- a/system/stack/btm/btm_sco.h +++ b/system/stack/btm/btm_sco.h @@ -302,3 +302,4 @@ void btm_send_sco_packet(std::vector<uint8_t> data); bool btm_peer_supports_esco_2m_phy(RawAddress remote_bda); bool btm_peer_supports_esco_3m_phy(RawAddress remote_bda); bool btm_peer_supports_esco_ev3(RawAddress remote_bda); +void btm_sco_acl_removed(const RawAddress* bda); diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc index 63be75bffd..149791017a 100644 --- a/system/stack/btm/btm_sec.cc +++ b/system/stack/btm/btm_sec.cc @@ -79,13 +79,8 @@ #include "types/bt_transport.h" #include "types/raw_address.h" -// TODO(b/369381361) Enfore -Wmissing-prototypes -#pragma GCC diagnostic ignored "-Wmissing-prototypes" - namespace { - constexpr char kBtmLogTag[] = "SEC"; - } using namespace bluetooth; @@ -103,9 +98,6 @@ extern tBTM_CB btm_cb; (BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED | BTM_SEC_LE_LINK_KEY_KNOWN | \ BTM_SEC_LE_LINK_KEY_AUTHED) -bool btm_ble_init_pseudo_addr(tBTM_SEC_DEV_REC* p_dev_rec, const RawAddress& new_pseudo_addr); -void bta_dm_remove_device(const RawAddress& bd_addr); - static tBTM_STATUS btm_sec_execute_procedure(tBTM_SEC_DEV_REC* p_dev_rec); static bool btm_sec_start_get_name(tBTM_SEC_DEV_REC* p_dev_rec); static void btm_sec_wait_and_start_authentication(tBTM_SEC_DEV_REC* p_dev_rec); @@ -177,12 +169,6 @@ static bool handleUnexpectedEncryptionChange() { return sHandleUnexpectedEncryptionChange; } -void NotifyBondingCanceled(tBTM_STATUS /* btm_status */) { - if (btm_sec_cb.api.p_bond_cancel_cmpl_callback) { - btm_sec_cb.api.p_bond_cancel_cmpl_callback(tBTM_STATUS::BTM_SUCCESS); - } -} - /******************************************************************************* * * Function btm_dev_authenticated @@ -2142,9 +2128,9 @@ static void call_registered_rmt_name_callbacks(const RawAddress* p_bd_addr, * nullptr if record is not found * ******************************************************************************/ -tBTM_SEC_DEV_REC* btm_rnr_add_name_to_security_record(const RawAddress* p_bd_addr, - const uint8_t* p_bd_name, - tHCI_STATUS hci_status) { +static tBTM_SEC_DEV_REC* btm_rnr_add_name_to_security_record(const RawAddress* p_bd_addr, + const uint8_t* p_bd_name, + tHCI_STATUS hci_status) { /* If remote name request failed, p_bd_addr is null and we need to search */ /* based on state assuming that we are doing 1 at a time */ tBTM_SEC_DEV_REC* p_dev_rec = nullptr; diff --git a/system/stack/btm/btm_sec.h b/system/stack/btm/btm_sec.h index 117edc0c3c..ade49b883e 100644 --- a/system/stack/btm/btm_sec.h +++ b/system/stack/btm/btm_sec.h @@ -60,6 +60,8 @@ bool BTM_IsLinkKeyAuthed(const RawAddress& bd_addr, tBT_TRANSPORT transport); bool BTM_IsLinkKeyKnown(const RawAddress& bd_addr, tBT_TRANSPORT transport); bool BTM_IsAuthenticated(const RawAddress& bd_addr, tBT_TRANSPORT transport); bool BTM_CanReadDiscoverableCharacteristics(const RawAddress& bd_addr); +void BTM_update_version_info(const RawAddress& bd_addr, + const remote_version_info& remote_version_info); /******************************************************************************* * @@ -401,7 +403,7 @@ void btm_sec_conn_req(const RawAddress& bda, const DEV_CLASS dc); * Returns void * ******************************************************************************/ -void btm_create_conn_cancel_complete(uint8_t status, const RawAddress bd_addr, uint32_t value); +void btm_create_conn_cancel_complete(uint8_t status, const RawAddress bd_addr); /******************************************************************************* * diff --git a/system/stack/btm/internal/btm_api.h b/system/stack/btm/internal/btm_api.h index 88960be4fd..21df1fe46c 100644 --- a/system/stack/btm/internal/btm_api.h +++ b/system/stack/btm/internal/btm_api.h @@ -49,6 +49,7 @@ void btm_free(); * DEVICE CONTROL and COMMON FUNCTIONS ****************************************************************************/ +void BTM_db_reset(void); void BTM_reset_complete(); /******************************************************************************* diff --git a/system/stack/fuzzers/rfcomm_fuzzer.cc b/system/stack/fuzzers/rfcomm_fuzzer.cc index 3418f3a6ed..82897723c6 100644 --- a/system/stack/fuzzers/rfcomm_fuzzer.cc +++ b/system/stack/fuzzers/rfcomm_fuzzer.cc @@ -52,10 +52,6 @@ void SnoopLogger::AcceptlistRfcommDlci(uint16_t, uint16_t, uint8_t) {} void SnoopLogger::SetRfcommPortOpen(uint16_t, uint16_t, uint8_t, uint16_t, bool) {} void SnoopLogger::SetRfcommPortClose(uint16_t, uint16_t, uint8_t, uint16_t) {} } // namespace hal - -namespace common { -uint64_t time_get_os_boottime_ms() { return 0; } -} // namespace common } // namespace bluetooth namespace { diff --git a/system/stack/gatt/gatt_attr.cc b/system/stack/gatt/gatt_attr.cc index a9d9e12b66..7b629e18e2 100644 --- a/system/stack/gatt/gatt_attr.cc +++ b/system/stack/gatt/gatt_attr.cc @@ -33,10 +33,12 @@ #include "eatt/eatt.h" #include "gatt_api.h" #include "gatt_int.h" +#include "device/include/interop.h" #include "internal_include/bt_target.h" #include "stack/include/bt_types.h" #include "stack/include/bt_uuid16.h" #include "stack/include/btm_sec_api.h" +#include "stack/include/btm_ble_addr.h" #include "types/bluetooth/uuid.h" #include "types/raw_address.h" @@ -847,6 +849,7 @@ static bool read_sr_sirk_req(tCONN_ID conn_id, uint8_t sirk_type, Octet16& sirk)> cb) { tGATT_READ_PARAM param = {}; + tBLE_ADDR_TYPE address_type = BLE_ADDR_PUBLIC; param.service.s_handle = 1; param.service.e_handle = 0xFFFF; @@ -854,10 +857,27 @@ static bool read_sr_sirk_req(tCONN_ID conn_id, param.service.uuid = bluetooth::Uuid::From16Bit(GATT_UUID_CSIS_SIRK); - if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, ¶m) != GATT_SUCCESS) { - log::error("Read GATT Support features GATT_Read Failed, conn_id: {}", - static_cast<int>(conn_id)); - return false; + uint8_t tcb_idx = gatt_get_tcb_idx(conn_id); + tGATT_TCB& tcb = gatt_cb.tcb[tcb_idx]; + + RawAddress identity_address = tcb.peer_bda; + + btm_random_pseudo_to_identity_addr(&identity_address, &address_type); + + if (address_type == BLE_ADDR_PUBLIC && + interop_match_addr(INTEROP_DISABLE_SIRK_READ_BY_TYPE, &identity_address)) { + if (GATTC_Read(conn_id, GATT_READ_CHAR_VALUE, ¶m) != GATT_SUCCESS) { + log::error("Read GATT Support features GATT_Read Failed, conn_id: {}", + static_cast<int>(conn_id)); + return false; + } + } + else{ + if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, ¶m) != GATT_SUCCESS) { + log::error("Read GATT Support features GATT_Read Failed, conn_id: {}", + static_cast<int>(conn_id)); + return false; + } } gatt_op_cb_data cb_data; diff --git a/system/stack/include/a2dp_vendor_ldac.h b/system/stack/include/a2dp_vendor_ldac.h index ec755c3b72..8f323a8c90 100644 --- a/system/stack/include/a2dp_vendor_ldac.h +++ b/system/stack/include/a2dp_vendor_ldac.h @@ -56,17 +56,6 @@ private: void debug_codec_dump(int fd) override; }; -class A2dpCodecConfigLdacSink : public A2dpCodecConfigLdacBase { -public: - A2dpCodecConfigLdacSink(btav_a2dp_codec_priority_t codec_priority); - virtual ~A2dpCodecConfigLdacSink(); - - bool init() override; - -private: - bool useRtpHeaderMarkerBit() const override; -}; - // Checks whether the codec capabilities contain a valid A2DP LDAC Source // codec. // NOTE: only codecs that are implemented are considered valid. @@ -166,13 +155,6 @@ std::string A2DP_VendorCodecInfoStringLdac(const uint8_t* p_codec_info); // supported, otherwise NULL. const tA2DP_ENCODER_INTERFACE* A2DP_VendorGetEncoderInterfaceLdac(const uint8_t* p_codec_info); -// Gets the current A2DP LDAC decoder interface that can be used to decode -// received A2DP packets - see |tA2DP_DECODER_INTERFACE|. -// |p_codec_info| contains the codec information. -// Returns the A2DP LDAC decoder interface if the |p_codec_info| is valid and -// supported, otherwise NULL. -const tA2DP_DECODER_INTERFACE* A2DP_VendorGetDecoderInterfaceLdac(const uint8_t* p_codec_info); - // Adjusts the A2DP LDAC codec, based on local support and Bluetooth // specification. // |p_codec_info| contains the codec information to adjust. @@ -184,23 +166,11 @@ bool A2DP_VendorAdjustCodecLdac(uint8_t* p_codec_info); // otherwise |BTAV_A2DP_CODEC_INDEX_MAX|. btav_a2dp_codec_index_t A2DP_VendorSourceCodecIndexLdac(const uint8_t* p_codec_info); -// Gets the A2DP LDAC Sink codec index for a given |p_codec_info|. -// Returns the corresponding |btav_a2dp_codec_index_t| on success, -// otherwise |BTAV_A2DP_CODEC_INDEX_MAX|. -btav_a2dp_codec_index_t A2DP_VendorSinkCodecIndexLdac(const uint8_t* p_codec_info); - // Gets the A2DP LDAC Source codec name. const char* A2DP_VendorCodecIndexStrLdac(void); -// Gets the A2DP LDAC Sink codec name. -const char* A2DP_VendorCodecIndexStrLdacSink(void); - // Initializes A2DP LDAC Source codec information into |AvdtpSepConfig| // configuration entry pointed by |p_cfg|. bool A2DP_VendorInitCodecConfigLdac(AvdtpSepConfig* p_cfg); -// Initializes A2DP LDAC Sink codec information into |AvdtpSepConfig| -// configuration entry pointed by |p_cfg|. -bool A2DP_VendorInitCodecConfigLdacSink(AvdtpSepConfig* p_cfg); - #endif // A2DP_VENDOR_LDAC_H diff --git a/system/stack/include/a2dp_vendor_ldac_decoder.h b/system/stack/include/a2dp_vendor_ldac_decoder.h deleted file mode 100644 index 3d15b8397a..0000000000 --- a/system/stack/include/a2dp_vendor_ldac_decoder.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Interface to the A2DP LDAC Decoder -// - -#ifndef A2DP_VENDOR_LDAC_DECODER_H -#define A2DP_VENDOR_LDAC_DECODER_H - -#include <stddef.h> -#include <stdint.h> - -#include "a2dp_codec_api.h" -#include "stack/include/bt_hdr.h" - -// Loads the A2DP LDAC decoder. -// Return true on success, otherwise false. -bool A2DP_VendorLoadDecoderLdac(void); - -// Unloads the A2DP LDAC decoder. -void A2DP_VendorUnloadDecoderLdac(void); - -// Initialize the A2DP LDAC decoder. -bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback); - -// Cleanup the A2DP LDAC decoder. -void a2dp_vendor_ldac_decoder_cleanup(void); - -// Decodes |p_buf|. Calls |decode_callback| passed into -// |a2dp_vendor_ldac_decoder_init| if decoded frames are available. -bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf); - -// Start the A2DP LDAC decoder. -void a2dp_vendor_ldac_decoder_start(void); - -// Suspend the A2DP LDAC decoder. -void a2dp_vendor_ldac_decoder_suspend(void); - -// A2DP LDAC decoder configuration. -void a2dp_vendor_ldac_decoder_configure(const uint8_t* p_codec_info); - -#endif // A2DP_VENDOR_LDAC_DECODER_H diff --git a/system/stack/include/avrc_api.h b/system/stack/include/avrc_api.h index 589ddb5021..b2f683cc0c 100644 --- a/system/stack/include/avrc_api.h +++ b/system/stack/include/avrc_api.h @@ -133,10 +133,6 @@ #define AVRC_1_3_STRING "avrcp13" #endif -#ifndef AVRC_DEFAULT_VERSION -#define AVRC_DEFAULT_VERSION AVRC_1_5_STRING -#endif - /* Configurable dynamic avrcp version enable key*/ #ifndef AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY #define AVRC_DYNAMIC_AVRCP_ENABLE_PROPERTY "persist.bluetooth.dynamic_avrcp.enable" diff --git a/system/stack/include/ble_appearance.h b/system/stack/include/ble_appearance.h new file mode 100644 index 0000000000..6dbb32996c --- /dev/null +++ b/system/stack/include/ble_appearance.h @@ -0,0 +1,446 @@ +/****************************************************************************** + * + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#ifndef BLE_APPEARANCE_H +#define BLE_APPEARANCE_H + +/* BLE appearance values as per BT spec assigned numbers */ +/* Category[15:6] 0x000 */ +#define BLE_APPEARANCE_UNKNOWN 0x0000 + +/* Category[15:6] 0x001 */ +#define BLE_APPEARANCE_GENERIC_PHONE 0x0040 + +/* Category[15:6] 0x002 */ +#define BLE_APPEARANCE_GENERIC_COMPUTER 0x0080 + +#define BLE_APPEARANCE_DESKTOP_WORKSTATION 0x81 +#define BLE_APPEARANCE_SERVER_CLASS_COMPUTER 0x82 +#define BLE_APPEARANCE_LAPTOP 0x83 +#define BLE_APPEARANCE_HANDHELD_PC_PDA 0x84 +#define BLE_APPEARANCE_PALM_SIZE_PC_PDA 0x85 +#define BLE_APPEARANCE_WEARABLE_COMPUTER_WATCH_SIZE 0x86 +#define BLE_APPEARANCE_TABLET 0x87 +#define BLE_APPEARANCE_DOCKING_STATION 0x88 +#define BLE_APPEARANCE_ALL_IN_ONE 0x89 +#define BLE_APPEARANCE_BLADE_SERVER 0x8A +#define BLE_APPEARANCE_CONVERTIBLE 0x8B +#define BLE_APPEARANCE_DETACHABLE 0x8C +#define BLE_APPEARANCE_IOT_GATEWAY 0x8D +#define BLE_APPEARANCE_MINI_PC 0x8E +#define BLE_APPEARANCE_STICK_PC 0x8F + +/* Category[15:6] 0x003 */ +#define BLE_APPEARANCE_GENERIC_WATCH 0x00C0 +#define BLE_APPEARANCE_SPORTS_WATCH 0x00C1 +#define BLE_APPEARANCE_SMART_WATCH 0x00C2 + +/* Category[15:6] 0x004 */ +#define BLE_APPEARANCE_GENERIC_CLOCK 0x0100 + +/* Category[15:6] 0x005 */ +#define BLE_APPEARANCE_GENERIC_DISPLAY 0x0140 + +/* Category[15:6] 0x006 */ +#define BLE_APPEARANCE_GENERIC_REMOTE 0x0180 + +/* Category[15:6] 0x007 */ +#define BLE_APPEARANCE_GENERIC_EYEGLASSES 0x01C0 + +/* Category[15:6] 0x008 */ +#define BLE_APPEARANCE_GENERIC_TAG 0x0200 + +/* Category[15:6] 0x009 */ +#define BLE_APPEARANCE_GENERIC_KEYRING 0x0240 + +/* Category[15:6] 0x00A */ +#define BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 0x0280 + +/* Category[15:6] 0x00B */ +#define BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 0x02C0 + +/* Category[15:6] 0x00C */ +#define BLE_APPEARANCE_GENERIC_THERMOMETER 0x0300 +#define BLE_APPEARANCE_THERMOMETER_EAR 0x0301 + +/* Category[15:6] 0x00D */ +#define BLE_APPEARANCE_GENERIC_HEART_RATE 0x0340 +#define BLE_APPEARANCE_HEART_RATE_BELT 0x0341 + +/* Category[15:6] 0x00E */ +#define BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 0x0380 +#define BLE_APPEARANCE_BLOOD_PRESSURE_ARM 0x0381 +#define BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 0x0382 + +/* Category[15:6] 0x00F */ +#define BLE_APPEARANCE_GENERIC_HID 0x03C0 +#define BLE_APPEARANCE_HID_KEYBOARD 0x03C1 +#define BLE_APPEARANCE_HID_MOUSE 0x03C2 +#define BLE_APPEARANCE_HID_JOYSTICK 0x03C3 +#define BLE_APPEARANCE_HID_GAMEPAD 0x03C4 +#define BLE_APPEARANCE_HID_DIGITIZER_TABLET 0x03C5 +#define BLE_APPEARANCE_HID_CARD_READER 0x03C6 +#define BLE_APPEARANCE_HID_DIGITAL_PEN 0x03C7 +#define BLE_APPEARANCE_HID_BARCODE_SCANNER 0x03C8 +#define BLE_APPEARANCE_HID_TOUCHPAD 0x03C9 +#define BLE_APPEARANCE_HID_PRESENTATION_REMOTE 0x03CA + +/* Category[15:6] 0x010 */ +#define BLE_APPEARANCE_GENERIC_GLUCOSE 0x0400 + +/* Category[15:6] 0x011 */ +#define BLE_APPEARANCE_GENERIC_WALKING 0x0440 +#define BLE_APPEARANCE_WALKING_IN_SHOE 0x0441 +#define BLE_APPEARANCE_WALKING_ON_SHOE 0x0442 +#define BLE_APPEARANCE_WALKING_ON_HIP 0x0443 + +/* Category[15:6] 0x012 */ +#define BLE_APPEARANCE_GENERIC_CYCLING 0x0480 +#define BLE_APPEARANCE_CYCLING_COMPUTER 0x0481 +#define BLE_APPEARANCE_CYCLING_SPEED 0x0482 +#define BLE_APPEARANCE_CYCLING_CADENCE 0x0483 +#define BLE_APPEARANCE_CYCLING_POWER 0x0484 +#define BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485 + +/* Category[15:6] 0x013 */ +#define BLE_APPEARANCE_GENERIC_CONTROL_DEVICE 0x04C0 +#define BLE_APPEARANCE_SWITCH 0x04C1 +#define BLE_APPEARANCE_MULTI_SWITCH 0x04C2 +#define BLE_APPEARANCE_SWITCH_BUTTON 0x04C3 +#define BLE_APPEARANCE_SWITCH_SLIDER 0x04C4 +#define BLE_APPEARANCE_ROTARY_SWITCH 0x04C5 +#define BLE_APPEARANCE_TOUCH_PANEL 0x04C6 +#define BLE_APPEARANCE_SINGLE_SWITCH 0x04C7 +#define BLE_APPEARANCE_DOUBLE_SWITCH 0x04C8 +#define BLE_APPEARANCE_TRIPLE_SWITCH 0x04C9 +#define BLE_APPEARANCE_BATTERY_SWITCH 0x04CA +#define BLE_APPEARANCE_ENERGY_HARVESTING_SWITCH 0x04CB +#define BLE_APPEARANCE_SWITCH_PUSH_BUTTON 0x04CC +#define BLE_APPEARANCE_SWITCH_DIAL 0x04CD + +/* Category[15:6] 0x014 */ +#define BLE_APPEARANCE_GENERIC_NETWORK_DEVICE 0x0500 +#define BLE_APPEARANCE_NETWORK_DEVICE_ACCESS_POINT 0x0501 +#define BLE_APPEARANCE_NETWORK_DEVICE_MESH_DEVICE 0x0502 +#define BLE_APPEARANCE_NETWORK_DEVICE_MESH_NETWORK_PROXY 0x0503 + +/* Category[15:6] 0x015 */ +#define BLE_APPEARANCE_GENERIC_SENSOR 0x0540 +#define BLE_APPEARANCE_MOTION_SENSOR 0x0541 +#define BLE_APPEARANCE_AIR_QUALITY_SENSOR 0x0542 +#define BLE_APPEARANCE_TEMPERATURE_SENSOR 0x0543 +#define BLE_APPEARANCE_HUMIDITY_SENSOR 0x0544 +#define BLE_APPEARANCE_LEAK_SENSOR 0x05 +#define BLE_APPEARANCE_SMOKE_SENSOR 0x0546 +#define BLE_APPEARANCE_OCCUPANCY_SENSOR 0x0547 +#define BLE_APPEARANCE_CONTACT_SENSOR 0x0548 +#define BLE_APPEARANCE_CARBON_MONOXIDE_SENSOR 0x0549 +#define BLE_APPEARANCE_CARBON_DIOXIDE_SENSOR 0x054A +#define BLE_APPEARANCE_AMBIENT_LIGHT_SENSOR 0x054B +#define BLE_APPEARANCE_ENERGY_SENSOR 0x054C +#define BLE_APPEARANCE_COLOR_LIGHT_SENSOR 0x054D +#define BLE_APPEARANCE_RAIN_SENSOR 0x054E +#define BLE_APPEARANCE_FIRE_SENSOR 0x054F +#define BLE_APPEARANCE_WIND_SENSOR 0x0550 +#define BLE_APPEARANCE_PROXIMITY_SENSOR 0x0551 +#define BLE_APPEARANCE_MULTI_SENSOR 0x0552 +#define BLE_APPEARANCE_FLUSH_MOUNTED_SENSOR 0x0553 +#define BLE_APPEARANCE_CEILING_MOUNTED_SENSOR 0x0554 +#define BLE_APPEARANCE_WALL_MOUNTED_SENSOR 0x0555 +#define BLE_APPEARANCE_MULTISENSOR 0x0556 +#define BLE_APPEARANCE_SENSOR_ENERGY_METER 0x0557 +#define BLE_APPEARANCE_SENSOR_FLAME_DETECTOR 0x0558 +#define BLE_APPEARANCE_VEHICLE_TIRE_PRESSURE_SENSOR 0x0559 + +/* Category[15:6] 0x016 */ +#define BLE_APPEARANCE_GENERIC_LIGHT_FIXTURE 0x0580 +#define BLE_APPEARANCE_WALL_LIGHT 0x0581 +#define BLE_APPEARANCE_CEILING_LIGHT 0x0582 +#define BLE_APPEARANCE_FLOOR_LIGHT 0x0583 +#define BLE_APPEARANCE_CABINET_LIGHT 0x0584 +#define BLE_APPEARANCE_DESK_LIGHT 0x0585 +#define BLE_APPEARANCE_TROFFER_LIGHT 0x0586 +#define BLE_APPEARANCE_PENDANT_LIGHT 0x0587 +#define BLE_APPEARANCE_IN_GROUND_LIGHT 0x0588 +#define BLE_APPEARANCE_FLOOD_LIGHT 0x0589 +#define BLE_APPEARANCE_UNDERWATER_LIGHT 0x058A +#define BLE_APPEARANCE_BOLLARD_WITH_LIGHT 0x058B +#define BLE_APPEARANCE_PATHWAY_LIGHT 0x058C +#define BLE_APPEARANCE_GARDEN_LIGHT 0x058D +#define BLE_APPEARANCE_POLE_TOP_LIGHT 0x058E +#define BLE_APPEARANCE_SPOTLIGHT 0x058F +#define BLE_APPEARANCE_LINEAR_LIGHT 0x0590 +#define BLE_APPEARANCE_STREET_LIGHT 0x0591 +#define BLE_APPEARANCE_SHELVES_LIGHT 0x0592 +#define BLE_APPEARANCE_BAY_LIGHT 0x0593 +#define BLE_APPEARANCE_EMERGENCY_EXIT_LIGHT 0x0594 +#define BLE_APPEARANCE_LIGHT_CONTROLLER 0x0595 +#define BLE_APPEARANCE_LIGHT_DRIVER 0x0596 +#define BLE_APPEARANCE_BULB 0x0597 +#define BLE_APPEARANCE_LOW_BAY_LIGHT 0x0598 +#define BLE_APPEARANCE_HIGH_BAY_LIGHT 0x0599 + +/* Category[15:6] 0x017 */ +#define BLE_APPEARANCE_GENERIC_FAN 0x05C0 +#define BLE_APPEARANCE_CEILING_FAN 0x05C1 +#define BLE_APPEARANCE_AXIAL_FAN 0x05C2 +#define BLE_APPEARANCE_EXHAUST_FAN 0x05C3 +#define BLE_APPEARANCE_PEDESTAL_FAN 0x05C4 +#define BLE_APPEARANCE_DESK_FAN 0x05C5 +#define BLE_APPEARANCE_WALL_FAN 0x05C6 + +/* Category[15:6] 0x018 */ +#define BLE_APPEARANCE_GENERIC_HVAC 0x0600 +#define BLE_APPEARANCE_HVAC_THERMOSTAT 0x0601 +#define BLE_APPEARANCE_HVAC_HUMIDIFIER 0x0602 +#define BLE_APPEARANCE_HVAC_DEHUMIDIFIER 0x0603 +#define BLE_APPEARANCE_HVAC_HEATER 0x0604 +#define BLE_APPEARANCE_HVAC_RADIATOR 0x0605 +#define BLE_APPEARANCE_HVAC_BOILER 0x0606 +#define BLE_APPEARANCE_HVAC_HEAT_PUMP 0x0607 +#define BLE_APPEARANCE_HVAC_INFRARED_HEATER 0x0608 +#define BLE_APPEARANCE_HVAC_RADIANT_PANEL_HEATER 0x0609 +#define BLE_APPEARANCE_HVAC_FAN_HEATER 0x060A +#define BLE_APPEARANCE_HVAC_AIR_CURTAIN 0x060B + +/* Category[15:6] 0x019 */ +#define BLE_APPEARANCE_GENERIC_AIR_CONDITIONING 0x0640 + +/* Category[15:6] 0x01A */ +#define BLE_APPEARANCE_GENERIC_HUMIDIFIER 0x0680 + +/* Category[15:6] 0x01B */ +#define BLE_APPEARANCE_GENERIC_HEATING 0x06C0 +#define BLE_APPEARANCE_HEATING_RADIATOR 0x06C1 +#define BLE_APPEARANCE_HEATING_BOILER 0x06C2 +#define BLE_APPEARANCE_HEATING_HEAT_PUMP 0x06C3 +#define BLE_APPEARANCE_HEATING_INFRARED_HEATER 0x06C4 +#define BLE_APPEARANCE_HEATING_RADIANT_PANEL_HEATER 0x06C5 +#define BLE_APPEARANCE_HEATING_FAN_HEATER 0x06C6 +#define BLE_APPEARANCE_HEATING_AIR_CURTAIN 0x06C7 + +/* Category[15:6] 0x01C */ +#define BLE_APPEARANCE_GENERIC_ACCESS_CONTROL 0x0700 +#define BLE_APPEARANCE_ACCESS_DOOR 0x0701 +#define BLE_APPEARANCE_ACCESS_CONTROL_GARAGE_DOOR 0x0702 +#define BLE_APPEARANCE_ACCESS_CONTROL_EMERGENCY_EXIT_DOOR 0x0703 +#define BLE_APPEARANCE_ACCESS_CONTROL_ACCESS_LOCK 0x0704 +#define BLE_APPEARANCE_ACCESS_CONTROL_ELEVATOR 0x0705 +#define BLE_APPEARANCE_ACCESS_CONTROL_WINDOW 0x0706 +#define BLE_APPEARANCE_ACCESS_CONTROL_ENTRANCE_GATE 0x0707 +#define BLE_APPEARANCE_ACCESS_CONTROL_DOOR_LOCK 0x0708 +#define BLE_APPEARANCE_ACCESS_CONTROL_LOCKER 0x0709 + +/* Category[15:6] 0x01D */ +#define BLE_APPEARANCE_GENERIC_MOTORIZED_DEVICE 0x0740 +#define BLE_APPEARANCE_MOTORIZED_GATE 0x0741 +#define BLE_APPEARANCE_MOTORIZED_AWNING 0x0742 +#define BLE_APPEARANCE_MOTORIZED_BLINDS_OR_SHADES 0x0743 +#define BLE_APPEARANCE_MOTORIZED_CURTAINS 0x0744 +#define BLE_APPEARANCE_MOTORIZED_SCREEN 0x0745 + +/* Category[15:6] 0x01E */ +#define BLE_APPEARANCE_GENERIC_POWER_DEVICE 0x0780 +#define BLE_APPEARANCE_POWER_OUTLET 0x0781 +#define BLE_APPEARANCE_POWER_STRIP 0x0782 +#define BLE_APPEARANCE_POWER_PLUG 0x0783 +#define BLE_APPEARANCE_POWER_SUPPLY 0x0784 + +/* Category[15:6] 0x01F */ +#define BLE_APPEARANCE_GENERIC_LIGHT_SOURCE 0x07C0 +#define BLE_APPEARANCE_LIGHT_SOURCE_INCANDESCENT_LIGHT_BULB 0x07C1 +#define BLE_APPEARANCE_LIGHT_SOURCE_LED_LAMP 0x07C2 +#define BLE_APPEARANCE_LIGHT_SOURCE_HID_LAMP 0x07C3 +#define BLE_APPEARANCE_LIGHT_SOURCE_FLUORESCENT_LAMP 0x07C4 +#define BLE_APPEARANCE_LIGHT_SOURCE_LED_ARRAY 0x07C5 +#define BLE_APPEARANCE_LIGHT_SOURCE_MULTI_COLOR_LED_ARRAY 0x07C6 +#define BLE_APPEARANCE_LIGHT_SOURCE_LOW_VOLTAGE_HALOGEN 0x07C7 +#define BLE_APPEARANCE_LIGHT_SOURCE_ORGANIC_LIGHT_EMITTING_DIODE_OLED 0x07C8 + +/* Category[15:6] 0x020 */ +#define BLE_APPEARANCE_GENERIC_WINDOW_COVERING 0x0800 +#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_SHADES 0x0801 +#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_BLINDS 0x0802 +#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_AWNING 0x0803 +#define BLE_APPEARANCE_WINDOW_COVERING_WINDOW_CURTAIN 0x0804 +#define BLE_APPEARANCE_WINDOW_COVERING_EXTERIOR_SHUTTER 0x0805 +#define BLE_APPEARANCE_WINDOW_COVERING_EXTERIOR_SCREEN 0x0806 + +/* Category[15:6] 0x021 */ +#define BLE_APPEARANCE_GENERIC_AUDIO_SINK 0x0840 +#define BLE_APPEARANCE_AUDIO_SINK_STANDALONE_SPEAKER 0x0841 +#define BLE_APPEARANCE_AUDIO_SINK_SOUNDBAR 0x0842 +#define BLE_APPEARANCE_AUDIO_SINK_BOOKSHELF_SPEAKER 0x0843 +#define BLE_APPEARANCE_AUDIO_SINK_STANDMOUNTED_SPEAKER 0x0844 +#define BLE_APPEARANCE_AUDIO_SINK_SPEAKERPHONE 0x0845 + +/* Category[15:6] 0x022 */ +#define BLE_APPEARANCE_GENERIC_AUDIO_SOURCE 0x0880 +#define BLE_APPEARANCE_AUDIO_SOURCE_MICROPHONE 0x0881 +#define BLE_APPEARANCE_AUDIO_SOURCE_ALARM 0x0882 +#define BLE_APPEARANCE_AUDIO_SOURCE_BELL 0x0883 +#define BLE_APPEARANCE_AUDIO_SOURCE_HORN 0x0884 +#define BLE_APPEARANCE_AUDIO_SOURCE_BROADCASTING_DEVICE 0x0885 +#define BLE_APPEARANCE_AUDIO_SOURCE_SERVICE_DESK 0x0886 +#define BLE_APPEARANCE_AUDIO_SOURCE_KIOSK 0x0887 +#define BLE_APPEARANCE_AUDIO_SOURCE_BROADCASTING_ROOM 0x0888 +#define BLE_APPEARANCE_AUDIO_SOURCE_AUDITORIUM 0x0889 + +/* Category[15:6] 0x023 */ +#define BLE_APPEARANCE_GENERIC_MOTORIZED_VEHICLE 0x08C0 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_CAR 0x08C1 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_LARGE_GOODS 0x08C2 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_2_WHEELED 0x08C3 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_MOTORBIKE 0x08C4 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_SCOOTER 0x08C5 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_MOPED 0x08C6 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_3_WHEELED 0x08C7 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_LIGHT_VEHICLE 0x08C8 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_QUAD_BIKE 0x08C9 +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_MINIBUS 0x08CA +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_BUS 0x08CB +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_TROLLEY 0x08CC +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_AGRICULTURAL_VEHICLE 0x08CD +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_CAMPER_CARAVAN 0x08CE +#define BLE_APPEARANCE_MOTORIZED_VEHICLE_RECREATIONAL_VEHICLE_MOTOR_HOME 0x08CF + +/* Category[15:6] 0x024 */ +#define BLE_APPEARANCE_GENERIC_DOMESTIC_APPLIANCE 0x0900 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_REFRIGERATOR 0x0901 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_FREEZER 0x0902 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_OVEN 0x0903 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_MICROWAVE 0x0904 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_TOASTER 0x0905 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_WASHING_MACHINE 0x0906 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_DRYER 0x0907 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_COFFEE_MAKER 0x0908 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_CLOTHES_IRON 0x0909 +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_CURLING_IRON 0x090A +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_HAIR_DRYER 0x090B +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_VACUUM_CLEANER 0x090C +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_ROBOTIC_VACUUM_CLEANER 0x090D +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_RICE_COOKER 0x090E +#define BLE_APPEARANCE_DOMESTIC_APPLIANCE_CLOTHES_STEAMER 0x090F + +/* Category[15:6] 0x025 */ +#define BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE 0x0940 +#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD 0x0941 +#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET 0x0942 +#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES 0x0943 +#define BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND 0x0944 + +/* Category[15:6] 0x026 */ +#define BLE_APPEARANCE_GENERIC_AIRCRAFT 0x0980 +#define BLE_APPEARANCE_AIRCRAFT_LIGHT 0x0981 +#define BLE_APPEARANCE_AIRCRAFT_MICROLIGHT 0x0982 +#define BLE_APPEARANCE_AIRCRAFT_PARAGLIDER 0x0983 +#define BLE_APPEARANCE_AIRCRAFT_LARGE_PASSENGER 0x0984 + +/* Category[15:6] 0x027 */ +#define BLE_APPEARANCE_GENERIC_AV_EQUIPMENT 0x09C0 +#define BLE_APPEARANCE_AV_EQUIPMENT_AMPLIFIER 0x09C1 +#define BLE_APPEARANCE_AV_EQUIPMENT_RECEIVER 0x09C2 +#define BLE_APPEARANCE_AV_EQUIPMENT_RADIO 0x09C3 +#define BLE_APPEARANCE_AV_EQUIPMENT_TUNER 0x09C4 +#define BLE_APPEARANCE_AV_EQUIPMENT_TURNTABLE 0x09C5 +#define BLE_APPEARANCE_AV_EQUIPMENT_CD_PLAYER 0x09C6 +#define BLE_APPEARANCE_AV_EQUIPMENT_DVD_PLAYER 0x09C7 +#define BLE_APPEARANCE_AV_EQUIPMENT_BLU_RAY_PLAYER 0x09C8 +#define BLE_APPEARANCE_AV_EQUIPMENT_OPTICAL_DISC_PLAYER 0x09C9 +#define BLE_APPEARANCE_AV_EQUIPMENT_SET_TOP_BOX 0x09CA + +/* Category[15:6] 0x028 */ +#define BLE_APPEARANCE_GENERIC_DISPLAY_EQUIPMENT 0x0A00 +#define BLE_APPEARANCE_DISPLAY_EQUIPMENT_TELEVISION 0x0A01 +#define BLE_APPEARANCE_DISPLAY_EQUIPMENT_MONITOR 0x0A02 +#define BLE_APPEARANCE_DISPLAY_EQUIPMENT_PROJECTOR 0x0A03 + +/* Category[15:6] 0x029 */ +#define BLE_APPEARANCE_GENERIC_HEARING_AID 0x0A40 +#define BLE_APPEARANCE_HEARING_AID_IN_EAR 0x0A41 +#define BLE_APPEARANCE_HEARING_AID_BEHIND_EAR 0x0A42 +#define BLE_APPEARANCE_HEARING_AID_COCHLLEAR_IMPLANT 0x0A43 + +/* Category[15:6] 0x02A */ +#define BLE_APPEARANCE_GENERIC_GAMING 0x0A80 +#define BLE_APPEARANCE_GAMING_HOME_VIDEO_GAME_CONSOLE 0x0A81 +#define BLE_APPEARANCE_GAMING_PORTABLE_HANDHELD_CONSOLE 0x0A82 + +/* Category[15:6] 0x02B */ +#define BLE_APPEARANCE_GENERIC_SIGNAGE 0x0AC0 +#define BLE_APPEARANCE_SIGNAGE_DIGITAL 0x0AC1 +#define BLE_APPEARANCE_SIGNAGE_ELECTRONIC_LABEL 0x0AC2 + +/* Category[15:6] 0x031 */ +#define BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40 +#define BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41 +#define BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42 + +/* Category[15:6] 0x032 */ +#define BLE_APPEARANCE_GENERIC_WEIGHT 0x0C80 + +/* Category[15:6] 0x033 */ +#define BLE_APPEARANCE_GENERIC_PERSONAL_MOBILITY_DEVICE 0x0CC0 +#define BLE_APPEARANCE_PERSONAL_MOBILITY_DEVICE_POWERED_WHEELCHAIR 0x0CC1 +#define BLE_APPEARANCE_PERSONAL_MOBILITY_DEVICE_MOBILITY_SCOOTER 0x0CC2 + +/* Category[15:6] 0x034 */ +#define BLE_APPEARANCE_GENERIC_CONTINUOUS_GLUCOSE_MONITOR 0x0D00 + +/* Category[15:6] 0x035 */ +#define BLE_APPEARANCE_GENERIC_INSULIN_PUMP 0x0D40 +#define BLE_APPEARANCE_INSULIN_PUMP_DURABLE 0x0D41 +#define BLE_APPEARANCE_INSULIN_PUMP_PATCH 0x0D44 +#define BLE_APPEARANCE_INSULIN_PUMP_PEN 0x0D48 + +/* Category[15:6] 0x036 */ +#define BLE_APPEARANCE_GENERIC_MEDICATION_DELIVERY 0x0D80 + +/* Category[15:6] 0x037 */ +#define BLE_APPEARANCE_GENERIC_SPIROMETER 0x0DC0 +#define BLE_APPEARANCE_SPIROMETER_HANDHELD 0x0DC1 + +/* Category[15:6] 0x051 */ +#define BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS 0x1440 +#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION 0x1441 +#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV 0x1442 +#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD 0x1443 +#define BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV 0x1444 + +/* Category[15:6] 0x052 */ +#define BLE_APPEARANCE_GENERIC_INDUSTRIAL_MEASUREMENT_DEVICE 0x1480 +#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_TORQUE_TESTING 0x1481 +#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_CALIPER 0x1482 +#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_DIAL_INDICATOR 0x1483 +#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_MICROMETER 0x1484 +#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_HEIGHT_GAUGE 0x1485 +#define BLE_APPEARANCE_INDUSTRIAL_MEASUREMENT_DEVICE_FORCE_GAUGE 0x1486 + +/* Category[15:6] 0x053 */ +#define BLE_APPEARANCE_GENERIC_INDUSTRIAL_TOOLS 0x14C0 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_MACHINE_TOOL_HOLDER 0x14C1 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_GENERIC_CLAMPING_DEVICE 0x14C2 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_CLAMPING_JAWS_JAWS_CHUCK 0x14C3 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_CLAMPING_COLLET_CHUCK 0x14C4 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_CLAMPING_MANDREL 0x14C5 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_VISE 0x14C6 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_ZERO_POINT_CLAMPING_SYSTEM 0x14C7 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_TORQUE_WRENCH 0x14C8 +#define BLE_APPEARANCE_INDUSTRIAL_TOOLS_TORQUE_SCREWDRIVER 0x14C9 + +#endif // BLE_APPEARANCE_H
\ No newline at end of file diff --git a/system/stack/include/ble_hci_link_interface.h b/system/stack/include/ble_hci_link_interface.h index 068368bfb6..41f689ecab 100644 --- a/system/stack/include/ble_hci_link_interface.h +++ b/system/stack/include/ble_hci_link_interface.h @@ -18,8 +18,10 @@ #pragma once #include <cstdint> +#include <vector> #include "stack/include/hci_error_code.h" +#include "stack/include/rnr_interface.h" // This header contains functions for HCI-ble to invoke void btm_ble_read_remote_features_complete(uint8_t* p, uint8_t length); @@ -30,3 +32,21 @@ void btm_ble_read_resolving_list_entry_complete(const uint8_t* p, uint16_t evt_l void btm_ble_remove_resolving_list_entry_complete(uint8_t* p, uint16_t evt_len); void btm_ble_add_resolving_list_entry_complete(uint8_t* p, uint16_t evt_len); void btm_ble_clear_resolving_list_complete(uint8_t* p, uint16_t evt_len); +tBTM_STATUS btm_ble_read_remote_cod(const RawAddress& remote_bda); +tBTM_STATUS btm_ble_read_remote_name(const RawAddress& remote_bda, tBTM_NAME_CMPL_CB* p_cb); +bool btm_ble_cancel_remote_name(const RawAddress& remote_bda); +void btm_ble_decrement_link_topology_mask(uint8_t link_role); +void btm_ble_increment_link_topology_mask(uint8_t link_role); +DEV_CLASS btm_ble_get_appearance_as_cod(std::vector<uint8_t> const& data); +void btm_ble_process_adv_addr(RawAddress& raw_address, tBLE_ADDR_TYPE* address_type); +void btm_ble_process_adv_pkt_cont_for_inquiry(uint16_t event_type, tBLE_ADDR_TYPE address_type, + const RawAddress& raw_address, uint8_t primary_phy, + uint8_t secondary_phy, uint8_t advertising_sid, + int8_t tx_power, int8_t rssi, + uint16_t periodic_adv_int, + std::vector<uint8_t> advertising_data); +void btm_ble_process_adv_pkt_cont(uint16_t evt_type, tBLE_ADDR_TYPE addr_type, + const RawAddress& bda, uint8_t primary_phy, uint8_t secondary_phy, + uint8_t advertising_sid, int8_t tx_power, int8_t rssi, + uint16_t periodic_adv_int, uint8_t data_len, const uint8_t* data, + const RawAddress& original_bda); diff --git a/system/stack/include/bt_dev_class.h b/system/stack/include/bt_dev_class.h index 1455f66fd2..ca951532ce 100644 --- a/system/stack/include/bt_dev_class.h +++ b/system/stack/include/bt_dev_class.h @@ -26,72 +26,222 @@ typedef std::array<uint8_t, kDevClassLength> DEV_CLASS; /* Device class */ inline constexpr DEV_CLASS kDevClassEmpty = {}; +/*************************** + * major device class field + * Note: All values are deduced by basing BIT_X to BIT_8, values as per + * BT-spec assigned-numbers. + ***************************/ +#define COD_MAJOR_MISC 0x00 +#define COD_MAJOR_COMPUTER 0x01 // BIT8 +#define COD_MAJOR_PHONE 0x02 // BIT9 +#define COD_MAJOR_LAN_NAP 0x03 // BIT8 | BIT9 +#define COD_MAJOR_AUDIO 0x04 // BIT10 +#define COD_MAJOR_PERIPHERAL 0x05 // BIT8 | BIT10 +#define COD_MAJOR_IMAGING 0x06 // BIT9 | BIT10 +#define COD_MAJOR_WEARABLE 0x07 // BIT8 | BIT9 | BIT10 +#define COD_MAJOR_TOY 0x08 // BIT11 +#define COD_MAJOR_HEALTH 0x09 // BIT8 | BIT11 +#define COD_MAJOR_UNCLASSIFIED 0x1F // BIT8 | BIT9 | BIT10 | BIT11 | BIT12 + +/*************************** + * service class fields + * Note: All values are deduced by basing BIT_X to BIT_8, values as per + * BT-spec assigned-numbers. + ***************************/ +#define COD_SERVICE_LMTD_DISCOVER 0x0020 // BIT13 (eg. 13-8 = BIT5 = 0x0020) +#define COD_SERVICE_LE_AUDIO 0x0040 // BIT14 +#define COD_SERVICE_POSITIONING 0x0100 // BIT16 +#define COD_SERVICE_NETWORKING 0x0200 // BIT17 +#define COD_SERVICE_RENDERING 0x0400 // BIT18 +#define COD_SERVICE_CAPTURING 0x0800 // BIT19 +#define COD_SERVICE_OBJ_TRANSFER 0x1000 // BIT20 +#define COD_SERVICE_AUDIO 0x2000 // BIT21 +#define COD_SERVICE_TELEPHONY 0x4000 // BIT22 +#define COD_SERVICE_INFORMATION 0x8000 // BIT23 + +/*************************** + * minor device class field + * Note: LSB[1:0] (2 bits) is don't care for minor device class. + ***************************/ +/* Minor Device class field - Computer Major Class (COD_MAJOR_COMPUTER) */ +#define COD_MAJOR_COMPUTER_MINOR_UNCATEGORIZED 0x00 +#define COD_MAJOR_COMPUTER_MINOR_DESKTOP_WORKSTATION 0x04 // BIT2 +#define COD_MAJOR_COMPUTER_MINOR_SERVER_CLASS_COMPUTER 0x08 // BIT3 +#define COD_MAJOR_COMPUTER_MINOR_LAPTOP 0x0C // BIT2 | BIT3 +#define COD_MAJOR_COMPUTER_MINOR_HANDHELD_PC_PDA 0x10 // BIT4 +#define COD_MAJOR_COMPUTER_MINOR_PALM_SIZE_PC_PDA 0x14 // BIT2 | BIT4 +#define COD_MAJOR_COMPUTER_MINOR_WEARABLE_COMPUTER_WATCH_SIZE 0x18 // BIT3 | BIT4 +#define COD_MAJOR_COMPUTER_MINOR_TABLET 0x1C // BIT2 | BIT3 | BIT4 + +/* Minor Device class field - Phone Major Class (COD_MAJOR_PHONE) */ +#define COD_MAJOR_PHONE_MINOR_UNCATEGORIZED 0x00 +#define COD_MAJOR_PHONE_MINOR_CELLULAR 0x04 // BIT2 +#define COD_MAJOR_PHONE_MINOR_CORDLESS 0x08 // BIT3 +#define COD_MAJOR_PHONE_MINOR_SMARTPHONE 0x0C // BIT2 | BIT3 +#define COD_MAJOR_PHONE_MINOR_WIRED_MODEM_OR_VOICE_GATEWAY 0x10 // BIT4 +#define COD_MAJOR_PHONE_MINOR_COMMON_ISDN_ACCESS 0x14 // BIT2 | BIT4 + +/* + * Minor Device class field - + * LAN/Network Access Point Major Class (COD_MAJOR_LAN_NAP) + */ +#define COD_MAJOR_LAN_NAP_MINOR_FULLY_AVAILABLE 0x00 +#define COD_MAJOR_LAN_NAP_MINOR_1_TO_17_PER_UTILIZED 0x20 // BIT5 +#define COD_MAJOR_LAN_NAP_MINOR_17_TO_33_PER_UTILIZED 0x40 // BIT6 +#define COD_MAJOR_LAN_NAP_MINOR_33_TO_50_PER_UTILIZED 0x60 // BIT5 | BIT6 +#define COD_MAJOR_LAN_NAP_MINOR_50_TO_67_PER_UTILIZED 0x80 // BIT7 +#define COD_MAJOR_LAN_NAP_MINOR_67_TO_83_PER_UTILIZED 0xA0 // BIT5 | BIT7 +#define COD_MAJOR_LAN_NAP_MINOR_83_TO_99_PER_UTILIZED 0xC0 // BIT6 | BIT7 +#define COD_MAJOR_LAN_NAP_MINOR_NO_SERVICE_AVAILABLE 0xE0 // BIT5 | BIT6 | BIT7 + +/* Minor Device class field - Audio/Video Major Class (COD_MAJOR_AUDIO) */ +/* 0x00 is used as unclassified for all minor device classes */ +#define COD_MINOR_UNCATEGORIZED 0x00 +#define COD_MAJOR_AUDIO_MINOR_WEARABLE_HEADSET 0x04 // BIT2 +#define COD_MAJOR_AUDIO_MINOR_CONFM_HANDSFREE 0x08 // BIT3 +#define COD_MAJOR_AUDIO_MINOR_MICROPHONE 0x10 // BIT4 +#define COD_MAJOR_AUDIO_MINOR_LOUDSPEAKER 0x14 // BIT2 | BIT4 +#define COD_MAJOR_AUDIO_MINOR_HEADPHONES 0x18 // BIT3 | BIT4 +#define COD_MAJOR_AUDIO_MINOR_PORTABLE_AUDIO 0x1C // BIT2 | BIT3 | BIT4 +#define COD_MAJOR_AUDIO_MINOR_CAR_AUDIO 0x20 // BIT5 +#define COD_MAJOR_AUDIO_MINOR_SET_TOP_BOX 0x24 // BIT2 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_HIFI_AUDIO 0x28 // BIT3 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_VCR 0x2C // BIT2 | BIT3 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_VIDEO_CAMERA 0x30 // BIT4 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_CAMCORDER 0x34 // BIT2 | BIT4 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_VIDEO_MONITOR 0x38 // BIT3 | BIT4 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_VIDEO_DISPLAY_AND_LOUDSPEAKER 0x3C // BIT2 | + // BIT3 | BIT4 | BIT5 +#define COD_MAJOR_AUDIO_MINOR_VIDEO_CONFERENCING 0x40 // BIT6 +#define COD_MAJOR_AUDIO_MINOR_GAMING_OR_TOY 0x48 // BIT3 | BIT6 + +/* Minor Device class field - Peripheral Major Class (COD_MAJOR_PERIPHERAL) */ +/* Bits 6-7 independently specify mouse, keyboard, or combo mouse/keyboard */ +#define COD_MAJOR_PERIPH_MINOR_KEYBOARD 0x40 // BIT6 +#define COD_MAJOR_PERIPH_MINOR_POINTING 0x80 // BIT7 +#define COD_MAJOR_PERIPH_MINOR_KEYBOARD_AND_POINTING_DEVICE 0xC0 // BIT6 | BIT7 + +/* Bits 2-5 OR'd with selection from bits 6-7 */ +#define COD_MAJOR_PERIPH_MINOR_JOYSTICK 0x04 // BIT2 +#define COD_MAJOR_PERIPH_MINOR_GAMEPAD 0x08 // BIT3 +#define COD_MAJOR_PERIPH_MINOR_REMOTE_CONTROL 0x0C // BIT2 | BIT3 +#define COD_MAJOR_PERIPH_MINOR_SENSING_DEVICE 0x10 // BIT4 +#define COD_MAJOR_PERIPH_MINOR_DIGITIZING_TABLET 0x14 // BIT2 | BIT4 +#define COD_MAJOR_PERIPH_MINOR_CARD_READER 0x18 /* e.g. SIM card reader, BIT3 | BIT4 */ +#define COD_MAJOR_PERIPH_MINOR_DIGITAL_PEN 0x1C // Pen, BIT2 | BIT3 | BIT4 +#define COD_MAJOR_PERIPH_MINOR_HANDHELD_SCANNER 0x20 // e.g. Barcode, RFID, BIT5 +#define COD_MAJOR_PERIPH_MINOR_HANDHELD_GESTURAL_INP_DEVICE 0x24 + // e.g. "wand" form factor, BIT2 | BIT5 + +/* Minor Device class field - Imaging Major Class (COD_MAJOR_IMAGING) + * + * Bits 5-7 independently specify display, camera, scanner, or printer + * Note: Apart from the set bit, all other bits are don't care. + */ +#define COD_MAJOR_IMAGING_MINOR_DISPLAY 0x10 // BIT4 +#define COD_MAJOR_IMAGING_MINOR_CAMERA 0x20 // BIT5 +#define COD_MAJOR_IMAGING_MINOR_SCANNER 0x40 // BIT6 +#define COD_MAJOR_IMAGING_MINOR_PRINTER 0x80 // BIT7 + +/* Minor Device class field - Wearable Major Class (COD_MAJOR_WEARABLE) */ +#define COD_MAJOR_WEARABLE_MINOR_WRIST_WATCH 0x04 // BIT2 +#define COD_MAJOR_WEARABLE_MINOR_PAGER 0x08 // BIT3 +#define COD_MJAOR_WEARABLE_MINOR_JACKET 0x0C // BIT2 | BIT3 +#define COD_MAJOR_WEARABLE_MINOR_HELMET 0x10 // BIT4 +#define COD_MAJOR_WEARABLE_MINOR_GLASSES 0x14 // BIT2 | BIT4 +#define COD_MAJOR_WEARABLE_MINOR_PIN 0x18 + // e.g. Label pin, broach, badge BIT3 | BIT4 + +/* Minor Device class field - Toy Major Class (COD_MAJOR_TOY) */ +#define COD_MAJOR_TOY_MINOR_ROBOT 0x04 // BIT2 +#define COD_MAJOR_TOY_MINOR_VEHICLE 0x08 // BIT3 +#define COD_MAJOR_TOY_MINOR_DOLL_OR_ACTION_FIGURE 0x0C // BIT2 | BIT3 +#define COD_MAJOR_TOY_MINOR_CONTROLLER 0x10 // BIT4 +#define COD_MAJOR_TOY_MINOR_GAME 0x14 // BIT2 | BIT4 + +/* Minor Device class field - Health Major Class (COD_MAJOR_HEALTH) */ +#define COD_MAJOR_HEALTH_MINOR_BLOOD_MONITOR 0x04 // Blood pressure monitor, BIT2 +#define COD_MAJOR_HEALTH_MINOR_THERMOMETER 0x08 // BIT3 +#define COD_MAJOR_HEALTH_MINOR_WEIGHING_SCALE 0x0C // BIT2 | BIT3 +#define COD_MAJOR_HEALTH_MINOR_GLUCOSE_METER 0x10 // BIT4 +#define COD_MAJOR_HEALTH_MINOR_PULSE_OXIMETER 0x14 // BIT2 | BIT4 +#define COD_MAJOR_HEALTH_MINOR_HEART_PULSE_MONITOR 0x18 // BIT3 | BIT4 +#define COD_MAJOR_HEALTH_MINOR_HEALTH_DATA_DISPLAY 0x1C // BIT2 | BIT3 | BIT4 +#define COD_MAJO_HEALTH_MINOR_STEP_COUNTER 0x20 // BIT5 +#define COD_MAJOR_HEALTH_MINOR_BODY_COMPOSITION_ANALYZER 0x24 // BIT2 | BIT5 +#define COD_MAJOR_HEALTH_MINOR_PEAK_FLOW_MONITOR 0x28 // BIT3 | BIT5 +#define COD_MAJOR_HEALTH_MINOR_MEDICATION_MONITOR 0x2C // BIT2 | BIT3 | BIT5 +#define COD_MAJOR_HEALTH_MINOR_KNEE_PROSTHESIS 0x30 // BIT4 | BIT5 +#define COD_MAJOR_HEALTH_MINOR_ANKLE_PROSTHESIS 0x34 // BIT3 | BIT4 | BIT5 +#define COD_MAJOR_HEALTH_MINOR_GENERIC_HEALTH_MANAGER 0x38 // BIT2 | BIT3 | BIT4 | BIT5 +#define COD_MAJOR_HEALTH_MINOR_PERSONAL_MOBILITY_DEVICE 0x3C // BIT4 | BIT5 + /* 0x00 is used as unclassified for all minor device classes */ -#define BTM_COD_MINOR_UNCLASSIFIED 0x00 -#define BTM_COD_MINOR_WEARABLE_HEADSET 0x04 -#define BTM_COD_MINOR_CONFM_HANDSFREE 0x08 -#define BTM_COD_MINOR_CAR_AUDIO 0x20 -#define BTM_COD_MINOR_SET_TOP_BOX 0x24 +#define BTM_COD_MINOR_UNCLASSIFIED COD_MINOR_UNCATEGORIZED +#define BTM_COD_MINOR_WEARABLE_HEADSET COD_MAJOR_AUDIO_MINOR_WEARABLE_HEADSET +#define BTM_COD_MINOR_CONFM_HANDSFREE COD_MAJOR_AUDIO_MINOR_CONFM_HANDSFREE +#define BTM_COD_MINOR_CAR_AUDIO COD_MAJOR_AUDIO_MINOR_CAR_AUDIO +#define BTM_COD_MINOR_SET_TOP_BOX COD_MAJOR_AUDIO_MINOR_SET_TOP_BOX /* minor device class field for Peripheral Major Class */ /* Bits 6-7 independently specify mouse, keyboard, or combo mouse/keyboard */ -#define BTM_COD_MINOR_KEYBOARD 0x40 -#define BTM_COD_MINOR_POINTING 0x80 +#define BTM_COD_MINOR_KEYBOARD COD_MAJOR_PERIPH_MINOR_KEYBOARD +#define BTM_COD_MINOR_POINTING COD_MAJOR_PERIPH_MINOR_POINTING /* Bits 2-5 OR'd with selection from bits 6-7 */ /* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ -#define BTM_COD_MINOR_JOYSTICK 0x04 -#define BTM_COD_MINOR_GAMEPAD 0x08 -#define BTM_COD_MINOR_REMOTE_CONTROL 0x0C -#define BTM_COD_MINOR_DIGITIZING_TABLET 0x14 -#define BTM_COD_MINOR_CARD_READER 0x18 /* e.g. SIM card reader */ -#define BTM_COD_MINOR_DIGITAL_PAN 0x1C +#define BTM_COD_MINOR_JOYSTICK COD_MAJOR_PERIPH_MINOR_JOYSTICK +#define BTM_COD_MINOR_GAMEPAD COD_MAJOR_PERIPH_MINOR_GAMEPAD +#define BTM_COD_MINOR_REMOTE_CONTROL COD_MAJOR_PERIPH_MINOR_REMOTE_CONTROL +#define BTM_COD_MINOR_DIGITIZING_TABLET COD_MAJOR_PERIPH_MINOR_DIGITIZING_TABLET +#define BTM_COD_MINOR_CARD_READER COD_MAJOR_PERIPH_MINOR_CARD_READER +#define BTM_COD_MINOR_DIGITAL_PAN COD_MAJOR_PERIPH_MINOR_DIGITAL_PEN /* minor device class field for Imaging Major Class */ /* Bits 5-7 independently specify display, camera, scanner, or printer */ -#define BTM_COD_MINOR_DISPLAY 0x10 +#define BTM_COD_MINOR_DISPLAY COD_MAJOR_IMAGING_MINOR_DISPLAY /* Bits 2-3 Reserved */ /* #define BTM_COD_MINOR_UNCLASSIFIED 0x00 */ /* minor device class field for Wearable Major Class */ /* Bits 2-7 meaningful */ -#define BTM_COD_MINOR_WRIST_WATCH 0x04 -#define BTM_COD_MINOR_GLASSES 0x14 +#define BTM_COD_MINOR_WRIST_WATCH COD_MAJOR_WEARABLE_MINOR_WRIST_WATCH +#define BTM_COD_MINOR_GLASSES COD_MAJOR_WEARABLE_MINOR_GLASSES /* minor device class field for Health Major Class */ /* Bits 2-7 meaningful */ -#define BTM_COD_MINOR_BLOOD_MONITOR 0x04 -#define BTM_COD_MINOR_THERMOMETER 0x08 -#define BTM_COD_MINOR_WEIGHING_SCALE 0x0C -#define BTM_COD_MINOR_GLUCOSE_METER 0x10 -#define BTM_COD_MINOR_PULSE_OXIMETER 0x14 -#define BTM_COD_MINOR_HEART_PULSE_MONITOR 0x18 -#define BTM_COD_MINOR_STEP_COUNTER 0x20 +#define BTM_COD_MINOR_BLOOD_MONITOR COD_MAJOR_HEALTH_MINOR_BLOOD_MONITOR +#define BTM_COD_MINOR_THERMOMETER COD_MAJOR_HEALTH_MINOR_THERMOMETER +#define BTM_COD_MINOR_WEIGHING_SCALE COD_MAJOR_HEALTH_MINOR_WEIGHING_SCALE +#define BTM_COD_MINOR_GLUCOSE_METER COD_MAJOR_HEALTH_MINOR_GLUCOSE_METER +#define BTM_COD_MINOR_PULSE_OXIMETER COD_MAJOR_HEALTH_MINOR_PULSE_OXIMETER +#define BTM_COD_MINOR_HEART_PULSE_MONITOR COD_MAJOR_HEALTH_MINOR_HEART_PULSE_MONITOR +#define BTM_COD_MINOR_STEP_COUNTER COD_MAJO_HEALTH_MINOR_STEP_COUNTER /*************************** * major device class field ***************************/ -#define BTM_COD_MAJOR_COMPUTER 0x01 -#define BTM_COD_MAJOR_PHONE 0x02 -#define BTM_COD_MAJOR_AUDIO 0x04 -#define BTM_COD_MAJOR_PERIPHERAL 0x05 -#define BTM_COD_MAJOR_IMAGING 0x06 -#define BTM_COD_MAJOR_WEARABLE 0x07 -#define BTM_COD_MAJOR_HEALTH 0x09 -#define BTM_COD_MAJOR_UNCLASSIFIED 0x1F +#define BTM_COD_MAJOR_COMPUTER COD_MAJOR_COMPUTER +#define BTM_COD_MAJOR_PHONE COD_MAJOR_PHONE +#define BTM_COD_MAJOR_AUDIO COD_MAJOR_AUDIO +#define BTM_COD_MAJOR_PERIPHERAL COD_MAJOR_PERIPHERAL +#define BTM_COD_MAJOR_IMAGING COD_MAJOR_IMAGING +#define BTM_COD_MAJOR_WEARABLE COD_MAJOR_WEARABLE +#define BTM_COD_MAJOR_HEALTH COD_MAJOR_HEALTH +#define BTM_COD_MAJOR_UNCLASSIFIED COD_MAJOR_UNCLASSIFIED /*************************** * service class fields ***************************/ -#define BTM_COD_SERVICE_LMTD_DISCOVER 0x0020 -#define BTM_COD_SERVICE_LE_AUDIO 0x0040 -#define BTM_COD_SERVICE_POSITIONING 0x0100 -#define BTM_COD_SERVICE_NETWORKING 0x0200 -#define BTM_COD_SERVICE_RENDERING 0x0400 -#define BTM_COD_SERVICE_CAPTURING 0x0800 -#define BTM_COD_SERVICE_OBJ_TRANSFER 0x1000 -#define BTM_COD_SERVICE_AUDIO 0x2000 -#define BTM_COD_SERVICE_TELEPHONY 0x4000 -#define BTM_COD_SERVICE_INFORMATION 0x8000 +#define BTM_COD_SERVICE_LMTD_DISCOVER COD_SERVICE_LMTD_DISCOVER +#define BTM_COD_SERVICE_LE_AUDIO COD_SERVICE_LE_AUDIO +#define BTM_COD_SERVICE_POSITIONING COD_SERVICE_POSITIONING +#define BTM_COD_SERVICE_NETWORKING COD_SERVICE_NETWORKING +#define BTM_COD_SERVICE_RENDERING COD_SERVICE_RENDERING +#define BTM_COD_SERVICE_CAPTURING COD_SERVICE_CAPTURING +#define BTM_COD_SERVICE_OBJ_TRANSFER COD_SERVICE_OBJ_TRANSFER +#define BTM_COD_SERVICE_AUDIO COD_SERVICE_AUDIO +#define BTM_COD_SERVICE_TELEPHONY COD_SERVICE_TELEPHONY +#define BTM_COD_SERVICE_INFORMATION COD_SERVICE_INFORMATION /* the COD masks */ #define BTM_COD_MINOR_CLASS_MASK 0xFC diff --git a/system/stack/include/btm_ble_api_types.h b/system/stack/include/btm_ble_api_types.h index 9a1679768b..d89028d27f 100644 --- a/system/stack/include/btm_ble_api_types.h +++ b/system/stack/include/btm_ble_api_types.h @@ -29,6 +29,7 @@ #include "stack/include/bt_octets.h" #include "stack/include/btm_status.h" #include "stack/include/hci_error_code.h" +#include "stack/include/ble_appearance.h" #include "types/ble_address_with_type.h" #include "types/raw_address.h" @@ -216,60 +217,70 @@ typedef uint8_t BLE_SIGNATURE[BTM_BLE_AUTH_SIGN_LEN]; /* Device address */ #endif /* Appearance Values Reported with BTM_BLE_AD_TYPE_APPEARANCE */ -#define BTM_BLE_APPEARANCE_UKNOWN 0x0000 -#define BTM_BLE_APPEARANCE_GENERIC_PHONE 0x0040 -#define BTM_BLE_APPEARANCE_GENERIC_COMPUTER 0x0080 -#define BTM_BLE_APPEARANCE_GENERIC_WATCH 0x00C0 -#define BTM_BLE_APPEARANCE_SPORTS_WATCH 0x00C1 -#define BTM_BLE_APPEARANCE_GENERIC_CLOCK 0x0100 -#define BTM_BLE_APPEARANCE_GENERIC_DISPLAY 0x0140 -#define BTM_BLE_APPEARANCE_GENERIC_REMOTE 0x0180 -#define BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES 0x01C0 -#define BTM_BLE_APPEARANCE_GENERIC_TAG 0x0200 -#define BTM_BLE_APPEARANCE_GENERIC_KEYRING 0x0240 -#define BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER 0x0280 -#define BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER 0x02C0 -#define BTM_BLE_APPEARANCE_GENERIC_THERMOMETER 0x0300 -#define BTM_BLE_APPEARANCE_THERMOMETER_EAR 0x0301 -#define BTM_BLE_APPEARANCE_GENERIC_HEART_RATE 0x0340 -#define BTM_BLE_APPEARANCE_HEART_RATE_BELT 0x0341 -#define BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE 0x0380 -#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM 0x0381 -#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST 0x0382 -#define BTM_BLE_APPEARANCE_GENERIC_HID 0x03C0 -#define BTM_BLE_APPEARANCE_HID_KEYBOARD 0x03C1 -#define BTM_BLE_APPEARANCE_HID_MOUSE 0x03C2 -#define BTM_BLE_APPEARANCE_HID_JOYSTICK 0x03C3 -#define BTM_BLE_APPEARANCE_HID_GAMEPAD 0x03C4 -#define BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET 0x03C5 -#define BTM_BLE_APPEARANCE_HID_CARD_READER 0x03C6 -#define BTM_BLE_APPEARANCE_HID_DIGITAL_PEN 0x03C7 -#define BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER 0x03C8 -#define BTM_BLE_APPEARANCE_GENERIC_GLUCOSE 0x0400 -#define BTM_BLE_APPEARANCE_GENERIC_WALKING 0x0440 -#define BTM_BLE_APPEARANCE_WALKING_IN_SHOE 0x0441 -#define BTM_BLE_APPEARANCE_WALKING_ON_SHOE 0x0442 -#define BTM_BLE_APPEARANCE_WALKING_ON_HIP 0x0443 -#define BTM_BLE_APPEARANCE_GENERIC_CYCLING 0x0480 -#define BTM_BLE_APPEARANCE_CYCLING_COMPUTER 0x0481 -#define BTM_BLE_APPEARANCE_CYCLING_SPEED 0x0482 -#define BTM_BLE_APPEARANCE_CYCLING_CADENCE 0x0483 -#define BTM_BLE_APPEARANCE_CYCLING_POWER 0x0484 -#define BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE 0x0485 -#define BTM_BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE 0x0940 -#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD 0x0941 -#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET 0x0942 -#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES 0x0943 -#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND 0x0944 -#define BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER 0x0C40 -#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP 0x0C41 -#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST 0x0C42 -#define BTM_BLE_APPEARANCE_GENERIC_WEIGHT 0x0C80 -#define BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS 0x1440 -#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION 0x1441 -#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV 0x1442 -#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD 0x1443 -#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV 0x1444 +#define BTM_BLE_APPEARANCE_UNKNOWN BLE_APPEARANCE_UNKNOWN +#define BTM_BLE_APPEARANCE_GENERIC_PHONE BLE_APPEARANCE_GENERIC_PHONE +#define BTM_BLE_APPEARANCE_GENERIC_COMPUTER BLE_APPEARANCE_GENERIC_COMPUTER +#define BTM_BLE_APPEARANCE_GENERIC_WATCH BLE_APPEARANCE_GENERIC_WATCH +#define BTM_BLE_APPEARANCE_SPORTS_WATCH BLE_APPEARANCE_SPORTS_WATCH +#define BTM_BLE_APPEARANCE_GENERIC_CLOCK BLE_APPEARANCE_GENERIC_CLOCK +#define BTM_BLE_APPEARANCE_GENERIC_DISPLAY BLE_APPEARANCE_GENERIC_DISPLAY +#define BTM_BLE_APPEARANCE_GENERIC_REMOTE BLE_APPEARANCE_GENERIC_REMOTE +#define BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES BLE_APPEARANCE_GENERIC_EYEGLASSES +#define BTM_BLE_APPEARANCE_GENERIC_TAG BLE_APPEARANCE_GENERIC_TAG +#define BTM_BLE_APPEARANCE_GENERIC_KEYRING BLE_APPEARANCE_GENERIC_KEYRING +#define BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER BLE_APPEARANCE_GENERIC_MEDIA_PLAYER +#define BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER BLE_APPEARANCE_GENERIC_BARCODE_SCANNER +#define BTM_BLE_APPEARANCE_GENERIC_THERMOMETER BLE_APPEARANCE_GENERIC_THERMOMETER +#define BTM_BLE_APPEARANCE_THERMOMETER_EAR BLE_APPEARANCE_THERMOMETER_EAR +#define BTM_BLE_APPEARANCE_GENERIC_HEART_RATE BLE_APPEARANCE_GENERIC_HEART_RATE +#define BTM_BLE_APPEARANCE_HEART_RATE_BELT BLE_APPEARANCE_HEART_RATE_BELT +#define BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE +#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM BLE_APPEARANCE_BLOOD_PRESSURE_ARM +#define BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST BLE_APPEARANCE_BLOOD_PRESSURE_WRIST +#define BTM_BLE_APPEARANCE_GENERIC_HID BLE_APPEARANCE_GENERIC_HID +#define BTM_BLE_APPEARANCE_HID_KEYBOARD BLE_APPEARANCE_HID_KEYBOARD +#define BTM_BLE_APPEARANCE_HID_MOUSE BLE_APPEARANCE_HID_MOUSE +#define BTM_BLE_APPEARANCE_HID_JOYSTICK BLE_APPEARANCE_HID_JOYSTICK +#define BTM_BLE_APPEARANCE_HID_GAMEPAD BLE_APPEARANCE_HID_GAMEPAD +#define BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET BLE_APPEARANCE_HID_DIGITIZER_TABLET +#define BTM_BLE_APPEARANCE_HID_CARD_READER BLE_APPEARANCE_HID_CARD_READER +#define BTM_BLE_APPEARANCE_HID_DIGITAL_PEN BLE_APPEARANCE_HID_DIGITAL_PEN +#define BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER BLE_APPEARANCE_HID_BARCODE_SCANNER +#define BTM_BLE_APPEARANCE_GENERIC_GLUCOSE BLE_APPEARANCE_GENERIC_GLUCOSE +#define BTM_BLE_APPEARANCE_GENERIC_WALKING BLE_APPEARANCE_GENERIC_WALKING +#define BTM_BLE_APPEARANCE_WALKING_IN_SHOE BLE_APPEARANCE_WALKING_IN_SHOE +#define BTM_BLE_APPEARANCE_WALKING_ON_SHOE BLE_APPEARANCE_WALKING_ON_SHOE +#define BTM_BLE_APPEARANCE_WALKING_ON_HIP BLE_APPEARANCE_WALKING_ON_HIP +#define BTM_BLE_APPEARANCE_GENERIC_CYCLING BLE_APPEARANCE_GENERIC_CYCLING +#define BTM_BLE_APPEARANCE_CYCLING_COMPUTER BLE_APPEARANCE_CYCLING_COMPUTER +#define BTM_BLE_APPEARANCE_CYCLING_SPEED BLE_APPEARANCE_CYCLING_SPEED +#define BTM_BLE_APPEARANCE_CYCLING_CADENCE BLE_APPEARANCE_CYCLING_CADENCE +#define BTM_BLE_APPEARANCE_CYCLING_POWER BLE_APPEARANCE_CYCLING_POWER +#define BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE BLE_APPEARANCE_CYCLING_SPEED_CADENCE +#define BTM_BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE \ + BLE_APPEARANCE_GENERIC_WEARABLE_AUDIO_DEVICE +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD \ + BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_EARBUD +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET \ + BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADSET +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES \ + BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_HEADPHONES +#define BTM_BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND \ + BLE_APPEARANCE_WEARABLE_AUDIO_DEVICE_NECK_BAND +#define BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER BLE_APPEARANCE_GENERIC_PULSE_OXIMETER +#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP +#define BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST BLE_APPEARANCE_PULSE_OXIMETER_WRIST +#define BTM_BLE_APPEARANCE_GENERIC_WEIGHT BLE_APPEARANCE_GENERIC_WEIGHT +#define BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS \ + BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION \ + BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV \ + BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD \ + BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD +#define BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV \ + BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV /* Structure returned with Rand/Encrypt complete callback */ typedef struct { diff --git a/system/stack/include/btm_inq.h b/system/stack/include/btm_inq.h index d0ae28b01b..c49912180c 100644 --- a/system/stack/include/btm_inq.h +++ b/system/stack/include/btm_inq.h @@ -158,3 +158,6 @@ void BTM_EnableInterlacedInquiryScan(); * ******************************************************************************/ void BTM_EnableInterlacedPageScan(); + +void btm_inq_db_reset(void); +void btm_clr_inq_result_flt(void); diff --git a/system/stack/include/btm_iot_config.h b/system/stack/include/btm_iot_config.h new file mode 100644 index 0000000000..19b575ffdf --- /dev/null +++ b/system/stack/include/btm_iot_config.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#pragma once + +#include "stack/acl/acl.h" + +void btm_iot_save_remote_properties(tACL_CONN* p_acl_cb); +void btm_iot_save_remote_versions(tACL_CONN* p_acl_cb); diff --git a/system/stack/include/sco_hci_link_interface.h b/system/stack/include/sco_hci_link_interface.h index 5aa2cd128b..9de4eea367 100644 --- a/system/stack/include/sco_hci_link_interface.h +++ b/system/stack/include/sco_hci_link_interface.h @@ -33,8 +33,5 @@ void btm_sco_connection_failed(tHCI_STATUS hci_status, const RawAddress& bda, ui tBTM_ESCO_DATA* p_esco_data); void btm_sco_create_command_status_failed(tHCI_STATUS hci_status); -bool btm_sco_removed(uint16_t hci_handle, tHCI_REASON reason); - -void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason); void btm_sco_on_esco_connect_request(const RawAddress&, const bluetooth::hci::ClassOfDevice&); void btm_sco_on_sco_connect_request(const RawAddress&, const bluetooth::hci::ClassOfDevice&); diff --git a/system/stack/include/smp_status.h b/system/stack/include/smp_status.h index 86670a5161..1d222ab419 100644 --- a/system/stack/include/smp_status.h +++ b/system/stack/include/smp_status.h @@ -41,25 +41,27 @@ typedef enum : uint8_t { SMP_NUMERIC_COMPAR_FAIL = 0x0C, SMP_BR_PARING_IN_PROGR = 0x0D, SMP_XTRANS_DERIVE_NOT_ALLOW = 0x0E, - SMP_MAX_FAIL_RSN_PER_SPEC = SMP_XTRANS_DERIVE_NOT_ALLOW, + SMP_KEY_REJECTED = 0x0F, + SMP_BUSY = 0x10, /*device is not ready to perform a pairing procedure*/ + SMP_MAX_FAIL_RSN_PER_SPEC = SMP_BUSY, /* self defined error code */ - SMP_PAIR_INTERNAL_ERR = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x01), /* 0x0F */ + SMP_PAIR_INTERNAL_ERR = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x01), /* 0x11 */ /* Unknown IO capability, unable to decide association model */ - SMP_UNKNOWN_IO_CAP = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x02), /* 0x10 */ + SMP_UNKNOWN_IO_CAP = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x02), /* 0x12 */ - SMP_BUSY = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x05), /* 0x13 */ - SMP_ENC_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x06), /* 0x14 */ - SMP_STARTED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x07), /* 0x15 */ - SMP_RSP_TIMEOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x08), /* 0x16 */ + SMP_IMPL_BUSY = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x05), /* 0x15 */ + SMP_ENC_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x06), /* 0x16 */ + SMP_STARTED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x07), /* 0x17 */ + SMP_RSP_TIMEOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x08), /* 0x18 */ /* Unspecified failure reason */ - SMP_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0A), /* 0x18 */ + SMP_FAIL = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0A), /* 0x1A */ - SMP_CONN_TOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0B), /* 0x19 */ - SMP_SIRK_DEVICE_INVALID = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0C), /* 0x1a */ - SMP_USER_CANCELLED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0D), /* 0x1b */ + SMP_CONN_TOUT = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0B), /* 0x1B */ + SMP_SIRK_DEVICE_INVALID = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0C), /* 0x1C */ + SMP_USER_CANCELLED = (SMP_MAX_FAIL_RSN_PER_SPEC + 0x0D), /* 0x1D */ } tSMP_STATUS; inline std::string smp_status_text(const tSMP_STATUS& status) { @@ -79,9 +81,11 @@ inline std::string smp_status_text(const tSMP_STATUS& status) { CASE_RETURN_TEXT(SMP_NUMERIC_COMPAR_FAIL); CASE_RETURN_TEXT(SMP_BR_PARING_IN_PROGR); CASE_RETURN_TEXT(SMP_XTRANS_DERIVE_NOT_ALLOW); + CASE_RETURN_TEXT(SMP_KEY_REJECTED); + CASE_RETURN_TEXT(SMP_BUSY); CASE_RETURN_TEXT(SMP_PAIR_INTERNAL_ERR); CASE_RETURN_TEXT(SMP_UNKNOWN_IO_CAP); - CASE_RETURN_TEXT(SMP_BUSY); + CASE_RETURN_TEXT(SMP_IMPL_BUSY); CASE_RETURN_TEXT(SMP_ENC_FAIL); CASE_RETURN_TEXT(SMP_STARTED); CASE_RETURN_TEXT(SMP_RSP_TIMEOUT); diff --git a/system/stack/l2cap/l2c_link.cc b/system/stack/l2cap/l2c_link.cc index a202c36549..9a78588840 100644 --- a/system/stack/l2cap/l2c_link.cc +++ b/system/stack/l2cap/l2c_link.cc @@ -34,7 +34,10 @@ #include "internal_include/bt_target.h" #include "osi/include/allocator.h" #include "stack/btm/btm_int_types.h" +#include "stack/btm/btm_sco.h" +#include "stack/btm/btm_sec.h" #include "stack/include/acl_api.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/bt_hdr.h" #include "stack/include/bt_types.h" #include "stack/include/btm_status.h" @@ -51,14 +54,6 @@ using namespace bluetooth; extern tBTM_CB btm_cb; -bool BTM_ReadPowerMode(const RawAddress& remote_bda, tBTM_PM_MODE* p_mode); -tBTM_STATUS btm_sec_disconnect(uint16_t handle, tHCI_STATUS reason, std::string); -void btm_acl_created(const RawAddress& bda, uint16_t hci_handle, uint8_t link_role, - tBT_TRANSPORT transport); -void btm_acl_removed(uint16_t handle); -void btm_ble_decrement_link_topology_mask(uint8_t link_role); -void btm_sco_acl_removed(const RawAddress* bda); - static void l2c_link_send_to_lower(tL2C_LCB* p_lcb, BT_HDR* p_buf, tL2C_TX_COMPLETE_CB_INFO* p_cbi); static BT_HDR* l2cu_get_next_buffer_to_send(tL2C_LCB* p_lcb, tL2C_TX_COMPLETE_CB_INFO* p_cbi); diff --git a/system/stack/rfcomm/port_rfc.cc b/system/stack/rfcomm/port_rfc.cc index 14a9cb5bf0..4dc7590d96 100644 --- a/system/stack/rfcomm/port_rfc.cc +++ b/system/stack/rfcomm/port_rfc.cc @@ -991,7 +991,7 @@ void port_rfc_closed(tPORT* p_port, uint8_t res) { log::warn("port_rfc_closed in OPENING state ignored"); rfc_port_timer_stop(p_port); - p_port->rfc.sm_cb.state = RFC_STATE_CLOSED; + rfc_set_state(RFC_STATE_CLOSED, p_port); if (p_mcb) { p_mcb->port_handles[p_port->dlci] = 0; @@ -1049,8 +1049,8 @@ void port_rfc_closed(tPORT* p_port, uint8_t res) { p_port->p_mgmt_callback(static_cast<tPORT_RESULT>(res2), p_port->handle); } - p_port->rfc.sm_cb.state = RFC_STATE_CLOSED; - + rfc_set_state(RFC_STATE_CLOSED, p_port); + p_port->rfc.sm_cb.close_reason = static_cast<tPORT_RESULT>(res); log::info( "RFCOMM connection closed, port_handle={}, state={}, reason={}[{}], " "UUID=0x{:x}, bd_addr={}, is_server={}", diff --git a/system/stack/rfcomm/rfc_int.h b/system/stack/rfcomm/rfc_int.h index b773ee2dc2..95ca7c6d07 100644 --- a/system/stack/rfcomm/rfc_int.h +++ b/system/stack/rfcomm/rfc_int.h @@ -237,6 +237,7 @@ void rfc_sec_check_complete(RawAddress bd_addr, tBT_TRANSPORT transport, void* p void rfc_inc_credit(tPORT* p_port, uint8_t credit); void rfc_dec_credit(tPORT* p_port); void rfc_check_send_cmd(tRFC_MCB* p_mcb, BT_HDR* p_buf); +void rfc_set_state(tRFC_PORT_STATE state, tPORT* p_port); /* * Functions provided by the rfc_ts_frames.cc diff --git a/system/stack/rfcomm/rfc_port_fsm.cc b/system/stack/rfcomm/rfc_port_fsm.cc index be47e22617..dec82b672b 100644 --- a/system/stack/rfcomm/rfc_port_fsm.cc +++ b/system/stack/rfcomm/rfc_port_fsm.cc @@ -74,7 +74,9 @@ static void rfc_set_port_settings(PortSettings* port_settings, MX_FRAME* p_frame * ******************************************************************************/ void rfc_port_sm_execute(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) { - log::assert_that(p_port != nullptr, "NULL port event {}", event); + log::assert_that(p_port != nullptr, "NULL port, event {}", event); + + p_port->rfc.sm_cb.last_event = event; // logs for state RFC_STATE_OPENED handled in rfc_port_sm_opened() if (p_port->rfc.sm_cb.state != RFC_STATE_OPENED) { @@ -122,7 +124,7 @@ void rfc_port_sm_execute(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) { void rfc_port_sm_state_closed(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) { switch (event) { case RFC_PORT_EVENT_OPEN: - p_port->rfc.sm_cb.state = RFC_STATE_ORIG_WAIT_SEC_CHECK; + rfc_set_state(RFC_STATE_ORIG_WAIT_SEC_CHECK, p_port); btm_sec_mx_access_request(p_port->rfc.p_mcb->bd_addr, true, p_port->sec_mask, &rfc_sec_check_complete, p_port); return; @@ -143,7 +145,7 @@ void rfc_port_sm_state_closed(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data rfc_timer_stop(p_port->rfc.p_mcb); /* Open will be continued after security checks are passed */ - p_port->rfc.sm_cb.state = RFC_STATE_TERM_WAIT_SEC_CHECK; + rfc_set_state(RFC_STATE_TERM_WAIT_SEC_CHECK, p_port); btm_sec_mx_access_request(p_port->rfc.p_mcb->bd_addr, false, p_port->sec_mask, &rfc_sec_check_complete, p_port); return; @@ -199,7 +201,7 @@ void rfc_port_sm_sabme_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_dat rfc_port_timer_start(p_port, RFC_DISC_TIMEOUT); rfc_send_disc(p_port->rfc.p_mcb, p_port->dlci); p_port->rfc.expected_rsp = 0; - p_port->rfc.sm_cb.state = RFC_STATE_DISC_WAIT_UA; + rfc_set_state(RFC_STATE_DISC_WAIT_UA, p_port); return; case RFC_PORT_EVENT_CLEAR: @@ -213,7 +215,7 @@ void rfc_port_sm_sabme_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_dat case RFC_PORT_EVENT_UA: rfc_port_timer_stop(p_port); - p_port->rfc.sm_cb.state = RFC_STATE_OPENED; + rfc_set_state(RFC_STATE_OPENED, p_port); if (uuid_logging_acceptlist.find(p_port->uuid) != uuid_logging_acceptlist.end()) { // Find Channel Control Block by Channel ID @@ -267,7 +269,7 @@ void rfc_port_sm_sabme_wait_ua(tPORT* p_port, tRFC_PORT_EVENT event, void* p_dat return; case RFC_PORT_EVENT_TIMEOUT: - p_port->rfc.sm_cb.state = RFC_STATE_CLOSED; + rfc_set_state(RFC_STATE_CLOSED, p_port); PORT_DlcEstablishCnf(p_port->rfc.p_mcb, p_port->dlci, p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR); return; @@ -334,7 +336,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void* case RFC_PORT_EVENT_DISC: btm_sec_abort_access_req(p_port->rfc.p_mcb->bd_addr); - p_port->rfc.sm_cb.state = RFC_STATE_CLOSED; + rfc_set_state(RFC_STATE_CLOSED, p_port); rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci); PORT_DlcReleaseInd(p_port->rfc.p_mcb, p_port->dlci); @@ -351,7 +353,7 @@ void rfc_port_sm_term_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void* } } else { rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci); - p_port->rfc.sm_cb.state = RFC_STATE_OPENED; + rfc_set_state(RFC_STATE_OPENED, p_port); if (uuid_logging_acceptlist.find(p_port->uuid) != uuid_logging_acceptlist.end()) { // Find Channel Control Block by Channel ID @@ -409,7 +411,7 @@ void rfc_port_sm_orig_wait_sec_check(tPORT* p_port, tRFC_PORT_EVENT event, void* rfcomm_port_state_text(p_port->rfc.sm_cb.state), p_port->handle); rfc_send_sabme(p_port->rfc.p_mcb, p_port->dlci); rfc_port_timer_start(p_port, RFC_PORT_T1_TIMEOUT); - p_port->rfc.sm_cb.state = RFC_STATE_SABME_WAIT_UA; + rfc_set_state(RFC_STATE_SABME_WAIT_UA, p_port); } return; @@ -462,7 +464,7 @@ void rfc_port_sm_opened(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) { rfc_port_timer_start(p_port, RFC_DISC_TIMEOUT); rfc_send_disc(p_port->rfc.p_mcb, p_port->dlci); p_port->rfc.expected_rsp = 0; - p_port->rfc.sm_cb.state = RFC_STATE_DISC_WAIT_UA; + rfc_set_state(RFC_STATE_DISC_WAIT_UA, p_port); return; case RFC_PORT_EVENT_CLEAR: @@ -510,7 +512,7 @@ void rfc_port_sm_opened(tPORT* p_port, tRFC_PORT_EVENT event, void* p_data) { case RFC_PORT_EVENT_DISC: log::info("RFC_PORT_EVENT_DISC bd_addr:{} port_handle:{} dlci:{} scn:{}", p_port->bd_addr, p_port->handle, p_port->dlci, p_port->scn); - p_port->rfc.sm_cb.state = RFC_STATE_CLOSED; + rfc_set_state(RFC_STATE_CLOSED, p_port); rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci); if (!fixed_queue_is_empty(p_port->rx.queue)) { /* give a chance to upper stack to close port properly */ diff --git a/system/stack/rfcomm/rfc_port_if.cc b/system/stack/rfcomm/rfc_port_if.cc index af6906fbb5..4fdd46059d 100644 --- a/system/stack/rfcomm/rfc_port_if.cc +++ b/system/stack/rfcomm/rfc_port_if.cc @@ -333,7 +333,12 @@ void RFCOMM_LineStatusReq(tRFC_MCB* p_mcb, uint8_t dlci, uint8_t status) { * ******************************************************************************/ void RFCOMM_DlcReleaseReq(tRFC_MCB* p_mcb, uint8_t dlci) { - rfc_port_sm_execute(port_find_mcb_dlci_port(p_mcb, dlci), RFC_PORT_EVENT_CLOSE, nullptr); + tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci); + if (p_port == nullptr) { + log::warn("Unable to find DLCI port dlci:{}", dlci); + return; + } + rfc_port_sm_execute(p_port, RFC_PORT_EVENT_CLOSE, nullptr); } /******************************************************************************* diff --git a/system/stack/rfcomm/rfc_utils.cc b/system/stack/rfcomm/rfc_utils.cc index 87beaaf566..d9389eda07 100644 --- a/system/stack/rfcomm/rfc_utils.cc +++ b/system/stack/rfcomm/rfc_utils.cc @@ -28,6 +28,7 @@ #include <cstdint> +#include "common/time_util.h" #include "internal_include/bt_target.h" #include "osi/include/allocator.h" #include "stack/include/bt_hdr.h" @@ -341,7 +342,7 @@ void rfc_sec_check_complete(RawAddress /* bd_addr */, tBT_TRANSPORT /* transport void rfc_port_closed(tPORT* p_port) { tRFC_MCB* p_mcb = p_port->rfc.p_mcb; rfc_port_timer_stop(p_port); - p_port->rfc.sm_cb.state = RFC_STATE_CLOSED; + rfc_set_state(RFC_STATE_CLOSED, p_port); /* If multiplexer channel was up mark it as down */ if (p_mcb) { @@ -434,3 +435,28 @@ void rfc_check_send_cmd(tRFC_MCB* p_mcb, BT_HDR* p_buf) { } } } + +/******************************************************************************* + * + * Function rfc_set_state + * + * Description This function updates the RfcommPortSm control block with a + * new state + * + ******************************************************************************/ +void rfc_set_state(tRFC_PORT_STATE state, tPORT* p_port) { + // nothing is going to change if the state doesn't change + if (p_port->rfc.sm_cb.state == state) { + log::debug("Already at state {}, no need to update", rfcomm_port_state_text(state)); + return; + } + + p_port->rfc.sm_cb.state_prior = p_port->rfc.sm_cb.state; + p_port->rfc.sm_cb.state = state; + + if (state == RFC_STATE_OPENED) { + p_port->rfc.sm_cb.open_timestamp = bluetooth::common::time_gettimeofday_us(); + } else if (state == RFC_STATE_CLOSED && p_port->rfc.sm_cb.open_timestamp != 0) { + p_port->rfc.sm_cb.close_timestamp = bluetooth::common::time_gettimeofday_us(); + } +} diff --git a/system/stack/rnr/remote_name_request.cc b/system/stack/rnr/remote_name_request.cc index bd0fcbce44..989a77bda0 100644 --- a/system/stack/rnr/remote_name_request.cc +++ b/system/stack/rnr/remote_name_request.cc @@ -25,15 +25,13 @@ #include "stack/btm/btm_int_types.h" #include "stack/btm/btm_sec.h" #include "stack/btm/security_device_record.h" +#include "stack/include/ble_hci_link_interface.h" #include "stack/include/btm_client_interface.h" using namespace bluetooth; extern tBTM_CB btm_cb; -tBTM_STATUS btm_ble_read_remote_name(const RawAddress& remote_bda, tBTM_NAME_CMPL_CB* p_cb); -bool btm_ble_cancel_remote_name(const RawAddress& remote_bda); - bool BTM_SecAddRmtNameNotifyCallback(tBTM_RMT_NAME_CALLBACK* p_callback) { int i; diff --git a/system/stack/smp/smp_act.cc b/system/stack/smp/smp_act.cc index c625217c7c..fd77fabbed 100644 --- a/system/stack/smp/smp_act.cc +++ b/system/stack/smp/smp_act.cc @@ -23,6 +23,7 @@ #include <cstring> +#include "bta/dm/bta_dm_sec_int.h" #include "btif/include/btif_common.h" #include "btif/include/core_callbacks.h" #include "btif/include/stack_manager_t.h" @@ -34,6 +35,7 @@ #include "stack/btm/btm_ble_sec.h" #include "stack/btm/btm_dev.h" #include "stack/btm/btm_sec.h" +#include "stack/include/acl_api.h" #include "stack/include/bt_octets.h" #include "stack/include/bt_types.h" #include "stack/include/btm_client_interface.h" @@ -548,6 +550,21 @@ void smp_proc_pair_cmd(tSMP_CB* p_cb, tSMP_INT_DATA* p_data) { /* erase all keys if it is peripheral proc pairing req */ if (p_dev_rec && (p_cb->role == HCI_ROLE_PERIPHERAL)) { + if (com::android::bluetooth::flags::key_missing_ble_peripheral()) { + tBTM_SEC_DEV_REC* p_rec = btm_find_dev(p_cb->pairing_bda); + /* If we bonded, but not encrypted, it's a key missing - disconnect. + * If we are bonded, its key upgrade and ok to continue. + * If we are not bonded, its new device pairing and ok. + */ + if (p_rec != NULL && p_rec->sec_rec.is_le_link_key_known() && + !p_rec->sec_rec.is_le_device_encrypted()) { + log::warn("bonded unencrypted central wants to pair {}", p_cb->pairing_bda); + bta_dm_remote_key_missing(p_cb->pairing_bda); + acl_disconnect_from_handle(p_rec->ble_hci_handle, HCI_ERR_AUTH_FAILURE, + "bonded unencrypted central wants to pair"); + return; + } + } btm_sec_clear_ble_keys(p_dev_rec); } diff --git a/system/stack/smp/smp_api.cc b/system/stack/smp/smp_api.cc index 0d66b0a064..a46f113aec 100644 --- a/system/stack/smp/smp_api.cc +++ b/system/stack/smp/smp_api.cc @@ -89,7 +89,7 @@ tSMP_STATUS SMP_Pair(const RawAddress& bd_addr, tBLE_ADDR_TYPE addr_type) { if (p_cb->state != SMP_STATE_IDLE || p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD || p_cb->smp_over_br) { /* pending security on going, reject this one */ - return SMP_BUSY; + return SMP_IMPL_BUSY; } else { p_cb->flags = SMP_PAIR_FLAGS_WE_STARTED_DD; p_cb->pairing_bda = bd_addr; @@ -135,7 +135,7 @@ tSMP_STATUS SMP_BR_PairWith(const RawAddress& bd_addr) { if (p_cb->state != SMP_STATE_IDLE || p_cb->smp_over_br || p_cb->flags & SMP_PAIR_FLAGS_WE_STARTED_DD) { /* pending security on going, reject this one */ - return SMP_BUSY; + return SMP_IMPL_BUSY; } p_cb->role = HCI_ROLE_CENTRAL; diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc index 81b005a78c..e5aefe3c6a 100644 --- a/system/stack/smp/smp_utils.cc +++ b/system/stack/smp/smp_utils.cc @@ -1231,7 +1231,7 @@ void smp_reject_unexpected_pairing_command(const RawAddress& bd_addr) { p = (uint8_t*)(p_buf + 1) + L2CAP_MIN_OFFSET; UINT8_TO_STREAM(p, SMP_OPCODE_PAIRING_FAILED); - UINT8_TO_STREAM(p, SMP_PAIR_NOT_SUPPORT); + UINT8_TO_STREAM(p, SMP_BUSY); p_buf->offset = L2CAP_MIN_OFFSET; p_buf->len = SMP_PAIR_FAIL_SIZE; diff --git a/system/stack/test/a2dp/a2dp_vendor_ldac_decoder_test.cc b/system/stack/test/a2dp/a2dp_vendor_ldac_decoder_test.cc deleted file mode 100644 index 4f78adbb00..0000000000 --- a/system/stack/test/a2dp/a2dp_vendor_ldac_decoder_test.cc +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stack/include/a2dp_vendor_ldac_decoder.h" - -#include <gtest/gtest.h> - -#include <cstdint> - -#include "osi/include/allocator.h" -#include "stack/include/bt_hdr.h" - -namespace { - -uint8_t* Data(BT_HDR* packet) { return packet->data + packet->offset; } - -} // namespace - -/** - * Test class to test selected functionality in stack/a2dp - */ -class A2dpStackTest : public ::testing::Test { -protected: - BT_HDR* AllocateL2capPacket(const std::vector<uint8_t> data) const { - auto packet = AllocatePacket(data.size()); - std::copy(data.cbegin(), data.cend(), Data(packet)); - return packet; - } - -private: - BT_HDR* AllocatePacket(size_t packet_length) const { - BT_HDR* packet = static_cast<BT_HDR*>(osi_calloc(sizeof(BT_HDR) + packet_length)); - packet->len = packet_length; - return packet; - } -}; - -TEST_F(A2dpStackTest, DecodePacket_ZeroLength) { - const std::vector<uint8_t> data; - BT_HDR* p_buf = AllocateL2capPacket(data); - ASSERT_FALSE(a2dp_vendor_ldac_decoder_decode_packet(p_buf)); - osi_free(p_buf); -} diff --git a/system/stack/test/a2dp/a2dp_vendor_ldac_unittest.cc b/system/stack/test/a2dp/a2dp_vendor_ldac_unittest.cc index d446d808ac..d6b19a1651 100644 --- a/system/stack/test/a2dp/a2dp_vendor_ldac_unittest.cc +++ b/system/stack/test/a2dp/a2dp_vendor_ldac_unittest.cc @@ -57,9 +57,6 @@ protected: encoder_iface_ = const_cast<tA2DP_ENCODER_INTERFACE*>( A2DP_VendorGetEncoderInterfaceLdac(kCodecInfoLdacCapability)); ASSERT_NE(encoder_iface_, nullptr); - decoder_iface_ = const_cast<tA2DP_DECODER_INTERFACE*>( - A2DP_VendorGetDecoderInterfaceLdac(kCodecInfoLdacCapability)); - ASSERT_NE(decoder_iface_, nullptr); } void TearDown() override { @@ -69,22 +66,15 @@ protected: if (encoder_iface_ != nullptr) { encoder_iface_->encoder_cleanup(); } - if (decoder_iface_ != nullptr) { - decoder_iface_->decoder_cleanup(); - } } // NOTE: Make a super func for all codecs void SetCodecConfig() { uint8_t source_codec_info_result[AVDT_CODEC_SIZE]; - btav_a2dp_codec_index_t peer_codec_index; a2dp_codecs_ = new A2dpCodecs(std::vector<btav_a2dp_codec_config_t>()); ASSERT_TRUE(a2dp_codecs_->init()); - peer_codec_index = A2DP_SinkCodecIndex(kCodecInfoLdacCapability); - ASSERT_NE(peer_codec_index, BTAV_A2DP_CODEC_INDEX_MAX); - ASSERT_EQ(peer_codec_index, BTAV_A2DP_CODEC_INDEX_SINK_LDAC); source_codec_config_ = a2dp_codecs_->findSourceCodecConfig(kCodecInfoLdacCapability); ASSERT_NE(source_codec_config_, nullptr); ASSERT_TRUE(a2dp_codecs_->setCodecConfig(kCodecInfoLdacCapability, true, @@ -103,7 +93,6 @@ protected: encoder_iface_->encoder_init(&peer_params, source_codec_config_, read_cb, enqueue_cb); } - void InitializeDecoder(decoded_data_callback_t data_cb) { decoder_iface_->decoder_init(data_cb); } BT_HDR* AllocateL2capPacket(const std::vector<uint8_t> data) const { auto packet = AllocatePacket(data.size()); std::copy(data.cbegin(), data.cend(), Data(packet)); @@ -118,7 +107,6 @@ protected: A2dpCodecConfig* source_codec_config_; A2dpCodecs* a2dp_codecs_; tA2DP_ENCODER_INTERFACE* encoder_iface_; - tA2DP_DECODER_INTERFACE* decoder_iface_; }; TEST_F(A2dpLdacTest, a2dp_source_read_underflow) { diff --git a/system/stack/test/a2dp/misc_fake.cc b/system/stack/test/a2dp/misc_fake.cc deleted file mode 100644 index ee82394629..0000000000 --- a/system/stack/test/a2dp/misc_fake.cc +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stack/include/a2dp_vendor_ldac.h" - -int A2DP_VendorGetTrackSampleRateLdac(const uint8_t* /*p_codec_info*/) { return 0; } -int A2DP_VendorGetTrackBitsPerSampleLdac(const uint8_t* /*p_codec_info*/) { return 0; } -int A2DP_VendorGetChannelModeCodeLdac(const uint8_t* /*p_codec_info*/) { return 0; } diff --git a/system/stack/test/btm/stack_btm_dm_inq_db_test.cc b/system/stack/test/btm/stack_btm_dm_inq_db_test.cc index 7f5e27f26f..7fc971a5c3 100644 --- a/system/stack/test/btm/stack_btm_dm_inq_db_test.cc +++ b/system/stack/test/btm/stack_btm_dm_inq_db_test.cc @@ -23,6 +23,7 @@ #include "stack/btm/btm_int_types.h" #include "stack/btm/neighbor_inquiry.h" +#include "stack/include/btm_inq.h" #include "stack/include/inq_hci_link_interface.h" #include "test/common/mock_functions.h" #include "test/fake/fake_looper.h" @@ -32,8 +33,6 @@ #include "test/mock/mock_osi_thread.h" extern tBTM_CB btm_cb; -extern void btm_init_inq_result_flt(void); -extern void btm_clr_inq_result_flt(void); namespace { constexpr size_t kNumberOfThreads = 8; diff --git a/system/stack/test/btm/stack_btm_sec_test.cc b/system/stack/test/btm/stack_btm_sec_test.cc index 3bfac8572d..58317afc29 100644 --- a/system/stack/test/btm/stack_btm_sec_test.cc +++ b/system/stack/test/btm/stack_btm_sec_test.cc @@ -48,17 +48,7 @@ const uint8_t kBdName[] = "kBdName"; constexpr char kTimeFormat[] = "%Y-%m-%d %H:%M:%S"; } // namespace -namespace bluetooth { -namespace testing { -namespace legacy { - -void wipe_secrets_and_remove(tBTM_SEC_DEV_REC* p_dev_rec); - -} // namespace legacy -} // namespace testing -} // namespace bluetooth - -using bluetooth::testing::legacy::wipe_secrets_and_remove; +using bluetooth::legacy::testing::wipe_secrets_and_remove; constexpr size_t kBtmSecMaxDeviceRecords = static_cast<size_t>(BTM_SEC_MAX_DEVICE_RECORDS + 1); diff --git a/system/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h b/system/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h index 87c9a55cc0..888fa563f6 100644 --- a/system/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h +++ b/system/stack/test/fuzzers/a2dp/codec/a2dpCodecHelperFunctions.h @@ -34,7 +34,6 @@ static const std::vector<btav_a2dp_codec_index_t> CODEC_INDEX_ENUM_VALS = { BTAV_A2DP_CODEC_INDEX_SINK_MIN, BTAV_A2DP_CODEC_INDEX_SINK_SBC, BTAV_A2DP_CODEC_INDEX_SINK_AAC, - BTAV_A2DP_CODEC_INDEX_SINK_LDAC, BTAV_A2DP_CODEC_INDEX_SINK_MAX, BTAV_A2DP_CODEC_INDEX_MIN, BTAV_A2DP_CODEC_INDEX_MAX}; diff --git a/system/stack/test/stack_a2dp_test.cc b/system/stack/test/stack_a2dp_test.cc index ee4d56ce54..e54a6be472 100644 --- a/system/stack/test/stack_a2dp_test.cc +++ b/system/stack/test/stack_a2dp_test.cc @@ -244,20 +244,6 @@ const uint8_t codec_info_non_a2dp_fake[AVDT_CODEC_SIZE] = { 10 // Unused }; -static const char* APTX_ENCODER_LIB_NAME = "libaptX_encoder.so"; -static const char* APTX_HD_ENCODER_LIB_NAME = "libaptXHD_encoder.so"; -static const char* LDAC_ENCODER_LIB_NAME = "libldacBT_enc.so"; -static const char* LDAC_DECODER_LIB_NAME = "libldacBT_dec.so"; - -static bool has_shared_library(const char* name) { - void* lib_handle = dlopen(name, RTLD_NOW); - if (lib_handle != nullptr) { - dlclose(lib_handle); - return true; - } - return false; -} - } // namespace class StackA2dpTest : public ::testing::Test { @@ -270,46 +256,22 @@ protected: bool supported = false; switch (codec_index) { case BTAV_A2DP_CODEC_INDEX_SOURCE_SBC: - supported = true; - break; case BTAV_A2DP_CODEC_INDEX_SOURCE_AAC: - supported = true; - break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX: - // Codec aptX is supported only if the device has the corresponding - // shared library installed. - supported = has_shared_library(APTX_ENCODER_LIB_NAME); - break; case BTAV_A2DP_CODEC_INDEX_SOURCE_APTX_HD: - // Codec aptX-HD is supported only if the device has the corresponding - // shared library installed. - supported = has_shared_library(APTX_HD_ENCODER_LIB_NAME); - break; case BTAV_A2DP_CODEC_INDEX_SOURCE_LDAC: - // Codec LDAC is supported only if the device has the corresponding - // shared library installed. - supported = has_shared_library(LDAC_ENCODER_LIB_NAME); - break; - case BTAV_A2DP_CODEC_INDEX_SINK_SBC: + case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: supported = true; break; + case BTAV_A2DP_CODEC_INDEX_SINK_SBC: case BTAV_A2DP_CODEC_INDEX_SINK_AAC: - supported = true; - break; - case BTAV_A2DP_CODEC_INDEX_SINK_LDAC: - // Codec LDAC is supported only if the device has the corresponding - // shared library installed. - supported = has_shared_library(LDAC_DECODER_LIB_NAME); - break; - case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: - break; - case BTAV_A2DP_CODEC_INDEX_SOURCE_OPUS: case BTAV_A2DP_CODEC_INDEX_SINK_OPUS: supported = true; break; case BTAV_A2DP_CODEC_INDEX_MAX: case BTAV_A2DP_CODEC_INDEX_SOURCE_MAX: case BTAV_A2DP_CODEC_INDEX_SINK_MAX: + case BTAV_A2DP_CODEC_INDEX_SOURCE_LC3: // Needed to avoid using "default:" case so we can capture when // a new codec is added, and it can be included here. break; diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc index 41d61e738b..8f020ae1c5 100644 --- a/system/stack/test/stack_smp_test.cc +++ b/system/stack/test/stack_smp_test.cc @@ -375,11 +375,13 @@ TEST(SmpStatusText, smp_status_text) { std::make_pair(SMP_NUMERIC_COMPAR_FAIL, "SMP_NUMERIC_COMPAR_FAIL"), std::make_pair(SMP_BR_PARING_IN_PROGR, "SMP_BR_PARING_IN_PROGR"), std::make_pair(SMP_XTRANS_DERIVE_NOT_ALLOW, "SMP_XTRANS_DERIVE_NOT_ALLOW"), + std::make_pair(SMP_KEY_REJECTED, "SMP_KEY_REJECTED"), + std::make_pair(SMP_BUSY, "SMP_BUSY"), std::make_pair(SMP_MAX_FAIL_RSN_PER_SPEC, - "SMP_XTRANS_DERIVE_NOT_ALLOW"), // NOTE: Dup + "SMP_BUSY"), // NOTE: Dup std::make_pair(SMP_PAIR_INTERNAL_ERR, "SMP_PAIR_INTERNAL_ERR"), std::make_pair(SMP_UNKNOWN_IO_CAP, "SMP_UNKNOWN_IO_CAP"), - std::make_pair(SMP_BUSY, "SMP_BUSY"), + std::make_pair(SMP_IMPL_BUSY, "SMP_IMPL_BUSY"), std::make_pair(SMP_ENC_FAIL, "SMP_ENC_FAIL"), std::make_pair(SMP_STARTED, "SMP_STARTED"), std::make_pair(SMP_RSP_TIMEOUT, "SMP_RSP_TIMEOUT"), diff --git a/system/test/headless/property.cc b/system/test/headless/property.cc index fe6e21f1ed..2b5df50ff2 100644 --- a/system/test/headless/property.cc +++ b/system/test/headless/property.cc @@ -99,6 +99,10 @@ std::map<::bt_property_type_t, return new headless::property::void_t(data, len, BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP); }}, + {BT_PROPERTY_UUIDS_LE, + [](const uint8_t* data, const size_t len) -> headless::bt_property_t* { + return new headless::property::uuid_t(data, len); + }}, }; } // namespace diff --git a/system/test/headless/property.h b/system/test/headless/property.h index 6af17a244f..1113495f24 100644 --- a/system/test/headless/property.h +++ b/system/test/headless/property.h @@ -52,6 +52,7 @@ inline std::string bt_property_type_text(const ::bt_property_type_t type) { CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_MODEL_NUM); CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_DEVICE_TIMESTAMP); CASE_RETURN_TEXT(BT_PROPERTY_REMOTE_ADDR_TYPE); + CASE_RETURN_TEXT(BT_PROPERTY_UUIDS_LE); CASE_RETURN_TEXT(BT_PROPERTY_RESERVED_0x14); default: RETURN_UNKNOWN_TYPE_STRING(::bt_property_type_t, type); diff --git a/system/test/mock/mock_stack_btm_ble.cc b/system/test/mock/mock_stack_btm_ble.cc index 7e1a2154b9..620b9e252c 100644 --- a/system/test/mock/mock_stack_btm_ble.cc +++ b/system/test/mock/mock_stack_btm_ble.cc @@ -81,8 +81,6 @@ struct btm_ble_update_sec_key_size btm_ble_update_sec_key_size; struct btm_get_local_div btm_get_local_div; struct btm_proc_smp_cback btm_proc_smp_cback; struct btm_sec_save_le_key btm_sec_save_le_key; -struct doNothing doNothing; -struct read_phy_cb read_phy_cb; } // namespace stack_btm_ble } // namespace mock @@ -292,15 +290,6 @@ void btm_sec_save_le_key(const RawAddress& bd_addr, tBTM_LE_KEY_TYPE key_type, inc_func_call_count(__func__); test::mock::stack_btm_ble::btm_sec_save_le_key(bd_addr, key_type, p_keys, pass_to_application); } -void doNothing(uint8_t* data, uint16_t len) { - inc_func_call_count(__func__); - test::mock::stack_btm_ble::doNothing(data, len); -} -void read_phy_cb(base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> cb, - uint8_t* data, uint16_t len) { - inc_func_call_count(__func__); - test::mock::stack_btm_ble::read_phy_cb(cb, data, len); -} // Mocked functions complete // END mockcify generation diff --git a/system/test/mock/mock_stack_btm_ble.h b/system/test/mock/mock_stack_btm_ble.h index 1133b60b4c..9c1b14d014 100644 --- a/system/test/mock/mock_stack_btm_ble.h +++ b/system/test/mock/mock_stack_btm_ble.h @@ -545,32 +545,6 @@ struct btm_sec_save_le_key { }; extern struct btm_sec_save_le_key btm_sec_save_le_key; -// Name: doNothing -// Params: uint8_t* data, uint16_t len -// Return: void -struct doNothing { - std::function<void(uint8_t* data, uint16_t len)> body{ - [](uint8_t* /* data */, uint16_t /* len */) {}}; - void operator()(uint8_t* data, uint16_t len) { body(data, len); } -}; -extern struct doNothing doNothing; - -// Name: read_phy_cb -// Params: base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status -// Return: void -struct read_phy_cb { - std::function<void(base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> callback, - uint8_t* data, uint16_t len)> - body{[](base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> - /* callback */, - uint8_t* /* data */, uint16_t /* len */) {}}; - void operator()(base::Callback<void(uint8_t tx_phy, uint8_t rx_phy, uint8_t status)> callback, - uint8_t* data, uint16_t len) { - body(callback, data, len); - } -}; -extern struct read_phy_cb read_phy_cb; - } // namespace stack_btm_ble } // namespace mock } // namespace test diff --git a/system/test/mock/mock_stack_btm_ble_gap.cc b/system/test/mock/mock_stack_btm_ble_gap.cc index 93e134c4a8..de8731ef54 100644 --- a/system/test/mock/mock_stack_btm_ble_gap.cc +++ b/system/test/mock/mock_stack_btm_ble_gap.cc @@ -46,10 +46,6 @@ using SyncReportCb = using SyncLostCb = base::Callback<void(uint16_t /*sync_handle*/)>; using SyncTransferCb = base::Callback<void(uint8_t /*status*/, RawAddress)>; -bool ble_vnd_is_included() { - inc_func_call_count(__func__); - return false; -} bool BTM_BleConfigPrivacy(bool /* privacy_mode */) { inc_func_call_count(__func__); return false; @@ -58,9 +54,9 @@ bool BTM_BleLocalPrivacyEnabled(void) { inc_func_call_count(__func__); return false; } -bool btm_ble_read_remote_cod(const RawAddress& /* remote_bda */) { +tBTM_STATUS btm_ble_read_remote_cod(const RawAddress& /* remote_bda */) { inc_func_call_count(__func__); - return false; + return tBTM_STATUS::BTM_SUCCESS; } bool btm_ble_cancel_remote_name(const RawAddress& /* remote_bda */) { inc_func_call_count(__func__); @@ -144,18 +140,7 @@ void btm_ble_process_adv_pkt_cont_for_inquiry( void btm_ble_read_remote_features_complete(uint8_t* /* p */, uint8_t /* length */) { inc_func_call_count(__func__); } -void btm_ble_read_remote_name_cmpl(bool /* status */, const RawAddress& /* bda */, - uint16_t /* length */, char* /* p_name */) { - inc_func_call_count(__func__); -} -void btm_ble_set_adv_flag(uint16_t /* connect_mode */, uint16_t /* disc_mode */) { - inc_func_call_count(__func__); -} void btm_ble_stop_inquiry(void) { inc_func_call_count(__func__); } -void btm_ble_update_dmt_flag_bits(uint8_t* /* adv_flag_value */, const uint16_t /* connect_mode */, - const uint16_t /* disc_mode */) { - inc_func_call_count(__func__); -} void btm_ble_update_mode_operation(uint8_t /* link_role */, const RawAddress* /* bd_addr */, tHCI_STATUS /* status */) { inc_func_call_count(__func__); diff --git a/system/test/mock/mock_stack_btm_devctl.cc b/system/test/mock/mock_stack_btm_devctl.cc index fc27fab6db..da8d8b2053 100644 --- a/system/test/mock/mock_stack_btm_devctl.cc +++ b/system/test/mock/mock_stack_btm_devctl.cc @@ -32,14 +32,6 @@ // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" -namespace test { -namespace mock { -namespace stack_btm_devctl { - -} -} // namespace mock -} // namespace test - tBTM_STATUS BTM_DeleteStoredLinkKey(const RawAddress* /* bd_addr */, tBTM_CMPL_CB* /* p_cb */) { inc_func_call_count(__func__); return tBTM_STATUS::BTM_SUCCESS; @@ -56,5 +48,3 @@ void BTM_db_reset(void) { inc_func_call_count(__func__); } void btm_delete_stored_link_key_complete(uint8_t* /* p */, uint16_t /* evt_len */) { inc_func_call_count(__func__); } -void btm_dev_free() { inc_func_call_count(__func__); } -void btm_dev_init() { inc_func_call_count(__func__); } diff --git a/system/test/mock/mock_stack_btm_inq.cc b/system/test/mock/mock_stack_btm_inq.cc index b137827eb9..ed99d05e6f 100644 --- a/system/test/mock/mock_stack_btm_inq.cc +++ b/system/test/mock/mock_stack_btm_inq.cc @@ -48,7 +48,6 @@ struct BTM_SetConnectability BTM_SetConnectability; struct BTM_SetDiscoverability BTM_SetDiscoverability; struct BTM_SetInquiryMode BTM_SetInquiryMode; struct BTM_StartInquiry BTM_StartInquiry; -struct btm_clear_all_pending_le_entry btm_clear_all_pending_le_entry; struct btm_clr_inq_db btm_clr_inq_db; struct btm_clr_inq_result_flt btm_clr_inq_result_flt; struct btm_inq_db_find btm_inq_db_find; @@ -56,8 +55,6 @@ struct btm_inq_db_new btm_inq_db_new; struct btm_inq_db_reset btm_inq_db_reset; struct btm_inq_find_bdaddr btm_inq_find_bdaddr; struct btm_process_inq_complete btm_process_inq_complete; -struct btm_set_eir_uuid btm_set_eir_uuid; -struct btm_sort_inq_result btm_sort_inq_result; } // namespace stack_btm_inq } // namespace mock @@ -119,10 +116,6 @@ tBTM_STATUS BTM_StartInquiry(tBTM_INQ_RESULTS_CB* p_results_cb, tBTM_CMPL_CB* p_ inc_func_call_count(__func__); return test::mock::stack_btm_inq::BTM_StartInquiry(p_results_cb, p_cmpl_cb); } -void btm_clear_all_pending_le_entry(void) { - inc_func_call_count(__func__); - test::mock::stack_btm_inq::btm_clear_all_pending_le_entry(); -} void btm_clr_inq_db(const RawAddress* p_bda) { inc_func_call_count(__func__); test::mock::stack_btm_inq::btm_clr_inq_db(p_bda); @@ -151,13 +144,5 @@ void btm_process_inq_complete(tHCI_STATUS status, uint8_t mode) { inc_func_call_count(__func__); test::mock::stack_btm_inq::btm_process_inq_complete(status, mode); } -void btm_set_eir_uuid(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results) { - inc_func_call_count(__func__); - test::mock::stack_btm_inq::btm_set_eir_uuid(p_eir, p_results); -} -void btm_sort_inq_result(void) { - inc_func_call_count(__func__); - test::mock::stack_btm_inq::btm_sort_inq_result(); -} // Mocked functions complete // END mockcify generation diff --git a/system/test/mock/mock_stack_btm_inq.h b/system/test/mock/mock_stack_btm_inq.h index d89dd534c2..4c0acd08a4 100644 --- a/system/test/mock/mock_stack_btm_inq.h +++ b/system/test/mock/mock_stack_btm_inq.h @@ -143,15 +143,6 @@ struct BTM_StartInquiry { }; extern struct BTM_StartInquiry BTM_StartInquiry; -// Name: btm_clear_all_pending_le_entry -// Params: void -// Return: void -struct btm_clear_all_pending_le_entry { - std::function<void(void)> body{[](void) {}}; - void operator()(void) { body(); } -}; -extern struct btm_clear_all_pending_le_entry btm_clear_all_pending_le_entry; - // Name: btm_clr_inq_db // Params: const RawAddress* p_bda // Return: void @@ -222,25 +213,6 @@ struct btm_process_inq_complete { }; extern struct btm_process_inq_complete btm_process_inq_complete; -// Name: btm_set_eir_uuid -// Params: const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results -// Return: void -struct btm_set_eir_uuid { - std::function<void(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results)> body{ - [](const uint8_t* /* p_eir */, tBTM_INQ_RESULTS* /* p_results */) {}}; - void operator()(const uint8_t* p_eir, tBTM_INQ_RESULTS* p_results) { body(p_eir, p_results); } -}; -extern struct btm_set_eir_uuid btm_set_eir_uuid; - -// Name: btm_sort_inq_result -// Params: void -// Return: void -struct btm_sort_inq_result { - std::function<void(void)> body{[](void) {}}; - void operator()(void) { body(); } -}; -extern struct btm_sort_inq_result btm_sort_inq_result; - } // namespace stack_btm_inq } // namespace mock } // namespace test diff --git a/system/test/mock/mock_stack_btm_sco.cc b/system/test/mock/mock_stack_btm_sco.cc index 949e635b09..0128227349 100644 --- a/system/test/mock/mock_stack_btm_sco.cc +++ b/system/test/mock/mock_stack_btm_sco.cc @@ -33,10 +33,6 @@ // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" -bool btm_sco_removed(uint16_t /* hci_handle */, tHCI_REASON /* reason */) { - inc_func_call_count(__func__); - return false; -} const RawAddress* BTM_ReadScoBdAddr(uint16_t /* sco_inx */) { inc_func_call_count(__func__); return nullptr; @@ -91,9 +87,6 @@ void btm_sco_on_sco_connect_request(const RawAddress& /* bda */, const bluetooth::hci::ClassOfDevice& /* cod */) { inc_func_call_count(__func__); } -void btm_sco_on_disconnected(uint16_t /* hci_handle */, tHCI_REASON /* reason */) { - inc_func_call_count(__func__); -} bool btm_peer_supports_esco_2m_phy(RawAddress /* bd_addr */) { inc_func_call_count(__func__); return true; diff --git a/system/test/mock/mock_stack_btm_sec.cc b/system/test/mock/mock_stack_btm_sec.cc index cdf4b97df6..4ca3189b30 100644 --- a/system/test/mock/mock_stack_btm_sec.cc +++ b/system/test/mock/mock_stack_btm_sec.cc @@ -64,7 +64,6 @@ struct BTM_SetEncryption BTM_SetEncryption; struct BTM_SetPinType BTM_SetPinType; struct BTM_SetSecurityLevel BTM_SetSecurityLevel; struct BTM_update_version_info BTM_update_version_info; -struct NotifyBondingCanceled NotifyBondingCanceled; struct btm_create_conn_cancel_complete btm_create_conn_cancel_complete; struct btm_get_dev_class btm_get_dev_class; struct btm_io_capabilities_req btm_io_capabilities_req; @@ -235,10 +234,6 @@ void BTM_update_version_info(const RawAddress& bd_addr, inc_func_call_count(__func__); test::mock::stack_btm_sec::BTM_update_version_info(bd_addr, remote_version_info); } -void NotifyBondingCanceled(tBTM_STATUS btm_status) { - inc_func_call_count(__func__); - test::mock::stack_btm_sec::NotifyBondingCanceled(btm_status); -} void btm_create_conn_cancel_complete(uint8_t status, const RawAddress bd_addr) { inc_func_call_count(__func__); test::mock::stack_btm_sec::btm_create_conn_cancel_complete(status, bd_addr); diff --git a/system/test/mock/mock_stack_btm_sec.h b/system/test/mock/mock_stack_btm_sec.h index c40e57c593..688ab5dca1 100644 --- a/system/test/mock/mock_stack_btm_sec.h +++ b/system/test/mock/mock_stack_btm_sec.h @@ -331,15 +331,6 @@ struct BTM_update_version_info { }; extern struct BTM_update_version_info BTM_update_version_info; -// Name: NotifyBondingCanceled -// Params: tBTM_STATUS btm_status -// Return: void -struct NotifyBondingCanceled { - std::function<void(tBTM_STATUS btm_status)> body{[](tBTM_STATUS /* btm_status */) {}}; - void operator()(tBTM_STATUS btm_status) { body(btm_status); } -}; -extern struct NotifyBondingCanceled NotifyBondingCanceled; - // Name: btm_create_conn_cancel_complete // Params: uint8_t status, RawAddress bd_addr // Return: void diff --git a/tools/rootcanal/model/controller/controller_properties.cc b/tools/rootcanal/model/controller/controller_properties.cc index 26718a9747..3f6c918bfe 100644 --- a/tools/rootcanal/model/controller/controller_properties.cc +++ b/tools/rootcanal/model/controller/controller_properties.cc @@ -1743,6 +1743,40 @@ ControllerProperties::ControllerProperties(rootcanal::configuration::Controller le_supported_states = 0x3ffffffffff; break; + case ControllerPreset::INTEL_BE200: + // Configuration extracted with the helper script controller_info.py + supports_csr_vendor_command = true; + br_supported = true; + le_supported = true; + hci_version = bluetooth::hci::HciVersion::V_5_4; + hci_subversion = 0x4363; + lmp_version = bluetooth::hci::LmpVersion::V_5_4; + lmp_subversion = 0x4363; + company_identifier = 0x2; + supported_commands = std::array<uint8_t, 64>{ + 0xbf, 0xff, 0xfb, 0x03, 0xcc, 0xff, 0x0f, 0xff, 0xbf, 0xff, 0xfc, 0x1f, 0xf2, + 0x0f, 0xe8, 0xfe, 0x3f, 0xf7, 0x8f, 0xff, 0x1c, 0x00, 0x04, 0x00, 0x61, 0xf7, + 0xff, 0xff, 0x7f, 0x38, 0x00, 0x00, 0xfe, 0xf0, 0xff, 0xff, 0xff, 0xe3, 0x80, + 0x07, 0x00, 0xe8, 0x1f, 0xfc, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + lmp_features = std::array<uint64_t, 3>{0x877bffdbfe0ffebf, 0x0, 0x300}; + acl_data_packet_length = 1021; + total_num_acl_data_packets = 4; + sco_data_packet_length = 96; + total_num_sco_data_packets = 6; + num_supported_iac = 2; + le_features = 0x80059ff; + le_acl_data_packet_length = 251; + total_num_le_acl_data_packets = 3; + le_filter_accept_list_size = 25; + le_resolving_list_size = 25; + le_supported_states = 0x3ffffffffff; + le_max_advertising_data_length = 160; + le_num_supported_advertising_sets = 12; + le_periodic_advertiser_list_size = 12; + break; + default: break; } diff --git a/tools/rootcanal/model/controller/dual_mode_controller.cc b/tools/rootcanal/model/controller/dual_mode_controller.cc index 15f788b438..c7b5d0bbe2 100644 --- a/tools/rootcanal/model/controller/dual_mode_controller.cc +++ b/tools/rootcanal/model/controller/dual_mode_controller.cc @@ -3290,6 +3290,11 @@ void DualModeController::WriteLoopbackMode(CommandView command) { ErrorCode::SUCCESS)); } +void DualModeController::IntelDdcConfigWrite(CommandView /*command*/) { + send_event_(bluetooth::hci::CommandCompleteBuilder::Create(kNumCommandPackets, OpCode::INTEL_DDC_CONFIG_WRITE, + std::vector<uint8_t> { static_cast<uint8_t>(ErrorCode::SUCCESS) })); +} + // Note: the list does not contain all defined opcodes. // Notable exceptions: // - Vendor commands @@ -4274,6 +4279,8 @@ const std::unordered_map<OpCode, DualModeController::CommandHandler> {OpCode::LE_GET_CONTROLLER_ACTIVITY_ENERGY_INFO, &DualModeController::LeGetControllerActivityEnergyInfo}, {OpCode::LE_EX_SET_SCAN_PARAMETERS, &DualModeController::LeExSetScanParameters}, - {OpCode::GET_CONTROLLER_DEBUG_INFO, &DualModeController::GetControllerDebugInfo}}; + {OpCode::GET_CONTROLLER_DEBUG_INFO, &DualModeController::GetControllerDebugInfo}, + {OpCode::INTEL_DDC_CONFIG_WRITE, &DualModeController::IntelDdcConfigWrite}, + }; } // namespace rootcanal diff --git a/tools/rootcanal/model/controller/dual_mode_controller.h b/tools/rootcanal/model/controller/dual_mode_controller.h index d0394a7bfd..c5574bda85 100644 --- a/tools/rootcanal/model/controller/dual_mode_controller.h +++ b/tools/rootcanal/model/controller/dual_mode_controller.h @@ -525,6 +525,7 @@ public: void LeGetControllerActivityEnergyInfo(CommandView command); void LeExSetScanParameters(CommandView command); void GetControllerDebugInfo(CommandView command); + void IntelDdcConfigWrite(CommandView command); // CSR vendor command. // Implement the command specific to the CSR controller diff --git a/tools/rootcanal/model/setup/test_command_handler.cc b/tools/rootcanal/model/setup/test_command_handler.cc index 93315ae16e..59071842a5 100644 --- a/tools/rootcanal/model/setup/test_command_handler.cc +++ b/tools/rootcanal/model/setup/test_command_handler.cc @@ -246,6 +246,8 @@ void TestCommandHandler::SetDeviceConfiguration(const vector<std::string>& args) preset = rootcanal::configuration::ControllerPreset::LAIRD_BL654; } else if (args[1] == "csr_rck_pts_dongle") { preset = rootcanal::configuration::ControllerPreset::CSR_RCK_PTS_DONGLE; + } else if (args[1] == "intel_be200") { + preset = rootcanal::configuration::ControllerPreset::INTEL_BE200; } else { response_string_ = "TestCommandHandler 'set_device_configuration' invalid configuration preset"; send_response_(response_string_); diff --git a/tools/rootcanal/packets/hci_packets.pdl b/tools/rootcanal/packets/hci_packets.pdl index 23abb93b29..8f3d3c1588 100644 --- a/tools/rootcanal/packets/hci_packets.pdl +++ b/tools/rootcanal/packets/hci_packets.pdl @@ -400,6 +400,7 @@ enum OpCode : 16 { // VENDOR_SPECIFIC // MSFT_OPCODE_xxxx below is needed for the tests. MSFT_OPCODE_INTEL = 0xFC1E, + INTEL_DDC_CONFIG_WRITE = 0xFC8B, LE_GET_VENDOR_CAPABILITIES = 0xFD53, LE_BATCH_SCAN = 0xFD56, LE_APCF = 0xFD57, diff --git a/tools/rootcanal/proto/rootcanal/configuration.proto b/tools/rootcanal/proto/rootcanal/configuration.proto index 0b6db15c89..0beb961291 100644 --- a/tools/rootcanal/proto/rootcanal/configuration.proto +++ b/tools/rootcanal/proto/rootcanal/configuration.proto @@ -24,6 +24,8 @@ enum ControllerPreset { LAIRD_BL654 = 1; // Official PTS dongle, CSR rck. CSR_RCK_PTS_DONGLE = 2; + // Official PTS dongle, Intel BE200. + INTEL_BE200 = 3; } message ControllerFeatures { diff --git a/tools/rootcanal/scripts/controller_info.py b/tools/rootcanal/scripts/controller_info.py index 10a74a0daf..cca4f0970b 100755 --- a/tools/rootcanal/scripts/controller_info.py +++ b/tools/rootcanal/scripts/controller_info.py @@ -98,66 +98,74 @@ async def br_edr_properties(host: Host): page2 = await host.expect_evt(hci.ReadLocalExtendedFeaturesComplete) print( - f"lmp_features: {{ 0x{page0.lmp_features:x}, 0x{page1.extended_lmp_features:x}, 0x{page2.extended_lmp_features:x} }}" + f"lmp_features = {{ 0x{page0.lmp_features:x}, 0x{page1.extended_lmp_features:x}, 0x{page2.extended_lmp_features:x} }};" ) await host.send_cmd(hci.ReadBufferSize()) evt = await host.expect_evt(hci.ReadBufferSizeComplete) - print(f"acl_data_packet_length: {evt.acl_data_packet_length}") - print(f"total_num_acl_data_packets: {evt.total_num_acl_data_packets}") - print(f"sco_data_packet_length: {evt.synchronous_data_packet_length}") - print(f"total_num_sco_data_packets: {evt.total_num_synchronous_data_packets}") + print(f"acl_data_packet_length = {evt.acl_data_packet_length};") + print(f"total_num_acl_data_packets = {evt.total_num_acl_data_packets};") + print(f"sco_data_packet_length = {evt.synchronous_data_packet_length};") + print(f"total_num_sco_data_packets = {evt.total_num_synchronous_data_packets};") await host.send_cmd(hci.ReadNumberOfSupportedIac()) evt = await host.expect_evt(hci.ReadNumberOfSupportedIacComplete) - print(f"num_supported_iac: {evt.num_support_iac}") + print(f"num_supported_iac = {evt.num_support_iac};") async def le_properties(host: Host): - await host.send_cmd(hci.LeReadLocalSupportedFeatures()) - evt = await host.expect_evt(hci.LeReadLocalSupportedFeaturesComplete) + await host.send_cmd(hci.LeReadLocalSupportedFeaturesPage0()) + evt = await host.expect_evt(hci.LeReadLocalSupportedFeaturesPage0Complete) - print(f"le_features: 0x{evt.le_features:x}") + print(f"le_features = 0x{evt.le_features:x};") - await host.send_cmd(hci.LeReadBufferSizeV2()) - evt = await host.expect_evt(hci.LeReadBufferSizeV2Complete) + try: + await host.send_cmd(hci.LeReadBufferSizeV2()) + evt = await host.expect_evt(hci.LeReadBufferSizeV2Complete) + + print(f"le_acl_data_packet_length = {evt.le_buffer_size.le_data_packet_length};") + print(f"total_num_le_acl_data_packets = {evt.le_buffer_size.total_num_le_packets};") + print(f"iso_data_packet_length = {evt.iso_buffer_size.le_data_packet_length};") + print(f"total_num_iso_data_packets = {evt.iso_buffer_size.total_num_le_packets};") + + except Exception: + await host.send_cmd(hci.LeReadBufferSizeV1()) + evt = await host.expect_evt(hci.LeReadBufferSizeV1Complete) - print(f"le_acl_data_packet_length: {evt.le_buffer_size.le_data_packet_length}") - print(f"total_num_le_acl_data_packets: {evt.le_buffer_size.total_num_le_packets}") - print(f"iso_data_packet_length: {evt.iso_buffer_size.le_data_packet_length}") - print(f"total_num_iso_data_packets: {evt.iso_buffer_size.total_num_le_packets}") + print(f"le_acl_data_packet_length = {evt.le_buffer_size.le_data_packet_length};") + print(f"total_num_le_acl_data_packets = {evt.le_buffer_size.total_num_le_packets};") await host.send_cmd(hci.LeReadFilterAcceptListSize()) evt = await host.expect_evt(hci.LeReadFilterAcceptListSizeComplete) - print(f"le_filter_accept_list_size: {evt.filter_accept_list_size}") + print(f"le_filter_accept_list_size = {evt.filter_accept_list_size};") await host.send_cmd(hci.LeReadResolvingListSize()) evt = await host.expect_evt(hci.LeReadResolvingListSizeComplete) - print(f"le_resolving_list_size: {evt.resolving_list_size}") + print(f"le_resolving_list_size = {evt.resolving_list_size};") await host.send_cmd(hci.LeReadSupportedStates()) evt = await host.expect_evt(hci.LeReadSupportedStatesComplete) - print(f"le_supported_states: 0x{evt.le_states:x}") + print(f"le_supported_states: 0x{evt.le_states:x};") await host.send_cmd(hci.LeReadMaximumAdvertisingDataLength()) evt = await host.expect_evt(hci.LeReadMaximumAdvertisingDataLengthComplete) - print(f"le_max_advertising_data_length: {evt.maximum_advertising_data_length}") + print(f"le_max_advertising_data_length = {evt.maximum_advertising_data_length};") await host.send_cmd(hci.LeReadNumberOfSupportedAdvertisingSets()) evt = await host.expect_evt(hci.LeReadNumberOfSupportedAdvertisingSetsComplete) - print(f"le_num_supported_advertising_sets: {evt.number_supported_advertising_sets}") + print(f"le_num_supported_advertising_sets = {evt.number_supported_advertising_sets};") await host.send_cmd(hci.LeReadPeriodicAdvertiserListSize()) evt = await host.expect_evt(hci.LeReadPeriodicAdvertiserListSizeComplete) - print(f"le_periodic_advertiser_list_size: {evt.periodic_advertiser_list_size}") + print(f"le_periodic_advertiser_list_size = {evt.periodic_advertiser_list_size};") async def run(tcp_port: int): @@ -170,16 +178,16 @@ async def run(tcp_port: int): await host.send_cmd(hci.ReadLocalVersionInformation()) evt = await host.expect_evt(hci.ReadLocalVersionInformationComplete) - print(f"hci_version: {evt.local_version_information.hci_version}") - print(f"hci_subversion: 0x{evt.local_version_information.hci_revision:x}") - print(f"lmp_version: {evt.local_version_information.lmp_version}") - print(f"lmp_subversion: 0x{evt.local_version_information.lmp_subversion:x}") - print(f"company_identifier: 0x{evt.local_version_information.manufacturer_name:x}") + print(f"hci_version = {evt.local_version_information.hci_version};") + print(f"hci_subversion = 0x{evt.local_version_information.hci_revision:x};") + print(f"lmp_version = {evt.local_version_information.lmp_version};") + print(f"lmp_subversion = 0x{evt.local_version_information.lmp_subversion:x};") + print(f"company_identifier = 0x{evt.local_version_information.manufacturer_name:x};") await host.send_cmd(hci.ReadLocalSupportedCommands()) evt = await host.expect_evt(hci.ReadLocalSupportedCommandsComplete) - print(f"supported_commands: {{ {', '.join([f'0x{b:x}' for b in evt.supported_commands])} }}") + print(f"supported_commands = {{ {', '.join([f'0x{b:02x}' for b in evt.supported_commands])} }};") try: await br_edr_properties(host) |