diff options
318 files changed, 7974 insertions, 8416 deletions
@@ -1,19 +1,6 @@ -# Bug component: 27441 +set noparent -# Project owners -girardier@google.com #{LAST_RESORT_SUGGESTION} -muhammadfalam@google.com #{LAST_RESORT_SUGGESTION} -siyuanh@google.com #{LAST_RESORT_SUGGESTION} -okamil@google.com #{LAST_RESORT_SUGGESTION} -wescande@google.com #{LAST_RESORT_SUGGESTION} - -# Per-file ownership - -# Build files / test_config / presubmit / preupload / linter file -per-file *.cfg,.cfg,TEST_MAPPING,*.bp,*.xml,.clang-tidy,pyrightconfig.json=file:/OWNERS_build - -# ChromeOS team owns Linux build files -# - build.py is used for Linux build -# - *.gn is for GN build rules -# - Cargo.toml is for Rust build rules -per-file build.py,*.gn,Cargo.toml=file:/OWNERS_chromeos +girardier@google.com +muhammadfalam@google.com +okamil@google.com +siyuanh@google.com diff --git a/OWNERS_automotive b/OWNERS_automotive deleted file mode 100644 index 20c7ac685b..0000000000 --- a/OWNERS_automotive +++ /dev/null @@ -1,3 +0,0 @@ -# Project owners -cmanton@google.com -salsavage@google.com diff --git a/OWNERS_build b/OWNERS_build deleted file mode 100644 index 5b3f4bad26..0000000000 --- a/OWNERS_build +++ /dev/null @@ -1 +0,0 @@ -wescande@google.com diff --git a/OWNERS_channel_sounding b/OWNERS_channel_sounding deleted file mode 100644 index 10a8fa893a..0000000000 --- a/OWNERS_channel_sounding +++ /dev/null @@ -1,9 +0,0 @@ -# Pixel DRI -aliceypkuo@google.com -chienyuanhuang@google.com - -# Bluetooth Ranging DRI -steveliu@google.com - -# Bluetooth Stack Reviewer -henrichataing@google.com diff --git a/OWNERS_chromeos b/OWNERS_chromeos deleted file mode 100644 index 075a04e5e3..0000000000 --- a/OWNERS_chromeos +++ /dev/null @@ -1,22 +0,0 @@ -# Project owners -abhishekpandit@google.com - -# Audio -enshuo@google.com -hychao@google.com -jrwu@google.com -whalechang@google.com - -# General review -michaelfsun@google.com -laikatherine@google.com -yinghsu@google.com -apusaka@google.com -deanliao@google.com -chharry@google.com -melhuishj@google.com -johnlai@google.com -mmandlik@google.com -sarveshkalwit@google.com -howardchung@google.com -jiangzp@google.com diff --git a/OWNERS_content b/OWNERS_content deleted file mode 100644 index 883d5dc782..0000000000 --- a/OWNERS_content +++ /dev/null @@ -1,2 +0,0 @@ -klhyun@google.com -hdmoon@google.com diff --git a/OWNERS_cs b/OWNERS_cs deleted file mode 100644 index 1cebb11002..0000000000 --- a/OWNERS_cs +++ /dev/null @@ -1,2 +0,0 @@ -steveliu@google.com -zning@google.com diff --git a/OWNERS_hearingaid b/OWNERS_hearingaid deleted file mode 100644 index d1c75de900..0000000000 --- a/OWNERS_hearingaid +++ /dev/null @@ -1 +0,0 @@ -henrichataing@google.com diff --git a/OWNERS_leaudio b/OWNERS_leaudio deleted file mode 100644 index 30db38d799..0000000000 --- a/OWNERS_leaudio +++ /dev/null @@ -1,3 +0,0 @@ -siyuanh@google.com -jpawlowski@google.com -rongxuan@google.com diff --git a/TEST_MAPPING b/TEST_MAPPING index b1cc9a7594..c807f7d8ec 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -133,9 +133,6 @@ "name": "net_test_bta_security" }, { - "name": "net_test_btcore" - }, - { "name": "net_test_btif" }, { @@ -365,9 +362,6 @@ "name": "net_test_bta_security" }, { - "name": "net_test_btcore" - }, - { "name": "net_test_btif" }, { diff --git a/android/BluetoothLegacyMigration/OWNERS b/android/BluetoothLegacyMigration/OWNERS deleted file mode 100644 index 66467c7b3e..0000000000 --- a/android/BluetoothLegacyMigration/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# Reviewers for /android/BluetoothLegacyMigration - -eruffieux@google.com -siyuanh@google.com -wescande@google.com diff --git a/android/ChannelSoundingTestApp/OWNERS b/android/ChannelSoundingTestApp/OWNERS deleted file mode 100644 index 29d6f601cc..0000000000 --- a/android/ChannelSoundingTestApp/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_cs diff --git a/android/apishim/OWNERS b/android/apishim/OWNERS deleted file mode 100644 index 586d2866f1..0000000000 --- a/android/apishim/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -# Reviewers for /android/apishim - -eruffieux@google.com -jpawlowski@google.com -siyuanh@google.com -wescande@google.com diff --git a/android/app/OWNERS b/android/app/OWNERS deleted file mode 100644 index 7b7e2e6679..0000000000 --- a/android/app/OWNERS +++ /dev/null @@ -1,15 +0,0 @@ -# Reviewers for /android/app - -bhaktha@google.com -eruffieux@google.com -hallstrom@google.com -henrichataing@google.com -jpawlowski@google.com -okamil@google.com -poahlo@google.com -siyuanh@google.com -wescande@google.com -rwt@google.com - -# Reviewers for Channel Sounding related files -per-file /src/com/android/bluetooth/gatt/DistanceMeasurement*.java=file:/OWNERS_channel_sounding diff --git a/android/app/aidl/android/bluetooth/BluetoothClass.aidl b/android/app/aidl/android/bluetooth/BluetoothClass.aidl index 8699b1eee3..124ee53760 100644 --- a/android/app/aidl/android/bluetooth/BluetoothClass.aidl +++ b/android/app/aidl/android/bluetooth/BluetoothClass.aidl @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl index 14f91140af..74f51a8037 100644 --- a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl +++ b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppQosSettings.aidl @@ -5,7 +5,7 @@ ** 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 +** 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, diff --git a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl index 87dd10ee15..a55a794c6e 100644 --- a/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl +++ b/android/app/aidl/android/bluetooth/BluetoothHidDeviceAppSdpSettings.aidl @@ -5,7 +5,7 @@ ** 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 +** 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, diff --git a/android/app/aidl/android/bluetooth/IBluetooth.aidl b/android/app/aidl/android/bluetooth/IBluetooth.aidl index bc46353f7c..d8a5421d6f 100644 --- a/android/app/aidl/android/bluetooth/IBluetooth.aidl +++ b/android/app/aidl/android/bluetooth/IBluetooth.aidl @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl b/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl index 9b2888eadb..6101044e29 100644 --- a/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl +++ b/android/app/aidl/android/bluetooth/IBluetoothCallback.aidl @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl index ce2397ff33..bd54982d13 100644 --- a/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl +++ b/android/app/aidl/android/bluetooth/IBluetoothSocketManager.aidl @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl b/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl index 38ffe6f699..c3d542c774 100644 --- a/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl +++ b/android/app/aidl/android/bluetooth/IncomingRfcommSocketInfo.aidl @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/jni/OWNERS b/android/app/jni/OWNERS deleted file mode 100644 index 826f56b4e0..0000000000 --- a/android/app/jni/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -per-file com_android_bluetooth_hearing_aid.cpp=file:/OWNERS_hearingaid -per-file com_android_bluetooth_csip_set_coordinator.cpp=file:/OWNERS_leaudio -per-file com_android_bluetooth_le_audio.cpp=file:/OWNERS_leaudio -per-file com_android_bluetooth_vc.cpp=file:/OWNERS_leaudio diff --git a/android/app/res/values-as/strings.xml b/android/app/res/values-as/strings.xml index 2776941946..c53ee123e2 100644 --- a/android/app/res/values-as/strings.xml +++ b/android/app/res/values-as/strings.xml @@ -35,7 +35,7 @@ <string name="incoming_file_confirm_content" msgid="6573502088511901157">"অন্তৰ্গামী ফাইল গ্ৰহণ কৰিবনে?"</string> <string name="incoming_file_confirm_cancel" msgid="9205906062663982692">"প্ৰত্যাখ্যান কৰক"</string> <string name="incoming_file_confirm_ok" msgid="5046926299036238623">"গ্ৰহণ কৰক"</string> - <string name="incoming_file_confirm_timeout_ok" msgid="8612187577686515660">"ঠিক"</string> + <string name="incoming_file_confirm_timeout_ok" msgid="8612187577686515660">"ঠিক আছে"</string> <string name="incoming_file_confirm_timeout_content" msgid="3221412098281076974">"\"<xliff:g id="SENDER">%1$s</xliff:g>\"ৰ পৰা লাভ কৰা ফাইলটো গ্ৰহণ কৰোঁতে সময় ওকলিছে"</string> <string name="incoming_file_confirm_Notification_title" msgid="5381395500920804895">"অন্তৰ্গামী ফাইল"</string> <string name="incoming_file_confirm_Notification_content" msgid="2669135531488877921">"<xliff:g id="SENDER">%1$s</xliff:g> এটা ফাইল পঠিয়াবলৈ সাজু: <xliff:g id="FILE">%2$s</xliff:g>"</string> @@ -58,18 +58,18 @@ <string name="download_fail_line1" msgid="3149552664349685007">"ফাইল লাভ কৰা নহ\'ল"</string> <string name="download_fail_line2" msgid="4289018531070750414">"ফাইল: <xliff:g id="FILE">%1$s</xliff:g>"</string> <string name="download_fail_line3" msgid="2214989413171231684">"কাৰণ: <xliff:g id="REASON">%1$s</xliff:g>"</string> - <string name="download_fail_ok" msgid="3272322648250767032">"ঠিক"</string> + <string name="download_fail_ok" msgid="3272322648250767032">"ঠিক আছে"</string> <string name="download_succ_line5" msgid="1720346308221503270">"ফাইল লাভ কৰা হ’ল"</string> <string name="download_succ_ok" msgid="7488662808922799824">"খোলক"</string> <string name="upload_line1" msgid="1912803923255989287">"প্ৰতি: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\""</string> <string name="upload_line3" msgid="5964902647036741603">"ফাইলৰ প্ৰকাৰ: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)"</string> <string name="upload_line5" msgid="3477751464103201364">"ফাইল প্ৰেৰণ কৰি থকা হৈছে…"</string> <string name="upload_succ_line5" msgid="165979135931118211">"ফাইল প্ৰেৰণ কৰা হ’ল"</string> - <string name="upload_succ_ok" msgid="6797291708604959167">"ঠিক"</string> + <string name="upload_succ_ok" msgid="6797291708604959167">"ঠিক আছে"</string> <string name="upload_fail_line1" msgid="7044307783071776426">"\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\"লৈ ফাইলটো পঠিয়াব পৰা নগ\'ল।"</string> <string name="upload_fail_line1_2" msgid="6102642590057711459">"ফাইল: <xliff:g id="FILE">%1$s</xliff:g>"</string> <string name="upload_fail_cancel" msgid="1632528037932779727">"বন্ধ কৰক"</string> - <string name="bt_error_btn_ok" msgid="2802751202009957372">"ঠিক"</string> + <string name="bt_error_btn_ok" msgid="2802751202009957372">"ঠিক আছে"</string> <string name="unknown_file" msgid="3719981572107052685">"অজ্ঞাত ফাইল"</string> <string name="unknown_file_desc" msgid="9185609398960437760">"এইধৰণৰ ফাইল পৰিচালনা কৰিবলৈ কোনো এপ্ নাই। \n"</string> <string name="not_exist_file" msgid="5097565588949092486">"কোনো ফাইল নাই"</string> diff --git a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java index d4fc9d54e8..a4c265adf7 100644 --- a/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java +++ b/android/app/src/com/android/bluetooth/BluetoothMethodProxy.java @@ -306,4 +306,9 @@ public class BluetoothMethodProxy { ComponentCaller caller, Uri uri, int modeFlags) { return caller.checkContentUriPermission(uri, modeFlags); } + + /** Proxies {@link Context#grantUriPermission(String, Uri, int)}. } */ + public void grantUriPermission(Context context, String packageName, Uri uri, int modeFlags) { + context.grantUriPermission(packageName, uri, modeFlags); + } } diff --git a/android/app/src/com/android/bluetooth/BluetoothObexTransport.java b/android/app/src/com/android/bluetooth/BluetoothObexTransport.java index 3c57ec1600..d101f1736e 100644 --- a/android/app/src/com/android/bluetooth/BluetoothObexTransport.java +++ b/android/app/src/com/android/bluetooth/BluetoothObexTransport.java @@ -18,7 +18,6 @@ package com.android.bluetooth; import android.annotation.SuppressLint; import android.bluetooth.BluetoothSocket; -import com.android.bluetooth.flags.Flags; import com.android.obex.ObexTransport; import java.io.DataInputStream; @@ -112,10 +111,7 @@ public class BluetoothObexTransport implements ObexTransport { if (mSocket == null) { return null; } - String identityAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(mSocket.getRemoteDevice()) - : mSocket.getRemoteDevice().getIdentityAddress(); + String identityAddress = Utils.getBrEdrAddress(mSocket.getRemoteDevice()); return mSocket.getConnectionType() == BluetoothSocket.TYPE_RFCOMM ? identityAddress : mSocket.getRemoteDevice().getAddress(); diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java index fe74b2facd..688f57ce8a 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpNativeInterface.java @@ -29,8 +29,6 @@ import android.bluetooth.BluetoothCodecType; import android.bluetooth.BluetoothDevice; import com.android.bluetooth.Utils; -import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Native; @@ -41,15 +39,12 @@ import java.util.List; public class A2dpNativeInterface { private static final String TAG = A2dpNativeInterface.class.getSimpleName(); - private final AdapterService mAdapterService; @Native private final A2dpNativeCallback mNativeCallback; private BluetoothCodecType[] mSupportedCodecTypes; @VisibleForTesting - A2dpNativeInterface( - @NonNull AdapterService adapterService, @NonNull A2dpNativeCallback nativeCallback) { - mAdapterService = requireNonNull(adapterService); + A2dpNativeInterface(@NonNull A2dpNativeCallback nativeCallback) { mNativeCallback = requireNonNull(nativeCallback); } @@ -132,15 +127,11 @@ public class A2dpNativeInterface { return setCodecConfigPreferenceNative(getByteAddress(device), codecConfigArray); } - private byte[] getByteAddress(BluetoothDevice device) { + private static byte[] getByteAddress(BluetoothDevice device) { if (device == null) { return Utils.getBytesFromAddress("00:00:00:00:00:00"); } - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getByteBrEdrAddress(device); - } else { - return mAdapterService.getByteIdentityAddress(device); - } + return Utils.getByteBrEdrAddress(device); } private native void initNative( diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java index 9481146d61..6633e4e2ea 100644 --- a/android/app/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java @@ -26,8 +26,6 @@ import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; -import static com.android.bluetooth.Utils.checkCallerTargetSdk; - import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElseGet; @@ -44,15 +42,12 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.bluetooth.BufferConstraints; -import android.bluetooth.IBluetoothA2dp; import android.companion.CompanionDeviceManager; -import android.content.AttributionSource; import android.content.Intent; import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.BluetoothProfileConnectionInfo; -import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -63,7 +58,6 @@ import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.ActiveDeviceManager; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.btservice.MetricsLogger; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.ServiceFactory; import com.android.bluetooth.btservice.storage.DatabaseManager; @@ -73,7 +67,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -132,7 +125,6 @@ public class A2dpService extends ProfileService { nativeInterface, () -> new A2dpNativeInterface( - adapterService, new A2dpNativeCallback(adapterService, this))); mDatabaseManager = requireNonNull(mAdapterService.getDatabase()); mAudioManager = requireNonNull(getSystemService(AudioManager.class)); @@ -168,7 +160,7 @@ public class A2dpService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothA2dpBinder(this); + return new A2dpServiceBinder(this); } @Override @@ -203,6 +195,10 @@ public class A2dpService extends ProfileService { mHandler.removeCallbacksAndMessages(null); } + CompanionDeviceManager getCompanionDeviceManager() { + return mCompanionDeviceManager; + } + public static synchronized A2dpService getA2dpService() { if (sA2dpService == null) { Log.w(TAG, "getA2dpService(): service is null"); @@ -1001,13 +997,7 @@ public class A2dpService extends ProfileService { return null; } Log.d(TAG, "Creating a new state machine for " + device); - sm = - new A2dpStateMachine( - this, - device, - mNativeInterface, - mA2dpOffloadEnabled, - mLooper); + sm = new A2dpStateMachine(this, device, mNativeInterface, mA2dpOffloadEnabled, mLooper); mStateMachines.put(device, sm); return sm; } @@ -1345,320 +1335,6 @@ public class A2dpService extends ProfileService { : null; } - /** Binder object: must be a static class or memory leak may occur. */ - @VisibleForTesting - static class BluetoothA2dpBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { - private A2dpService mService; - - @RequiresPermission(BLUETOOTH_CONNECT) - private A2dpService getServiceAndEnforceConnect(AttributionSource source) { - // Cache mService because it can change while getService is called - A2dpService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - private A2dpService getService() { - // Cache mService because it can change while getService is called - A2dpService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) { - return null; - } - return service; - } - - BluetoothA2dpBinder(A2dpService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - if (device == null) { - return service.removeActiveDevice(false); - } else { - return service.setActiveDevice(device); - } - } - - @Override - public BluetoothDevice getActiveDevice(AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return null; - } - - return service.getActiveDevice(); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionPolicy(device); - } - - @Override - public void setAvrcpAbsoluteVolume(int volume, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - service.setAvrcpAbsoluteVolume(volume); - } - - @Override - public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - return service.isA2dpPlaying(device); - } - - @Override - public List<BluetoothCodecType> getSupportedCodecTypes() { - A2dpService service = getService(); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getSupportedCodecTypes(); - } - - @Override - public BluetoothCodecStatus getCodecStatus( - BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return null; - } - - Utils.enforceCdmAssociationIfNotBluetoothPrivileged( - service, service.mCompanionDeviceManager, source, device); - - return service.getCodecStatus(device); - } - - @Override - public void setCodecConfigPreference( - BluetoothDevice device, - BluetoothCodecConfig codecConfig, - AttributionSource source) { - requireNonNull(device); - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - Utils.enforceCdmAssociationIfNotBluetoothPrivileged( - service, service.mCompanionDeviceManager, source, device); - - service.setCodecConfigPreference(device, codecConfig); - } - - @Override - public void enableOptionalCodecs(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - if (checkCallerTargetSdk( - mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - } - service.enableOptionalCodecs(device); - } - - @Override - public void disableOptionalCodecs(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - if (checkCallerTargetSdk( - mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - } - service.disableOptionalCodecs(device); - } - - @Override - public int isOptionalCodecsSupported(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; - } - - if (checkCallerTargetSdk( - mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - } - return service.getSupportsOptionalCodecs(device); - } - - @Override - public int isOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; - } - - if (checkCallerTargetSdk( - mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - } - return service.getOptionalCodecsEnabled(device); - } - - @Override - public void setOptionalCodecsEnabled( - BluetoothDevice device, int value, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - if (checkCallerTargetSdk( - mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - } - service.setOptionalCodecsEnabled(device, value); - } - - @Override - public int getDynamicBufferSupport(AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE; - } - - return service.getDynamicBufferSupport(); - } - - @Override - public BufferConstraints getBufferConstraints(AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return null; - } - - return service.getBufferConstraints(); - } - - @Override - public boolean setBufferLengthMillis(int codec, int value, AttributionSource source) { - A2dpService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - return service.setBufferLengthMillis(codec, value); - } - } - @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java b/android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java new file mode 100644 index 0000000000..da8aaae42a --- /dev/null +++ b/android/app/src/com/android/bluetooth/a2dp/A2dpServiceBinder.java @@ -0,0 +1,336 @@ +/* + * 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.a2dp; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static com.android.bluetooth.Utils.checkCallerTargetSdk; + +import static java.util.Objects.requireNonNull; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothCodecConfig; +import android.bluetooth.BluetoothCodecStatus; +import android.bluetooth.BluetoothCodecType; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BufferConstraints; +import android.bluetooth.IBluetoothA2dp; +import android.content.AttributionSource; +import android.os.Build; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class A2dpServiceBinder extends IBluetoothA2dp.Stub implements IProfileServiceBinder { + private static final String TAG = A2dpServiceBinder.class.getSimpleName(); + + private A2dpService mService; + + A2dpServiceBinder(A2dpService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + private A2dpService getService() { + A2dpService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) { + return null; + } + return service; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private A2dpService getServiceAndEnforceConnect(AttributionSource source) { + A2dpService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + if (device == null) { + return service.removeActiveDevice(false); + } else { + return service.setActiveDevice(device); + } + } + + @Override + public BluetoothDevice getActiveDevice(AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return null; + } + return service.getActiveDevice(); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public void setAvrcpAbsoluteVolume(int volume, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setAvrcpAbsoluteVolume(volume); + } + + @Override + public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + return service.isA2dpPlaying(device); + } + + @Override + public List<BluetoothCodecType> getSupportedCodecTypes() { + A2dpService service = getService(); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getSupportedCodecTypes(); + } + + @Override + public BluetoothCodecStatus getCodecStatus(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return null; + } + + Utils.enforceCdmAssociationIfNotBluetoothPrivileged( + service, service.getCompanionDeviceManager(), source, device); + + return service.getCodecStatus(device); + } + + @Override + public void setCodecConfigPreference( + BluetoothDevice device, BluetoothCodecConfig codecConfig, AttributionSource source) { + requireNonNull(device); + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + Utils.enforceCdmAssociationIfNotBluetoothPrivileged( + service, service.getCompanionDeviceManager(), source, device); + + service.setCodecConfigPreference(device, codecConfig); + } + + @Override + public void enableOptionalCodecs(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + } + service.enableOptionalCodecs(device); + } + + @Override + public void disableOptionalCodecs(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + } + service.disableOptionalCodecs(device); + } + + @Override + public int isOptionalCodecsSupported(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN; + } + + if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + } + return service.getSupportsOptionalCodecs(device); + } + + @Override + public int isOptionalCodecsEnabled(BluetoothDevice device, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN; + } + + if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + } + return service.getOptionalCodecsEnabled(device); + } + + @Override + public void setOptionalCodecsEnabled( + BluetoothDevice device, int value, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + if (checkCallerTargetSdk(mService, source.getPackageName(), Build.VERSION_CODES.TIRAMISU)) { + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + } + service.setOptionalCodecsEnabled(device, value); + } + + @Override + public int getDynamicBufferSupport(AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return BluetoothA2dp.DYNAMIC_BUFFER_SUPPORT_NONE; + } + + return service.getDynamicBufferSupport(); + } + + @Override + public BufferConstraints getBufferConstraints(AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return null; + } + + return service.getBufferConstraints(); + } + + @Override + public boolean setBufferLengthMillis(int codec, int value, AttributionSource source) { + A2dpService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + return service.setBufferLengthMillis(codec, value); + } +} diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java index e3b4c400a3..6822e4e734 100644 --- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java +++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface.java @@ -23,7 +23,6 @@ import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -78,14 +77,6 @@ public class A2dpSinkNativeInterface { return mAdapterService.getDeviceFromByte(address); } - private byte[] getByteAddress(BluetoothDevice device) { - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getByteBrEdrAddress(device); - } else { - return mAdapterService.getByteIdentityAddress(device); - } - } - /** * Initiates an A2DP connection to a remote device. * @@ -93,7 +84,7 @@ public class A2dpSinkNativeInterface { * @return true on success, otherwise false. */ public boolean connectA2dpSink(BluetoothDevice device) { - return connectA2dpNative(getByteAddress(device)); + return connectA2dpNative(Utils.getByteBrEdrAddress(device)); } /** @@ -103,7 +94,7 @@ public class A2dpSinkNativeInterface { * @return true on success, otherwise false. */ public boolean disconnectA2dpSink(BluetoothDevice device) { - return disconnectA2dpNative(getByteAddress(device)); + return disconnectA2dpNative(Utils.getByteBrEdrAddress(device)); } /** @@ -120,7 +111,7 @@ public class A2dpSinkNativeInterface { // Translate to byte address for JNI. Use an all 0 MAC for no active device byte[] address = null; if (device != null) { - address = getByteAddress(device); + address = Utils.getByteBrEdrAddress(device); } else { address = Utils.getBytesFromAddress("00:00:00:00:00:00"); } diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java index 10b9249bad..6750a9a9c8 100644 --- a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java +++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java @@ -13,30 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.bluetooth.a2dpsink; -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAudioConfig; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; -import android.bluetooth.IBluetoothA2dpSink; -import android.content.AttributionSource; import android.os.Looper; import android.sysprop.BluetoothProperties; import android.util.Log; -import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.storage.DatabaseManager; @@ -45,7 +39,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -177,136 +170,6 @@ public class A2dpSinkService extends ProfileService { return new A2dpSinkServiceBinder(this); } - // Binder object: Must be static class or memory leak may occur - @VisibleForTesting - static class A2dpSinkServiceBinder extends IBluetoothA2dpSink.Stub - implements IProfileServiceBinder { - private A2dpSinkService mService; - - A2dpSinkServiceBinder(A2dpSinkService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private A2dpSinkService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - A2dpSinkService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return false; - } - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionPolicy(device); - } - - @Override - public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.isA2dpPlaying(device); - } - - @Override - public BluetoothAudioConfig getAudioConfig( - BluetoothDevice device, AttributionSource source) { - A2dpSinkService service = getService(source); - if (service == null) { - return null; - } - return service.getAudioConfig(device); - } - } - /* Generic Profile Code */ /** diff --git a/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java new file mode 100644 index 0000000000..851e48a286 --- /dev/null +++ b/android/app/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinder.java @@ -0,0 +1,157 @@ +/* + * 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.a2dpsink; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothAudioConfig; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothA2dpSink; +import android.content.AttributionSource; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class A2dpSinkServiceBinder extends IBluetoothA2dpSink.Stub implements IProfileServiceBinder { + private static final String TAG = A2dpSinkServiceBinder.class.getSimpleName(); + + private A2dpSinkService mService; + + A2dpSinkServiceBinder(A2dpSinkService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private A2dpSinkService getService(AttributionSource source) { + A2dpSinkService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public boolean isA2dpPlaying(BluetoothDevice device, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isA2dpPlaying(device); + } + + @Override + public BluetoothAudioConfig getAudioConfig(BluetoothDevice device, AttributionSource source) { + A2dpSinkService service = getService(source); + if (service == null) { + return null; + } + return service.getAudioConfig(device); + } +} diff --git a/android/app/src/com/android/bluetooth/a2dpsink/OWNERS b/android/app/src/com/android/bluetooth/a2dpsink/OWNERS deleted file mode 100644 index ecd59038a1..0000000000 --- a/android/app/src/com/android/bluetooth/a2dpsink/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_automotive diff --git a/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java b/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java index 720ce84dd8..85d1043023 100644 --- a/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java +++ b/android/app/src/com/android/bluetooth/avrcp/AvrcpNativeInterface.java @@ -28,7 +28,6 @@ import com.android.bluetooth.audio_util.PlayStatus; import com.android.bluetooth.audio_util.PlayerInfo; import com.android.bluetooth.audio_util.PlayerSettingsManager.PlayerSettingsValues; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -93,10 +92,7 @@ public class AvrcpNativeInterface { } void setBipClientStatus(BluetoothDevice device, boolean connected) { - String identityAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : mAdapterService.getIdentityAddress(device.getAddress()); + String identityAddress = Utils.getBrEdrAddress(device); setBipClientStatusNative(identityAddress, connected); } @@ -237,10 +233,7 @@ public class AvrcpNativeInterface { } boolean disconnectDevice(BluetoothDevice device) { - String identityAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : mAdapterService.getIdentityAddress(device.getAddress()); + String identityAddress = Utils.getBrEdrAddress(device); d("disconnectDevice: identityAddress=" + identityAddress); return disconnectDeviceNative(identityAddress); } @@ -278,10 +271,7 @@ public class AvrcpNativeInterface { void sendVolumeChanged(BluetoothDevice device, int volume) { d("sendVolumeChanged: volume=" + volume); - String identityAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : mAdapterService.getIdentityAddress(device.getAddress()); + String identityAddress = Utils.getBrEdrAddress(device); sendVolumeChangedNative(identityAddress, volume); } diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java index 4551f320da..c4ef6a2437 100644 --- a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java +++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java @@ -16,19 +16,14 @@ package com.android.bluetooth.avrcpcontroller; -import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothAvrcpPlayerSettings; import android.bluetooth.BluetoothDevice; -import android.bluetooth.IBluetoothAvrcpController; -import android.content.AttributionSource; import android.content.Intent; import android.media.AudioManager; import android.support.v4.media.MediaBrowserCompat.MediaItem; @@ -46,7 +41,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.UUID; @@ -368,90 +362,6 @@ public class AvrcpControllerService extends ProfileService { return new AvrcpControllerServiceBinder(this); } - // Binder object: Must be static class or memory leak may occur - @VisibleForTesting - static class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub - implements IProfileServiceBinder { - private AvrcpControllerService mService; - - AvrcpControllerServiceBinder(AvrcpControllerService service) { - mService = service; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private AvrcpControllerService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - AvrcpControllerService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - AvrcpControllerService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - AvrcpControllerService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - AvrcpControllerService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - return service.getConnectionState(device); - } - - @Override - public void sendGroupNavigationCmd( - BluetoothDevice device, int keyCode, int keyState, AttributionSource source) { - getService(source); - Log.w(TAG, "sendGroupNavigationCmd not implemented"); - } - - @Override - public void setPlayerApplicationSetting( - BluetoothAvrcpPlayerSettings settings, AttributionSource source) { - getService(source); - Log.w(TAG, "setPlayerApplicationSetting not implemented"); - } - - @Override - public BluetoothAvrcpPlayerSettings getPlayerSettings( - BluetoothDevice device, AttributionSource source) { - getService(source); - Log.w(TAG, "getPlayerSettings not implemented"); - return null; - } - } - // Called by JNI when a device has connected or disconnected. @VisibleForTesting synchronized void onConnectionStateChanged( diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java new file mode 100644 index 0000000000..105e9a8709 --- /dev/null +++ b/android/app/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinder.java @@ -0,0 +1,116 @@ +/* + * 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.avrcpcontroller; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothAvrcpPlayerSettings; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothAvrcpController; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class AvrcpControllerServiceBinder extends IBluetoothAvrcpController.Stub + implements IProfileServiceBinder { + private static final String TAG = AvrcpControllerServiceBinder.class.getSimpleName(); + + private AvrcpControllerService mService; + + AvrcpControllerServiceBinder(AvrcpControllerService service) { + mService = service; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private AvrcpControllerService getService(AttributionSource source) { + AvrcpControllerService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + AvrcpControllerService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + AvrcpControllerService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + AvrcpControllerService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public void sendGroupNavigationCmd( + BluetoothDevice device, int keyCode, int keyState, AttributionSource source) { + getService(source); + Log.w(TAG, "sendGroupNavigationCmd not implemented"); + } + + @Override + public void setPlayerApplicationSetting( + BluetoothAvrcpPlayerSettings settings, AttributionSource source) { + getService(source); + Log.w(TAG, "setPlayerApplicationSetting not implemented"); + } + + @Override + public BluetoothAvrcpPlayerSettings getPlayerSettings( + BluetoothDevice device, AttributionSource source) { + getService(source); + Log.w(TAG, "getPlayerSettings not implemented"); + return null; + } +} diff --git a/android/app/src/com/android/bluetooth/avrcpcontroller/OWNERS b/android/app/src/com/android/bluetooth/avrcpcontroller/OWNERS deleted file mode 100644 index ecd59038a1..0000000000 --- a/android/app/src/com/android/bluetooth/avrcpcontroller/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_automotive 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 edf88f9331..42528676b4 100644 --- a/android/app/src/com/android/bluetooth/bass_client/BassClientService.java +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientService.java @@ -16,9 +16,6 @@ package com.android.bluetooth.bass_client; -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; -import static android.Manifest.permission.BLUETOOTH_SCAN; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; @@ -36,7 +33,6 @@ import static com.android.bluetooth.flags.Flags.leaudioSortScansToSyncByFails; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; @@ -50,7 +46,6 @@ import android.bluetooth.BluetoothLeBroadcastSubgroup; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothLeBroadcastAssistant; import android.bluetooth.IBluetoothLeBroadcastAssistantCallback; import android.bluetooth.le.IScannerCallback; import android.bluetooth.le.PeriodicAdvertisingCallback; @@ -60,7 +55,6 @@ import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanRecord; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; -import android.content.AttributionSource; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -737,7 +731,7 @@ public class BassClientService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothLeBroadcastAssistantBinder(this); + return new BassClientServiceBinder(this); } @Override @@ -4829,235 +4823,4 @@ public class BassClientService extends ProfileService { sEventLogger.dump(sb); sb.append("\n"); } - - /** Binder object: must be a static class or memory leak may occur */ - @VisibleForTesting - static class BluetoothLeBroadcastAssistantBinder extends IBluetoothLeBroadcastAssistant.Stub - implements IProfileServiceBinder { - BassClientService mService; - - BluetoothLeBroadcastAssistantBinder(BassClientService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) - private BassClientService getServiceAndEnforceConnect(AttributionSource source) { - // Cache mService because it can change while getService is called - BassClientService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service; - } - - @RequiresPermission(allOf = {BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED}) - private BassClientService getServiceAndEnforceScan(AttributionSource source) { - // Cache mService because it can change while getService is called - BassClientService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkScanPermissionForDataDelivery( - service, source, TAG, "getServiceAndEnforceScan")) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service; - } - - @Override - public int getConnectionState(BluetoothDevice sink, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return STATE_DISCONNECTED; - } - return service.getConnectionState(sink); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return Collections.emptyList(); - } - return service.getConnectedDevices(); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return false; - } - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return CONNECTION_POLICY_FORBIDDEN; - } - return service.getConnectionPolicy(device); - } - - @Override - public void registerCallback( - IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.registerCallback(cb); - } - - @Override - public void unregisterCallback( - IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.unregisterCallback(cb); - } - - @Override - public void startSearchingForSources(List<ScanFilter> filters, AttributionSource source) { - BassClientService service = getServiceAndEnforceScan(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.startSearchingForSources(filters); - } - - @Override - public void stopSearchingForSources(AttributionSource source) { - BassClientService service = getServiceAndEnforceScan(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.stopSearchingForSources(); - } - - @Override - public boolean isSearchInProgress(AttributionSource source) { - BassClientService service = getServiceAndEnforceScan(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return false; - } - return service.isSearchInProgress(); - } - - @Override - public void addSource( - BluetoothDevice sink, - BluetoothLeBroadcastMetadata sourceMetadata, - boolean isGroupOp, - AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.addSource(sink, sourceMetadata, isGroupOp); - } - - @Override - public void modifySource( - BluetoothDevice sink, - int sourceId, - BluetoothLeBroadcastMetadata updatedMetadata, - AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.modifySource(sink, sourceId, updatedMetadata); - } - - @Override - public void removeSource(BluetoothDevice sink, int sourceId, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return; - } - service.removeSource(sink, sourceId); - } - - @Override - public List<BluetoothLeBroadcastReceiveState> getAllSources( - BluetoothDevice sink, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return Collections.emptyList(); - } - return service.getAllSources(sink); - } - - @Override - public int getMaximumSourceCapacity(BluetoothDevice sink, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return 0; - } - return service.getMaximumSourceCapacity(sink); - } - - @Override - public BluetoothLeBroadcastMetadata getSourceMetadata( - BluetoothDevice sink, int sourceId, AttributionSource source) { - BassClientService service = getServiceAndEnforceConnect(source); - if (service == null) { - Log.e(TAG, "Service is null"); - return null; - } - return service.getSourceMetadata(sink, sourceId); - } - } } diff --git a/android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java b/android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java new file mode 100644 index 0000000000..d319e6ed47 --- /dev/null +++ b/android/app/src/com/android/bluetooth/bass_client/BassClientServiceBinder.java @@ -0,0 +1,268 @@ +/* + * 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.bass_client; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.Manifest.permission.BLUETOOTH_SCAN; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastReceiveState; +import android.bluetooth.IBluetoothLeBroadcastAssistant; +import android.bluetooth.IBluetoothLeBroadcastAssistantCallback; +import android.bluetooth.le.ScanFilter; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class BassClientServiceBinder extends IBluetoothLeBroadcastAssistant.Stub + implements IProfileServiceBinder { + private static final String TAG = BassClientServiceBinder.class.getSimpleName(); + + private BassClientService mService; + + BassClientServiceBinder(BassClientService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) + private BassClientService getServiceAndEnforceConnect(AttributionSource source) { + BassClientService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + + return service; + } + + @RequiresPermission(allOf = {BLUETOOTH_SCAN, BLUETOOTH_PRIVILEGED}) + private BassClientService getServiceAndEnforceScan(AttributionSource source) { + BassClientService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkScanPermissionForDataDelivery( + service, source, TAG, "getServiceAndEnforceScan")) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + + return service; + } + + @Override + public int getConnectionState(BluetoothDevice sink, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return STATE_DISCONNECTED; + } + return service.getConnectionState(sink); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return CONNECTION_POLICY_FORBIDDEN; + } + return service.getConnectionPolicy(device); + } + + @Override + public void registerCallback( + IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.registerCallback(cb); + } + + @Override + public void unregisterCallback( + IBluetoothLeBroadcastAssistantCallback cb, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.unregisterCallback(cb); + } + + @Override + public void startSearchingForSources(List<ScanFilter> filters, AttributionSource source) { + BassClientService service = getServiceAndEnforceScan(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.startSearchingForSources(filters); + } + + @Override + public void stopSearchingForSources(AttributionSource source) { + BassClientService service = getServiceAndEnforceScan(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.stopSearchingForSources(); + } + + @Override + public boolean isSearchInProgress(AttributionSource source) { + BassClientService service = getServiceAndEnforceScan(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return false; + } + return service.isSearchInProgress(); + } + + @Override + public void addSource( + BluetoothDevice sink, + BluetoothLeBroadcastMetadata sourceMetadata, + boolean isGroupOp, + AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.addSource(sink, sourceMetadata, isGroupOp); + } + + @Override + public void modifySource( + BluetoothDevice sink, + int sourceId, + BluetoothLeBroadcastMetadata updatedMetadata, + AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.modifySource(sink, sourceId, updatedMetadata); + } + + @Override + public void removeSource(BluetoothDevice sink, int sourceId, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return; + } + service.removeSource(sink, sourceId); + } + + @Override + public List<BluetoothLeBroadcastReceiveState> getAllSources( + BluetoothDevice sink, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return Collections.emptyList(); + } + return service.getAllSources(sink); + } + + @Override + public int getMaximumSourceCapacity(BluetoothDevice sink, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return 0; + } + return service.getMaximumSourceCapacity(sink); + } + + @Override + public BluetoothLeBroadcastMetadata getSourceMetadata( + BluetoothDevice sink, int sourceId, AttributionSource source) { + BassClientService service = getServiceAndEnforceConnect(source); + if (service == null) { + Log.e(TAG, "Service is null"); + return null; + } + return service.getSourceMetadata(sink, sourceId); + } +} diff --git a/android/app/src/com/android/bluetooth/bass_client/OWNERS b/android/app/src/com/android/bluetooth/bass_client/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/src/com/android/bluetooth/bass_client/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java index bd483f5925..013c799af7 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterProperties.java @@ -55,7 +55,6 @@ import androidx.annotation.VisibleForTesting; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.RemoteDevices.DeviceProperties; -import com.android.bluetooth.flags.Flags; import com.android.modules.utils.build.SdkLevel; import java.io.FileDescriptor; @@ -531,10 +530,7 @@ class AdapterProperties { void cleanupPrevBondRecordsFor(BluetoothDevice device) { String address = device.getAddress(); - String identityAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device, mService) - : mService.getIdentityAddress(address); + String identityAddress = Utils.getBrEdrAddress(device, mService); int deviceType = mRemoteDevices.getDeviceProperties(device).getDeviceType(); debugLog("cleanupPrevBondRecordsFor: " + device + ", device type: " + deviceType); if (identityAddress == null) { @@ -547,10 +543,7 @@ class AdapterProperties { for (BluetoothDevice existingDevice : mBondedDevices) { String existingAddress = existingDevice.getAddress(); - String existingIdentityAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(existingDevice, mService) - : mService.getIdentityAddress(existingAddress); + String existingIdentityAddress = Utils.getBrEdrAddress(existingDevice, mService); int existingDeviceType = mRemoteDevices.getDeviceProperties(existingDevice).getDeviceType(); @@ -1100,10 +1093,7 @@ class AdapterProperties { StringBuilder sb = new StringBuilder(); for (BluetoothDevice device : mBondedDevices) { String address = device.getAddress(); - String brEdrAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : mService.getIdentityAddress(address); + String brEdrAddress = Utils.getBrEdrAddress(device); if (brEdrAddress.equals(address)) { writer.println( " " diff --git a/android/app/src/com/android/bluetooth/btservice/AdapterService.java b/android/app/src/com/android/bluetooth/btservice/AdapterService.java index 9bec63769d..182e75cceb 100644 --- a/android/app/src/com/android/bluetooth/btservice/AdapterService.java +++ b/android/app/src/com/android/bluetooth/btservice/AdapterService.java @@ -2689,12 +2689,8 @@ public class AdapterService extends Service { return Utils.getBytesFromAddress(deviceProp.getIdentityAddress()); } - if (Flags.identityAddressNullIfNotKnown()) { - // Return null if identity address unknown - return null; - } else { - return Utils.getByteAddress(device); - } + // Return null if identity address unknown + return null; } public BluetoothDevice getDeviceFromByte(byte[] address) { @@ -2712,14 +2708,9 @@ public class AdapterService extends Service { DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp != null && deviceProp.getIdentityAddress() != null) { return deviceProp.getIdentityAddress(); - } else { - if (Flags.identityAddressNullIfNotKnown()) { - // Return null if identity address unknown - return null; - } else { - return address; - } } + // Return null if identity address unknown + return null; } /** @@ -2745,11 +2736,7 @@ public class AdapterService extends Service { } identityAddressType = deviceProp.getIdentityAddressType(); } else { - if (Flags.identityAddressNullIfNotKnown()) { - identityAddress = null; - } else { - identityAddress = address; - } + identityAddress = null; } return new BluetoothAddress(identityAddress, identityAddressType); @@ -3119,16 +3106,13 @@ public class AdapterService extends Service { public int getConnectionState(BluetoothDevice device) { final String address = device.getAddress(); - if (Flags.apiGetConnectionStateUsingIdentityAddress()) { - int connectionState = mNativeInterface.getConnectionState(getBytesFromAddress(address)); - final String identityAddress = getIdentityAddress(address); - if (identityAddress != null) { - connectionState |= - mNativeInterface.getConnectionState(getBytesFromAddress(identityAddress)); - } - return connectionState; + int connectionState = mNativeInterface.getConnectionState(getBytesFromAddress(address)); + final String identityAddress = getIdentityAddress(address); + if (identityAddress != null) { + connectionState |= + mNativeInterface.getConnectionState(getBytesFromAddress(identityAddress)); } - return mNativeInterface.getConnectionState(getBytesFromAddress(address)); + return connectionState; } int getConnectionHandle(BluetoothDevice device, int transport) { diff --git a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java index f9dcbe8017..0bdebd30e5 100644 --- a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java +++ b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java @@ -29,7 +29,6 @@ import android.os.ParcelUuid; import android.util.Log; import com.android.bluetooth.Utils; -import com.android.bluetooth.flags.Flags; class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { private static final String TAG = BluetoothSocketManagerBinder.class.getSimpleName(); @@ -58,10 +57,7 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { return null; } - String brEdrAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : mService.getIdentityAddress(device.getAddress()); + String brEdrAddress = Utils.getBrEdrAddress(device); Log.i( TAG, @@ -118,10 +114,7 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { mService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); enforceSocketOffloadSupport(type); } - String brEdrAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : mService.getIdentityAddress(device.getAddress()); + String brEdrAddress = Utils.getBrEdrAddress(device); Log.i( TAG, diff --git a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java index 87794605dc..f59e69a42d 100644 --- a/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java +++ b/android/app/src/com/android/bluetooth/btservice/MetricsLogger.java @@ -37,6 +37,12 @@ import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVEN import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__EVENT_TYPE__PROFILE_CONNECTION_VOLUME_CONTROL; import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__STATE_BONDED; import static com.android.bluetooth.BluetoothStatsLog.BLUETOOTH_CROSS_LAYER_EVENT_REPORTED__STATE__STATE_NONE; +import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__ASHA; +import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__CLASSIC; +import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__LE_AUDIO; +import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__DAY; +import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__MONTH; +import static com.android.bluetooth.BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__WEEK; import static com.android.bluetooth.BtRestrictedStatsLog.RESTRICTED_BLUETOOTH_DEVICE_NAME_REPORTED; import android.app.AlarmManager; @@ -45,6 +51,7 @@ import android.bluetooth.BluetoothA2dpSink; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAvrcpController; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHapClient; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHearingAid; @@ -60,11 +67,13 @@ import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothSap; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Build; import android.os.SystemClock; +import android.provider.Settings; import android.util.Log; import android.util.proto.ProtoOutputStream; @@ -87,11 +96,14 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.function.BiPredicate; /** Class of Bluetooth Metrics */ public class MetricsLogger { @@ -273,6 +285,7 @@ public class MetricsLogger { filter.addAction(BluetoothPbap.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED); filter.addAction(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED); mAdapterService.registerReceiver(mReceiver, filter); } @@ -285,9 +298,18 @@ public class MetricsLogger { Log.w(TAG, "Received intent with null action"); return; } + BluetoothDevice device = + intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); switch (action) { case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.A2DP, intent); + if (state == BluetoothProfile.STATE_CONNECTED + && isMedicalDevice(device)) { + updateHearingDeviceActiveTime( + device, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__CLASSIC); + } break; case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.A2DP_SINK, intent); @@ -297,12 +319,23 @@ public class MetricsLogger { break; case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.HEADSET, intent); + if (state == BluetoothProfile.STATE_CONNECTED + && isMedicalDevice(device)) { + updateHearingDeviceActiveTime( + device, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__CLASSIC); + } break; case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.HEADSET_CLIENT, intent); break; case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.HEARING_AID, intent); + if (state == BluetoothProfile.STATE_CONNECTED) { + updateHearingDeviceActiveTime( + device, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__ASHA); + } break; case BluetoothHidDevice.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.HID_DEVICE, intent); @@ -331,6 +364,14 @@ public class MetricsLogger { case BluetoothSap.ACTION_CONNECTION_STATE_CHANGED: logConnectionStateChanges(BluetoothProfile.SAP, intent); break; + case BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED: + if (state == BluetoothProfile.STATE_CONNECTED) { + updateHearingDeviceActiveTime( + device, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__DEVICE_TYPE__LE_AUDIO + ); + } + break; default: Log.w(TAG, "Received unknown intent " + intent); break; @@ -915,4 +956,56 @@ public class MetricsLogger { syncStatus, getRemoteDeviceInfoProto(device, false)); } + + void logHearingDeviceActiveEvent(BluetoothDevice device, int type, int timePeriod) { + BluetoothStatsLog.write( + BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED, + type, + timePeriod, + getRemoteDeviceInfoProto(device, true)); + } + + void updateHearingDeviceActiveTime(BluetoothDevice device, int deviceTypeProto) { + // Time comparison includes a +/- 1 hour tolerance to prevent data loss + updateLastActiveTime( + device, + deviceTypeProto, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__DAY, + "last_active_day", + (now, lastActive) -> now.isAfter(lastActive.plusDays(1).minusHours(1))); + updateLastActiveTime( + device, + deviceTypeProto, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__WEEK, + "last_active_week", + (now, lastActive) -> now.isAfter(lastActive.plusWeeks(1).minusHours(1))); + updateLastActiveTime( + device, + deviceTypeProto, + HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__MONTH, + "last_active_month", + (now, lastActive) -> now.isAfter(lastActive.plusMonths(1).minusHours(1))); + } + + private void updateLastActiveTime( + BluetoothDevice device, + int deviceTypeProto, + int timePeriodProto, + String timePeriodSettingsKey, + BiPredicate<LocalDateTime, LocalDateTime> timeComparison) { + final ContentResolver contentResolver = mAdapterService.getContentResolver(); + final String lastActive = Settings.Secure.getString(contentResolver, timePeriodSettingsKey); + final LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); + if (lastActive == null || timeComparison.test(now, LocalDateTime.parse(lastActive))) { + Settings.Secure.putString(contentResolver, timePeriodSettingsKey, now.toString()); + logHearingDeviceActiveEvent(device, deviceTypeProto, timePeriodProto); + } + } + + private boolean isMedicalDevice(BluetoothDevice device) { + final String deviceName = mAdapterService.getRemoteName(device); + final List<String> wordBreakdownList = getWordBreakdownList(deviceName); + boolean isMedicalDevice = !getMatchedStringForMedicalDevice(wordBreakdownList).isEmpty(); + return isMedicalDevice; + } } diff --git a/android/app/src/com/android/bluetooth/content_profiles/OWNERS b/android/app/src/com/android/bluetooth/content_profiles/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/src/com/android/bluetooth/content_profiles/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java index 2b49f467a8..7a6597f5b0 100644 --- a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java +++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorService.java @@ -17,7 +17,6 @@ package com.android.bluetooth.csip; -import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; @@ -31,7 +30,6 @@ import static java.util.Objects.requireNonNullElseGet; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothCsipSetCoordinator; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; @@ -40,7 +38,6 @@ import android.bluetooth.BluetoothUuid; import android.bluetooth.IBluetoothCsipSetCoordinator; import android.bluetooth.IBluetoothCsipSetCoordinatorCallback; import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback; -import android.content.AttributionSource; import android.content.Intent; import android.os.Handler; import android.os.HandlerThread; @@ -152,7 +149,7 @@ public class CsipSetCoordinatorService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothCsisBinder(this); + return new CsipSetCoordinatorServiceBinder(this); } @Override @@ -991,171 +988,6 @@ public class CsipSetCoordinatorService extends ProfileService { BluetoothProfile.CSIP_SET_COORDINATOR, device, fromState, toState); } - /** Binder object: must be a static class or memory leak may occur */ - @VisibleForTesting - static class BluetoothCsisBinder extends IBluetoothCsipSetCoordinator.Stub - implements IProfileServiceBinder { - private CsipSetCoordinatorService mService; - - BluetoothCsisBinder(CsipSetCoordinatorService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) - private CsipSetCoordinatorService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - CsipSetCoordinatorService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service; - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return false; - } - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - return service.getConnectionPolicy(device); - } - - @Override - public ParcelUuid lockGroup( - int groupId, - @NonNull IBluetoothCsipSetCoordinatorLockCallback callback, - AttributionSource source) { - requireNonNull(callback); - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return null; - } - - UUID lockUuid = service.lockGroup(groupId, callback); - return lockUuid == null ? null : new ParcelUuid(lockUuid); - } - - @Override - public void unlockGroup(@NonNull ParcelUuid lockUuid, AttributionSource source) { - requireNonNull(lockUuid); - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return; - } - - service.unlockGroup(lockUuid.getUuid()); - } - - @Override - public List<Integer> getAllGroupIds(ParcelUuid uuid, AttributionSource source) { - requireNonNull(uuid); - requireNonNull(source); - - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getAllGroupIds(uuid); - } - - @Override - public Map<Integer, ParcelUuid> getGroupUuidMapByDevice( - BluetoothDevice device, AttributionSource source) { - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return null; - } - - return service.getGroupUuidMapByDevice(device); - } - - @Override - public int getDesiredGroupSize(int groupId, AttributionSource source) { - CsipSetCoordinatorService service = getService(source); - if (service == null) { - return IBluetoothCsipSetCoordinator.CSIS_GROUP_SIZE_UNKNOWN; - } - - return service.getDesiredGroupSize(groupId); - } - } - @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java new file mode 100644 index 0000000000..5b08c1983b --- /dev/null +++ b/android/app/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinder.java @@ -0,0 +1,194 @@ +/* + * 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.csip; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static java.util.Objects.requireNonNull; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothCsipSetCoordinator; +import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback; +import android.content.AttributionSource; +import android.os.ParcelUuid; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +class CsipSetCoordinatorServiceBinder extends IBluetoothCsipSetCoordinator.Stub + implements IProfileServiceBinder { + private static final String TAG = CsipSetCoordinatorServiceBinder.class.getSimpleName(); + + private CsipSetCoordinatorService mService; + + CsipSetCoordinatorServiceBinder(CsipSetCoordinatorService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) + private CsipSetCoordinatorService getService(AttributionSource source) { + CsipSetCoordinatorService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service; + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + return service.getConnectionPolicy(device); + } + + @Override + public ParcelUuid lockGroup( + int groupId, + @NonNull IBluetoothCsipSetCoordinatorLockCallback callback, + AttributionSource source) { + requireNonNull(callback); + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return null; + } + + UUID lockUuid = service.lockGroup(groupId, callback); + return lockUuid == null ? null : new ParcelUuid(lockUuid); + } + + @Override + public void unlockGroup(@NonNull ParcelUuid lockUuid, AttributionSource source) { + requireNonNull(lockUuid); + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return; + } + service.unlockGroup(lockUuid.getUuid()); + } + + @Override + public List<Integer> getAllGroupIds(ParcelUuid uuid, AttributionSource source) { + requireNonNull(uuid); + requireNonNull(source); + + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getAllGroupIds(uuid); + } + + @Override + public Map<Integer, ParcelUuid> getGroupUuidMapByDevice( + BluetoothDevice device, AttributionSource source) { + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return null; + } + return service.getGroupUuidMapByDevice(device); + } + + @Override + public int getDesiredGroupSize(int groupId, AttributionSource source) { + CsipSetCoordinatorService service = getService(source); + if (service == null) { + return IBluetoothCsipSetCoordinator.CSIS_GROUP_SIZE_UNKNOWN; + } + return service.getDesiredGroupSize(groupId); + } +} diff --git a/android/app/src/com/android/bluetooth/csip/OWNERS b/android/app/src/com/android/bluetooth/csip/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/src/com/android/bluetooth/csip/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java index 6e5faefa9f..10190b09ef 100644 --- a/android/app/src/com/android/bluetooth/gatt/GattService.java +++ b/android/app/src/com/android/bluetooth/gatt/GattService.java @@ -1025,8 +1025,7 @@ public class GattService extends ProfileService { if (!checkConnectPermissionForDataDelivery(this, source, TAG, "registerClient")) { return; } - if (Flags.gattClientDynamicAllocation() - && mClientMap.countByAppUid(Binder.getCallingUid()) >= GATT_CLIENT_LIMIT_PER_APP) { + if (mClientMap.countByAppUid(Binder.getCallingUid()) >= GATT_CLIENT_LIMIT_PER_APP) { Log.w(TAG, "registerClient() - failed due to too many clients"); callbackToApp(() -> callback.onClientRegistered(BluetoothGatt.GATT_FAILURE, 0)); return; @@ -2320,21 +2319,14 @@ public class GattService extends ProfileService { int handle = 0; Integer connId = 0; - if (!Flags.gattServerRequestsFix()) { - HandleMap.Entry entry = mHandleMap.getByRequestId(requestId); - if (entry != null) { - handle = entry.mHandle; - } - connId = mServerMap.connIdByAddress(serverIf, address); + HandleMap.RequestData requestData = mHandleMap.getRequestDataByRequestId(requestId); + if (requestData != null) { + handle = requestData.handle(); + connId = requestData.connId(); } else { - HandleMap.RequestData requestData = mHandleMap.getRequestDataByRequestId(requestId); - if (requestData != null) { - handle = requestData.handle(); - connId = requestData.connId(); - } else { - connId = mServerMap.connIdByAddress(serverIf, address); - } + connId = mServerMap.connIdByAddress(serverIf, address); } + mNativeInterface.gattServerSendResponse( serverIf, connId != null ? connId : 0, diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java index 731f3da90c..9b8092763c 100644 --- a/android/app/src/com/android/bluetooth/hap/HapClientService.java +++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java @@ -159,7 +159,7 @@ public class HapClientService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new HapClientBinder(this); + return new HapClientServiceBinder(this); } @Override diff --git a/android/app/src/com/android/bluetooth/hap/HapClientBinder.java b/android/app/src/com/android/bluetooth/hap/HapClientServiceBinder.java index eba1d9db4a..e036e43bb2 100644 --- a/android/app/src/com/android/bluetooth/hap/HapClientBinder.java +++ b/android/app/src/com/android/bluetooth/hap/HapClientServiceBinder.java @@ -36,21 +36,19 @@ import android.content.AttributionSource; import android.util.Log; import com.android.bluetooth.Utils; -import com.android.bluetooth.btservice.ProfileService; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; import com.android.internal.annotations.VisibleForTesting; import java.util.Collections; import java.util.List; -/** HapClientBinder class */ @VisibleForTesting -class HapClientBinder extends IBluetoothHapClient.Stub - implements ProfileService.IProfileServiceBinder { - private static final String TAG = HapClientBinder.class.getSimpleName(); +class HapClientServiceBinder extends IBluetoothHapClient.Stub implements IProfileServiceBinder { + private static final String TAG = HapClientServiceBinder.class.getSimpleName(); private HapClientService mService; - HapClientBinder(HapClientService svc) { + HapClientServiceBinder(HapClientService svc) { mService = svc; } @@ -62,7 +60,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) private HapClientService getService(AttributionSource source) { requireNonNull(source); - // Cache mService because it can change while getService is called HapClientService service = mService; if (Utils.isInstrumentationTestMode()) { @@ -86,7 +83,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub if (service == null) { return Collections.emptyList(); } - return service.getConnectedDevices(); } @@ -97,7 +93,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub if (service == null) { return Collections.emptyList(); } - return service.getDevicesMatchingConnectionStates(states); } @@ -109,7 +104,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getConnectionState(device); } @@ -139,7 +133,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getConnectionPolicy(device); } @@ -151,7 +144,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getActivePresetIndex(device); } @@ -164,7 +156,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getActivePresetInfo(device); } @@ -176,7 +167,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getHapGroup(device); } @@ -188,7 +178,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - service.selectPreset(device, presetIndex); } @@ -198,7 +187,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub if (service == null) { return; } - service.selectPresetForGroup(groupId, presetIndex); } @@ -210,7 +198,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - service.switchToNextPreset(device); } @@ -220,7 +207,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub if (service == null) { return; } - service.switchToNextPresetForGroup(groupId); } @@ -232,7 +218,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - service.switchToPreviousPreset(device); } @@ -242,7 +227,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub if (service == null) { return; } - service.switchToPreviousPresetForGroup(groupId); } @@ -255,7 +239,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getPresetInfo(device, presetIndex); } @@ -268,7 +251,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getAllPresetInfo(device); } @@ -280,7 +262,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(device); - return service.getFeatures(device); } @@ -294,7 +275,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub requireNonNull(device); requireNonNull(name); - service.setPresetName(device, presetIndex, name); } @@ -307,7 +287,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(name); - service.setPresetNameForGroup(groupId, presetIndex, name); } @@ -319,7 +298,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(callback); - service.registerCallback(callback); } @@ -331,7 +309,6 @@ class HapClientBinder extends IBluetoothHapClient.Stub } requireNonNull(callback); - service.unregisterCallback(callback); } } diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java index a884944bac..0bf2650bb1 100644 --- a/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java +++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidService.java @@ -17,7 +17,6 @@ package com.android.bluetooth.hearingaid; import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; @@ -27,14 +26,11 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothHearingAid.AdvertisementServiceData; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothHearingAid; -import android.content.AttributionSource; import android.content.Intent; import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; @@ -57,7 +53,6 @@ import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -130,7 +125,7 @@ public class HearingAidService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothHearingAidBinder(this); + return new HearingAidServiceBinder(this); } @Override @@ -490,7 +485,7 @@ public class HearingAidService extends ProfileService { return mDeviceCapabilitiesMap.getOrDefault(device, -1); } - private AdvertisementServiceData getAdvertisementServiceData(BluetoothDevice device) { + AdvertisementServiceData getAdvertisementServiceData(BluetoothDevice device) { int capability = mAdapterService.getAshaCapability(device); int id = mAdapterService.getAshaTruncatedHiSyncId(device); if (capability < 0) { @@ -855,214 +850,6 @@ public class HearingAidService extends ProfileService { device, BluetoothProfile.HEARING_AID, toState, fromState); } - /** Binder object: must be a static class or memory leak may occur */ - @VisibleForTesting - static class BluetoothHearingAidBinder extends IBluetoothHearingAid.Stub - implements IProfileServiceBinder { - private HearingAidService mService; - - BluetoothHearingAidBinder(HearingAidService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private HearingAidService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - HearingAidService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return false; - } - - if (device == null) { - return service.removeActiveDevice(false); - } else { - return service.setActiveDevice(device); - } - } - - @Override - public List<BluetoothDevice> getActiveDevices(AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getActiveDevices(); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionPolicy(device); - } - - @Override - public void setVolume(int volume, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - service.setVolume(volume); - } - - @Override - public long getHiSyncId(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return BluetoothHearingAid.HI_SYNC_ID_INVALID; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getHiSyncId(device); - } - - @Override - public int getDeviceSide(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return BluetoothHearingAid.SIDE_UNKNOWN; - } - - int side = service.getCapabilities(device); - if (side != BluetoothHearingAid.SIDE_UNKNOWN) { - side &= 1; - } - - return side; - } - - @Override - public int getDeviceMode(BluetoothDevice device, AttributionSource source) { - HearingAidService service = getService(source); - if (service == null) { - return BluetoothHearingAid.MODE_UNKNOWN; - } - - int mode = service.getCapabilities(device); - if (mode != BluetoothHearingAid.MODE_UNKNOWN) { - mode = mode >> 1 & 1; - } - - return mode; - } - - @Override - public AdvertisementServiceData getAdvertisementServiceData( - BluetoothDevice device, AttributionSource source) { - HearingAidService service = mService; - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkScanPermissionForDataDelivery( - service, source, TAG, "getAdvertisementServiceData")) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getAdvertisementServiceData(device); - } - } - @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java b/android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java new file mode 100644 index 0000000000..de384fba5c --- /dev/null +++ b/android/app/src/com/android/bluetooth/hearingaid/HearingAidServiceBinder.java @@ -0,0 +1,230 @@ +/* + * 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.hearingaid; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothHearingAid.AdvertisementServiceData; +import android.bluetooth.IBluetoothHearingAid; +import android.content.AttributionSource; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class HearingAidServiceBinder extends IBluetoothHearingAid.Stub implements IProfileServiceBinder { + private static final String TAG = HearingAidServiceBinder.class.getSimpleName(); + + private HearingAidService mService; + + HearingAidServiceBinder(HearingAidService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private HearingAidService getService(AttributionSource source) { + HearingAidService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return false; + } + + if (device == null) { + return service.removeActiveDevice(false); + } else { + return service.setActiveDevice(device); + } + } + + @Override + public List<BluetoothDevice> getActiveDevices(AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getActiveDevices(); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public void setVolume(int volume, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setVolume(volume); + } + + @Override + public long getHiSyncId(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return BluetoothHearingAid.HI_SYNC_ID_INVALID; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getHiSyncId(device); + } + + @Override + public int getDeviceSide(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return BluetoothHearingAid.SIDE_UNKNOWN; + } + + int side = service.getCapabilities(device); + if (side != BluetoothHearingAid.SIDE_UNKNOWN) { + side &= 1; + } + + return side; + } + + @Override + public int getDeviceMode(BluetoothDevice device, AttributionSource source) { + HearingAidService service = getService(source); + if (service == null) { + return BluetoothHearingAid.MODE_UNKNOWN; + } + + int mode = service.getCapabilities(device); + if (mode != BluetoothHearingAid.MODE_UNKNOWN) { + mode = mode >> 1 & 1; + } + + return mode; + } + + @Override + public AdvertisementServiceData getAdvertisementServiceData( + BluetoothDevice device, AttributionSource source) { + HearingAidService service = mService; + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkScanPermissionForDataDelivery( + service, source, TAG, "getAdvertisementServiceData")) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getAdvertisementServiceData(device); + } +} diff --git a/android/app/src/com/android/bluetooth/hearingaid/OWNERS b/android/app/src/com/android/bluetooth/hearingaid/OWNERS deleted file mode 100644 index bbaa85ddbb..0000000000 --- a/android/app/src/com/android/bluetooth/hearingaid/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_hearingaid diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java index 5afe78cee5..e3816ed636 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java @@ -24,7 +24,6 @@ import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.GuardedBy; /** @@ -84,16 +83,12 @@ public class HeadsetNativeInterface { return mAdapterService.getDeviceFromByte(address); } - private byte[] getByteAddress(BluetoothDevice device) { + private static byte[] getByteAddress(BluetoothDevice device) { if (device == null) { // Set bt_stack's active device to default if java layer set active device to null return Utils.getBytesFromAddress("00:00:00:00:00:00"); } - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getByteBrEdrAddress(device); - } else { - return mAdapterService.getByteIdentityAddress(device); - } + return Utils.getByteBrEdrAddress(device); } void onConnectionStateChanged(int state, byte[] address) { diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java index 251f347ea2..c5ab1f8dde 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetService.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetService.java @@ -17,8 +17,6 @@ package com.android.bluetooth.hfp; import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; -import static android.Manifest.permission.MODIFY_PHONE_STATE; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; @@ -33,15 +31,12 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothSinkAudioPolicy; import android.bluetooth.BluetoothStatusCodes; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothHeadset; -import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -80,7 +75,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -237,7 +231,7 @@ public class HeadsetService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new BluetoothHeadsetBinder(this); + return new HeadsetServiceBinder(this); } @Override @@ -516,303 +510,6 @@ public class HeadsetService extends ProfileService { } } - /** Handlers for incoming service calls */ - @VisibleForTesting - static class BluetoothHeadsetBinder extends IBluetoothHeadset.Stub - implements IProfileServiceBinder { - private HeadsetService mService; - - BluetoothHeadsetBinder(HeadsetService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private HeadsetService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - HeadsetService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getConnectionPolicy(device); - } - - @Override - public boolean isNoiseReductionSupported(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - return service.isNoiseReductionSupported(device); - } - - @Override - public boolean isVoiceRecognitionSupported( - BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - return service.isVoiceRecognitionSupported(device); - } - - @Override - public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - requireNonNull(device); - - return service.startVoiceRecognition(device); - } - - @Override - public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - return service.stopVoiceRecognition(device); - } - - @Override - public boolean isAudioConnected(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - return service.isAudioConnected(device); - } - - @Override - public int getAudioState(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getAudioState(device); - } - - @Override - public int connectAudio(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.connectAudio(); - } - - @Override - public int disconnectAudio(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.disconnectAudio(); - } - - @Override - public void setAudioRouteAllowed(boolean allowed, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setAudioRouteAllowed(allowed); - } - - @Override - public boolean getAudioRouteAllowed(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getAudioRouteAllowed(); - } - - @Override - public void setForceScoAudio(boolean forced, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return; - } - - service.setForceScoAudio(forced); - } - - @Override - public boolean startScoUsingVirtualVoiceCall(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.startScoUsingVirtualVoiceCall(); - } - - @Override - public boolean stopScoUsingVirtualVoiceCall(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.stopScoUsingVirtualVoiceCall(); - } - - @Override - public boolean sendVendorSpecificResultCode( - BluetoothDevice device, String command, String arg, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - return service.sendVendorSpecificResultCode(device, command, arg); - } - - @Override - public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); - - return service.setActiveDevice(device); - } - - @Override - public BluetoothDevice getActiveDevice(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return null; - } - - return service.getActiveDevice(); - } - - @Override - public boolean isInbandRingingEnabled(AttributionSource source) { - HeadsetService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.isInbandRingingEnabled(); - } - } - // API methods public static synchronized HeadsetService getHeadsetService() { if (sHeadsetService == null) { @@ -947,7 +644,6 @@ public class HeadsetService extends ProfileService { * @param states an array of states from {@link BluetoothProfile} * @return a list of devices matching the array of connection states */ - @VisibleForTesting public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { ArrayList<BluetoothDevice> devices = new ArrayList<>(); synchronized (mStateMachines) { @@ -1659,7 +1355,6 @@ public class HeadsetService extends ProfileService { } } - @VisibleForTesting boolean startScoUsingVirtualVoiceCall() { Log.i(TAG, "startScoUsingVirtualVoiceCall: " + Utils.getUidPidString()); synchronized (mStateMachines) { @@ -2134,8 +1829,7 @@ public class HeadsetService extends ProfileService { } } - private boolean sendVendorSpecificResultCode( - BluetoothDevice device, String command, String arg) { + boolean sendVendorSpecificResultCode(BluetoothDevice device, String command, String arg) { synchronized (mStateMachines) { final HeadsetStateMachine stateMachine = mStateMachines.get(device); if (stateMachine == null) { @@ -2396,9 +2090,10 @@ public class HeadsetService extends ProfileService { // Do it here because some controllers cannot handle SCO and CIS // co-existence see {@link LeAudioService#setInactiveForHfpHandover} LeAudioService leAudioService = mFactory.getLeAudioService(); - boolean isLeAudioConnectedDeviceNotActive = leAudioService != null - && !leAudioService.getConnectedDevices().isEmpty() - && leAudioService.getActiveDevices().get(0) == null; + boolean isLeAudioConnectedDeviceNotActive = + leAudioService != null + && !leAudioService.getConnectedDevices().isEmpty() + && leAudioService.getActiveDevices().get(0) == null; // usually controller limitation cause CONNECTING -> DISCONNECTED, so only // resume LE audio active device if it is HFP audio only and SCO disconnected if (fromState != BluetoothHeadset.STATE_AUDIO_CONNECTING diff --git a/android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java b/android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java new file mode 100644 index 0000000000..d93ad5dc5f --- /dev/null +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetServiceBinder.java @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2022 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.hfp; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.Manifest.permission.MODIFY_PHONE_STATE; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static java.util.Objects.requireNonNull; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadset; +import android.bluetooth.BluetoothStatusCodes; +import android.bluetooth.IBluetoothHeadset; +import android.content.AttributionSource; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class HeadsetServiceBinder extends IBluetoothHeadset.Stub implements IProfileServiceBinder { + private static final String TAG = HeadsetServiceBinder.class.getSimpleName(); + + private HeadsetService mService; + + HeadsetServiceBinder(HeadsetService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private HeadsetService getService(AttributionSource source) { + HeadsetService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public boolean isNoiseReductionSupported(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + return service.isNoiseReductionSupported(device); + } + + @Override + public boolean isVoiceRecognitionSupported(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + return service.isVoiceRecognitionSupported(device); + } + + @Override + public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + requireNonNull(device); + return service.startVoiceRecognition(device); + } + + @Override + public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + return service.stopVoiceRecognition(device); + } + + @Override + public boolean isAudioConnected(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + return service.isAudioConnected(device); + } + + @Override + public int getAudioState(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return BluetoothHeadset.STATE_AUDIO_DISCONNECTED; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getAudioState(device); + } + + @Override + public int connectAudio(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.connectAudio(); + } + + @Override + public int disconnectAudio(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.disconnectAudio(); + } + + @Override + public void setAudioRouteAllowed(boolean allowed, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setAudioRouteAllowed(allowed); + } + + @Override + public boolean getAudioRouteAllowed(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getAudioRouteAllowed(); + } + + @Override + public void setForceScoAudio(boolean forced, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return; + } + service.setForceScoAudio(forced); + } + + @Override + public boolean startScoUsingVirtualVoiceCall(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + + return service.startScoUsingVirtualVoiceCall(); + } + + @Override + public boolean stopScoUsingVirtualVoiceCall(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + + return service.stopScoUsingVirtualVoiceCall(); + } + + @Override + public boolean sendVendorSpecificResultCode( + BluetoothDevice device, String command, String arg, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + return service.sendVendorSpecificResultCode(device, command, arg); + } + + @Override + public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null); + return service.setActiveDevice(device); + } + + @Override + public BluetoothDevice getActiveDevice(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return null; + } + return service.getActiveDevice(); + } + + @Override + public boolean isInbandRingingEnabled(AttributionSource source) { + HeadsetService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isInbandRingingEnabled(); + } +} diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java index 9c0e416168..7d3a5ff43f 100644 --- a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java +++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientService.java @@ -16,11 +16,8 @@ package com.android.bluetooth.hfpclient; -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; @@ -28,15 +25,11 @@ import static android.content.pm.PackageManager.FEATURE_WATCH; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothHeadsetClient; import android.bluetooth.BluetoothHeadsetClientCall; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothSinkAudioPolicy; import android.bluetooth.BluetoothStatusCodes; -import android.bluetooth.IBluetoothHeadsetClient; -import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -58,7 +51,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -145,7 +137,7 @@ public class HeadsetClientService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new BluetoothHeadsetClientBinder(this); + return new HeadsetClientServiceBinder(this); } @Override @@ -290,340 +282,6 @@ public class HeadsetClientService extends ProfileService { call.isInBandRing()); } - /** Handlers for incoming service calls */ - @VisibleForTesting - static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub - implements IProfileServiceBinder { - private HeadsetClientService mService; - - BluetoothHeadsetClientBinder(HeadsetClientService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private HeadsetClientService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - HeadsetClientService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionPolicy(device); - } - - @Override - public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.startVoiceRecognition(device); - } - - @Override - public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.stopVoiceRecognition(device); - } - - @Override - public int getAudioState(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; - } - - return service.getAudioState(device); - } - - @Override - public void setAudioRouteAllowed( - BluetoothDevice device, boolean allowed, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - Log.w(TAG, "Service handle is null for setAudioRouteAllowed!"); - return; - } - - service.setAudioRouteAllowed(device, allowed); - } - - @Override - public boolean getAudioRouteAllowed(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - Log.w(TAG, "Service handle is null for getAudioRouteAllowed!"); - return false; - } - - return service.getAudioRouteAllowed(device); - } - - @Override - public boolean connectAudio(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.connectAudio(device); - } - - @Override - public boolean disconnectAudio(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.disconnectAudio(device); - } - - @Override - public boolean acceptCall(BluetoothDevice device, int flag, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.acceptCall(device, flag); - } - - @Override - public boolean rejectCall(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.rejectCall(device); - } - - @Override - public boolean holdCall(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.holdCall(device); - } - - @Override - public boolean terminateCall( - BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - Log.w(TAG, "service is null"); - return false; - } - - return service.terminateCall(device, call != null ? call.getUUID() : null); - } - - @Override - public boolean explicitCallTransfer(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.explicitCallTransfer(device); - } - - @Override - public boolean enterPrivateMode( - BluetoothDevice device, int index, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.enterPrivateMode(device, index); - } - - @Override - public BluetoothHeadsetClientCall dial( - BluetoothDevice device, String number, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return null; - } - - return toLegacyCall(service.dial(device, number)); - } - - @Override - public List<BluetoothHeadsetClientCall> getCurrentCalls( - BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - List<BluetoothHeadsetClientCall> currentCalls = new ArrayList<>(); - if (service == null) { - return currentCalls; - } - - List<HfpClientCall> calls = service.getCurrentCalls(device); - if (calls != null) { - for (HfpClientCall call : calls) { - currentCalls.add(toLegacyCall(call)); - } - } - return currentCalls; - } - - @Override - public boolean sendDTMF(BluetoothDevice device, byte code, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.sendDTMF(device, code); - } - - @Override - public boolean getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.getLastVoiceTagNumber(device); - } - - @Override - public Bundle getCurrentAgEvents(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return null; - } - - service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getCurrentAgEvents(device); - } - - @Override - public boolean sendVendorAtCommand( - BluetoothDevice device, int vendorId, String atCommand, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return false; - } - - return service.sendVendorAtCommand(device, vendorId, atCommand); - } - - @Override - public Bundle getCurrentAgFeatures(BluetoothDevice device, AttributionSource source) { - HeadsetClientService service = getService(source); - if (service == null) { - return null; - } - - return service.getCurrentAgFeaturesBundle(device); - } - } - // API methods public static synchronized HeadsetClientService getHeadsetClientService() { if (sHeadsetClientService == null) { diff --git a/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java new file mode 100644 index 0000000000..3232fccd4f --- /dev/null +++ b/android/app/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinder.java @@ -0,0 +1,345 @@ +/* + * 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.hfpclient; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHeadsetClient; +import android.bluetooth.BluetoothHeadsetClientCall; +import android.bluetooth.IBluetoothHeadsetClient; +import android.content.AttributionSource; +import android.os.Bundle; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** Handlers for incoming service calls */ +class HeadsetClientServiceBinder extends IBluetoothHeadsetClient.Stub + implements IProfileServiceBinder { + private static final String TAG = HeadsetClientServiceBinder.class.getSimpleName(); + + private HeadsetClientService mService; + + HeadsetClientServiceBinder(HeadsetClientService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private HeadsetClientService getService(AttributionSource source) { + HeadsetClientService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + + service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public boolean startVoiceRecognition(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.startVoiceRecognition(device); + } + + @Override + public boolean stopVoiceRecognition(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.stopVoiceRecognition(device); + } + + @Override + public int getAudioState(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; + } + return service.getAudioState(device); + } + + @Override + public void setAudioRouteAllowed( + BluetoothDevice device, boolean allowed, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + Log.w(TAG, "Service handle is null for setAudioRouteAllowed!"); + return; + } + service.setAudioRouteAllowed(device, allowed); + } + + @Override + public boolean getAudioRouteAllowed(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + Log.w(TAG, "Service handle is null for getAudioRouteAllowed!"); + return false; + } + return service.getAudioRouteAllowed(device); + } + + @Override + public boolean connectAudio(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.connectAudio(device); + } + + @Override + public boolean disconnectAudio(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.disconnectAudio(device); + } + + @Override + public boolean acceptCall(BluetoothDevice device, int flag, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.acceptCall(device, flag); + } + + @Override + public boolean rejectCall(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.rejectCall(device); + } + + @Override + public boolean holdCall(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.holdCall(device); + } + + @Override + public boolean terminateCall( + BluetoothDevice device, BluetoothHeadsetClientCall call, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + Log.w(TAG, "service is null"); + return false; + } + return service.terminateCall(device, call != null ? call.getUUID() : null); + } + + @Override + public boolean explicitCallTransfer(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.explicitCallTransfer(device); + } + + @Override + public boolean enterPrivateMode(BluetoothDevice device, int index, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.enterPrivateMode(device, index); + } + + @Override + public BluetoothHeadsetClientCall dial( + BluetoothDevice device, String number, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return null; + } + return HeadsetClientService.toLegacyCall(service.dial(device, number)); + } + + @Override + public List<BluetoothHeadsetClientCall> getCurrentCalls( + BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + List<BluetoothHeadsetClientCall> currentCalls = new ArrayList<>(); + if (service == null) { + return currentCalls; + } + + List<HfpClientCall> calls = service.getCurrentCalls(device); + if (calls != null) { + for (HfpClientCall call : calls) { + currentCalls.add(HeadsetClientService.toLegacyCall(call)); + } + } + return currentCalls; + } + + @Override + public boolean sendDTMF(BluetoothDevice device, byte code, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.sendDTMF(device, code); + } + + @Override + public boolean getLastVoiceTagNumber(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.getLastVoiceTagNumber(device); + } + + @Override + public Bundle getCurrentAgEvents(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return null; + } + + service.enforceCallingPermission(BLUETOOTH_PRIVILEGED, null); + return service.getCurrentAgEvents(device); + } + + @Override + public boolean sendVendorAtCommand( + BluetoothDevice device, int vendorId, String atCommand, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return false; + } + return service.sendVendorAtCommand(device, vendorId, atCommand); + } + + @Override + public Bundle getCurrentAgFeatures(BluetoothDevice device, AttributionSource source) { + HeadsetClientService service = getService(source); + if (service == null) { + return null; + } + return service.getCurrentAgFeaturesBundle(device); + } +} diff --git a/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java b/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java index fbb68747fe..bcbb49fba6 100644 --- a/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java +++ b/android/app/src/com/android/bluetooth/hfpclient/NativeInterface.java @@ -27,7 +27,6 @@ import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -334,12 +333,8 @@ public class NativeInterface { return mAdapterService.getDeviceFromByte(address); } - private byte[] getByteAddress(BluetoothDevice device) { - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getByteBrEdrAddress(device); - } else { - return mAdapterService.getByteIdentityAddress(device); - } + private static byte[] getByteAddress(BluetoothDevice device) { + return Utils.getByteBrEdrAddress(device); } // Callbacks from the native back into the java framework. All callbacks are routed via the diff --git a/android/app/src/com/android/bluetooth/hfpclient/OWNERS b/android/app/src/com/android/bluetooth/hfpclient/OWNERS deleted file mode 100644 index ecd59038a1..0000000000 --- a/android/app/src/com/android/bluetooth/hfpclient/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_automotive diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java b/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java index 2ca9078103..dc269b7abf 100644 --- a/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java +++ b/android/app/src/com/android/bluetooth/hid/HidDeviceNativeInterface.java @@ -29,7 +29,6 @@ import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; /** HID Device Native Interface to/from JNI. */ @@ -123,7 +122,7 @@ public class HidDeviceNativeInterface { * @return the result of the native call */ public boolean connect(BluetoothDevice device) { - return connectNative(getByteAddress(device)); + return connectNative(Utils.getByteBrEdrAddress(device)); } /** @@ -243,14 +242,6 @@ public class HidDeviceNativeInterface { return mAdapterService.getDeviceFromByte(address); } - private byte[] getByteAddress(BluetoothDevice device) { - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getByteBrEdrAddress(device); - } else { - return mAdapterService.getByteIdentityAddress(device); - } - } - private native void initNative(); private native void cleanupNative(); diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java index 2c2f11940e..d3d84d9630 100644 --- a/android/app/src/com/android/bluetooth/hid/HidDeviceService.java +++ b/android/app/src/com/android/bluetooth/hid/HidDeviceService.java @@ -34,9 +34,7 @@ import android.bluetooth.BluetoothHidDevice; import android.bluetooth.BluetoothHidDeviceAppQosSettings; import android.bluetooth.BluetoothHidDeviceAppSdpSettings; import android.bluetooth.BluetoothProfile; -import android.bluetooth.IBluetoothHidDevice; import android.bluetooth.IBluetoothHidDeviceCallback; -import android.content.AttributionSource; import android.content.Intent; import android.os.Binder; import android.os.Handler; @@ -50,15 +48,12 @@ import android.util.Log; import com.android.bluetooth.Utils; 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.internal.annotations.VisibleForTesting; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; @@ -328,204 +323,9 @@ public class HidDeviceService extends ProfileService { } }; - @VisibleForTesting - static class BluetoothHidDeviceBinder extends IBluetoothHidDevice.Stub - implements IProfileServiceBinder { - - private static final String TAG = BluetoothHidDeviceBinder.class.getSimpleName(); - - private HidDeviceService mService; - - BluetoothHidDeviceBinder(HidDeviceService service) { - mService = service; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private HidDeviceService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - HidDeviceService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - @Override - public boolean registerApp( - BluetoothHidDeviceAppSdpSettings sdp, - BluetoothHidDeviceAppQosSettings inQos, - BluetoothHidDeviceAppQosSettings outQos, - IBluetoothHidDeviceCallback callback, - AttributionSource source) { - Log.d(TAG, "registerApp()"); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.registerApp(sdp, inQos, outQos, callback); - } - - @Override - public boolean unregisterApp(AttributionSource source) { - Log.d(TAG, "unregisterApp()"); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.unregisterApp(); - } - - @Override - public boolean sendReport( - BluetoothDevice device, int id, byte[] data, AttributionSource source) { - Log.d(TAG, "sendReport(): device=" + device + " id=" + id); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.sendReport(device, id, data); - } - - @Override - public boolean replyReport( - BluetoothDevice device, byte type, byte id, byte[] data, AttributionSource source) { - Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.replyReport(device, type, id, data); - } - - @Override - public boolean unplug(BluetoothDevice device, AttributionSource source) { - Log.d(TAG, "unplug(): device=" + device); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.unplug(device); - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - Log.d(TAG, "connect(): device=" + device); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - Log.d(TAG, "disconnect(): device=" + device); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - Log.d( - TAG, - "setConnectionPolicy():" - + (" device=" + device) - + (" connectionPolicy=" + connectionPolicy)); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public boolean reportError(BluetoothDevice device, byte error, AttributionSource source) { - Log.d(TAG, "reportError(): device=" + device + " error=" + error); - - HidDeviceService service = getService(source); - if (service == null) { - return false; - } - - return service.reportError(device, error); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - Log.d(TAG, "getConnectionState(): device=" + device); - - HidDeviceService service = getService(source); - if (service == null) { - return BluetoothHidDevice.STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - Log.d(TAG, "getConnectedDevices()"); - - return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - Log.d(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); - - HidDeviceService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public String getUserAppName(AttributionSource source) { - HidDeviceService service = getService(source); - if (service == null) { - return ""; - } - return service.getUserAppName(); - } - } - @Override protected IProfileServiceBinder initBinder() { - return new BluetoothHidDeviceBinder(this); + return new HidDeviceServiceBinder(this); } private boolean checkDevice(BluetoothDevice device) { diff --git a/android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java b/android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java new file mode 100644 index 0000000000..0cd0e1d301 --- /dev/null +++ b/android/app/src/com/android/bluetooth/hid/HidDeviceServiceBinder.java @@ -0,0 +1,218 @@ +/* + * 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.hid; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHidDevice; +import android.bluetooth.BluetoothHidDeviceAppQosSettings; +import android.bluetooth.BluetoothHidDeviceAppSdpSettings; +import android.bluetooth.IBluetoothHidDevice; +import android.bluetooth.IBluetoothHidDeviceCallback; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +class HidDeviceServiceBinder extends IBluetoothHidDevice.Stub implements IProfileServiceBinder { + private static final String TAG = HidDeviceServiceBinder.class.getSimpleName(); + + private HidDeviceService mService; + + HidDeviceServiceBinder(HidDeviceService service) { + mService = service; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private HidDeviceService getService(AttributionSource source) { + // Cache mService because it can change while getService is called + HidDeviceService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @Override + public boolean registerApp( + BluetoothHidDeviceAppSdpSettings sdp, + BluetoothHidDeviceAppQosSettings inQos, + BluetoothHidDeviceAppQosSettings outQos, + IBluetoothHidDeviceCallback callback, + AttributionSource source) { + Log.d(TAG, "registerApp()"); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.registerApp(sdp, inQos, outQos, callback); + } + + @Override + public boolean unregisterApp(AttributionSource source) { + Log.d(TAG, "unregisterApp()"); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.unregisterApp(); + } + + @Override + public boolean sendReport( + BluetoothDevice device, int id, byte[] data, AttributionSource source) { + Log.d(TAG, "sendReport(): device=" + device + " id=" + id); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.sendReport(device, id, data); + } + + @Override + public boolean replyReport( + BluetoothDevice device, byte type, byte id, byte[] data, AttributionSource source) { + Log.d(TAG, "replyReport(): device=" + device + " type=" + type + " id=" + id); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.replyReport(device, type, id, data); + } + + @Override + public boolean unplug(BluetoothDevice device, AttributionSource source) { + Log.d(TAG, "unplug(): device=" + device); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.unplug(device); + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + Log.d(TAG, "connect(): device=" + device); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + Log.d(TAG, "disconnect(): device=" + device); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + Log.d( + TAG, + "setConnectionPolicy():" + + (" device=" + device) + + (" connectionPolicy=" + connectionPolicy)); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public boolean reportError(BluetoothDevice device, byte error, AttributionSource source) { + Log.d(TAG, "reportError(): device=" + device + " error=" + error); + + HidDeviceService service = getService(source); + if (service == null) { + return false; + } + return service.reportError(device, error); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + Log.d(TAG, "getConnectionState(): device=" + device); + + HidDeviceService service = getService(source); + if (service == null) { + return BluetoothHidDevice.STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + Log.d(TAG, "getConnectedDevices()"); + + return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + Log.d(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states)); + + HidDeviceService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public String getUserAppName(AttributionSource source) { + HidDeviceService service = getService(source); + if (service == null) { + return ""; + } + return service.getUserAppName(); + } +} diff --git a/android/app/src/com/android/bluetooth/hid/HidHostService.java b/android/app/src/com/android/bluetooth/hid/HidHostService.java index 3a29ed5b62..68c2be4a8b 100644 --- a/android/app/src/com/android/bluetooth/hid/HidHostService.java +++ b/android/app/src/com/android/bluetooth/hid/HidHostService.java @@ -17,7 +17,6 @@ package com.android.bluetooth.hid; import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; @@ -27,14 +26,11 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHidHost; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothHidHost; -import android.content.AttributionSource; import android.content.Intent; import android.os.Bundle; import android.os.Handler; @@ -145,7 +141,7 @@ public class HidHostService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new BluetoothHidHostBinder(this); + return new HidHostServiceBinder(this); } @Override @@ -166,14 +162,6 @@ public class HidHostService extends ProfileService { setHidHostService(null); } - private byte[] getIdentityAddress(BluetoothDevice device) { - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getByteBrEdrAddress(mAdapterService, device); - } else { - return mAdapterService.getByteIdentityAddress(device); - } - } - private byte[] getByteAddress(BluetoothDevice device, int transport) { final ParcelUuid[] uuids = mAdapterService.getRemoteUuids(device); @@ -181,14 +169,14 @@ public class HidHostService extends ProfileService { // Use pseudo address when HOGP is to be used return Utils.getByteAddress(device); } else if (transport == BluetoothDevice.TRANSPORT_BREDR) { - // Use identity address if HID is to be used - return getIdentityAddress(device); + // Use BR/EDR address if HID is to be used + return Utils.getByteBrEdrAddress(mAdapterService, device); } else { // BluetoothDevice.TRANSPORT_AUTO boolean hidSupported = Utils.arrayContains(uuids, BluetoothUuid.HID); // Prefer HID over HOGP if (hidSupported) { - // Use identity address if HID is available - return getIdentityAddress(device); + // Use BR/EDR address if HID is available + return Utils.getByteBrEdrAddress(mAdapterService, device); } else { // Otherwise use pseudo address return Utils.getByteAddress(device); @@ -713,207 +701,6 @@ public class HidHostService extends ProfileService { return true; } - @VisibleForTesting - static class BluetoothHidHostBinder extends IBluetoothHidHost.Stub - implements IProfileServiceBinder { - private HidHostService mService; - - BluetoothHidHostBinder(HidHostService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private HidHostService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - HidHostService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.disconnect(device); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - return service.getConnectionState(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getConnectionPolicy(device); - } - - @Override - public boolean setPreferredTransport( - BluetoothDevice device, int transport, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.setPreferredTransport(device, transport); - } - - @Override - public int getPreferredTransport(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return BluetoothDevice.TRANSPORT_AUTO; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getPreferredTransport(device); - } - - /* The following APIs regarding test app for compliance */ - @Override - public boolean getProtocolMode(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.getProtocolMode(device); - } - - @Override - public boolean virtualUnplug(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.virtualUnplug(device); - } - - @Override - public boolean setProtocolMode( - BluetoothDevice device, int protocolMode, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.setProtocolMode(device, protocolMode); - } - - @Override - public boolean getReport( - BluetoothDevice device, - byte reportType, - byte reportId, - int bufferSize, - AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.getReport(device, reportType, reportId, bufferSize); - } - - @Override - public boolean setReport( - BluetoothDevice device, byte reportType, String report, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.setReport(device, reportType, report); - } - - @Override - public boolean sendData(BluetoothDevice device, String report, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.sendData(device, report); - } - - @Override - public boolean setIdleTime( - BluetoothDevice device, byte idleTime, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.setIdleTime(device, idleTime); - } - - @Override - public boolean getIdleTime(BluetoothDevice device, AttributionSource source) { - HidHostService service = getService(source); - if (service == null) { - return false; - } - return service.getIdleTime(device); - } - } - ; - // APIs /** @@ -982,7 +769,6 @@ public class HidHostService extends ProfileService { return STATE_DISCONNECTED; } - @VisibleForTesting List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { Log.d(TAG, "getDevicesMatchingConnectionStates()"); return mInputDevices.entrySet().stream() @@ -1073,7 +859,6 @@ public class HidHostService extends ProfileService { /** * @see BluetoothHidHost#getPreferredTransport */ - @VisibleForTesting int getPreferredTransport(BluetoothDevice device) { Log.d(TAG, "getPreferredTransport: device=" + device); @@ -1082,7 +867,6 @@ public class HidHostService extends ProfileService { } /* The following APIs regarding test app for compliance */ - @VisibleForTesting boolean getProtocolMode(BluetoothDevice device) { Log.d(TAG, "getProtocolMode: device=" + device); int state = this.getConnectionState(device); @@ -1094,7 +878,6 @@ public class HidHostService extends ProfileService { return true; } - @VisibleForTesting boolean virtualUnplug(BluetoothDevice device) { Log.d(TAG, "virtualUnplug: device=" + device); int state = this.getConnectionState(device); @@ -1106,7 +889,6 @@ public class HidHostService extends ProfileService { return true; } - @VisibleForTesting boolean setProtocolMode(BluetoothDevice device, int protocolMode) { Log.d(TAG, "setProtocolMode: device=" + device); int state = this.getConnectionState(device); @@ -1120,7 +902,6 @@ public class HidHostService extends ProfileService { return true; } - @VisibleForTesting boolean getReport(BluetoothDevice device, byte reportType, byte reportId, int bufferSize) { Log.d(TAG, "getReport: device=" + device); int state = this.getConnectionState(device); @@ -1138,7 +919,6 @@ public class HidHostService extends ProfileService { return true; } - @VisibleForTesting boolean setReport(BluetoothDevice device, byte reportType, String report) { Log.d(TAG, "setReport: device=" + device); int state = this.getConnectionState(device); @@ -1155,7 +935,6 @@ public class HidHostService extends ProfileService { return true; } - @VisibleForTesting boolean sendData(BluetoothDevice device, String report) { Log.d(TAG, "sendData: device=" + device); int state = this.getConnectionState(device); @@ -1393,8 +1172,9 @@ public class HidHostService extends ProfileService { // Allow this connection only if the device is bonded. Any attempt to connect // while bonding would potentially lead to an unauthorized connection. if (bondState != BluetoothDevice.BOND_BONDED) { - Log.w(TAG, "okToConnect: return false, device=" + device + " bondState=" - + bondState); + Log.w( + TAG, + "okToConnect: return false, device=" + device + " bondState=" + bondState); return false; } } diff --git a/android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java b/android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java new file mode 100644 index 0000000000..3295c83f01 --- /dev/null +++ b/android/app/src/com/android/bluetooth/hid/HidHostServiceBinder.java @@ -0,0 +1,232 @@ +/* + * 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.hid; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothHidHost; +import android.content.AttributionSource; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class HidHostServiceBinder extends IBluetoothHidHost.Stub implements IProfileServiceBinder { + private static final String TAG = HidHostServiceBinder.class.getSimpleName(); + + private HidHostService mService; + + HidHostServiceBinder(HidHostService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private HidHostService getService(AttributionSource source) { + HidHostService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.disconnect(device); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + return getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}, source); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public boolean setPreferredTransport( + BluetoothDevice device, int transport, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setPreferredTransport(device, transport); + } + + @Override + public int getPreferredTransport(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return BluetoothDevice.TRANSPORT_AUTO; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getPreferredTransport(device); + } + + /* The following APIs regarding test app for compliance */ + @Override + public boolean getProtocolMode(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.getProtocolMode(device); + } + + @Override + public boolean virtualUnplug(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.virtualUnplug(device); + } + + @Override + public boolean setProtocolMode( + BluetoothDevice device, int protocolMode, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.setProtocolMode(device, protocolMode); + } + + @Override + public boolean getReport( + BluetoothDevice device, + byte reportType, + byte reportId, + int bufferSize, + AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.getReport(device, reportType, reportId, bufferSize); + } + + @Override + public boolean setReport( + BluetoothDevice device, byte reportType, String report, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.setReport(device, reportType, report); + } + + @Override + public boolean sendData(BluetoothDevice device, String report, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.sendData(device, report); + } + + @Override + public boolean setIdleTime(BluetoothDevice device, byte idleTime, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.setIdleTime(device, idleTime); + } + + @Override + public boolean getIdleTime(BluetoothDevice device, AttributionSource source) { + HidHostService service = getService(source); + if (service == null) { + return false; + } + return service.getIdleTime(device); + } +} 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 de152f0ae1..4f0d215e5b 100644 --- a/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioService.java @@ -36,7 +36,6 @@ import static com.android.modules.utils.build.SdkLevel.isAtLeastU; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; @@ -61,7 +60,6 @@ import android.bluetooth.le.IScannerCallback; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; -import android.content.AttributionSource; import android.content.Context; import android.content.Intent; import android.media.AudioDeviceCallback; @@ -621,7 +619,7 @@ public class LeAudioService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothLeAudioBinder(this); + return new LeAudioServiceBinder(this); } public static boolean isEnabled() { @@ -805,12 +803,12 @@ public class LeAudioService extends ProfileService { mLeAudioBroadcasterNativeInterface.ifPresent(i -> i.cleanup()); - try { - mStateMachinesThread.quitSafely(); - mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); - } catch (InterruptedException e) { - // Do not rethrow as we are shutting down anyway - } + try { + mStateMachinesThread.quitSafely(); + mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); + } catch (InterruptedException e) { + // Do not rethrow as we are shutting down anyway + } mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback); @@ -1834,8 +1832,10 @@ public class LeAudioService extends ProfileService { if (device != null && mActiveAudioInDevice != null) { LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(mActiveAudioInDevice); if (deviceDescriptor == null) { - Log.e(TAG, "updateActiveInDevice: No valid descriptor for device: " - + mActiveAudioInDevice); + Log.e( + TAG, + "updateActiveInDevice: No valid descriptor for device: " + + mActiveAudioInDevice); return false; } @@ -1900,8 +1900,10 @@ public class LeAudioService extends ProfileService { if (device != null && mActiveAudioOutDevice != null) { LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(mActiveAudioOutDevice); if (deviceDescriptor == null) { - Log.e(TAG, "updateActiveOutDevice: No valid descriptor for device: " - + mActiveAudioOutDevice); + Log.e( + TAG, + "updateActiveOutDevice: No valid descriptor for device: " + + mActiveAudioOutDevice); return false; } @@ -2129,9 +2131,7 @@ public class LeAudioService extends ProfileService { Log.d(TAG, "Scanner is not running (mScannerId=" + mScannerId + ")"); return; } - mAdapterService - .getBluetoothScanController() - .stopScanInternal(mScannerId); + mAdapterService.getBluetoothScanController().stopScanInternal(mScannerId); mAdapterService.getBluetoothScanController().unregisterScannerInternal(mScannerId); mScannerId = SCANNER_NOT_INITIALIZED; @@ -3226,8 +3226,10 @@ public class LeAudioService extends ProfileService { boolean ringtoneContextAvailable = false; if (groupDescriptor.mAvailableContexts != null) { - ringtoneContextAvailable = ((groupDescriptor.mAvailableContexts & - BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) != 0); + ringtoneContextAvailable = + ((groupDescriptor.mAvailableContexts + & BluetoothLeAudio.CONTEXT_TYPE_RINGTONE) + != 0); } /* Enables in-band ringtone only for the currently active device or @@ -3724,9 +3726,7 @@ public class LeAudioService extends ProfileService { } else if (isInitial) { Log.i( TAG, - " New group " - + groupId - + " with no context types available"); + " New group " + groupId + " with no context types available"); descriptor.mInactivatedDueToContextType = true; } return; @@ -3794,9 +3794,7 @@ public class LeAudioService extends ProfileService { { LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId); if (descriptor == null) { - Log.e( - TAG, - "deviceDisconnected: no descriptors for group: " + groupId); + Log.e(TAG, "deviceDisconnected: no descriptors for group: " + groupId); return; } @@ -4349,7 +4347,8 @@ public class LeAudioService extends ProfileService { if (getConnectedPeerDevices(groupId).isEmpty()) { descriptor.mIsConnected = false; descriptor.mAutoActiveModeEnabled = true; - descriptor.mAvailableContexts = Flags.leaudioUnicastNoAvailableContexts() ? null : 0; + descriptor.mAvailableContexts = + Flags.leaudioUnicastNoAvailableContexts() ? null : 0; if (descriptor.isActive()) { /* Notify Native layer */ removeActiveDevice(hasFallbackDevice); @@ -5524,12 +5523,15 @@ public class LeAudioService extends ProfileService { * device should be removed from active devices. */ int newDirection = AUDIO_DIRECTION_NONE; - int oldDirection = oldFallbackGroupDescriptor != null - ? oldFallbackGroupDescriptor.mDirection : AUDIO_DIRECTION_NONE; + int oldDirection = + oldFallbackGroupDescriptor != null + ? oldFallbackGroupDescriptor.mDirection + : AUDIO_DIRECTION_NONE; boolean notifyAndUpdateInactiveOutDeviceOnly = false; - boolean hasFallbackDeviceWhenGettingInactive = oldFallbackGroupDescriptor != null - ? oldFallbackGroupDescriptor.mHasFallbackDeviceWhenGettingInactive - : false; + boolean hasFallbackDeviceWhenGettingInactive = + oldFallbackGroupDescriptor != null + ? oldFallbackGroupDescriptor.mHasFallbackDeviceWhenGettingInactive + : false; if (groupId != LE_AUDIO_GROUP_ID_INVALID) { newDirection = AUDIO_DIRECTION_INPUT_BIT; notifyAndUpdateInactiveOutDeviceOnly = true; @@ -5723,525 +5725,6 @@ public class LeAudioService extends ProfileService { } } - /** Binder object: must be a static class or memory leak may occur */ - @VisibleForTesting - static class BluetoothLeAudioBinder extends IBluetoothLeAudio.Stub - implements IProfileServiceBinder { - private LeAudioService mService; - - BluetoothLeAudioBinder(LeAudioService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private LeAudioService getServiceAndEnforceConnect(AttributionSource source) { - requireNonNull(source); - // Cache mService because it can change while getService is called - LeAudioService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - private LeAudioService getService() { - // Cache mService because it can change while getService is called - LeAudioService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) { - return null; - } - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public BluetoothDevice getConnectedGroupLeadDevice(int groupId, AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return null; - } - - return service.getConnectedGroupLeadDevice(groupId); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - if (device == null) { - return service.removeActiveDevice(true); - } else { - return service.setActiveDevice(device); - } - } - - @Override - public List<BluetoothDevice> getActiveDevices(AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getActiveDevices(); - } - - @Override - public int getAudioLocation(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return BluetoothLeAudio.AUDIO_LOCATION_INVALID; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getAudioLocation(device); - } - - @Override - public boolean isInbandRingtoneEnabled(AttributionSource source, int groupId) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.isInbandRingtoneEnabled(groupId); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getConnectionPolicy(device); - } - - @Override - public void setCcidInformation( - ParcelUuid userUuid, int ccid, int contextType, AttributionSource source) { - requireNonNull(userUuid); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setCcidInformation(userUuid, ccid, contextType); - } - - @Override - public int getGroupId(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return LE_AUDIO_GROUP_ID_INVALID; - } - - return service.getGroupId(device); - } - - @Override - public boolean groupAddNode(int groupId, BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.groupAddNode(groupId, device); - } - - @Override - public void setInCall(boolean inCall, AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setInCall(inCall); - } - - @Override - public void setInactiveForHfpHandover( - BluetoothDevice hfpHandoverDevice, AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setInactiveForHfpHandover(hfpHandoverDevice); - } - - @Override - public boolean groupRemoveNode( - int groupId, BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.groupRemoveNode(groupId, device); - } - - @Override - public void setVolume(int volume, AttributionSource source) { - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setVolume(volume); - } - - @Override - public void registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source) { - requireNonNull(callback); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - service.registerCallback(callback); - } - - @Override - public void unregisterCallback( - IBluetoothLeAudioCallback callback, AttributionSource source) { - requireNonNull(callback); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - service.unregisterCallback(callback); - } - - @Override - public void registerLeBroadcastCallback( - IBluetoothLeBroadcastCallback callback, AttributionSource source) { - requireNonNull(callback); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.registerLeBroadcastCallback(callback); - } - - @Override - public void unregisterLeBroadcastCallback( - IBluetoothLeBroadcastCallback callback, AttributionSource source) { - requireNonNull(callback); - requireNonNull(source); - - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.unregisterLeBroadcastCallback(callback); - } - - @Override - public void startBroadcast( - BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.createBroadcast(broadcastSettings); - } - - @Override - public void stopBroadcast(int broadcastId, AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.stopBroadcast(broadcastId); - } - - @Override - public void updateBroadcast( - int broadcastId, - BluetoothLeBroadcastSettings broadcastSettings, - AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.updateBroadcast(broadcastId, broadcastSettings); - } - - @Override - public boolean isPlaying(int broadcastId, AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.isPlaying(broadcastId); - } - - @Override - public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata( - AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getAllBroadcastMetadata(); - } - - @Override - public int getMaximumNumberOfBroadcasts() { - LeAudioService service = getService(); - if (service == null) { - return 0; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getMaximumNumberOfBroadcasts(); - } - - @Override - public int getMaximumStreamsPerBroadcast() { - LeAudioService service = getService(); - if (service == null) { - return 0; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getMaximumStreamsPerBroadcast(); - } - - @Override - public int getMaximumSubgroupsPerBroadcast() { - LeAudioService service = getService(); - if (service == null) { - return 0; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getMaximumSubgroupsPerBroadcast(); - } - - @Override - public BluetoothLeAudioCodecStatus getCodecStatus(int groupId, AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getCodecStatus(groupId); - } - - @Override - public void setCodecConfigPreference( - int groupId, - BluetoothLeAudioCodecConfig inputCodecConfig, - BluetoothLeAudioCodecConfig outputCodecConfig, - AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig); - } - - @Override - public void setBroadcastToUnicastFallbackGroup(int groupId, AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setBroadcastToUnicastFallbackGroup(groupId); - } - - @Override - public int getBroadcastToUnicastFallbackGroup(AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return LE_AUDIO_GROUP_ID_INVALID; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getBroadcastToUnicastFallbackGroup(); - } - - @Override - public boolean isBroadcastActive(AttributionSource source) { - LeAudioService service = getServiceAndEnforceConnect(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.isBroadcastActive(); - } - } - @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java b/android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java new file mode 100644 index 0000000000..e774c8fe33 --- /dev/null +++ b/android/app/src/com/android/bluetooth/le_audio/LeAudioServiceBinder.java @@ -0,0 +1,548 @@ +/* + * 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_audio; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; +import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + +import static java.util.Objects.requireNonNull; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothLeAudio; +import android.bluetooth.BluetoothLeAudioCodecConfig; +import android.bluetooth.BluetoothLeAudioCodecStatus; +import android.bluetooth.BluetoothLeBroadcastMetadata; +import android.bluetooth.BluetoothLeBroadcastSettings; +import android.bluetooth.IBluetoothLeAudio; +import android.bluetooth.IBluetoothLeAudioCallback; +import android.bluetooth.IBluetoothLeBroadcastCallback; +import android.content.AttributionSource; +import android.os.ParcelUuid; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class LeAudioServiceBinder extends IBluetoothLeAudio.Stub implements IProfileServiceBinder { + private static final String TAG = LeAudioServiceBinder.class.getSimpleName(); + + private LeAudioService mService; + + LeAudioServiceBinder(LeAudioService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + private LeAudioService getService() { + LeAudioService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) { + return null; + } + return service; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private LeAudioService getServiceAndEnforceConnect(AttributionSource source) { + requireNonNull(source); + LeAudioService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public BluetoothDevice getConnectedGroupLeadDevice(int groupId, AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return null; + } + return service.getConnectedGroupLeadDevice(groupId); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setActiveDevice(BluetoothDevice device, AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + if (device == null) { + return service.removeActiveDevice(true); + } else { + return service.setActiveDevice(device); + } + } + + @Override + public List<BluetoothDevice> getActiveDevices(AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getActiveDevices(); + } + + @Override + public int getAudioLocation(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return BluetoothLeAudio.AUDIO_LOCATION_INVALID; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getAudioLocation(device); + } + + @Override + public boolean isInbandRingtoneEnabled(AttributionSource source, int groupId) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isInbandRingtoneEnabled(groupId); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public void setCcidInformation( + ParcelUuid userUuid, int ccid, int contextType, AttributionSource source) { + requireNonNull(userUuid); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setCcidInformation(userUuid, ccid, contextType); + } + + @Override + public int getGroupId(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return LE_AUDIO_GROUP_ID_INVALID; + } + return service.getGroupId(device); + } + + @Override + public boolean groupAddNode(int groupId, BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.groupAddNode(groupId, device); + } + + @Override + public void setInCall(boolean inCall, AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setInCall(inCall); + } + + @Override + public void setInactiveForHfpHandover( + BluetoothDevice hfpHandoverDevice, AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setInactiveForHfpHandover(hfpHandoverDevice); + } + + @Override + public boolean groupRemoveNode(int groupId, BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.groupRemoveNode(groupId, device); + } + + @Override + public void setVolume(int volume, AttributionSource source) { + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setVolume(volume); + } + + @Override + public void registerCallback(IBluetoothLeAudioCallback callback, AttributionSource source) { + requireNonNull(callback); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.registerCallback(callback); + } + + @Override + public void unregisterCallback(IBluetoothLeAudioCallback callback, AttributionSource source) { + requireNonNull(callback); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.unregisterCallback(callback); + } + + @Override + public void registerLeBroadcastCallback( + IBluetoothLeBroadcastCallback callback, AttributionSource source) { + requireNonNull(callback); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.registerLeBroadcastCallback(callback); + } + + @Override + public void unregisterLeBroadcastCallback( + IBluetoothLeBroadcastCallback callback, AttributionSource source) { + requireNonNull(callback); + requireNonNull(source); + + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.unregisterLeBroadcastCallback(callback); + } + + @Override + public void startBroadcast( + BluetoothLeBroadcastSettings broadcastSettings, AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.createBroadcast(broadcastSettings); + } + + @Override + public void stopBroadcast(int broadcastId, AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.stopBroadcast(broadcastId); + } + + @Override + public void updateBroadcast( + int broadcastId, + BluetoothLeBroadcastSettings broadcastSettings, + AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.updateBroadcast(broadcastId, broadcastSettings); + } + + @Override + public boolean isPlaying(int broadcastId, AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isPlaying(broadcastId); + } + + @Override + public List<BluetoothLeBroadcastMetadata> getAllBroadcastMetadata(AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getAllBroadcastMetadata(); + } + + @Override + public int getMaximumNumberOfBroadcasts() { + LeAudioService service = getService(); + if (service == null) { + return 0; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getMaximumNumberOfBroadcasts(); + } + + @Override + public int getMaximumStreamsPerBroadcast() { + LeAudioService service = getService(); + if (service == null) { + return 0; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getMaximumStreamsPerBroadcast(); + } + + @Override + public int getMaximumSubgroupsPerBroadcast() { + LeAudioService service = getService(); + if (service == null) { + return 0; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getMaximumSubgroupsPerBroadcast(); + } + + @Override + public BluetoothLeAudioCodecStatus getCodecStatus(int groupId, AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getCodecStatus(groupId); + } + + @Override + public void setCodecConfigPreference( + int groupId, + BluetoothLeAudioCodecConfig inputCodecConfig, + BluetoothLeAudioCodecConfig outputCodecConfig, + AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setCodecConfigPreference(groupId, inputCodecConfig, outputCodecConfig); + } + + @Override + public void setBroadcastToUnicastFallbackGroup(int groupId, AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setBroadcastToUnicastFallbackGroup(groupId); + } + + @Override + public int getBroadcastToUnicastFallbackGroup(AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return LE_AUDIO_GROUP_ID_INVALID; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getBroadcastToUnicastFallbackGroup(); + } + + @Override + public boolean isBroadcastActive(AttributionSource source) { + LeAudioService service = getServiceAndEnforceConnect(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isBroadcastActive(); + } +} diff --git a/android/app/src/com/android/bluetooth/le_audio/OWNERS b/android/app/src/com/android/bluetooth/le_audio/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/src/com/android/bluetooth/le_audio/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java index bac8bdc247..b43af84dcc 100644 --- a/android/app/src/com/android/bluetooth/map/BluetoothMapService.java +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapService.java @@ -18,7 +18,6 @@ package com.android.bluetooth.map; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; @@ -33,9 +32,7 @@ import android.bluetooth.BluetoothMap; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothMap; import android.bluetooth.SdpMnsRecord; -import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -66,7 +63,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -149,7 +145,6 @@ public class BluetoothMapService extends ProfileService { private static BluetoothMapService sBluetoothMapService; - private static final ParcelUuid[] MAP_UUIDS = { BluetoothUuid.MAP, BluetoothUuid.MNS, }; @@ -705,7 +700,7 @@ public class BluetoothMapService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothMapBinder(this); + return new BluetoothMapServiceBinder(this); } /** @@ -1190,227 +1185,6 @@ public class BluetoothMapService extends ProfileService { } } - // Binder object: Must be static class or memory leak may occur - - /** - * This class implements the IBluetoothMap interface - or actually it validates the - * preconditions for calling the actual functionality in the MapService, and calls it. - */ - @VisibleForTesting - static class BluetoothMapBinder extends IBluetoothMap.Stub implements IProfileServiceBinder { - private BluetoothMapService mService; - - BluetoothMapBinder(BluetoothMapService service) { - mService = service; - } - - @Override - public synchronized void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private BluetoothMapService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - BluetoothMapService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - @Override - public int getState(AttributionSource source) { - Log.v(TAG, "getState()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - return BluetoothMap.STATE_DISCONNECTED; - } - - return service.getState(); - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 16); - throw e; - } - } - - @Override - public BluetoothDevice getClient(AttributionSource source) { - Log.v(TAG, "getClient()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - Log.v(TAG, "getClient() - no service - returning " + null); - return null; - } - BluetoothDevice client = service.getRemoteDevice(); - Log.v(TAG, "getClient() - returning " + client); - return client; - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 17); - throw e; - } - } - - @Override - public boolean isConnected(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "isConnected()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - return false; - } - - return service.getConnectionState(device) == STATE_CONNECTED; - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 18); - throw e; - } - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "disconnect()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - return false; - } - - service.disconnect(device); - return true; - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 19); - throw e; - } - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - Log.v(TAG, "getConnectedDevices()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getConnectedDevices(); - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 20); - throw e; - } - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - Log.v(TAG, "getDevicesMatchingConnectionStates()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 21); - throw e; - } - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "getConnectionState()"); - try { - BluetoothMapService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 22); - throw e; - } - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - try { - BluetoothMapService service = getService(source); - if (service == null) { - return false; - } - - return service.setConnectionPolicy(device, connectionPolicy); - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 23); - throw e; - } - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - try { - BluetoothMapService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - return service.getConnectionPolicy(device); - } catch (RuntimeException e) { - ContentProfileErrorReportUtils.report( - BluetoothProfile.MAP, - BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, - BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, - 24); - throw e; - } - } - } - @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java b/android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java new file mode 100644 index 0000000000..0706bcab6f --- /dev/null +++ b/android/app/src/com/android/bluetooth/map/BluetoothMapServiceBinder.java @@ -0,0 +1,251 @@ +/* + * 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.map; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothMap; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProtoEnums; +import android.bluetooth.IBluetoothMap; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.BluetoothStatsLog; +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; +import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; + +import java.util.Collections; +import java.util.List; + +/** + * This class implements the IBluetoothMap interface - or actually it validates the preconditions + * for calling the actual functionality in the MapService, and calls it. + */ +class BluetoothMapServiceBinder extends IBluetoothMap.Stub implements IProfileServiceBinder { + private static final String TAG = BluetoothMapServiceBinder.class.getSimpleName(); + + private BluetoothMapService mService; + + BluetoothMapServiceBinder(BluetoothMapService service) { + mService = service; + } + + @Override + public synchronized void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private BluetoothMapService getService(AttributionSource source) { + BluetoothMapService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @Override + public int getState(AttributionSource source) { + Log.v(TAG, "getState()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + return BluetoothMap.STATE_DISCONNECTED; + } + return service.getState(); + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 16); + throw e; + } + } + + @Override + public BluetoothDevice getClient(AttributionSource source) { + Log.v(TAG, "getClient()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + Log.v(TAG, "getClient() - no service - returning " + null); + return null; + } + BluetoothDevice client = service.getRemoteDevice(); + Log.v(TAG, "getClient() - returning " + client); + return client; + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 17); + throw e; + } + } + + @Override + public boolean isConnected(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "isConnected()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + return false; + } + return service.getConnectionState(device) == STATE_CONNECTED; + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 18); + throw e; + } + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "disconnect()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + return false; + } + service.disconnect(device); + return true; + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 19); + throw e; + } + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + Log.v(TAG, "getConnectedDevices()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectedDevices(); + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 20); + throw e; + } + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + Log.v(TAG, "getDevicesMatchingConnectionStates()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 21); + throw e; + } + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "getConnectionState()"); + try { + BluetoothMapService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 22); + throw e; + } + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + try { + BluetoothMapService service = getService(source); + if (service == null) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 23); + throw e; + } + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + try { + BluetoothMapService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + return service.getConnectionPolicy(device); + } catch (RuntimeException e) { + ContentProfileErrorReportUtils.report( + BluetoothProfile.MAP, + BluetoothProtoEnums.BLUETOOTH_MAP_SERVICE, + BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__EXCEPTION, + 24); + throw e; + } + } +} diff --git a/android/app/src/com/android/bluetooth/map/OWNERS b/android/app/src/com/android/bluetooth/map/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/src/com/android/bluetooth/map/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java index 5e2be59b11..fdecfddf15 100644 --- a/android/app/src/com/android/bluetooth/mapclient/MapClientService.java +++ b/android/app/src/com/android/bluetooth/mapclient/MapClientService.java @@ -16,11 +16,8 @@ package com.android.bluetooth.mapclient; -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_CONNECTING; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; @@ -28,16 +25,12 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElseGet; -import android.Manifest; -import android.annotation.RequiresPermission; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothMapClient; import android.bluetooth.SdpMasRecord; -import android.content.AttributionSource; import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -46,7 +39,6 @@ import android.os.Parcelable; import android.sysprop.BluetoothProperties; import android.util.Log; -import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.btservice.storage.DatabaseManager; @@ -54,7 +46,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -307,7 +298,7 @@ public class MapClientService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new Binder(this); + return new MapClientServiceBinder(this); } @Override @@ -408,172 +399,6 @@ public class MapClientService extends ProfileService { } } - // Binder object: Must be static class or memory leak may occur - - /** - * This class implements the IClient interface - or actually it validates the preconditions for - * calling the actual functionality in the MapClientService, and calls it. - */ - @VisibleForTesting - static class Binder extends IBluetoothMapClient.Stub implements IProfileServiceBinder { - private MapClientService mService; - - Binder(MapClientService service) { - mService = service; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private MapClientService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - MapClientService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !(getCallingUserHandle().isSystem() - || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - return service; - } - - @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) - private MapClientService getServiceAndEnforcePrivileged(AttributionSource source) { - // Cache mService because it can change while getService is called - MapClientService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !(getCallingUserHandle().isSystem() - || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "connect()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return false; - } - - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "disconnect()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - Log.v(TAG, "getConnectedDevices()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - Log.v(TAG, "getDevicesMatchingConnectionStates()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "getConnectionState()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - Log.v(TAG, "setConnectionPolicy()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return false; - } - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "getConnectionPolicy()"); - - MapClientService service = getServiceAndEnforcePrivileged(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - return service.getConnectionPolicy(device); - } - - @Override - public boolean sendMessage( - BluetoothDevice device, - Uri[] contacts, - String message, - PendingIntent sentIntent, - PendingIntent deliveredIntent, - AttributionSource source) { - Log.v(TAG, "sendMessage()"); - - MapClientService service = getService(source); - if (service == null) { - return false; - } - - Log.d(TAG, "Checking Permission of sendMessage"); - mService.enforceCallingOrSelfPermission( - Manifest.permission.SEND_SMS, "Need SEND_SMS permission"); - - return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); - } - } - public void aclDisconnected(BluetoothDevice device, int transport) { mHandler.post(() -> handleAclDisconnected(device, transport)); } diff --git a/android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java b/android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java new file mode 100644 index 0000000000..d6be8975e8 --- /dev/null +++ b/android/app/src/com/android/bluetooth/mapclient/MapClientServiceBinder.java @@ -0,0 +1,193 @@ +/* + * 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.mapclient; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.Manifest; +import android.annotation.RequiresPermission; +import android.app.PendingIntent; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothMapClient; +import android.content.AttributionSource; +import android.net.Uri; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +/** + * This class implements the IClient interface - or actually it validates the preconditions for + * calling the actual functionality in the MapClientService, and calls it. + */ +class MapClientServiceBinder extends IBluetoothMapClient.Stub implements IProfileServiceBinder { + private static final String TAG = MapClientServiceBinder.class.getSimpleName(); + + private MapClientService mService; + + MapClientServiceBinder(MapClientService service) { + mService = service; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private MapClientService getService(AttributionSource source) { + MapClientService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !(getCallingUserHandle().isSystem() + || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + return service; + } + + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) + private MapClientService getServiceAndEnforcePrivileged(AttributionSource source) { + MapClientService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !(getCallingUserHandle().isSystem() + || Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG)) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "connect()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return false; + } + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "disconnect()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + Log.v(TAG, "getConnectedDevices()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + Log.v(TAG, "getDevicesMatchingConnectionStates()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "getConnectionState()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + Log.v(TAG, "setConnectionPolicy()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "getConnectionPolicy()"); + + MapClientService service = getServiceAndEnforcePrivileged(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + return service.getConnectionPolicy(device); + } + + @Override + public boolean sendMessage( + BluetoothDevice device, + Uri[] contacts, + String message, + PendingIntent sentIntent, + PendingIntent deliveredIntent, + AttributionSource source) { + Log.v(TAG, "sendMessage()"); + + MapClientService service = getService(source); + if (service == null) { + return false; + } + + Log.d(TAG, "Checking Permission of sendMessage"); + mService.enforceCallingOrSelfPermission( + Manifest.permission.SEND_SMS, "Need SEND_SMS permission"); + + return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); + } +} diff --git a/android/app/src/com/android/bluetooth/mapclient/OWNERS b/android/app/src/com/android/bluetooth/mapclient/OWNERS deleted file mode 100644 index b0b54fc3d2..0000000000 --- a/android/app/src/com/android/bluetooth/mapclient/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -include /OWNERS_automotive -include /OWNERS_content diff --git a/android/app/src/com/android/bluetooth/mcp/McpService.java b/android/app/src/com/android/bluetooth/mcp/McpService.java index c0ba4f12bb..6f08be0061 100644 --- a/android/app/src/com/android/bluetooth/mcp/McpService.java +++ b/android/app/src/com/android/bluetooth/mcp/McpService.java @@ -17,12 +17,9 @@ package com.android.bluetooth.mcp; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import android.bluetooth.BluetoothDevice; -import android.bluetooth.IBluetoothMcpServiceManager; -import android.content.AttributionSource; import android.content.Context; import android.os.ParcelUuid; import android.sysprop.BluetoothProperties; @@ -88,7 +85,7 @@ public class McpService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothMcpServiceBinder(this); + return new McpServiceBinder(this); } @Override @@ -198,41 +195,4 @@ public class McpService extends ProfileService { int ccid, BluetoothDevice device, ParcelUuid charUuid, boolean doNotify) { mGmcs.setNotificationSubscription(ccid, device, charUuid, doNotify); } - - /** Binder object: must be a static class or memory leak may occur */ - static class BluetoothMcpServiceBinder extends IBluetoothMcpServiceManager.Stub - implements IProfileServiceBinder { - private McpService mService; - - BluetoothMcpServiceBinder(McpService svc) { - mService = svc; - } - - private McpService getService() { - if (mService != null && mService.isAvailable()) { - return mService; - } - Log.e(TAG, "getService() - Service requested, but not available!"); - return null; - } - - @Override - public void setDeviceAuthorized( - BluetoothDevice device, boolean isAuthorized, AttributionSource source) { - McpService service = getService(); - if (service == null) { - return; - } - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setDeviceAuthorized(device, isAuthorized); - } - - @Override - public void cleanup() { - if (mService != null) { - mService.cleanup(); - } - mService = null; - } - } } diff --git a/android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java b/android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java new file mode 100644 index 0000000000..b22e87cadf --- /dev/null +++ b/android/app/src/com/android/bluetooth/mcp/McpServiceBinder.java @@ -0,0 +1,63 @@ +/* + * 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.mcp; + +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothMcpServiceManager; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +class McpServiceBinder extends IBluetoothMcpServiceManager.Stub implements IProfileServiceBinder { + private static final String TAG = McpServiceBinder.class.getSimpleName(); + + private McpService mService; + + McpServiceBinder(McpService svc) { + mService = svc; + } + + private McpService getService() { + if (mService != null && mService.isAvailable()) { + return mService; + } + Log.e(TAG, "getService() - Service requested, but not available!"); + return null; + } + + @Override + public void setDeviceAuthorized( + BluetoothDevice device, boolean isAuthorized, AttributionSource source) { + McpService service = getService(); + if (service == null) { + return; + } + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setDeviceAuthorized(device, isAuthorized); + } + + @Override + public void cleanup() { + if (mService != null) { + mService.cleanup(); + } + mService = null; + } +} diff --git a/android/app/src/com/android/bluetooth/mcp/OWNERS b/android/app/src/com/android/bluetooth/mcp/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/src/com/android/bluetooth/mcp/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java index 81c59f8ad2..d87bd420fe 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java @@ -25,7 +25,6 @@ import android.util.Log; import com.android.bluetooth.BluetoothMethodProxy; import com.android.bluetooth.Utils; -import com.android.bluetooth.flags.Flags; import java.util.ArrayList; @@ -82,10 +81,7 @@ public class BluetoothOppHandoverReceiver extends BroadcastReceiver { if (device == null) { return; } - String brEdrAddress = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device) - : device.getIdentityAddress(); + String brEdrAddress = Utils.getBrEdrAddress(device); Log.d(TAG, "Adding " + brEdrAddress + " to acceptlist"); BluetoothOppManager.getInstance(context).addToAcceptlist(brEdrAddress); } else if (action.equals(Constants.ACTION_STOP_HANDOVER)) { diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java index 048c134848..73cba4e954 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppLauncherActivity.java @@ -248,6 +248,12 @@ public class BluetoothOppLauncherActivity extends Activity { permittedUris, false /* isHandover */, true /* fromExternal */); + if (Flags.sendOppDevicePickerExtraIntent()) { + BluetoothOppUtility + .grantPermissionToNearbyComponent( + BluetoothOppLauncherActivity.this, + uris); + } // Done getting file info..Launch device picker // and finish this activity launchDevicePicker(); @@ -322,6 +328,11 @@ public class BluetoothOppLauncherActivity extends Activity { in1.putExtra(BluetoothDevicePicker.EXTRA_LAUNCH_PACKAGE, getPackageName()); in1.putExtra( BluetoothDevicePicker.EXTRA_LAUNCH_CLASS, BluetoothOppReceiver.class.getName()); + if (Flags.sendOppDevicePickerExtraIntent()) { + in1.putExtra( + BluetoothDevicePicker.EXTRA_DEVICE_PICKER_ORIGINAL_SEND_INTENT, + getIntent()); + } Log.v(TAG, "Launching " + BluetoothDevicePicker.ACTION_LAUNCH); startActivity(in1); } @@ -555,6 +566,10 @@ public class BluetoothOppLauncherActivity extends Activity { void sendFileInfo(String mimeType, String uriString, boolean isHandover, boolean fromExternal) { BluetoothOppManager manager = BluetoothOppManager.getInstance(getApplicationContext()); try { + if (Flags.sendOppDevicePickerExtraIntent()) { + BluetoothOppUtility.grantPermissionToNearbyComponent( + this, List.of(Uri.parse(uriString))); + } manager.saveSendingFileInfo(mimeType, uriString, isHandover, fromExternal); launchDevicePicker(); finish(); diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppManager.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppManager.java index 730c1e9ce2..c745f243d4 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppManager.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppManager.java @@ -54,7 +54,6 @@ import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; @@ -480,11 +479,7 @@ public class BluetoothOppManager { } values.put(BluetoothShare.MIMETYPE, contentType); - values.put( - BluetoothShare.DESTINATION, - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(mRemoteDevice) - : mRemoteDevice.getIdentityAddress()); + values.put(BluetoothShare.DESTINATION, Utils.getBrEdrAddress(mRemoteDevice)); values.put(BluetoothShare.TIMESTAMP, ts); if (mIsHandoverInitiated) { values.put( @@ -512,11 +507,7 @@ public class BluetoothOppManager { ContentValues values = new ContentValues(); values.put(BluetoothShare.URI, mUri); values.put(BluetoothShare.MIMETYPE, mTypeOfSingleFile); - values.put( - BluetoothShare.DESTINATION, - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(mRemoteDevice) - : mRemoteDevice.getIdentityAddress()); + values.put(BluetoothShare.DESTINATION, Utils.getBrEdrAddress(mRemoteDevice)); if (mIsHandoverInitiated) { values.put( BluetoothShare.USER_CONFIRMATION, diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppPreference.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppPreference.java index 6164cb4f58..114adfe631 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppPreference.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppPreference.java @@ -32,7 +32,6 @@ package com.android.bluetooth.opp; -import android.annotation.SuppressLint; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUtils; import android.content.Context; @@ -41,7 +40,6 @@ import android.content.SharedPreferences.Editor; import android.util.Log; import com.android.bluetooth.Utils; -import com.android.bluetooth.flags.Flags; import java.util.HashMap; @@ -87,11 +85,11 @@ public class BluetoothOppPreference { } private static String getChannelKey(BluetoothDevice remoteDevice, int uuid) { - return getBrEdrAddress(remoteDevice) + "_" + Integer.toHexString(uuid); + return Utils.getBrEdrAddress(remoteDevice) + "_" + Integer.toHexString(uuid); } public String getName(BluetoothDevice remoteDevice) { - String identityAddress = getBrEdrAddress(remoteDevice); + String identityAddress = Utils.getBrEdrAddress(remoteDevice); if (identityAddress != null && identityAddress.equals("FF:FF:FF:00:00:00")) { return "localhost"; } @@ -113,7 +111,8 @@ public class BluetoothOppPreference { Log.v( TAG, "getChannel for " - + BluetoothUtils.toAnonymizedAddress(getBrEdrAddress(remoteDevice)) + + BluetoothUtils.toAnonymizedAddress( + Utils.getBrEdrAddress(remoteDevice)) + "_" + Integer.toHexString(uuid) + " as " @@ -123,7 +122,7 @@ public class BluetoothOppPreference { } public void setName(BluetoothDevice remoteDevice, String name) { - String brEdrAddress = getBrEdrAddress(remoteDevice); + String brEdrAddress = Utils.getBrEdrAddress(remoteDevice); Log.v( TAG, "setName for " + BluetoothUtils.toAnonymizedAddress(brEdrAddress) + " to " + name); @@ -139,7 +138,7 @@ public class BluetoothOppPreference { Log.v( TAG, "setChannel for " - + BluetoothUtils.toAnonymizedAddress(getBrEdrAddress(remoteDevice)) + + BluetoothUtils.toAnonymizedAddress(Utils.getBrEdrAddress(remoteDevice)) + "_" + Integer.toHexString(uuid) + " to " @@ -163,7 +162,7 @@ public class BluetoothOppPreference { public void removeName(BluetoothDevice remoteDevice) { Editor ed = mNamePreference.edit(); - String key = getBrEdrAddress(remoteDevice); + String key = Utils.getBrEdrAddress(remoteDevice); ed.remove(key); ed.apply(); mNames.remove(key); @@ -175,12 +174,4 @@ public class BluetoothOppPreference { Log.d(TAG, "Dumping Channels: "); Log.d(TAG, mChannels.toString()); } - - @SuppressLint("AndroidFrameworkRequiresPermission") - private static String getBrEdrAddress(BluetoothDevice device) { - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getBrEdrAddress(device); - } - return device.getIdentityAddress(); - } } diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java index eb9b7f91c4..77dadd9a65 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppReceiver.java @@ -52,7 +52,6 @@ import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.R; import com.android.bluetooth.Utils; import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; -import com.android.bluetooth.flags.Flags; /** * Receives and handles: system broadcasts; Intents from other applications; Intents from @@ -81,9 +80,7 @@ public class BluetoothOppReceiver extends BroadcastReceiver { TAG, "Received BT device selected intent, bt device: " + BluetoothUtils.toAnonymizedAddress( - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(remoteDevice) - : remoteDevice.getIdentityAddress())); + Utils.getBrEdrAddress(remoteDevice))); // Insert transfer session record to database mOppManager.startTransfer(remoteDevice); diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java index 2548b9e45c..f043cbcc53 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java @@ -121,7 +121,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti // Since ContentObserver is created with Handler, onChange() can be called // even after the observer is unregistered. - if (Flags.oppIgnoreContentObserverAfterServiceStop() && mObserver != this) { + if (mObserver != this) { Log.d(TAG, "onChange() called after stop() is called."); return; } @@ -1346,9 +1346,7 @@ public class BluetoothOppService extends ProfileService implements IObexConnecti + socket + " \n :device :" + BluetoothUtils.toAnonymizedAddress( - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device, mAdapterService) - : mAdapterService.getIdentityAddress(device.getAddress()))); + Utils.getBrEdrAddress(device, mAdapterService))); if (!mAcceptNewConnections) { Log.d(TAG, " onConnect BluetoothSocket :" + socket + " rejected"); return false; diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java index 1ff73d1b48..5e92f1e81e 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppTransfer.java @@ -63,7 +63,6 @@ import com.android.bluetooth.BluetoothObexTransport; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils; import com.android.bluetooth.content_profiles.ContentProfileErrorReportUtils; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.VisibleForTesting; import com.android.obex.ObexTransport; @@ -116,7 +115,8 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch Log.e( TAG, "device : " - + BluetoothUtils.toAnonymizedAddress(getBrEdrAddress(device)) + + BluetoothUtils.toAnonymizedAddress( + Utils.getBrEdrAddress(device)) + " mBatch :" + mBatch + " mCurrentShare :" @@ -133,7 +133,8 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch Log.v( TAG, "Device :" - + BluetoothUtils.toAnonymizedAddress(getBrEdrAddress(device)) + + BluetoothUtils.toAnonymizedAddress( + Utils.getBrEdrAddress(device)) + "- OPP device: " + mBatch.mDestination + " \n mCurrentShare.mConfirm == " @@ -180,8 +181,8 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch 3); return; } - String deviceIdentityAddress = getBrEdrAddress(device); - String transferDeviceIdentityAddress = getBrEdrAddress(mDevice); + String deviceIdentityAddress = Utils.getBrEdrAddress(device); + String transferDeviceIdentityAddress = Utils.getBrEdrAddress(mDevice); if (deviceIdentityAddress == null || transferDeviceIdentityAddress == null || !deviceIdentityAddress.equalsIgnoreCase( @@ -1008,11 +1009,4 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch } } } - - private static String getBrEdrAddress(BluetoothDevice device) { - if (Flags.identityAddressNullIfNotKnown()) { - return Utils.getBrEdrAddress(device); - } - return device.getIdentityAddress(); - } } diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java index d7b75bfde2..4e8892ab35 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppUtility.java @@ -38,6 +38,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.content.ActivityNotFoundException; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; @@ -50,6 +51,7 @@ import android.net.Uri; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; +import android.provider.Settings; import android.util.EventLog; import android.util.Log; @@ -77,6 +79,9 @@ import java.util.concurrent.ConcurrentHashMap; public class BluetoothOppUtility { private static final String TAG = BluetoothOppUtility.class.getSimpleName(); + // TODO(b/398120192): use API instead of a hardcode string. + private static final String NEARBY_SHARING_COMPONENT = "nearby_sharing_component"; + /** Whether the device has the "nosdcard" characteristic, or null if not-yet-known. */ private static Boolean sNoSdCard = null; @@ -572,4 +577,30 @@ public class BluetoothOppUtility { NotificationManager nm = ctx.getSystemService(NotificationManager.class); nm.cancel(BluetoothOppNotification.NOTIFICATION_ID_PROGRESS); } + + /** Grants uri read permission to nearby sharing component. */ + static void grantPermissionToNearbyComponent(Context context, List<Uri> uris) { + String packageName = getNearbyComponentPackageName(context); + if (packageName == null) { + return; + } + for (Uri uri : uris) { + BluetoothMethodProxy.getInstance() + .grantUriPermission( + context, packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + } + + private static String getNearbyComponentPackageName(Context context) { + String componentString = + Settings.Secure.getString(context.getContentResolver(), NEARBY_SHARING_COMPONENT); + if (componentString == null) { + return null; + } + ComponentName componentName = ComponentName.unflattenFromString(componentString); + if (componentName == null) { + return null; + } + return componentName.getPackageName(); + } } diff --git a/android/app/src/com/android/bluetooth/opp/OWNERS b/android/app/src/com/android/bluetooth/opp/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/src/com/android/bluetooth/opp/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/src/com/android/bluetooth/pan/OWNERS b/android/app/src/com/android/bluetooth/pan/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/src/com/android/bluetooth/pan/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/src/com/android/bluetooth/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java index 311e100b52..9196288928 100644 --- a/android/app/src/com/android/bluetooth/pan/PanService.java +++ b/android/app/src/com/android/bluetooth/pan/PanService.java @@ -17,8 +17,6 @@ package com.android.bluetooth.pan; import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; -import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; @@ -30,15 +28,12 @@ import static android.bluetooth.BluetoothUtils.logRemoteException; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElseGet; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothPan.LocalPanRole; import android.bluetooth.BluetoothPan.RemotePanRole; import android.bluetooth.BluetoothProfile; -import android.bluetooth.IBluetoothPan; import android.bluetooth.IBluetoothPanCallback; -import android.content.AttributionSource; import android.content.Intent; import android.content.res.Resources.NotFoundException; import android.net.TetheringInterface; @@ -55,12 +50,10 @@ import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; 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.modules.utils.HandlerExecutor; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -105,9 +98,7 @@ public class PanService extends ProfileService { Log.e(TAG, "Error setting up tether interface: " + error); for (BluetoothDevice device : mPanDevices.keySet()) { mNativeInterface.disconnect( - Flags.panUseIdentityAddress() - ? Utils.getByteBrEdrAddress(mAdapterService, device) - : Utils.getByteAddress(device)); + Utils.getByteBrEdrAddress(mAdapterService, device)); } mPanDevices.clear(); mIsTethering = false; @@ -154,7 +145,7 @@ public class PanService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new BluetoothPanBinder(this); + return new PanServiceBinder(this); } public static synchronized PanService getPanService() { @@ -218,9 +209,7 @@ public class PanService extends ProfileService { case MESSAGE_CONNECT: BluetoothDevice connectDevice = (BluetoothDevice) msg.obj; if (!mNativeInterface.connect( - Flags.identityAddressNullIfNotKnown() - ? Utils.getByteBrEdrAddress(mAdapterService, connectDevice) - : mAdapterService.getByteIdentityAddress(connectDevice))) { + Utils.getByteBrEdrAddress(mAdapterService, connectDevice))) { handlePanDeviceStateChange( connectDevice, null, @@ -238,9 +227,7 @@ public class PanService extends ProfileService { case MESSAGE_DISCONNECT: BluetoothDevice disconnectDevice = (BluetoothDevice) msg.obj; if (!mNativeInterface.disconnect( - Flags.identityAddressNullIfNotKnown() - ? Utils.getByteBrEdrAddress(mAdapterService, disconnectDevice) - : mAdapterService.getByteIdentityAddress(disconnectDevice))) { + Utils.getByteBrEdrAddress(mAdapterService, disconnectDevice))) { handlePanDeviceStateChange( disconnectDevice, mPanIfName, @@ -276,137 +263,6 @@ public class PanService extends ProfileService { } } - /** Handlers for incoming service calls */ - @VisibleForTesting - static class BluetoothPanBinder extends IBluetoothPan.Stub implements IProfileServiceBinder { - private PanService mService; - - BluetoothPanBinder(PanService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private PanService getService(AttributionSource source) { - if (Utils.isInstrumentationTestMode()) { - return mService; - } - if (!Utils.checkServiceAvailable(mService, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) - || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { - return null; - } - return mService; - } - - @Override - public boolean connect(BluetoothDevice device, AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.connect(device); - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return BluetoothPan.STATE_DISCONNECTED; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionState(device); - } - - @Override - public boolean isTetheringOn(AttributionSource source) { - // TODO(BT) have a variable marking the on/off state - PanService service = getService(source); - if (service == null) { - return false; - } - - return service.isTetheringOn(); - } - - @Override - public void setBluetoothTethering( - IBluetoothPanCallback callback, int id, boolean value, AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return; - } - - Log.d( - TAG, - "setBluetoothTethering:" - + (" value=" + value) - + (" pkgName= " + source.getPackageName()) - + (" mTetherOn= " + service.mTetherOn)); - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.enforceCallingOrSelfPermission(TETHER_PRIVILEGED, null); - - service.setBluetoothTethering(callback, id, source.getUid(), value); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - PanService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getDevicesMatchingConnectionStates(states); - } - } - public boolean connect(BluetoothDevice device) { if (mUserManager.isGuestUser()) { Log.w(TAG, "Guest user does not have the permission to change the WiFi network"); @@ -641,10 +497,7 @@ public class PanService extends ProfileService { "handlePanDeviceStateChange BT tethering is off/Local role" + " is PANU drop the connection"); mPanDevices.remove(device); - mNativeInterface.disconnect( - Flags.panUseIdentityAddress() - ? Utils.getByteBrEdrAddress(mAdapterService, device) - : Utils.getByteAddress(device)); + mNativeInterface.disconnect(Utils.getByteBrEdrAddress(mAdapterService, device)); return; } Log.d(TAG, "handlePanDeviceStateChange LOCAL_NAP_ROLE:REMOTE_PANU_ROLE"); diff --git a/android/app/src/com/android/bluetooth/pan/PanServiceBinder.java b/android/app/src/com/android/bluetooth/pan/PanServiceBinder.java new file mode 100644 index 0000000000..e3baae21dc --- /dev/null +++ b/android/app/src/com/android/bluetooth/pan/PanServiceBinder.java @@ -0,0 +1,161 @@ +/* + * 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.pan; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.Manifest.permission.TETHER_PRIVILEGED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothPan; +import android.bluetooth.IBluetoothPan; +import android.bluetooth.IBluetoothPanCallback; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +/** Handlers for incoming service calls */ +class PanServiceBinder extends IBluetoothPan.Stub implements IProfileServiceBinder { + private static final String TAG = PanServiceBinder.class.getSimpleName(); + + private PanService mService; + + PanServiceBinder(PanService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private PanService getService(AttributionSource source) { + if (Utils.isInstrumentationTestMode()) { + return mService; + } + if (!Utils.checkServiceAvailable(mService, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG) + || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { + return null; + } + return mService; + } + + @Override + public boolean connect(BluetoothDevice device, AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.connect(device); + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return BluetoothPan.STATE_DISCONNECTED; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionState(device); + } + + @Override + public boolean isTetheringOn(AttributionSource source) { + // TODO(BT) have a variable marking the on/off state + PanService service = getService(source); + if (service == null) { + return false; + } + return service.isTetheringOn(); + } + + @Override + public void setBluetoothTethering( + IBluetoothPanCallback callback, int id, boolean value, AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return; + } + + Log.d( + TAG, + "setBluetoothTethering:" + + (" value=" + value) + + (" pkgName= " + source.getPackageName()) + + (" mTetherOn= " + service.isTetheringOn())); + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.enforceCallingOrSelfPermission(TETHER_PRIVILEGED, null); + + service.setBluetoothTethering(callback, id, source.getUid(), value); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + PanService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getDevicesMatchingConnectionStates(states); + } +} diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java index 7223516e88..18fff92890 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapActivity.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java index 55419908eb..2e788d424a 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapAuthenticator.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java index 6fec2895e6..391d759065 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapConfig.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java index 88f7023b03..c5bb01f426 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapObexServer.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java index 00194f5d78..2d67fc9947 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapService.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -17,7 +17,6 @@ package com.android.bluetooth.pbap; import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED; import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; @@ -28,19 +27,15 @@ import static com.android.bluetooth.Utils.joinUninterruptibly; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.app.Activity; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUtils; -import android.bluetooth.IBluetoothPbap; -import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -75,7 +70,6 @@ import com.android.bluetooth.util.DevicePolicyUtils; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -734,7 +728,7 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect @Override protected IProfileServiceBinder initBinder() { - return new PbapBinder(this); + return new BluetoothPbapServiceBinder(this); } @Override @@ -775,97 +769,6 @@ public class BluetoothPbapService extends ProfileService implements IObexConnect sBluetoothPbapService = instance; } - @VisibleForTesting - static class PbapBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder { - private BluetoothPbapService mService; - - PbapBinder(BluetoothPbapService service) { - Log.v(TAG, "PbapBinder()"); - mService = service; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private BluetoothPbapService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - BluetoothPbapService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - Log.d(TAG, "getConnectedDevices"); - BluetoothPbapService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - Log.d(TAG, "getDevicesMatchingConnectionStates"); - BluetoothPbapService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - Log.d(TAG, "getConnectionState: " + device); - BluetoothPbapService service = getService(source); - if (service == null) { - return BluetoothAdapter.STATE_DISCONNECTED; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - Log.d(TAG, "setConnectionPolicy for device=" + device + " policy=" + connectionPolicy); - BluetoothPbapService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public void disconnect(BluetoothDevice device, AttributionSource source) { - Log.d(TAG, "disconnect"); - BluetoothPbapService service = getService(source); - if (service == null) { - return; - } - service.disconnect(device); - } - } - @Override public boolean onConnect(BluetoothDevice remoteDevice, BluetoothSocket socket) { if (remoteDevice == null || socket == null) { diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java new file mode 100644 index 0000000000..67ad07169a --- /dev/null +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinder.java @@ -0,0 +1,121 @@ +/* + * 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.pbap; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothPbap; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +class BluetoothPbapServiceBinder extends IBluetoothPbap.Stub implements IProfileServiceBinder { + private static final String TAG = BluetoothPbapServiceBinder.class.getSimpleName(); + + private BluetoothPbapService mService; + + BluetoothPbapServiceBinder(BluetoothPbapService service) { + mService = service; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private BluetoothPbapService getService(AttributionSource source) { + BluetoothPbapService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + Log.d(TAG, "getConnectedDevices"); + BluetoothPbapService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + Log.d(TAG, "getDevicesMatchingConnectionStates"); + BluetoothPbapService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + Log.d(TAG, "getConnectionState: " + device); + BluetoothPbapService service = getService(source); + if (service == null) { + return BluetoothAdapter.STATE_DISCONNECTED; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + Log.d(TAG, "setConnectionPolicy for device=" + device + " policy=" + connectionPolicy); + BluetoothPbapService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public void disconnect(BluetoothDevice device, AttributionSource source) { + Log.d(TAG, "disconnect"); + BluetoothPbapService service = getService(source); + if (service == null) { + return; + } + service.disconnect(device); + } +} diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java index db427991a2..c63ec28105 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapSimVcardManager.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java index e70706f84d..75a1b3d9f8 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapUtils.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java index 5fa66a785c..c3b08a2e89 100644 --- a/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java +++ b/android/app/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java @@ -5,7 +5,7 @@ * 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 + * 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, diff --git a/android/app/src/com/android/bluetooth/pbap/OWNERS b/android/app/src/com/android/bluetooth/pbap/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/src/com/android/bluetooth/pbap/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/src/com/android/bluetooth/pbapclient/OWNERS b/android/app/src/com/android/bluetooth/pbapclient/OWNERS deleted file mode 100644 index b0b54fc3d2..0000000000 --- a/android/app/src/com/android/bluetooth/pbapclient/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -include /OWNERS_automotive -include /OWNERS_content diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java index c83e856a72..9b75de98f0 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java +++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientService.java @@ -173,7 +173,7 @@ public class PbapClientService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new PbapClientBinder(this); + return new PbapClientServiceBinder(this); } @Override diff --git a/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java b/android/app/src/com/android/bluetooth/pbapclient/PbapClientServiceBinder.java index f3bcd1e7b5..5a45896f02 100644 --- a/android/app/src/com/android/bluetooth/pbapclient/PbapClientBinder.java +++ b/android/app/src/com/android/bluetooth/pbapclient/PbapClientServiceBinder.java @@ -35,12 +35,12 @@ import java.util.Collections; import java.util.List; /** Handler for incoming service calls destined for PBAP Client */ -class PbapClientBinder extends IBluetoothPbapClient.Stub implements IProfileServiceBinder { - private static final String TAG = PbapClientBinder.class.getSimpleName(); +class PbapClientServiceBinder extends IBluetoothPbapClient.Stub implements IProfileServiceBinder { + private static final String TAG = PbapClientServiceBinder.class.getSimpleName(); private PbapClientService mService; - PbapClientBinder(PbapClientService service) { + PbapClientServiceBinder(PbapClientService service) { mService = service; } diff --git a/android/app/src/com/android/bluetooth/sap/OWNERS b/android/app/src/com/android/bluetooth/sap/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/src/com/android/bluetooth/sap/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/src/com/android/bluetooth/sap/SapService.java b/android/app/src/com/android/bluetooth/sap/SapService.java index c8bda8e046..54391d24e3 100644 --- a/android/app/src/com/android/bluetooth/sap/SapService.java +++ b/android/app/src/com/android/bluetooth/sap/SapService.java @@ -20,7 +20,6 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; -import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; @@ -37,8 +36,6 @@ import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IBluetoothSap; -import android.content.AttributionSource; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -61,7 +58,6 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class SapService extends ProfileService implements AdapterService.BluetoothStateCallback { @@ -672,7 +668,7 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo @Override protected IProfileServiceBinder initBinder() { - return new SapBinder(this); + return new SapServiceBinder(this); } @Override @@ -881,145 +877,4 @@ public class SapService extends ProfileService implements AdapterService.Bluetoo mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE); } } - - // Binder object: Must be static class or memory leak may occur - - /** - * This class implements the IBluetoothSap interface - or actually it validates the - * preconditions for calling the actual functionality in the SapService, and calls it. - */ - private static class SapBinder extends IBluetoothSap.Stub implements IProfileServiceBinder { - private SapService mService; - - SapBinder(SapService service) { - Log.v(TAG, "SapBinder()"); - mService = service; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private SapService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - SapService service = mService; - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public int getState(AttributionSource source) { - Log.v(TAG, "getState()"); - - SapService service = getService(source); - if (service == null) { - return BluetoothSap.STATE_DISCONNECTED; - } - - return service.getState(); - } - - @Override - public BluetoothDevice getClient(AttributionSource source) { - Log.v(TAG, "getClient()"); - - SapService service = getService(source); - if (service == null) { - return null; - } - - Log.v(TAG, "getClient() - returning " + service.getRemoteDevice()); - return service.getRemoteDevice(); - } - - @Override - public boolean isConnected(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "isConnected()"); - - SapService service = getService(source); - if (service == null) { - return false; - } - - return service.getConnectionState(device) == STATE_CONNECTED; - } - - @Override - public boolean disconnect(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "disconnect()"); - - SapService service = getService(source); - if (service == null) { - return false; - } - - return service.disconnect(device); - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - Log.v(TAG, "getConnectedDevices()"); - - SapService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - Log.v(TAG, "getDevicesMatchingConnectionStates()"); - - SapService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - Log.v(TAG, "getConnectionState()"); - - SapService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - SapService service = getService(source); - if (service == null) { - return false; - } - - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - SapService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - return service.getConnectionPolicy(device); - } - } } diff --git a/android/app/src/com/android/bluetooth/sap/SapServiceBinder.java b/android/app/src/com/android/bluetooth/sap/SapServiceBinder.java new file mode 100644 index 0000000000..4c8a96901a --- /dev/null +++ b/android/app/src/com/android/bluetooth/sap/SapServiceBinder.java @@ -0,0 +1,166 @@ +/* + * 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.sap; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSap; +import android.bluetooth.IBluetoothSap; +import android.content.AttributionSource; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.Collections; +import java.util.List; + +/** + * This class implements the IBluetoothSap interface - or actually it validates the preconditions + * for calling the actual functionality in the SapService, and calls it. + */ +class SapServiceBinder extends IBluetoothSap.Stub implements IProfileServiceBinder { + private static final String TAG = SapServiceBinder.class.getSimpleName(); + + private SapService mService; + + SapServiceBinder(SapService service) { + mService = service; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private SapService getService(AttributionSource source) { + SapService service = mService; + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public int getState(AttributionSource source) { + Log.v(TAG, "getState()"); + + SapService service = getService(source); + if (service == null) { + return BluetoothSap.STATE_DISCONNECTED; + } + return service.getState(); + } + + @Override + public BluetoothDevice getClient(AttributionSource source) { + Log.v(TAG, "getClient()"); + + SapService service = getService(source); + if (service == null) { + return null; + } + + Log.v(TAG, "getClient() - returning " + service.getRemoteDevice()); + return service.getRemoteDevice(); + } + + @Override + public boolean isConnected(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "isConnected()"); + + SapService service = getService(source); + if (service == null) { + return false; + } + return service.getConnectionState(device) == STATE_CONNECTED; + } + + @Override + public boolean disconnect(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "disconnect()"); + + SapService service = getService(source); + if (service == null) { + return false; + } + return service.disconnect(device); + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + Log.v(TAG, "getConnectedDevices()"); + + SapService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + Log.v(TAG, "getDevicesMatchingConnectionStates()"); + + SapService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + Log.v(TAG, "getConnectionState()"); + + SapService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + SapService service = getService(source); + if (service == null) { + return false; + } + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + SapService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + return service.getConnectionPolicy(device); + } +} diff --git a/android/app/src/com/android/bluetooth/sdp/SdpManager.java b/android/app/src/com/android/bluetooth/sdp/SdpManager.java index 3a316f8457..de6193aeb2 100644 --- a/android/app/src/com/android/bluetooth/sdp/SdpManager.java +++ b/android/app/src/com/android/bluetooth/sdp/SdpManager.java @@ -35,7 +35,6 @@ import android.util.Log; import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AbstractionLayer; import com.android.bluetooth.btservice.AdapterService; -import com.android.bluetooth.flags.Flags; import com.android.internal.annotations.GuardedBy; import java.util.ArrayList; @@ -143,16 +142,10 @@ public class SdpManager { SdpSearchInstance getSearchInstance(byte[] address, byte[] uuidBytes) { String addressString = Utils.getAddressStringFromByte(address); - addressString = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(addressString, mAdapterService) - : mAdapterService.getIdentityAddress(addressString); + addressString = Utils.getBrEdrAddress(addressString, mAdapterService); ParcelUuid uuid = Utils.byteArrayToUuid(uuidBytes)[0]; for (SdpSearchInstance inst : mList) { - String instAddressString = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(inst.getDevice(), mAdapterService) - : mAdapterService.getIdentityAddress(inst.getDevice().getAddress()); + String instAddressString = Utils.getBrEdrAddress(inst.getDevice(), mAdapterService); if (instAddressString.equals(addressString) && inst.getUuid().equals(uuid)) { return inst; } @@ -161,15 +154,9 @@ public class SdpManager { } boolean isSearching(BluetoothDevice device, ParcelUuid uuid) { - String addressString = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(device, mAdapterService) - : mAdapterService.getIdentityAddress(device.getAddress()); + String addressString = Utils.getBrEdrAddress(device, mAdapterService); for (SdpSearchInstance inst : mList) { - String instAddressString = - Flags.identityAddressNullIfNotKnown() - ? Utils.getBrEdrAddress(inst.getDevice(), mAdapterService) - : mAdapterService.getIdentityAddress(inst.getDevice().getAddress()); + String instAddressString = Utils.getBrEdrAddress(inst.getDevice(), mAdapterService); if (instAddressString != null && addressString != null && instAddressString.equals(addressString) @@ -473,9 +460,7 @@ public class SdpManager { inst.startSearch(); // Trigger timeout message mNativeInterface.sdpSearch( - Flags.identityAddressNullIfNotKnown() - ? Utils.getByteBrEdrAddress(inst.getDevice()) - : mAdapterService.getByteIdentityAddress(inst.getDevice()), + Utils.getByteBrEdrAddress(inst.getDevice()), Utils.uuidToByteArray(inst.getUuid())); } else { // Else queue is empty. Log.d( diff --git a/android/app/src/com/android/bluetooth/tbs/TbsService.java b/android/app/src/com/android/bluetooth/tbs/TbsService.java index ebd25f8229..3bb2fa45e7 100644 --- a/android/app/src/com/android/bluetooth/tbs/TbsService.java +++ b/android/app/src/com/android/bluetooth/tbs/TbsService.java @@ -17,19 +17,13 @@ package com.android.bluetooth.tbs; -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; import static java.util.Objects.requireNonNull; -import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeCall; -import android.bluetooth.IBluetoothLeCallControl; import android.bluetooth.IBluetoothLeCallControlCallback; -import android.content.AttributionSource; -import android.os.ParcelUuid; import android.os.RemoteException; import android.sysprop.BluetoothProperties; import android.util.Log; @@ -38,7 +32,6 @@ import com.android.bluetooth.Utils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.bluetooth.le_audio.LeAudioService; -import com.android.internal.annotations.VisibleForTesting; import java.util.HashMap; import java.util.List; @@ -68,7 +61,7 @@ public class TbsService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new TbsServerBinder(this); + return new TbsServiceBinder(this); } @Override @@ -206,105 +199,6 @@ public class TbsService extends ProfileService { mTbsGeneric.clearInbandRingtoneSupport(device); } - /** Binder object: must be a static class or memory leak may occur */ - @VisibleForTesting - static class TbsServerBinder extends IBluetoothLeCallControl.Stub - implements IProfileServiceBinder { - private TbsService mService; - - TbsServerBinder(TbsService service) { - mService = service; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) - private TbsService getService(AttributionSource source) { - // Cache mService because it can change while getService is called - TbsService service = mService; - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service; - } - - @Override - public void registerBearer( - String token, - IBluetoothLeCallControlCallback callback, - String uci, - List<String> uriSchemes, - int capabilities, - String providerName, - int technology, - AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.registerBearer( - token, callback, uci, uriSchemes, capabilities, providerName, technology); - } - } - - @Override - public void unregisterBearer(String token, AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.unregisterBearer(token); - } - } - - @Override - public void requestResult(int ccid, int requestId, int result, AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.requestResult(ccid, requestId, result); - } - } - - @Override - public void callAdded(int ccid, BluetoothLeCall call, AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.callAdded(ccid, call); - } - } - - @Override - public void callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.callRemoved(ccid, callId.getUuid(), reason); - } - } - - @Override - public void callStateChanged( - int ccid, ParcelUuid callId, int state, AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.callStateChanged(ccid, callId.getUuid(), state); - } - } - - @Override - public void currentCallsList( - int ccid, List<BluetoothLeCall> calls, AttributionSource source) { - TbsService service = getService(source); - if (service != null) { - service.currentCallsList(ccid, calls); - } - } - } - - @VisibleForTesting void registerBearer( String token, IBluetoothLeCallControlCallback callback, @@ -335,42 +229,36 @@ public class TbsService extends ProfileService { Log.d(TAG, "registerBearer: token=" + token + " success=" + success); } - @VisibleForTesting void unregisterBearer(String token) { Log.d(TAG, "unregisterBearer: token=" + token); mTbsGeneric.removeBearer(token); } - @VisibleForTesting public void requestResult(int ccid, int requestId, int result) { Log.d(TAG, "requestResult: ccid=" + ccid + " requestId=" + requestId + " result=" + result); mTbsGeneric.requestResult(ccid, requestId, result); } - @VisibleForTesting void callAdded(int ccid, BluetoothLeCall call) { Log.d(TAG, "callAdded: ccid=" + ccid + " call=" + call); mTbsGeneric.callAdded(ccid, call); } - @VisibleForTesting void callRemoved(int ccid, UUID callId, int reason) { Log.d(TAG, "callRemoved: ccid=" + ccid + " callId=" + callId + " reason=" + reason); mTbsGeneric.callRemoved(ccid, callId, reason); } - @VisibleForTesting void callStateChanged(int ccid, UUID callId, int state) { Log.d(TAG, "callStateChanged: ccid=" + ccid + " callId=" + callId + " state=" + state); mTbsGeneric.callStateChanged(ccid, callId, state); } - @VisibleForTesting void currentCallsList(int ccid, List<BluetoothLeCall> calls) { Log.d(TAG, "currentCallsList: ccid=" + ccid + " calls=" + calls); diff --git a/android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java b/android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java new file mode 100644 index 0000000000..88a1a3435c --- /dev/null +++ b/android/app/src/com/android/bluetooth/tbs/TbsServiceBinder.java @@ -0,0 +1,126 @@ +/* + * 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.tbs; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; + +import android.annotation.RequiresPermission; +import android.bluetooth.BluetoothLeCall; +import android.bluetooth.IBluetoothLeCallControl; +import android.bluetooth.IBluetoothLeCallControlCallback; +import android.content.AttributionSource; +import android.os.ParcelUuid; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; + +import java.util.List; + +class TbsServiceBinder extends IBluetoothLeCallControl.Stub implements IProfileServiceBinder { + private static final String TAG = TbsServiceBinder.class.getSimpleName(); + + private TbsService mService; + + TbsServiceBinder(TbsService service) { + mService = service; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) + private TbsService getService(AttributionSource source) { + TbsService service = mService; + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service; + } + + @Override + public void registerBearer( + String token, + IBluetoothLeCallControlCallback callback, + String uci, + List<String> uriSchemes, + int capabilities, + String providerName, + int technology, + AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.registerBearer( + token, callback, uci, uriSchemes, capabilities, providerName, technology); + } + } + + @Override + public void unregisterBearer(String token, AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.unregisterBearer(token); + } + } + + @Override + public void requestResult(int ccid, int requestId, int result, AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.requestResult(ccid, requestId, result); + } + } + + @Override + public void callAdded(int ccid, BluetoothLeCall call, AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.callAdded(ccid, call); + } + } + + @Override + public void callRemoved(int ccid, ParcelUuid callId, int reason, AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.callRemoved(ccid, callId.getUuid(), reason); + } + } + + @Override + public void callStateChanged(int ccid, ParcelUuid callId, int state, AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.callStateChanged(ccid, callId.getUuid(), state); + } + } + + @Override + public void currentCallsList(int ccid, List<BluetoothLeCall> calls, AttributionSource source) { + TbsService service = getService(source); + if (service != null) { + service.currentCallsList(ccid, calls); + } + } +} diff --git a/android/app/src/com/android/bluetooth/vc/OWNERS b/android/app/src/com/android/bluetooth/vc/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/src/com/android/bluetooth/vc/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java index 628a36b6e8..b21d4e19b3 100644 --- a/android/app/src/com/android/bluetooth/vc/VolumeControlService.java +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlService.java @@ -17,8 +17,6 @@ package com.android.bluetooth.vc; -import static android.Manifest.permission.BLUETOOTH_CONNECT; -import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.bluetooth.BluetoothDevice.BOND_BONDED; import static android.bluetooth.BluetoothDevice.BOND_NONE; import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; @@ -36,19 +34,14 @@ import static com.android.bluetooth.flags.Flags.vcpDeviceVolumeApiImprovements; import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNullElseGet; -import android.annotation.RequiresPermission; import android.bluetooth.AudioInputControl.AudioInputStatus; import android.bluetooth.AudioInputControl.AudioInputType; import android.bluetooth.AudioInputControl.GainMode; import android.bluetooth.AudioInputControl.Mute; -import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; -import android.bluetooth.IAudioInputCallback; -import android.bluetooth.IBluetoothVolumeControl; import android.bluetooth.IBluetoothVolumeControlCallback; -import android.content.AttributionSource; import android.media.AudioManager; import android.os.Handler; import android.os.HandlerThread; @@ -70,22 +63,13 @@ import com.android.bluetooth.le_audio.LeAudioService; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import libcore.util.SneakyThrow; - import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.FutureTask; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.function.Function; public class VolumeControlService extends ProfileService { private static final String TAG = VolumeControlService.class.getSimpleName(); @@ -166,7 +150,7 @@ public class VolumeControlService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothVolumeControlBinder(this); + return new VolumeControlServiceBinder(this); } @Override @@ -209,6 +193,14 @@ public class VolumeControlService extends ProfileService { } } + Handler getHandler() { + return mHandler; + } + + Map<BluetoothDevice, VolumeControlInputDescriptor> getAudioInputs() { + return mAudioInputs; + } + /** * Get the VolumeControlService instance * @@ -534,7 +526,6 @@ public class VolumeControlService extends ProfileService { mNativeInterface.setExtAudioOutVolumeOffset(device, instanceId, volumeOffset); } - @VisibleForTesting synchronized void setDeviceVolume(BluetoothDevice device, int volume, boolean isGroupOp) { Log.d( TAG, @@ -1685,501 +1676,6 @@ public class VolumeControlService extends ProfileService { BluetoothProfile.VOLUME_CONTROL, device, fromState, toState); } - /** Binder object: must be a static class or memory leak may occur */ - @VisibleForTesting - static class BluetoothVolumeControlBinder extends IBluetoothVolumeControl.Stub - implements IProfileServiceBinder { - @VisibleForTesting boolean mIsTesting = false; - private VolumeControlService mService; - - BluetoothVolumeControlBinder(VolumeControlService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - @RequiresPermission(BLUETOOTH_CONNECT) - private VolumeControlService getService(AttributionSource source) { - requireNonNull(source); - - // Cache mService because it can change while getService is called - VolumeControlService service = mService; - - if (Utils.isInstrumentationTestMode()) { - return service; - } - - if (!Utils.checkServiceAvailable(service, TAG) - || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) - || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { - return null; - } - - return service; - } - - @Override - public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getConnectedDevices(); - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return Collections.emptyList(); - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - return service.getDevicesMatchingConnectionStates(states); - } - - @Override - public int getConnectionState(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return STATE_DISCONNECTED; - } - - return service.getConnectionState(device); - } - - @Override - public boolean setConnectionPolicy( - BluetoothDevice device, int connectionPolicy, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.setConnectionPolicy(device, connectionPolicy); - } - - @Override - public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return CONNECTION_POLICY_UNKNOWN; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getConnectionPolicy(device); - } - - @Override - public boolean isVolumeOffsetAvailable(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return false; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.isVolumeOffsetAvailable(device); - } - - @Override - public int getNumberOfVolumeOffsetInstances( - BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return 0; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - return service.getNumberOfVolumeOffsetInstances(device); - } - - @Override - public void setVolumeOffset( - BluetoothDevice device, - int instanceId, - int volumeOffset, - AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setVolumeOffset(device, instanceId, volumeOffset); - } - - @Override - public void setDeviceVolume( - BluetoothDevice device, int volume, boolean isGroupOp, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - service.setDeviceVolume(device, volume, isGroupOp); - } - - @Override - public void setGroupVolume(int groupId, int volume, AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.setGroupVolume(groupId, volume); - } - - @Override - public int getGroupVolume(int groupId, AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return 0; - } - - return service.getGroupVolume(groupId); - } - - @Override - public void setGroupActive(int groupId, boolean active, AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.setGroupActive(groupId, active); - } - - @Override - public void mute(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.mute(device); - } - - @Override - public void muteGroup(int groupId, AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.muteGroup(groupId); - } - - @Override - public void unmute(BluetoothDevice device, AttributionSource source) { - requireNonNull(device); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.unmute(device); - } - - @Override - public void unmuteGroup(int groupId, AttributionSource source) { - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.unmuteGroup(groupId); - } - - private static void postAndWait(Handler handler, Runnable runnable) { - FutureTask<Void> task = new FutureTask(Executors.callable(runnable)); - - handler.post(task); - try { - task.get(1, TimeUnit.SECONDS); - } catch (TimeoutException | InterruptedException e) { - SneakyThrow.sneakyThrow(e); - } catch (ExecutionException e) { - SneakyThrow.sneakyThrow(e.getCause()); - } - } - - @Override - public void registerCallback( - IBluetoothVolumeControlCallback callback, AttributionSource source) { - requireNonNull(callback); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - postAndWait(service.mHandler, () -> service.registerCallback(callback)); - } - - @Override - public void unregisterCallback( - IBluetoothVolumeControlCallback callback, AttributionSource source) { - requireNonNull(callback); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - postAndWait(service.mHandler, () -> service.unregisterCallback(callback)); - } - - @Override - public void notifyNewRegisteredCallback( - IBluetoothVolumeControlCallback callback, AttributionSource source) { - requireNonNull(callback); - - VolumeControlService service = getService(source); - if (service == null) { - return; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - postAndWait(service.mHandler, () -> service.notifyNewRegisteredCallback(callback)); - } - - private static void validateBluetoothDevice(BluetoothDevice device) { - requireNonNull(device); - String address = device.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - throw new IllegalArgumentException("Invalid device address: " + address); - } - } - - @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) - private <R> R aicsWrapper( - AttributionSource source, - BluetoothDevice device, - Function<VolumeControlInputDescriptor, R> fn, - R defaultValue) { - validateBluetoothDevice(device); - - VolumeControlService service = getService(source); - if (service == null) { - return defaultValue; - } - - service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); - - VolumeControlInputDescriptor inputs = service.mAudioInputs.get(device); - if (inputs == null) { - Log.w(TAG, "No audio inputs for " + device); - return defaultValue; - } - - return fn.apply(inputs); - } - - @Override - public int getNumberOfAudioInputControlServices( - AttributionSource source, BluetoothDevice device) { - validateBluetoothDevice(device); - Log.d(TAG, "getNumberOfAudioInputControlServices(" + device + ")"); - return aicsWrapper(source, device, i -> i.size(), 0); - } - - @Override - public void registerAudioInputControlCallback( - AttributionSource source, - BluetoothDevice device, - int instanceId, - IAudioInputCallback callback) { - requireNonNull(callback); - Log.d( - TAG, - "registerAudioInputControlCallback(" - + (device + ", " + instanceId + ", " + callback) - + ")"); - aicsWrapper( - source, - device, - i -> { - i.registerCallback(instanceId, callback); - return null; - }, - null); - } - - @Override - public void unregisterAudioInputControlCallback( - AttributionSource source, - BluetoothDevice device, - int instanceId, - IAudioInputCallback callback) { - requireNonNull(callback); - Log.d( - TAG, - "unregisterAudioInputControlCallback(" - + (device + ", " + instanceId + ", " + callback) - + ")"); - aicsWrapper( - source, - device, - i -> { - i.unregisterCallback(instanceId, callback); - return null; - }, - null); - } - - @Override - public int getAudioInputGainSettingUnit( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputGainSettingUnit(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.getGainSettingUnit(instanceId), 0); - } - - @Override - public int getAudioInputGainSettingMin( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputGainSettingMin(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.getGainSettingMin(instanceId), 0); - } - - @Override - public int getAudioInputGainSettingMax( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputGainSettingMax(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.getGainSettingMax(instanceId), 0); - } - - @Override - public String getAudioInputDescription( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputDescription(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.getDescription(instanceId), ""); - } - - @Override - public boolean isAudioInputDescriptionWritable( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "isAudioInputDescriptionWritable(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.isDescriptionWritable(instanceId), false); - } - - @Override - public boolean setAudioInputDescription( - AttributionSource source, - BluetoothDevice device, - int instanceId, - String description) { - requireNonNull(description); - Log.d(TAG, "setAudioInputDescription(" + device + ", " + instanceId + ")"); - return aicsWrapper( - source, device, i -> i.setDescription(instanceId, description), false); - } - - @Override - public @AudioInputStatus int getAudioInputStatus( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputStatus(" + device + ", " + instanceId + ")"); - return aicsWrapper( - source, - device, - i -> i.getStatus(instanceId), - (int) bluetooth.constants.aics.AudioInputStatus.INACTIVE); - } - - @Override - public @AudioInputType int getAudioInputType( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputType(" + device + ", " + instanceId + ")"); - return aicsWrapper( - source, - device, - i -> i.getType(instanceId), - bluetooth.constants.AudioInputType.UNSPECIFIED); - } - - @Override - public int getAudioInputGainSetting( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputGainSetting(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.getGainSetting(instanceId), 0); - } - - @Override - public boolean setAudioInputGainSetting( - AttributionSource source, BluetoothDevice device, int instanceId, int gainSetting) { - Log.d(TAG, "setAudioInputGainSetting(" + device + ", " + instanceId + ")"); - return aicsWrapper( - source, device, i -> i.setGainSetting(instanceId, gainSetting), false); - } - - @Override - public @GainMode int getAudioInputGainMode( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputGainMode(" + device + ", " + instanceId + ")"); - return aicsWrapper( - source, - device, - i -> i.getGainMode(instanceId), - (int) bluetooth.constants.aics.GainMode.AUTOMATIC_ONLY); - } - - @Override - public boolean setAudioInputGainMode( - AttributionSource source, - BluetoothDevice device, - int instanceId, - @GainMode int gainMode) { - Log.d(TAG, "setAudioInputGainMode(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.setGainMode(instanceId, gainMode), false); - } - - @Override - public @Mute int getAudioInputMute( - AttributionSource source, BluetoothDevice device, int instanceId) { - Log.d(TAG, "getAudioInputMute(" + device + ", " + instanceId + ")"); - return aicsWrapper( - source, - device, - i -> i.getMute(instanceId), - (int) bluetooth.constants.aics.Mute.DISABLED); - } - - @Override - public boolean setAudioInputMute( - AttributionSource source, BluetoothDevice device, int instanceId, @Mute int mute) { - Log.d(TAG, "setAudioInputMute(" + device + ", " + instanceId + ")"); - return aicsWrapper(source, device, i -> i.setMute(instanceId, mute), false); - } - } - @Override public void dump(StringBuilder sb) { super.dump(sb); diff --git a/android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java b/android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java new file mode 100644 index 0000000000..7438ab14cd --- /dev/null +++ b/android/app/src/com/android/bluetooth/vc/VolumeControlServiceBinder.java @@ -0,0 +1,528 @@ +/* + * 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.vc; + +import static android.Manifest.permission.BLUETOOTH_CONNECT; +import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static java.util.Objects.requireNonNull; + +import android.annotation.RequiresPermission; +import android.bluetooth.AudioInputControl.AudioInputStatus; +import android.bluetooth.AudioInputControl.AudioInputType; +import android.bluetooth.AudioInputControl.GainMode; +import android.bluetooth.AudioInputControl.Mute; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IAudioInputCallback; +import android.bluetooth.IBluetoothVolumeControl; +import android.bluetooth.IBluetoothVolumeControlCallback; +import android.content.AttributionSource; +import android.os.Handler; +import android.util.Log; + +import com.android.bluetooth.Utils; +import com.android.bluetooth.btservice.ProfileService.IProfileServiceBinder; +import com.android.internal.annotations.VisibleForTesting; + +import libcore.util.SneakyThrow; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Function; + +class VolumeControlServiceBinder extends IBluetoothVolumeControl.Stub + implements IProfileServiceBinder { + private static final String TAG = VolumeControlServiceBinder.class.getSimpleName(); + + @VisibleForTesting boolean mIsTesting = false; + private VolumeControlService mService; + + VolumeControlServiceBinder(VolumeControlService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + @RequiresPermission(BLUETOOTH_CONNECT) + private VolumeControlService getService(AttributionSource source) { + requireNonNull(source); + + VolumeControlService service = mService; + + if (Utils.isInstrumentationTestMode()) { + return service; + } + + if (!Utils.checkServiceAvailable(service, TAG) + || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) + || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { + return null; + } + + return service; + } + + @Override + public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectedDevices(); + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return Collections.emptyList(); + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getDevicesMatchingConnectionStates(states); + } + + @Override + public int getConnectionState(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return STATE_DISCONNECTED; + } + return service.getConnectionState(device); + } + + @Override + public boolean setConnectionPolicy( + BluetoothDevice device, int connectionPolicy, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.setConnectionPolicy(device, connectionPolicy); + } + + @Override + public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return CONNECTION_POLICY_UNKNOWN; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getConnectionPolicy(device); + } + + @Override + public boolean isVolumeOffsetAvailable(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return false; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.isVolumeOffsetAvailable(device); + } + + @Override + public int getNumberOfVolumeOffsetInstances(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return 0; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + return service.getNumberOfVolumeOffsetInstances(device); + } + + @Override + public void setVolumeOffset( + BluetoothDevice device, int instanceId, int volumeOffset, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setVolumeOffset(device, instanceId, volumeOffset); + } + + @Override + public void setDeviceVolume( + BluetoothDevice device, int volume, boolean isGroupOp, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + service.setDeviceVolume(device, volume, isGroupOp); + } + + @Override + public void setGroupVolume(int groupId, int volume, AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return; + } + service.setGroupVolume(groupId, volume); + } + + @Override + public int getGroupVolume(int groupId, AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return 0; + } + return service.getGroupVolume(groupId); + } + + @Override + public void setGroupActive(int groupId, boolean active, AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return; + } + service.setGroupActive(groupId, active); + } + + @Override + public void mute(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + service.mute(device); + } + + @Override + public void muteGroup(int groupId, AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return; + } + service.muteGroup(groupId); + } + + @Override + public void unmute(BluetoothDevice device, AttributionSource source) { + requireNonNull(device); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + service.unmute(device); + } + + @Override + public void unmuteGroup(int groupId, AttributionSource source) { + VolumeControlService service = getService(source); + if (service == null) { + return; + } + service.unmuteGroup(groupId); + } + + private static void postAndWait(Handler handler, Runnable runnable) { + FutureTask<Void> task = new FutureTask(Executors.callable(runnable)); + + handler.post(task); + try { + task.get(1, TimeUnit.SECONDS); + } catch (TimeoutException | InterruptedException e) { + SneakyThrow.sneakyThrow(e); + } catch (ExecutionException e) { + SneakyThrow.sneakyThrow(e.getCause()); + } + } + + @Override + public void registerCallback( + IBluetoothVolumeControlCallback callback, AttributionSource source) { + requireNonNull(callback); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + postAndWait(service.getHandler(), () -> service.registerCallback(callback)); + } + + @Override + public void unregisterCallback( + IBluetoothVolumeControlCallback callback, AttributionSource source) { + requireNonNull(callback); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + postAndWait(service.getHandler(), () -> service.unregisterCallback(callback)); + } + + @Override + public void notifyNewRegisteredCallback( + IBluetoothVolumeControlCallback callback, AttributionSource source) { + requireNonNull(callback); + + VolumeControlService service = getService(source); + if (service == null) { + return; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + postAndWait(service.getHandler(), () -> service.notifyNewRegisteredCallback(callback)); + } + + private static void validateBluetoothDevice(BluetoothDevice device) { + requireNonNull(device); + String address = device.getAddress(); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + throw new IllegalArgumentException("Invalid device address: " + address); + } + } + + @RequiresPermission(allOf = {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}) + private <R> R aicsWrapper( + AttributionSource source, + BluetoothDevice device, + Function<VolumeControlInputDescriptor, R> fn, + R defaultValue) { + validateBluetoothDevice(device); + + VolumeControlService service = getService(source); + if (service == null) { + return defaultValue; + } + + service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); + + VolumeControlInputDescriptor inputs = service.getAudioInputs().get(device); + if (inputs == null) { + Log.w(TAG, "No audio inputs for " + device); + return defaultValue; + } + + return fn.apply(inputs); + } + + @Override + public int getNumberOfAudioInputControlServices( + AttributionSource source, BluetoothDevice device) { + validateBluetoothDevice(device); + Log.d(TAG, "getNumberOfAudioInputControlServices(" + device + ")"); + return aicsWrapper(source, device, i -> i.size(), 0); + } + + @Override + public void registerAudioInputControlCallback( + AttributionSource source, + BluetoothDevice device, + int instanceId, + IAudioInputCallback callback) { + requireNonNull(callback); + Log.d( + TAG, + "registerAudioInputControlCallback(" + + (device + ", " + instanceId + ", " + callback) + + ")"); + aicsWrapper( + source, + device, + i -> { + i.registerCallback(instanceId, callback); + return null; + }, + null); + } + + @Override + public void unregisterAudioInputControlCallback( + AttributionSource source, + BluetoothDevice device, + int instanceId, + IAudioInputCallback callback) { + requireNonNull(callback); + Log.d( + TAG, + "unregisterAudioInputControlCallback(" + + (device + ", " + instanceId + ", " + callback) + + ")"); + aicsWrapper( + source, + device, + i -> { + i.unregisterCallback(instanceId, callback); + return null; + }, + null); + } + + @Override + public int getAudioInputGainSettingUnit( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputGainSettingUnit(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.getGainSettingUnit(instanceId), 0); + } + + @Override + public int getAudioInputGainSettingMin( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputGainSettingMin(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.getGainSettingMin(instanceId), 0); + } + + @Override + public int getAudioInputGainSettingMax( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputGainSettingMax(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.getGainSettingMax(instanceId), 0); + } + + @Override + public String getAudioInputDescription( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputDescription(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.getDescription(instanceId), ""); + } + + @Override + public boolean isAudioInputDescriptionWritable( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "isAudioInputDescriptionWritable(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.isDescriptionWritable(instanceId), false); + } + + @Override + public boolean setAudioInputDescription( + AttributionSource source, BluetoothDevice device, int instanceId, String description) { + requireNonNull(description); + Log.d(TAG, "setAudioInputDescription(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.setDescription(instanceId, description), false); + } + + @Override + public @AudioInputStatus int getAudioInputStatus( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputStatus(" + device + ", " + instanceId + ")"); + return aicsWrapper( + source, + device, + i -> i.getStatus(instanceId), + (int) bluetooth.constants.aics.AudioInputStatus.INACTIVE); + } + + @Override + public @AudioInputType int getAudioInputType( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputType(" + device + ", " + instanceId + ")"); + return aicsWrapper( + source, + device, + i -> i.getType(instanceId), + bluetooth.constants.AudioInputType.UNSPECIFIED); + } + + @Override + public int getAudioInputGainSetting( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputGainSetting(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.getGainSetting(instanceId), 0); + } + + @Override + public boolean setAudioInputGainSetting( + AttributionSource source, BluetoothDevice device, int instanceId, int gainSetting) { + Log.d(TAG, "setAudioInputGainSetting(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.setGainSetting(instanceId, gainSetting), false); + } + + @Override + public @GainMode int getAudioInputGainMode( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputGainMode(" + device + ", " + instanceId + ")"); + return aicsWrapper( + source, + device, + i -> i.getGainMode(instanceId), + (int) bluetooth.constants.aics.GainMode.AUTOMATIC_ONLY); + } + + @Override + public boolean setAudioInputGainMode( + AttributionSource source, + BluetoothDevice device, + int instanceId, + @GainMode int gainMode) { + Log.d(TAG, "setAudioInputGainMode(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.setGainMode(instanceId, gainMode), false); + } + + @Override + public @Mute int getAudioInputMute( + AttributionSource source, BluetoothDevice device, int instanceId) { + Log.d(TAG, "getAudioInputMute(" + device + ", " + instanceId + ")"); + return aicsWrapper( + source, + device, + i -> i.getMute(instanceId), + (int) bluetooth.constants.aics.Mute.DISABLED); + } + + @Override + public boolean setAudioInputMute( + AttributionSource source, BluetoothDevice device, int instanceId, @Mute int mute) { + Log.d(TAG, "setAudioInputMute(" + device + ", " + instanceId + ")"); + return aicsWrapper(source, device, i -> i.setMute(instanceId, mute), false); + } +} diff --git a/android/app/tests/OWNERS b/android/app/tests/OWNERS deleted file mode 100644 index ecd59038a1..0000000000 --- a/android/app/tests/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_automotive diff --git a/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java b/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java index fd9531105b..7bb7bbfbc3 100644 --- a/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java +++ b/android/app/tests/instrumentation/com/android/bluetooth/opp/BluetoothOppLauncherActivityTest.java @@ -46,6 +46,7 @@ import android.net.Uri; import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; +import android.provider.Settings; import android.sysprop.BluetoothProperties; import androidx.lifecycle.Lifecycle; @@ -382,6 +383,30 @@ public class BluetoothOppLauncherActivityTest { assertThat(argument.getValue().getData()).isEqualTo(Uri.EMPTY); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_SEND_OPP_DEVICE_PICKER_EXTRA_INTENT) + public void onCreate_withActionSend_grantUriPermissionToNearbyComponent() { + doReturn(true).when(mMethodProxy).bluetoothAdapterIsEnabled(any()); + doReturn(PackageManager.PERMISSION_GRANTED) + .when(mMethodProxy) + .componentCallerCheckContentUriPermission(any(), any(), anyInt()); + String uriString = "content://test.provider/1"; + Settings.Secure.putString( + mTargetContext.getContentResolver(), + "nearby_sharing_component", + "com.example/.BComponent"); + + ActivityScenario<BluetoothOppLauncherActivity> unused = + ActivityScenario.launch(createSendIntent(uriString)); + + verify(mMethodProxy) + .grantUriPermission( + any(), + eq("com.example"), + eq(Uri.parse(uriString)), + eq(Intent.FLAG_GRANT_READ_URI_PERMISSION)); + } + @Ignore("b/263724420") @Test public void launchDevicePicker_bluetoothNotEnabled_launchEnableActivity() throws Exception { diff --git a/android/app/tests/unit/Android.bp b/android/app/tests/unit/Android.bp index a5b7dc36c8..6bf4f72d9f 100644 --- a/android/app/tests/unit/Android.bp +++ b/android/app/tests/unit/Android.bp @@ -69,7 +69,7 @@ android_test { "src/**/*.kt", ], jacoco: { - include_filter: ["android.bluetooth.*"], + include_filter: ["com.android.bluetooth.*"], exclude_filter: [], }, diff --git a/android/app/tests/unit/src/com/android/bluetooth/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/OWNERS deleted file mode 100644 index 621eaef441..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# MessageQueue-related classes -per-file TestLooper.java = mfasheh@google.com, shayba@google.com
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java index 23e7a5ed1c..65d00773ba 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceBinderTest.java @@ -36,15 +36,20 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.platform.test.flag.junit.SetFlagsRule; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link A2dpServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) public class A2dpServiceBinderTest { - private static final AttributionSource sSource = new AttributionSource.Builder(0).build(); - private static final BluetoothDevice sDevice = getTestDevice(0); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -53,7 +58,10 @@ public class A2dpServiceBinderTest { @Mock private A2dpService mA2dpService; @Mock private PackageManager mPackageManager; - private A2dpService.BluetoothA2dpBinder mBinder; + private final AttributionSource sSource = new AttributionSource.Builder(0).build(); + private final BluetoothDevice sDevice = getTestDevice(0); + + private A2dpServiceBinder mBinder; @Before public void setUp() throws Exception { @@ -62,7 +70,7 @@ public class A2dpServiceBinderTest { appInfo.targetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT; doReturn(appInfo).when(mPackageManager).getApplicationInfo(any(), anyInt()); - mBinder = new A2dpService.BluetoothA2dpBinder(mA2dpService); + mBinder = new A2dpServiceBinder(mA2dpService); } @After diff --git a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java index aad4f1019a..6f7723caa9 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/a2dpsink/A2dpSinkServiceBinderTest.java @@ -27,21 +27,29 @@ import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; import android.content.AttributionSource; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link A2dpSinkServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) public class A2dpSinkServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private A2dpSinkService mService; - private A2dpSinkService.A2dpSinkServiceBinder mBinder; + private A2dpSinkServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new A2dpSinkService.A2dpSinkServiceBinder(mService); + mBinder = new A2dpSinkServiceBinder(mService); } @After diff --git a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java index 2cbd8f356a..c4bc5dad1a 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerServiceBinderTest.java @@ -24,6 +24,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -34,52 +35,53 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link AvrcpControllerServiceBinder} */ @SmallTest @RunWith(AndroidJUnit4.class) public class AvrcpControllerServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private AvrcpControllerService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(49); - AvrcpControllerService.AvrcpControllerServiceBinder mBinder; + private AvrcpControllerServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new AvrcpControllerService.AvrcpControllerServiceBinder(mService); + mBinder = new AvrcpControllerServiceBinder(mService); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); - + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); - + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @Test public void sendGroupNavigationCmd_notImplemented_doesNothing() { - mBinder.sendGroupNavigationCmd(mDevice, 1, 2, null); + mBinder.sendGroupNavigationCmd(mDevice, 1, 2, mAttributionSource); } @Test public void getPlayerSettings_notImplemented_doesNothing() { - mBinder.getPlayerSettings(mDevice, null); + mBinder.getPlayerSettings(mDevice, mAttributionSource); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BleBroadcastAssistantBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceBinderTest.java index ae402d7d0f..584f0cdeea 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/BleBroadcastAssistantBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/bass_client/BassClientServiceBinderTest.java @@ -36,21 +36,24 @@ import android.bluetooth.IBluetoothLeBroadcastAssistantCallback; import android.bluetooth.le.ScanFilter; import android.content.AttributionSource; +import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.mockito.Mock; import org.mockito.Mockito; import java.util.Collections; import java.util.List; -@RunWith(JUnit4.class) -public class BleBroadcastAssistantBinderTest { +/** Test cases for {@link BassClientServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class BassClientServiceBinderTest { @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @@ -64,17 +67,16 @@ public class BleBroadcastAssistantBinderTest { @Mock private BassClientService mService; - private BassClientService.BluetoothLeBroadcastAssistantBinder mBinder; + private BassClientServiceBinder mBinder; @Before public void setUp() { - mBinder = new BassClientService.BluetoothLeBroadcastAssistantBinder(mService); + mBinder = new BassClientServiceBinder(mService); } @Test public void cleanUp() { mBinder.cleanup(); - assertThat(mBinder.mService).isNull(); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/bass_client/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/bass_client/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/bass_client/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java index 955f579ce4..7e143a2851 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceBinderTest.java @@ -30,15 +30,21 @@ import android.bluetooth.IBluetoothOobDataCallback; import android.content.AttributionSource; import android.os.ParcelUuid; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import java.io.FileDescriptor; -/** Test cases for {@link AdapterServiceBinder}. */ +/** Test cases for {@link AdapterServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) public class AdapterServiceBinderTest { @Rule public final MockitoRule mMockitoRule = new MockitoRule(); diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java index a9e01b3086..6c7b1ed44e 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/AdapterServiceTest.java @@ -907,10 +907,6 @@ public class AdapterServiceTest { // Create device properties RemoteDevices remoteDevices = mAdapterService.getRemoteDevices(); remoteDevices.addDeviceProperties(Utils.getBytesFromAddress((TEST_BT_ADDR_1))); - String identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1); - if (!Flags.identityAddressNullIfNotKnown()) { - assertThat(identityAddress).isEqualTo(TEST_BT_ADDR_1); - } // Trigger address consolidate callback remoteDevices.addressConsolidateCallback( @@ -918,7 +914,7 @@ public class AdapterServiceTest { Utils.getBytesFromAddress(TEST_BT_ADDR_2)); // Verify we can get correct identity address - identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1); + String identityAddress = mAdapterService.getIdentityAddress(TEST_BT_ADDR_1); assertThat(identityAddress).isEqualTo(TEST_BT_ADDR_2); assertThat(mLooper.nextMessage()).isNull(); } @@ -955,7 +951,6 @@ public class AdapterServiceTest { } @Test - @EnableFlags(Flags.FLAG_IDENTITY_ADDRESS_NULL_IF_NOT_KNOWN) public void testIdentityAddressNullIfUnknown() { BluetoothDevice device = getTestDevice(0); diff --git a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java index 1b057b3a7e..ae3db21382 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/btservice/MetricsLoggerTest.java @@ -20,12 +20,24 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + import android.bluetooth.BluetoothDevice; +import android.content.ContentResolver; +import android.provider.Settings; +import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.BluetoothMetricsProto.BluetoothRemoteDeviceInformation; +import com.android.bluetooth.BluetoothStatsLog; import com.google.common.hash.BloomFilter; import com.google.common.hash.Funnels; @@ -37,9 +49,12 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.HashMap; import java.util.Map; @@ -236,6 +251,51 @@ public class MetricsLoggerTest { assertThat(mTestableMetricsLogger.logAllowlistedDeviceNameHash(1, "")).isEmpty(); } + @Test + public void testUpdateHearingDeviceActiveTime() { + BluetoothDevice bluetoothDevice = getTestDevice(0); + int day = BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__DAY; + int week = BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__WEEK; + int month = BluetoothStatsLog.HEARING_DEVICE_ACTIVE_EVENT_REPORTED__TIME_PERIOD__MONTH; + doReturn(ApplicationProvider.getApplicationContext().getContentResolver()) + .when(mAdapterService) + .getContentResolver(); + + // last active time is 2 days ago, should update last active day + TestableMetricsLogger logger = spy(mTestableMetricsLogger); + prepareLastActiveTimeDaysAgo(2); + logger.updateHearingDeviceActiveTime(bluetoothDevice, 1); + verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(day)); + verify(logger, never()).logHearingDeviceActiveEvent(any(), anyInt(), eq(week)); + verify(logger, never()).logHearingDeviceActiveEvent(any(), anyInt(), eq(month)); + + // last active time is 8 days ago, should update last active day and week + Mockito.reset(logger); + prepareLastActiveTimeDaysAgo(8); + logger.updateHearingDeviceActiveTime(bluetoothDevice, 1); + verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(day)); + verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(week)); + verify(logger, never()).logHearingDeviceActiveEvent(any(), anyInt(), eq(month)); + + // last active time is 60 days ago, should update last active day, week and month + Mockito.reset(logger); + prepareLastActiveTimeDaysAgo(60); + logger.updateHearingDeviceActiveTime(bluetoothDevice, 1); + verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(day)); + verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(week)); + verify(logger).logHearingDeviceActiveEvent(any(), anyInt(), eq(month)); + } + + private static void prepareLastActiveTimeDaysAgo(int days) { + final ContentResolver contentResolver = + ApplicationProvider.getApplicationContext().getContentResolver(); + final LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); + final String lastActive = now.minusDays(days).toString(); + Settings.Secure.putString(contentResolver, "last_active_day", lastActive); + Settings.Secure.putString(contentResolver, "last_active_week", lastActive); + Settings.Secure.putString(contentResolver, "last_active_month", lastActive); + } + private void initTestingBloomfilter() throws IOException { byte[] bloomfilterData = DeviceBloomfilterGenerator.hexStringToByteArray( diff --git a/android/app/tests/unit/src/com/android/bluetooth/content_profiles/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/content_profiles/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/content_profiles/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/BluetoothCsisBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinderTest.java index 9233602c47..81adae18f4 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/csip/BluetoothCsisBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceBinderTest.java @@ -30,12 +30,20 @@ import android.bluetooth.IBluetoothCsipSetCoordinatorLockCallback; import android.content.AttributionSource; import android.os.ParcelUuid; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -public class BluetoothCsisBinderTest { +/** Test cases for {@link CsipSetCoordinatorServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class CsipSetCoordinatorServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private CsipSetCoordinatorService mService; @@ -43,11 +51,11 @@ public class BluetoothCsisBinderTest { private final BluetoothDevice mDevice = getTestDevice(45); private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); - private CsipSetCoordinatorService.BluetoothCsisBinder mBinder; + private CsipSetCoordinatorServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new CsipSetCoordinatorService.BluetoothCsisBinder(mService); + mBinder = new CsipSetCoordinatorServiceBinder(mService); } @Test @@ -59,6 +67,7 @@ public class BluetoothCsisBinderTest { @Test public void getDevicesMatchingConnectionStates() { int[] states = new int[] {STATE_CONNECTED}; + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @@ -72,6 +81,7 @@ public class BluetoothCsisBinderTest { @Test public void setConnectionPolicy() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @@ -87,6 +97,7 @@ public class BluetoothCsisBinderTest { int groupId = 100; IBluetoothCsipSetCoordinatorLockCallback cb = mock(IBluetoothCsipSetCoordinatorLockCallback.class); + mBinder.lockGroup(groupId, cb, mAttributionSource); verify(mService).lockGroup(groupId, cb); } @@ -94,6 +105,7 @@ public class BluetoothCsisBinderTest { @Test public void unlockGroup() { ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + mBinder.unlockGroup(uuid, mAttributionSource); verify(mService).unlockGroup(uuid.getUuid()); } @@ -101,6 +113,7 @@ public class BluetoothCsisBinderTest { @Test public void getAllGroupIds() { ParcelUuid uuid = ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB"); + mBinder.getAllGroupIds(uuid, mAttributionSource); verify(mService).getAllGroupIds(uuid); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java index ac64b097ae..72fbd125b6 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/csip/CsipSetCoordinatorServiceTest.java @@ -130,7 +130,6 @@ public class CsipSetCoordinatorServiceTest { new CsipSetCoordinatorService( mAdapterService, mLooper.getLooper(), mNativeInterface, mServiceFactory); mService.setAvailable(true); - } @After diff --git a/android/app/tests/unit/src/com/android/bluetooth/csip/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/csip/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/csip/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java index 1a340840bc..f9bb1d556d 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceBinderTest.java @@ -42,6 +42,7 @@ import org.mockito.Mock; import java.util.UUID; +/** Test cases for {@link GattServiceBinder} */ @SmallTest @RunWith(AndroidJUnit4.class) public class GattServiceBinderTest { diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java index 76d6dd53ff..1e8a55b994 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/GattServiceTest.java @@ -42,7 +42,6 @@ import android.content.res.Resources; import android.location.LocationManager; import android.os.Bundle; import android.os.Process; -import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.test.mock.MockContentProvider; import android.test.mock.MockContentResolver; @@ -54,7 +53,6 @@ import androidx.test.runner.AndroidJUnit4; import com.android.bluetooth.TestUtils; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.CompanionManager; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.le_scan.PeriodicScanManager; import com.android.bluetooth.le_scan.ScanManager; import com.android.bluetooth.le_scan.ScanObjectsFactory; @@ -79,7 +77,6 @@ import java.util.UUID; @RunWith(AndroidJUnit4.class) public class GattServiceTest { @Rule public final MockitoRule mMockitoRule = new MockitoRule(); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private ContextMap<IBluetoothGattCallback> mClientMap; @Mock private ScanManager mScanManager; @@ -443,7 +440,6 @@ public class GattServiceTest { @Test public void registerClient_checkLimitPerApp() { - mSetFlagsRule.enableFlags(Flags.FLAG_GATT_CLIENT_DYNAMIC_ALLOCATION); doReturn(GattService.GATT_CLIENT_LIMIT_PER_APP).when(mClientMap).countByAppUid(anyInt()); UUID uuid = UUID.randomUUID(); IBluetoothGattCallback callback = mock(IBluetoothGattCallback.class); diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceBinderTest.java index dd5ede57b2..3544578497 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientServiceBinderTest.java @@ -44,9 +44,10 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; +/** Test cases for {@link HapClientServiceBinder} */ @MediumTest @RunWith(AndroidJUnit4.class) -public class HapClientBinderTest { +public class HapClientServiceBinderTest { @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private HapClientService mHapClientService; @@ -59,11 +60,11 @@ public class HapClientBinderTest { private final AttributionSource mAttributionSource = mAdapter.getAttributionSource(); private final BluetoothDevice mDevice = getTestDevice(0); - private HapClientBinder mBinder; + private HapClientServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new HapClientBinder(mHapClientService); + mBinder = new HapClientServiceBinder(mHapClientService); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java new file mode 100644 index 0000000000..8dae3bf594 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceBinderTest.java @@ -0,0 +1,200 @@ +/* + * 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.hearingaid; + +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED; +import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN; +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static com.android.bluetooth.TestUtils.MockitoRule; +import static com.android.bluetooth.TestUtils.getTestDevice; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothManager; +import android.content.AttributionSource; +import android.content.Context; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.ArrayList; +import java.util.List; + +/** Test cases for {@link HearingAidServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class HearingAidServiceBinderTest { + + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); + + @Mock private HearingAidService mService; + + private HearingAidServiceBinder mBinder; + private AttributionSource mAttributionSource; + private BluetoothDevice mTestDevice; + + @Before + public void setUp() throws Exception { + when(mService.isAvailable()).thenReturn(true); + mBinder = new HearingAidServiceBinder(mService); + Context context = InstrumentationRegistry.getTargetContext(); + mAttributionSource = + context.getSystemService(BluetoothManager.class) + .getAdapter() + .getAttributionSource(); + mTestDevice = getTestDevice(0); + } + + @Test + public void connect() { + mBinder.connect(mTestDevice, mAttributionSource); + verify(mService).connect(mTestDevice); + } + + @Test + public void disconnect() { + mBinder.disconnect(mTestDevice, mAttributionSource); + verify(mService).disconnect(mTestDevice); + } + + @Test + public void getConnectedDevices() { + List<BluetoothDevice> connectedDevices = new ArrayList<>(); + connectedDevices.add(mTestDevice); + when(mService.getConnectedDevices()).thenReturn(connectedDevices); + + mBinder.getConnectedDevices(mAttributionSource); + verify(mService).getConnectedDevices(); + } + + @Test + public void getDevicesMatchingConnectionStates() { + int[] states = new int[] {STATE_CONNECTED, STATE_DISCONNECTED}; + List<BluetoothDevice> devices = new ArrayList<>(); + devices.add(mTestDevice); + when(mService.getDevicesMatchingConnectionStates(states)).thenReturn(devices); + + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); + verify(mService).getDevicesMatchingConnectionStates(states); + } + + @Test + public void getConnectionState() { + when(mService.getConnectionState(mTestDevice)).thenReturn(STATE_CONNECTED); + + mBinder.getConnectionState(mTestDevice, mAttributionSource); + verify(mService).getConnectionState(mTestDevice); + } + + @Test + public void setActiveDevice() { + mBinder.setActiveDevice(mTestDevice, mAttributionSource); + verify(mService).setActiveDevice(mTestDevice); + } + + @Test + public void removeActiveDevice() { + mBinder.setActiveDevice(null, mAttributionSource); + verify(mService).removeActiveDevice(false); + } + + @Test + public void getActiveDevices() { + List<BluetoothDevice> activeDevices = new ArrayList<>(); + activeDevices.add(mTestDevice); + when(mService.getActiveDevices()).thenReturn(activeDevices); + + mBinder.getActiveDevices(mAttributionSource); + verify(mService).getActiveDevices(); + } + + @Test + public void setConnectionPolicy() { + mBinder.setConnectionPolicy(mTestDevice, CONNECTION_POLICY_ALLOWED, mAttributionSource); + verify(mService).setConnectionPolicy(mTestDevice, CONNECTION_POLICY_ALLOWED); + } + + @Test + public void getConnectionPolicy() { + when(mService.getConnectionPolicy(mTestDevice)).thenReturn(CONNECTION_POLICY_FORBIDDEN); + + mBinder.getConnectionPolicy(mTestDevice, mAttributionSource); + verify(mService).getConnectionPolicy(mTestDevice); + } + + @Test + public void setVolume() { + int volume = 50; + + mBinder.setVolume(volume, mAttributionSource); + verify(mService).setVolume(volume); + } + + @Test + public void getHiSyncId() { + long hiSyncId = 1234567890L; + when(mService.getHiSyncId(mTestDevice)).thenReturn(hiSyncId); + + mBinder.getHiSyncId(mTestDevice, mAttributionSource); + verify(mService).getHiSyncId(mTestDevice); + } + + @Test + public void getDeviceSide() { + int side = BluetoothHearingAid.SIDE_LEFT; + when(mService.getCapabilities(mTestDevice)).thenReturn(side); + + mBinder.getDeviceSide(mTestDevice, mAttributionSource); + verify(mService).getCapabilities(mTestDevice); + } + + @Test + public void getDeviceMode() { + int mode = BluetoothHearingAid.MODE_BINAURAL; + when(mService.getCapabilities(mTestDevice)).thenReturn(mode << 1); + + mBinder.getDeviceMode(mTestDevice, mAttributionSource); + verify(mService).getCapabilities(mTestDevice); + } + + @Test + public void getAdvertisementServiceData() { + BluetoothHearingAid.AdvertisementServiceData data = + new BluetoothHearingAid.AdvertisementServiceData(0, 0); + when(mService.getAdvertisementServiceData(mTestDevice)).thenReturn(data); + + mBinder.getAdvertisementServiceData(mTestDevice, mAttributionSource); + verify(mService).getAdvertisementServiceData(mTestDevice); + } + + @Test + public void cleanup_doesNotCrash() { + mBinder.cleanup(); + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java index 757a6488be..a284d6a414 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/HearingAidServiceTest.java @@ -97,7 +97,7 @@ public class HearingAidServiceTest { private final BluetoothDevice mSingleDevice = getTestDevice(13); private HearingAidService mService; - private HearingAidService.BluetoothHearingAidBinder mBinder; + private HearingAidServiceBinder mBinder; private InOrder mInOrder; private TestLooper mLooper; @@ -124,7 +124,7 @@ public class HearingAidServiceTest { mService = new HearingAidService(mAdapterService, mLooper.getLooper(), mNativeInterface); mService.setAvailable(true); - mBinder = (HearingAidService.BluetoothHearingAidBinder) mService.initBinder(); + mBinder = (HearingAidServiceBinder) mService.initBinder(); } @After diff --git a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/hearingaid/OWNERS deleted file mode 100644 index bbaa85ddbb..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/hearingaid/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_hearingaid diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfp/BluetoothHeadsetBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceBinderTest.java index d125fbee4c..73f3610505 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/BluetoothHeadsetBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceBinderTest.java @@ -27,12 +27,20 @@ import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; import android.content.AttributionSource; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -public class BluetoothHeadsetBinderTest { +/** Test cases for {@link HeadsetServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class HeadsetServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private HeadsetService mService; @@ -40,11 +48,11 @@ public class BluetoothHeadsetBinderTest { private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(39); - private HeadsetService.BluetoothHeadsetBinder mBinder; + private HeadsetServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new HeadsetService.BluetoothHeadsetBinder(mService); + mBinder = new HeadsetServiceBinder(mService); } @Test @@ -68,6 +76,7 @@ public class BluetoothHeadsetBinderTest { @Test public void getDevicesMatchingConnectionStates() { int[] states = new int[] {STATE_CONNECTED}; + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @@ -142,6 +151,7 @@ public class BluetoothHeadsetBinderTest { @Test public void setAudioRouteAllowed() { boolean allowed = true; + mBinder.setAudioRouteAllowed(allowed, mAttributionSource); verify(mService).setAudioRouteAllowed(allowed); } @@ -155,6 +165,7 @@ public class BluetoothHeadsetBinderTest { @Test public void setForceScoAudio() { boolean forced = true; + mBinder.setForceScoAudio(forced, mAttributionSource); verify(mService).setForceScoAudio(forced); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java index b3171bc191..87f18055e5 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfpclient/HeadsetClientServiceBinderTest.java @@ -25,6 +25,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -35,6 +36,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link HeadsetClientServiceBinder} */ @SmallTest @RunWith(AndroidJUnit4.class) public class HeadsetClientServiceBinderTest { @@ -42,32 +44,33 @@ public class HeadsetClientServiceBinderTest { @Mock private HeadsetClientService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(54); - HeadsetClientService.BluetoothHeadsetClientBinder mBinder; + private HeadsetClientServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new HeadsetClientService.BluetoothHeadsetClientBinder(mService); + mBinder = new HeadsetClientServiceBinder(mService); } @Test public void connect_callsServiceMethod() { - mBinder.connect(mDevice, null); + mBinder.connect(mDevice, mAttributionSource); verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { - mBinder.disconnect(mDevice, null); + mBinder.disconnect(mDevice, mAttributionSource); verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getConnectedDevices(); } @@ -75,14 +78,14 @@ public class HeadsetClientServiceBinderTest { @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @@ -90,35 +93,35 @@ public class HeadsetClientServiceBinderTest { @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { - mBinder.getConnectionPolicy(mDevice, null); + mBinder.getConnectionPolicy(mDevice, mAttributionSource); verify(mService).getConnectionPolicy(mDevice); } @Test public void startVoiceRecognition_callsServiceMethod() { - mBinder.startVoiceRecognition(mDevice, null); + mBinder.startVoiceRecognition(mDevice, mAttributionSource); verify(mService).startVoiceRecognition(mDevice); } @Test public void stopVoiceRecognition_callsServiceMethod() { - mBinder.stopVoiceRecognition(mDevice, null); + mBinder.stopVoiceRecognition(mDevice, mAttributionSource); verify(mService).stopVoiceRecognition(mDevice); } @Test public void getAudioState_callsServiceMethod() { - mBinder.getAudioState(mDevice, null); + mBinder.getAudioState(mDevice, mAttributionSource); verify(mService).getAudioState(mDevice); } @@ -126,28 +129,28 @@ public class HeadsetClientServiceBinderTest { @Test public void setAudioRouteAllowed_callsServiceMethod() { boolean allowed = true; - mBinder.setAudioRouteAllowed(mDevice, allowed, null); + mBinder.setAudioRouteAllowed(mDevice, allowed, mAttributionSource); verify(mService).setAudioRouteAllowed(mDevice, allowed); } @Test public void getAudioRouteAllowed_callsServiceMethod() { - mBinder.getAudioRouteAllowed(mDevice, null); + mBinder.getAudioRouteAllowed(mDevice, mAttributionSource); verify(mService).getAudioRouteAllowed(mDevice); } @Test public void connectAudio_callsServiceMethod() { - mBinder.connectAudio(mDevice, null); + mBinder.connectAudio(mDevice, mAttributionSource); verify(mService).connectAudio(mDevice); } @Test public void disconnectAudio_callsServiceMethod() { - mBinder.disconnectAudio(mDevice, null); + mBinder.disconnectAudio(mDevice, mAttributionSource); verify(mService).disconnectAudio(mDevice); } @@ -155,35 +158,35 @@ public class HeadsetClientServiceBinderTest { @Test public void acceptCall_callsServiceMethod() { int flag = 2; - mBinder.acceptCall(mDevice, flag, null); + mBinder.acceptCall(mDevice, flag, mAttributionSource); verify(mService).acceptCall(mDevice, flag); } @Test public void rejectCall_callsServiceMethod() { - mBinder.rejectCall(mDevice, null); + mBinder.rejectCall(mDevice, mAttributionSource); verify(mService).rejectCall(mDevice); } @Test public void holdCall_callsServiceMethod() { - mBinder.holdCall(mDevice, null); + mBinder.holdCall(mDevice, mAttributionSource); verify(mService).holdCall(mDevice); } @Test public void terminateCall_callsServiceMethod() { - mBinder.terminateCall(mDevice, null, null); + mBinder.terminateCall(mDevice, null, mAttributionSource); verify(mService).terminateCall(mDevice, null); } @Test public void explicitCallTransfer_callsServiceMethod() { - mBinder.explicitCallTransfer(mDevice, null); + mBinder.explicitCallTransfer(mDevice, mAttributionSource); verify(mService).explicitCallTransfer(mDevice); } @@ -191,7 +194,7 @@ public class HeadsetClientServiceBinderTest { @Test public void enterPrivateMode_callsServiceMethod() { int index = 1; - mBinder.enterPrivateMode(mDevice, index, null); + mBinder.enterPrivateMode(mDevice, index, mAttributionSource); verify(mService).enterPrivateMode(mDevice, index); } @@ -199,7 +202,7 @@ public class HeadsetClientServiceBinderTest { @Test public void dial_callsServiceMethod() { String number = "12532523"; - mBinder.dial(mDevice, number, null); + mBinder.dial(mDevice, number, mAttributionSource); verify(mService).dial(mDevice, number); } @@ -207,21 +210,21 @@ public class HeadsetClientServiceBinderTest { @Test public void sendDTMF_callsServiceMethod() { byte code = 21; - mBinder.sendDTMF(mDevice, code, null); + mBinder.sendDTMF(mDevice, code, mAttributionSource); verify(mService).sendDTMF(mDevice, code); } @Test public void getLastVoiceTagNumber_callsServiceMethod() { - mBinder.getLastVoiceTagNumber(mDevice, null); + mBinder.getLastVoiceTagNumber(mDevice, mAttributionSource); verify(mService).getLastVoiceTagNumber(mDevice); } @Test public void getCurrentAgEvents_callsServiceMethod() { - mBinder.getCurrentAgEvents(mDevice, null); + mBinder.getCurrentAgEvents(mDevice, mAttributionSource); verify(mService).getCurrentAgEvents(mDevice); } @@ -231,14 +234,14 @@ public class HeadsetClientServiceBinderTest { int vendorId = 5; String cmd = "test_command"; - mBinder.sendVendorAtCommand(mDevice, vendorId, cmd, null); + mBinder.sendVendorAtCommand(mDevice, vendorId, cmd, mAttributionSource); verify(mService).sendVendorAtCommand(mDevice, vendorId, cmd); } @Test public void getCurrentAgFeatures_callsServiceMethod() { - mBinder.getCurrentAgFeatures(mDevice, null); + mBinder.getCurrentAgFeatures(mDevice, mAttributionSource); verify(mService).getCurrentAgFeaturesBundle(mDevice); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/BluetoothHidDeviceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceBinderTest.java index 8a03fe7186..4369e85310 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hid/BluetoothHidDeviceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceBinderTest.java @@ -33,12 +33,20 @@ import android.bluetooth.BluetoothHidDeviceAppSdpSettings; import android.bluetooth.IBluetoothHidDeviceCallback; import android.content.AttributionSource; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; -public class BluetoothHidDeviceBinderTest { +/** Test cases for {@link HidDeviceServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class HidDeviceServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private HidDeviceService mService; @@ -46,12 +54,12 @@ public class BluetoothHidDeviceBinderTest { private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(29); - private HidDeviceService.BluetoothHidDeviceBinder mBinder; + private HidDeviceServiceBinder mBinder; @Before public void setUp() throws Exception { when(mService.isAvailable()).thenReturn(true); - mBinder = new HidDeviceService.BluetoothHidDeviceBinder(mService); + mBinder = new HidDeviceServiceBinder(mService); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceTest.java index 5ddb36e4c6..a242f054a7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidDeviceServiceTest.java @@ -76,7 +76,7 @@ import org.mockito.hamcrest.MockitoHamcrest; @MediumTest @RunWith(AndroidJUnit4.class) -public class HidDeviceTest { +public class HidDeviceServiceTest { @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private AdapterService mAdapterService; diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java index 02bee889e8..2c393118bb 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceBinderTest.java @@ -25,6 +25,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.filters.SmallTest; @@ -36,108 +37,103 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link HidHostServiceBinder}. */ @SmallTest @RunWith(AndroidJUnit4.class) public class HidHostServiceBinderTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private HidHostService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(50); - private HidHostService.BluetoothHidHostBinder mBinder; + private HidHostServiceBinder mBinder; @Before public void setUp() { - mBinder = new HidHostService.BluetoothHidHostBinder(mService); + mBinder = new HidHostServiceBinder(mService); } @Test public void connect_callsServiceMethod() { - mBinder.connect(mDevice, null); - + mBinder.connect(mDevice, mAttributionSource); verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { - mBinder.disconnect(mDevice, null); - + mBinder.disconnect(mDevice, mAttributionSource); verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); - + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(new int[] {STATE_CONNECTED}); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); - + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { - mBinder.getConnectionPolicy(mDevice, null); - + mBinder.getConnectionPolicy(mDevice, mAttributionSource); verify(mService).getConnectionPolicy(mDevice); } @Test public void setPreferredTransport_callsServiceMethod() { int preferredTransport = BluetoothDevice.TRANSPORT_AUTO; - mBinder.setPreferredTransport(mDevice, preferredTransport, null); + mBinder.setPreferredTransport(mDevice, preferredTransport, mAttributionSource); verify(mService).setPreferredTransport(mDevice, preferredTransport); } @Test public void getPreferredTransport_callsServiceMethod() { - mBinder.getPreferredTransport(mDevice, null); - + mBinder.getPreferredTransport(mDevice, mAttributionSource); verify(mService).getPreferredTransport(mDevice); } @Test public void getProtocolMode_callsServiceMethod() { - mBinder.getProtocolMode(mDevice, null); - + mBinder.getProtocolMode(mDevice, mAttributionSource); verify(mService).getProtocolMode(mDevice); } @Test public void virtualUnplug_callsServiceMethod() { - mBinder.virtualUnplug(mDevice, null); - + mBinder.virtualUnplug(mDevice, mAttributionSource); verify(mService).virtualUnplug(mDevice); } @Test public void setProtocolMode_callsServiceMethod() { int protocolMode = 1; - mBinder.setProtocolMode(mDevice, protocolMode, null); + mBinder.setProtocolMode(mDevice, protocolMode, mAttributionSource); verify(mService).setProtocolMode(mDevice, protocolMode); } @@ -146,8 +142,8 @@ public class HidHostServiceBinderTest { byte reportType = 1; byte reportId = 2; int bufferSize = 16; - mBinder.getReport(mDevice, reportType, reportId, bufferSize, null); + mBinder.getReport(mDevice, reportType, reportId, bufferSize, mAttributionSource); verify(mService).getReport(mDevice, reportType, reportId, bufferSize); } @@ -155,31 +151,30 @@ public class HidHostServiceBinderTest { public void setReport_callsServiceMethod() { byte reportType = 1; String report = "test_report"; - mBinder.setReport(mDevice, reportType, report, null); + mBinder.setReport(mDevice, reportType, report, mAttributionSource); verify(mService).setReport(mDevice, reportType, report); } @Test public void sendData_callsServiceMethod() { String report = "test_report"; - mBinder.sendData(mDevice, report, null); + mBinder.sendData(mDevice, report, mAttributionSource); verify(mService).sendData(mDevice, report); } @Test public void setIdleTime_callsServiceMethod() { byte idleTime = 1; - mBinder.setIdleTime(mDevice, idleTime, null); + mBinder.setIdleTime(mDevice, idleTime, mAttributionSource); verify(mService).setIdleTime(mDevice, idleTime); } @Test public void getIdleTime_callsServiceMethod() { - mBinder.getIdleTime(mDevice, null); - + mBinder.getIdleTime(mDevice, mAttributionSource); verify(mService).getIdleTime(mDevice); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java index a41f93d5d6..10f5f3257f 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hid/HidHostServiceTest.java @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package com.android.bluetooth.hid; import static android.bluetooth.BluetoothDevice.BOND_BONDED; @@ -53,6 +54,7 @@ import java.util.List; @MediumTest @RunWith(AndroidJUnit4.class) public class HidHostServiceTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private AdapterService mAdapterService; diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceBinderTest.java index 15b3563df1..68677ffedb 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_audio/LeAudioServiceBinderTest.java @@ -36,15 +36,23 @@ import android.content.AttributionSource; import android.os.ParcelUuid; import android.platform.test.flag.junit.SetFlagsRule; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import java.util.UUID; -public class LeAudioBinderTest { +/** Test cases for {@link LeAudioServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LeAudioServiceBinderTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @@ -53,11 +61,11 @@ public class LeAudioBinderTest { private static final String TEST_BROADCAST_NAME = "TEST"; private static final int TEST_QUALITY = BluetoothLeBroadcastSubgroupSettings.QUALITY_STANDARD; - private LeAudioService.BluetoothLeAudioBinder mBinder; + private LeAudioServiceBinder mBinder; @Before public void setUp() { - mBinder = new LeAudioService.BluetoothLeAudioBinder(mService); + mBinder = new LeAudioServiceBinder(mService); } @Test diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_audio/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/le_audio/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/le_audio/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java index fa0c3d3f35..0ebb4e7bd2 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/map/BluetoothMapServiceBinderTest.java @@ -25,6 +25,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -35,88 +36,85 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link BluetoothMapServiceBinder} */ @SmallTest @RunWith(AndroidJUnit4.class) public class BluetoothMapServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private BluetoothMapService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(98); - private BluetoothMapService.BluetoothMapBinder mBinder; + private BluetoothMapServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new BluetoothMapService.BluetoothMapBinder(mService); + mBinder = new BluetoothMapServiceBinder(mService); } @Test public void disconnect_callsServiceMethod() { - mBinder.disconnect(mDevice, null); - + mBinder.disconnect(mDevice, mAttributionSource); verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); - + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); - + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { - mBinder.getConnectionPolicy(mDevice, null); - + mBinder.getConnectionPolicy(mDevice, mAttributionSource); verify(mService).getConnectionPolicy(mDevice); } @Test public void getState_callsServiceMethod() { - mBinder.getState(null); - + mBinder.getState(mAttributionSource); verify(mService).getState(); } @Test public void isConnected_callsServiceStaticMethod() { - mBinder.isConnected(mDevice, null); - + mBinder.isConnected(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @Test public void getClient_callsServiceStaticMethod() { - mBinder.getClient(null); + mBinder.getClient(mAttributionSource); verify(mService).getRemoteDevice(); } @Test - public void cleanUp_doesNotCrash() { + public void cleanup_doesNotCrash() { mBinder.cleanup(); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/map/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/map/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/map/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapapi/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/mapapi/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/mapapi/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java index 0830f8b9f9..4e6910718f 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientServiceBinderTest.java @@ -24,6 +24,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import android.net.Uri; import androidx.test.filters.MediumTest; @@ -35,70 +36,68 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link MapClientServiceBinder} */ @MediumTest @RunWith(AndroidJUnit4.class) public class MapClientServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private MapClientService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(65); - MapClientService.Binder mBinder; + private MapClientServiceBinder mBinder; @Before public void setUp() { - mBinder = new MapClientService.Binder(mService); + mBinder = new MapClientServiceBinder(mService); } @Test public void connect_callsServiceMethod() { - mBinder.connect(mDevice, null); - + mBinder.connect(mDevice, mAttributionSource); verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { - mBinder.disconnect(mDevice, null); - + mBinder.disconnect(mDevice, mAttributionSource); verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); - + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); - + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { - mBinder.getConnectionPolicy(mDevice, null); - + mBinder.getConnectionPolicy(mDevice, mAttributionSource); verify(mService).getConnectionPolicy(mDevice); } @@ -106,13 +105,13 @@ public class MapClientServiceBinderTest { public void sendMessage_callsServiceMethod() { Uri[] contacts = new Uri[] {}; String message = "test_message"; - mBinder.sendMessage(mDevice, contacts, message, null, null, null); + mBinder.sendMessage(mDevice, contacts, message, null, null, mAttributionSource); verify(mService).sendMessage(mDevice, contacts, message, null, null); } @Test - public void cleanUp_doesNotCrash() { + public void cleanup_doesNotCrash() { mBinder.cleanup(); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/mapclient/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/mapclient/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/mapclient/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java new file mode 100644 index 0000000000..dc21e810b8 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/mcp/McpServiceBinderTest.java @@ -0,0 +1,69 @@ +/* + * 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.mcp; + +import static com.android.bluetooth.TestUtils.MockitoRule; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +/** Test cases for {@link McpServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class McpServiceBinderTest { + + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); + + @Mock private McpService mService; + + private AttributionSource mAttributionSource; + private McpServiceBinder mBinder; + + @Before + public void setUp() throws Exception { + when(mService.isAvailable()).thenReturn(true); + mBinder = new McpServiceBinder(mService); + mAttributionSource = new AttributionSource.Builder(1).build(); + } + + @Test + public void setDeviceAuthorized() { + BluetoothDevice device = mock(BluetoothDevice.class); + boolean isAuthorized = true; + + mBinder.setDeviceAuthorized(device, isAuthorized, mAttributionSource); + verify(mService).setDeviceAuthorized(device, isAuthorized); + } + + @Test + public void cleanup_doesNotCrash() { + mBinder.cleanup(); + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/mcp/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/mcp/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/mcp/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/tests/unit/src/com/android/bluetooth/obex/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/obex/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/obex/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java index f6c393909c..3ab02065e3 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppTransferTest.java @@ -390,7 +390,6 @@ public class BluetoothOppTransferTest { @Test public void oppConnectionReceiver_onReceiveWithActionSdpRecord_withoutSdpRecord() { - mSetFlagRule.enableFlags(Flags.FLAG_IDENTITY_ADDRESS_NULL_IF_NOT_KNOWN); BluetoothDevice device = InstrumentationRegistry.getInstrumentation() .getTargetContext() diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java index 705c91d518..cadff94862 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppUtilityTest.java @@ -44,6 +44,7 @@ import android.content.pm.ResolveInfo; import android.database.Cursor; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.provider.Settings; import androidx.test.platform.app.InstrumentationRegistry; @@ -432,4 +433,22 @@ public class BluetoothOppUtilityTest { assertWithMessage("Exception should not happen. " + e).fail(); } } + + @Test + public void grantPermissionToNearbyComponent() { + Uri originalUri = Uri.parse("content://test.provider/1"); + Settings.Secure.putString( + mContext.getContentResolver(), + "nearby_sharing_component", + "com.example/.BComponent"); + Context spiedContext = spy(new ContextWrapper(mContext)); + + BluetoothOppUtility.grantPermissionToNearbyComponent(spiedContext, List.of(originalUri)); + + verify(spiedContext) + .grantUriPermission( + eq("com.example"), + eq(originalUri), + eq(Intent.FLAG_GRANT_READ_URI_PERMISSION)); + } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/opp/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/pan/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/pan/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/pan/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java index d798666437..272ada98d7 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java @@ -25,6 +25,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -35,75 +36,73 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link PanServiceBinder} */ @SmallTest @RunWith(AndroidJUnit4.class) public class PanServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private PanService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(64); - PanService.BluetoothPanBinder mBinder; + private PanServiceBinder mBinder; @Before public void setUp() throws Exception { - mBinder = new PanService.BluetoothPanBinder(mService); + mBinder = new PanServiceBinder(mService); } @Test public void connect_callsServiceMethod() { - mBinder.connect(mDevice, null); - + mBinder.connect(mDevice, mAttributionSource); verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { - mBinder.disconnect(mDevice, null); - + mBinder.disconnect(mDevice, mAttributionSource); verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); - + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); - + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void isTetheringOn_callsServiceMethod() { - mBinder.isTetheringOn(null); - + mBinder.isTetheringOn(mAttributionSource); verify(mService).isTetheringOn(); } @Test - public void cleanUp_doesNotCrash() { + public void cleanup_doesNotCrash() { mBinder.cleanup(); } } diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java index 95b2bb25ef..5c713d62b5 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pbap/BluetoothPbapServiceBinderTest.java @@ -25,6 +25,7 @@ import static com.android.bluetooth.TestUtils.getTestDevice; import static org.mockito.Mockito.verify; import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; @@ -35,32 +36,35 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +/** Test cases for {@link BluetoothPbapServiceBinder} */ @MediumTest @RunWith(AndroidJUnit4.class) public class BluetoothPbapServiceBinderTest { + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private BluetoothPbapService mService; + private final AttributionSource mAttributionSource = new AttributionSource.Builder(1).build(); private final BluetoothDevice mDevice = getTestDevice(60); - BluetoothPbapService.PbapBinder mBinder; + private BluetoothPbapServiceBinder mBinder; @Before public void setUp() { - mBinder = new BluetoothPbapService.PbapBinder(mService); + mBinder = new BluetoothPbapServiceBinder(mService); } @Test public void disconnect_callsServiceMethod() { - mBinder.disconnect(mDevice, null); + mBinder.disconnect(mDevice, mAttributionSource); verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { - mBinder.getConnectedDevices(null); + mBinder.getConnectedDevices(mAttributionSource); verify(mService).getConnectedDevices(); } @@ -68,14 +72,14 @@ public class BluetoothPbapServiceBinderTest { @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { - mBinder.getConnectionState(mDevice, null); + mBinder.getConnectionState(mDevice, mAttributionSource); verify(mService).getConnectionState(mDevice); } @@ -83,7 +87,7 @@ public class BluetoothPbapServiceBinderTest { @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbap/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/pbap/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/pbap/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexTransportTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexTransportTest.java index dfcc542467..b594ac70b4 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexTransportTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientObexTransportTest.java @@ -183,7 +183,6 @@ public class PbapClientObexTransportTest { public void testGetRemoteAddress_transportRfcomm_returnsDeviceIdentityAddress() { doReturn(BluetoothSocket.TYPE_RFCOMM).when(mMockSocket).getConnectionType(); PbapClientObexTransport transport = new PbapClientObexTransport(mMockSocket); - // See "Flags.identityAddressNullIfNotKnown():" // Identity address won't be "known" by the stack for a test device, so it'll return null. // assertThat(transport.getRemoteAddress()).isNull(); assertThat(transport.getRemoteAddress()).isEqualTo(mTestDevice.getAddress()); diff --git a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceBinderTest.java index aff8f250a7..94b51136dd 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientBinderTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/pbapclient/PbapClientServiceBinderTest.java @@ -47,29 +47,30 @@ import org.mockito.Mock; import java.util.List; +/** Test cases for {@link PbapClientServiceBinder} */ @MediumTest @RunWith(AndroidJUnit4.class) -public class PbapClientBinderTest { +public class PbapClientServiceBinderTest { @Rule public final MockitoRule mMockitoRule = new MockitoRule(); @Mock private PbapClientService mMockService; private BluetoothDevice mTestDevice; private AttributionSource mAttributionSource; - private PbapClientBinder mPbapClientBinder; + private PbapClientServiceBinder mPbapClientServiceBinder; @Before public void setUp() throws Exception { mTestDevice = getTestDevice(1); mAttributionSource = new AttributionSource.Builder(1).build(); - mPbapClientBinder = new PbapClientBinder(mMockService); + mPbapClientServiceBinder = new PbapClientServiceBinder(mMockService); } @After public void tearDown() throws Exception { - if (mPbapClientBinder != null) { - mPbapClientBinder.cleanup(); - mPbapClientBinder = null; + if (mPbapClientServiceBinder != null) { + mPbapClientServiceBinder.cleanup(); + mPbapClientServiceBinder = null; } } @@ -79,45 +80,46 @@ public class PbapClientBinderTest { @Test public void testConnect() { - mPbapClientBinder.connect(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.connect(mTestDevice, mAttributionSource); verify(mMockService).connect(eq(mTestDevice)); } @Test public void testDisconnect() { - mPbapClientBinder.disconnect(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.disconnect(mTestDevice, mAttributionSource); verify(mMockService).disconnect(eq(mTestDevice)); } @Test public void testGetConnectedDevices() { - mPbapClientBinder.getConnectedDevices(mAttributionSource); + mPbapClientServiceBinder.getConnectedDevices(mAttributionSource); verify(mMockService).getConnectedDevices(); } @Test public void testGetDevicesMatchingConnectionStates() { int[] states = new int[] {STATE_CONNECTED}; - mPbapClientBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); + mPbapClientServiceBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); verify(mMockService).getDevicesMatchingConnectionStates(eq(states)); } @Test public void testGetConnectionState() { - mPbapClientBinder.getConnectionState(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.getConnectionState(mTestDevice, mAttributionSource); verify(mMockService).getConnectionState(eq(mTestDevice)); } @Test public void testSetConnectionPolicy() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mPbapClientBinder.setConnectionPolicy(mTestDevice, connectionPolicy, mAttributionSource); + mPbapClientServiceBinder.setConnectionPolicy( + mTestDevice, connectionPolicy, mAttributionSource); verify(mMockService).setConnectionPolicy(eq(mTestDevice), eq(connectionPolicy)); } @Test public void testGetConnectionPolicy() { - mPbapClientBinder.getConnectionPolicy(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.getConnectionPolicy(mTestDevice, mAttributionSource); verify(mMockService).getConnectionPolicy(eq(mTestDevice)); } @@ -127,52 +129,54 @@ public class PbapClientBinderTest { @Test public void testConnect_afterCleanup_returnsFalse() { - mPbapClientBinder.cleanup(); - boolean result = mPbapClientBinder.connect(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.cleanup(); + boolean result = mPbapClientServiceBinder.connect(mTestDevice, mAttributionSource); verify(mMockService, never()).connect(any(BluetoothDevice.class)); assertThat(result).isFalse(); } @Test public void testDisconnect_afterCleanup_returnsFalse() { - mPbapClientBinder.cleanup(); - boolean result = mPbapClientBinder.disconnect(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.cleanup(); + boolean result = mPbapClientServiceBinder.disconnect(mTestDevice, mAttributionSource); verify(mMockService, never()).disconnect(any(BluetoothDevice.class)); assertThat(result).isFalse(); } @Test public void testGetConnectedDevices_afterCleanup_returnsEmptyList() { - mPbapClientBinder.cleanup(); - List<BluetoothDevice> devices = mPbapClientBinder.getConnectedDevices(mAttributionSource); + mPbapClientServiceBinder.cleanup(); + List<BluetoothDevice> devices = + mPbapClientServiceBinder.getConnectedDevices(mAttributionSource); verify(mMockService, never()).getConnectedDevices(); assertThat(devices).isEmpty(); } @Test public void testGetDevicesMatchingConnectionStates_afterCleanup_returnsEmptyList() { - mPbapClientBinder.cleanup(); + mPbapClientServiceBinder.cleanup(); int[] states = new int[] {STATE_CONNECTED}; List<BluetoothDevice> devices = - mPbapClientBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); + mPbapClientServiceBinder.getDevicesMatchingConnectionStates( + states, mAttributionSource); verify(mMockService, never()).getDevicesMatchingConnectionStates(any(int[].class)); assertThat(devices).isEmpty(); } @Test public void testGetConnectionState_afterCleanup_returnsDisconnected() { - mPbapClientBinder.cleanup(); - int state = mPbapClientBinder.getConnectionState(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.cleanup(); + int state = mPbapClientServiceBinder.getConnectionState(mTestDevice, mAttributionSource); verify(mMockService, never()).getConnectionState(any(BluetoothDevice.class)); assertThat(state).isEqualTo(STATE_DISCONNECTED); } @Test public void testSetConnectionPolicy_afterCleanup_returnsFalse() { - mPbapClientBinder.cleanup(); + mPbapClientServiceBinder.cleanup(); int connectionPolicy = CONNECTION_POLICY_ALLOWED; boolean result = - mPbapClientBinder.setConnectionPolicy( + mPbapClientServiceBinder.setConnectionPolicy( mTestDevice, connectionPolicy, mAttributionSource); verify(mMockService, never()).setConnectionPolicy(any(BluetoothDevice.class), anyInt()); assertThat(result).isFalse(); @@ -180,8 +184,8 @@ public class PbapClientBinderTest { @Test public void testGetConnectionPolicy_afterCleanup_returnsUnknown() { - mPbapClientBinder.cleanup(); - int result = mPbapClientBinder.getConnectionPolicy(mTestDevice, mAttributionSource); + mPbapClientServiceBinder.cleanup(); + int result = mPbapClientServiceBinder.getConnectionPolicy(mTestDevice, mAttributionSource); verify(mMockService, never()).getConnectionPolicy(any(BluetoothDevice.class)); assertThat(result).isEqualTo(CONNECTION_POLICY_UNKNOWN); } diff --git a/android/app/tests/unit/src/com/android/bluetooth/sap/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/sap/OWNERS deleted file mode 100644 index 8f87191393..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/sap/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_content
\ No newline at end of file diff --git a/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java new file mode 100644 index 0000000000..10d1a092e0 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/sap/SapServiceBinderTest.java @@ -0,0 +1,132 @@ +/* + * 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.sap; + +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; +import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; + +import static com.android.bluetooth.TestUtils.MockitoRule; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.content.AttributionSource; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +/** Test cases for {@link SapServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SapServiceBinderTest { + + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); + + @Mock private SapService mService; + + private AttributionSource mAttributionSource; + private SapServiceBinder mBinder; + + @Before + public void setUp() throws Exception { + when(mService.isAvailable()).thenReturn(true); + mBinder = new SapServiceBinder(mService); + mAttributionSource = new AttributionSource.Builder(1).build(); + } + + @Test + public void getState() { + mBinder.getState(mAttributionSource); + verify(mService).getState(); + } + + @Test + public void getClient() { + mBinder.getClient(mAttributionSource); + // times(2) due to the Log + verify(mService, times(2)).getRemoteDevice(); + } + + @Test + public void isConnected() { + BluetoothDevice device = mock(BluetoothDevice.class); + + mBinder.isConnected(device, mAttributionSource); + verify(mService).getConnectionState(device); + } + + @Test + public void disconnect() { + BluetoothDevice device = mock(BluetoothDevice.class); + + mBinder.disconnect(device, mAttributionSource); + verify(mService).disconnect(device); + } + + @Test + public void getConnectedDevices() { + mBinder.getConnectedDevices(mAttributionSource); + verify(mService).getConnectedDevices(); + } + + @Test + public void getDevicesMatchingConnectionStates() { + int[] states = new int[] {STATE_CONNECTED, STATE_DISCONNECTED}; + + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); + verify(mService).getDevicesMatchingConnectionStates(states); + } + + @Test + public void getConnectionState() { + BluetoothDevice device = mock(BluetoothDevice.class); + + mBinder.getConnectionState(device, mAttributionSource); + verify(mService).getConnectionState(device); + } + + @Test + public void setConnectionPolicy() { + BluetoothDevice device = mock(BluetoothDevice.class); + int connectionPolicy = 1; + + mBinder.setConnectionPolicy(device, connectionPolicy, mAttributionSource); + verify(mService).setConnectionPolicy(device, connectionPolicy); + } + + @Test + public void getConnectionPolicy() { + BluetoothDevice device = mock(BluetoothDevice.class); + + mBinder.getConnectionPolicy(device, mAttributionSource); + verify(mService).getConnectionPolicy(device); + } + + @Test + public void cleanup_doesNotCrash() { + mBinder.cleanup(); + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java new file mode 100644 index 0000000000..7758d101b1 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/tbs/TbsServiceBinderTest.java @@ -0,0 +1,146 @@ +/* + * 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.tbs; + +import static com.android.bluetooth.TestUtils.MockitoRule; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothLeCall; +import android.bluetooth.IBluetoothLeCallControlCallback; +import android.content.AttributionSource; +import android.os.ParcelUuid; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** Test cases for {@link TbsServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TbsServiceBinderTest { + + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); + + @Mock private TbsService mService; + + private AttributionSource mAttributionSource; + private TbsServiceBinder mBinder; + + @Before + public void setUp() throws Exception { + when(mService.isAvailable()).thenReturn(true); + mBinder = new TbsServiceBinder(mService); + mAttributionSource = new AttributionSource.Builder(1).build(); + } + + @Test + public void registerBearer() { + String token = "token"; + IBluetoothLeCallControlCallback callback = mock(IBluetoothLeCallControlCallback.class); + String uci = "uci"; + List<String> uriSchemes = new ArrayList<>(); + int capabilities = 1; + String providerName = "providerName"; + int technology = 2; + + mBinder.registerBearer( + token, + callback, + uci, + uriSchemes, + capabilities, + providerName, + technology, + mAttributionSource); + verify(mService) + .registerBearer( + token, callback, uci, uriSchemes, capabilities, providerName, technology); + } + + @Test + public void unregisterBearer() { + String token = "token"; + + mBinder.unregisterBearer(token, mAttributionSource); + verify(mService).unregisterBearer(token); + } + + @Test + public void requestResult() { + int ccid = 1; + int requestId = 2; + int result = 3; + + mBinder.requestResult(ccid, requestId, result, mAttributionSource); + verify(mService).requestResult(ccid, requestId, result); + } + + @Test + public void callAdded() { + int ccid = 1; + BluetoothLeCall call = mock(BluetoothLeCall.class); + + mBinder.callAdded(ccid, call, mAttributionSource); + verify(mService).callAdded(ccid, call); + } + + @Test + public void callRemoved() { + int ccid = 1; + UUID callId = UUID.randomUUID(); + int reason = 2; + + mBinder.callRemoved(ccid, new ParcelUuid(callId), reason, mAttributionSource); + verify(mService).callRemoved(ccid, callId, reason); + } + + @Test + public void callStateChanged() { + int ccid = 1; + UUID callId = UUID.randomUUID(); + int state = 2; + + mBinder.callStateChanged(ccid, new ParcelUuid(callId), state, mAttributionSource); + verify(mService).callStateChanged(ccid, callId, state); + } + + @Test + public void currentCallsList() { + int ccid = 1; + List<BluetoothLeCall> calls = new ArrayList<>(); + + mBinder.currentCallsList(ccid, calls, mAttributionSource); + verify(mService).currentCallsList(ccid, calls); + } + + @Test + public void cleanup_doesNotCrash() { + mBinder.cleanup(); + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/OWNERS b/android/app/tests/unit/src/com/android/bluetooth/vc/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/vc/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java new file mode 100644 index 0000000000..32296381c8 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceBinderTest.java @@ -0,0 +1,294 @@ +/* + * 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.vc; + +import static android.bluetooth.BluetoothProfile.STATE_CONNECTED; + +import static com.android.bluetooth.TestUtils.MockitoRule; +import static com.android.bluetooth.TestUtils.getTestDevice; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IAudioInputCallback; +import android.content.AttributionSource; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +/** Test cases for {@link VolumeControlServiceBinder} */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class VolumeControlServiceBinderTest { + + @Rule public final MockitoRule mMockitoRule = new MockitoRule(); + + @Mock private VolumeControlService mService; + + private final BluetoothDevice mDevice = getTestDevice(25); + + private AttributionSource mAttributionSource; + private VolumeControlServiceBinder mBinder; + + @Before + public void setUp() throws Exception { + when(mService.isAvailable()).thenReturn(true); + mBinder = new VolumeControlServiceBinder(mService); + mAttributionSource = new AttributionSource.Builder(1).build(); + } + + @Test + public void getConnectedDevices() { + mBinder.getConnectedDevices(mAttributionSource); + verify(mService).getConnectedDevices(); + } + + @Test + public void getDevicesMatchingConnectionStates() { + int[] states = new int[] {STATE_CONNECTED}; + + mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); + verify(mService).getDevicesMatchingConnectionStates(states); + } + + @Test + public void getConnectionState() { + mBinder.getConnectionState(mDevice, mAttributionSource); + verify(mService).getConnectionState(mDevice); + } + + @Test + public void setConnectionPolicy() { + int connectionPolicy = 1; + + mBinder.setConnectionPolicy(mDevice, connectionPolicy, mAttributionSource); + verify(mService).setConnectionPolicy(mDevice, connectionPolicy); + } + + @Test + public void getConnectionPolicy() { + mBinder.getConnectionPolicy(mDevice, mAttributionSource); + verify(mService).getConnectionPolicy(mDevice); + } + + @Test + public void isVolumeOffsetAvailable() { + mBinder.isVolumeOffsetAvailable(mDevice, mAttributionSource); + verify(mService).isVolumeOffsetAvailable(mDevice); + } + + @Test + public void getNumberOfVolumeOffsetInstances() { + mBinder.getNumberOfVolumeOffsetInstances(mDevice, mAttributionSource); + verify(mService).getNumberOfVolumeOffsetInstances(mDevice); + } + + @Test + public void setVolumeOffset() { + int instanceId = 1; + int volumeOffset = 2; + + mBinder.setVolumeOffset(mDevice, instanceId, volumeOffset, mAttributionSource); + verify(mService).setVolumeOffset(mDevice, instanceId, volumeOffset); + } + + @Test + public void setDeviceVolume() { + int volume = 1; + boolean isGroupOp = true; + + mBinder.setDeviceVolume(mDevice, volume, isGroupOp, mAttributionSource); + verify(mService).setDeviceVolume(mDevice, volume, isGroupOp); + } + + @Test + public void setGroupVolume() { + int groupId = 1; + int volume = 2; + + mBinder.setGroupVolume(groupId, volume, mAttributionSource); + verify(mService).setGroupVolume(groupId, volume); + } + + @Test + public void getGroupVolume() { + int groupId = 1; + + mBinder.getGroupVolume(groupId, mAttributionSource); + verify(mService).getGroupVolume(groupId); + } + + @Test + public void setGroupActive() { + int groupId = 1; + boolean active = true; + + mBinder.setGroupActive(groupId, active, mAttributionSource); + verify(mService).setGroupActive(groupId, active); + } + + @Test + public void mute() { + mBinder.mute(mDevice, mAttributionSource); + verify(mService).mute(mDevice); + } + + @Test + public void muteGroup() { + int groupId = 1; + mBinder.muteGroup(groupId, mAttributionSource); + verify(mService).muteGroup(groupId); + } + + @Test + public void unmute() { + mBinder.unmute(mDevice, mAttributionSource); + verify(mService).unmute(mDevice); + } + + @Test + public void unmuteGroup() { + int groupId = 1; + + mBinder.unmuteGroup(groupId, mAttributionSource); + verify(mService).unmuteGroup(groupId); + } + + @Test + public void getNumberOfAudioInputControlServices() { + mBinder.getNumberOfAudioInputControlServices(mAttributionSource, mDevice); + } + + @Test + public void registerAudioInputControlCallback() { + int instanceId = 1; + IAudioInputCallback callback = mock(IAudioInputCallback.class); + + mBinder.registerAudioInputControlCallback( + mAttributionSource, mDevice, instanceId, callback); + } + + @Test + public void unregisterAudioInputControlCallback() { + int instanceId = 1; + IAudioInputCallback callback = mock(IAudioInputCallback.class); + + mBinder.unregisterAudioInputControlCallback( + mAttributionSource, mDevice, instanceId, callback); + } + + @Test + public void getAudioInputGainSettingUnit() { + int instanceId = 1; + mBinder.getAudioInputGainSettingUnit(mAttributionSource, mDevice, instanceId); + } + + @Test + public void getAudioInputGainSettingMin() { + int instanceId = 1; + mBinder.getAudioInputGainSettingMin(mAttributionSource, mDevice, instanceId); + } + + @Test + public void getAudioInputGainSettingMax() { + int instanceId = 1; + mBinder.getAudioInputGainSettingMax(mAttributionSource, mDevice, instanceId); + } + + @Test + public void getAudioInputDescription() { + int instanceId = 1; + mBinder.getAudioInputDescription(mAttributionSource, mDevice, instanceId); + } + + @Test + public void isAudioInputDescriptionWritable() { + int instanceId = 1; + mBinder.isAudioInputDescriptionWritable(mAttributionSource, mDevice, instanceId); + } + + @Test + public void setAudioInputDescription() { + int instanceId = 1; + String description = "test"; + mBinder.setAudioInputDescription(mAttributionSource, mDevice, instanceId, description); + } + + @Test + public void getAudioInputStatus() { + int instanceId = 1; + mBinder.getAudioInputStatus(mAttributionSource, mDevice, instanceId); + } + + @Test + public void getAudioInputType() { + int instanceId = 1; + mBinder.getAudioInputType(mAttributionSource, mDevice, instanceId); + } + + @Test + public void getAudioInputGainSetting() { + int instanceId = 1; + mBinder.getAudioInputGainSetting(mAttributionSource, mDevice, instanceId); + } + + @Test + public void setAudioInputGainSetting() { + int instanceId = 1; + int gainSetting = 2; + mBinder.setAudioInputGainSetting(mAttributionSource, mDevice, instanceId, gainSetting); + } + + @Test + public void getAudioInputGainMode() { + int instanceId = 1; + mBinder.getAudioInputGainMode(mAttributionSource, mDevice, instanceId); + } + + @Test + public void setAudioInputGainMode() { + int instanceId = 1; + int gainMode = 2; + mBinder.setAudioInputGainMode(mAttributionSource, mDevice, instanceId, gainMode); + } + + @Test + public void getAudioInputMute() { + int instanceId = 1; + mBinder.getAudioInputMute(mAttributionSource, mDevice, instanceId); + } + + @Test + public void setAudioInputMute() { + int instanceId = 1; + int mute = 2; + mBinder.setAudioInputMute(mAttributionSource, mDevice, instanceId, mute); + } + + @Test + public void cleanup_doesNotCrash() { + mBinder.cleanup(); + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java index 35d8ee3ce1..9412ebfe40 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/vc/VolumeControlServiceTest.java @@ -128,7 +128,7 @@ public class VolumeControlServiceTest { private AttributionSource mAttributionSource; private VolumeControlService mService; - private VolumeControlService.BluetoothVolumeControlBinder mBinder; + private VolumeControlServiceBinder mBinder; private InOrder mInOrder; private TestLooper mLooper; @@ -174,7 +174,7 @@ public class VolumeControlServiceTest { mService.setAvailable(true); mService.mFactory = mServiceFactory; - mBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder(); + mBinder = (VolumeControlServiceBinder) mService.initBinder(); } @After diff --git a/android/leaudio/OWNERS b/android/leaudio/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/android/leaudio/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/android/pandora/OWNERS b/android/pandora/OWNERS deleted file mode 100644 index c199a82a60..0000000000 --- a/android/pandora/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 1099313 -girardier@google.com diff --git a/android/pandora/test/pairing/OWNERS b/android/pandora/test/pairing/OWNERS deleted file mode 100644 index b430ae987d..0000000000 --- a/android/pandora/test/pairing/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Bug component: 27441 -rwt@google.com diff --git a/apex/OWNERS b/apex/OWNERS deleted file mode 100644 index cc88477d0e..0000000000 --- a/apex/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Reviewers for /apex - -wescande@google.com - diff --git a/apex/hiddenapi/OWNERS b/apex/hiddenapi/OWNERS deleted file mode 100644 index ac8a2b627c..0000000000 --- a/apex/hiddenapi/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# soong-team@ as the hiddenapi files are tightly coupled with Soong -file:platform/build/soong:/OWNERS - -# compat-team@ for changes to hiddenapi files -file:tools/platform-compat:/OWNERS diff --git a/apex/permissions/OWNERS b/apex/permissions/OWNERS deleted file mode 100644 index 8b8ec67028..0000000000 --- a/apex/permissions/OWNERS +++ /dev/null @@ -1 +0,0 @@ -per-file *.xml,OWNERS = file:platform/frameworks/base:/data/etc/OWNERS @@ -86,7 +86,6 @@ HOST_TESTS = [ # 'bluetooth_test_common', # 'bluetoothtbd_test', # 'net_test_avrcp', - # 'net_test_btcore', # 'net_test_types', # 'net_test_btm_iso', # 'net_test_btpackets', diff --git a/common/OWNERS b/common/OWNERS deleted file mode 100644 index 5b3f4bad26..0000000000 --- a/common/OWNERS +++ /dev/null @@ -1 +0,0 @@ -wescande@google.com diff --git a/flags/OWNERS b/flags/OWNERS deleted file mode 100644 index b4f2f7376b..0000000000 --- a/flags/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 1387432 -girardier@google.com -wescande@google.com diff --git a/flags/a2dp.aconfig b/flags/a2dp.aconfig index e7b9aa21b0..1fd8bbd085 100644 --- a/flags/a2dp.aconfig +++ b/flags/a2dp.aconfig @@ -115,3 +115,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "avdtp_prevent_double_suspend" + namespace: "bluetooth" + description: "Add state to prevent sending multiple AVDTP Suspend command in a row" + bug: "402241316" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/bta_dm.aconfig b/flags/bta_dm.aconfig index 19e8418785..be510f63cd 100644 --- a/flags/bta_dm.aconfig +++ b/flags/bta_dm.aconfig @@ -9,16 +9,6 @@ flag { } flag { - name: "cancel_open_discovery_client" - namespace: "bluetooth" - description: "Cancel connection from discovery client correctly" - bug: "356168480" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "extend_and_randomize_role_switch_delay" namespace: "bluetooth" description: "Fix the possible conflicts between role switch and authentication" diff --git a/flags/connectivity.aconfig b/flags/connectivity.aconfig index 482c7e68e8..738c294078 100644 --- a/flags/connectivity.aconfig +++ b/flags/connectivity.aconfig @@ -2,30 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.bt" flag { - name: "api_get_connection_state_using_identity_address" - namespace: "bluetooth" - description: "Use identity address to check current connection state" - bug: "319471537" -} - -flag { - name: "use_le_shim_connection_map_guard" - namespace: "bluetooth" - description: "Guard the le shim connection map with a mutex" - bug: "302054609" -} - -flag { - name: "improve_create_connection_for_already_connecting_device" - namespace: "bluetooth" - description: "Make sure to not stop controller with create connection cancel when not needed" - bug: "356593752" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "allow_gatt_connect_from_the_apps_without_making_leaudio_device_active" namespace: "bluetooth" description: "Allows for GATT connection without making LeAudio device active after connection" diff --git a/flags/framework.aconfig b/flags/framework.aconfig index dc48541118..0151b46c70 100644 --- a/flags/framework.aconfig +++ b/flags/framework.aconfig @@ -10,16 +10,6 @@ flag { } flag { - name: "identity_address_null_if_not_known" - namespace: "bluetooth" - description: "Return null for identity address if identity address is not known" - bug: "317120534" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "support_bluetooth_quality_report_v6" is_exported: true namespace: "bluetooth" diff --git a/flags/gap.aconfig b/flags/gap.aconfig index 4a03337332..856f4f7bb4 100644 --- a/flags/gap.aconfig +++ b/flags/gap.aconfig @@ -50,33 +50,6 @@ flag { } flag { - name: "gatt_client_dynamic_allocation" - namespace: "bluetooth" - description: "Allocate GATT clients in heap to unlimit the number of clients" - bug: "348559823" -} - -flag { - name: "gatt_fix_multiple_direct_connect" - namespace: "bluetooth" - description: "Do not remove device when multiple clients attempt to connect" - bug: "349232327" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "pan_use_identity_address" - namespace: "bluetooth" - description: "Use identity address when communicate with native interface" - bug: "351961492" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "android_os_identifier" namespace: "bluetooth" description: "Add a custom service to provide Android OS identifier" @@ -97,33 +70,6 @@ flag { } flag { - name: "non_wake_alarm_for_rpa_rotation" - namespace: "bluetooth" - description: "Use non-wake alarm for LE RPA rotation. go/non-wake-alarm-for-rpa-rotation" - bug: "360743527" -} - -flag { - name: "gatt_disconnect_fix" - namespace: "bluetooth" - description: "Fix GATT disconnect handling" - bug: "361538527" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { - name: "gatt_server_requests_fix" - namespace: "bluetooth" - description: "Fix GATT server handling" - bug: "361331170" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "le_scan_msft_support" namespace: "bluetooth" description: "Support MSFT HCI extension for LE Scanning. go/bt-msft-aosp-dd" @@ -199,16 +145,6 @@ flag { } flag { - name: "drop_acl_fragment_on_disconnect" - namespace: "bluetooth" - description: "Drop pending ACL packet fragments for disconnected connection" - bug: "376379859" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "configure_scan_on_resume" namespace: "bluetooth" description: "Configure scan paramters when start scanning from onResume" @@ -317,3 +253,20 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "le_disconnect_notification_handling" + namespace: "bluetooth" + description: "Fix LE link disconnection notification to stack's upper layers" + bug: "403433488" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "rssi_scan_filter" + namespace: "bluetooth" + description: "Add RSSI ScanFilter" + bug: "404986559" +} diff --git a/flags/hfp.aconfig b/flags/hfp.aconfig index c1611242aa..9e3bd50b5d 100644 --- a/flags/hfp.aconfig +++ b/flags/hfp.aconfig @@ -132,3 +132,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "hfp_volume_control_property" + namespace: "bluetooth" + description: "Check system property before hands-free volume UI request." + bug: "404152232" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/hid.aconfig b/flags/hid.aconfig index d40e55821f..350367818b 100644 --- a/flags/hid.aconfig +++ b/flags/hid.aconfig @@ -20,16 +20,6 @@ flag { } flag { - name: "serialize_hogp_and_dis" - namespace: "bluetooth" - description: "start HOGP service discovery only after DIS discovery is finished" - bug: "367910199" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "dont_send_hid_set_idle" namespace: "bluetooth" description: "Don't send the SET IDLE HID message" @@ -40,16 +30,6 @@ flag { } flag { - name: "forward_get_set_report_failure_to_uhid" - namespace: "bluetooth" - description: "Forward GET/SET REPORT reply to UHID even when the request fails" - bug: "369748430" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "remove_pending_hid_connection" namespace: "bluetooth" description: "Remove the pending BTA HH connection instance when the device is removed" @@ -118,3 +98,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "hh_state_update_race_fix" + namespace: "bluetooth" + description: "Fix stuck in connecting state due to race when updating state" + bug: "403420458" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "simpler_hid_connection_policy" + namespace: "bluetooth" + description: "Simplify native HID host connection policy enforcement" + bug: "404590499" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/leaudio.aconfig b/flags/leaudio.aconfig index 9d2c86afa3..a2f77b5658 100644 --- a/flags/leaudio.aconfig +++ b/flags/leaudio.aconfig @@ -344,3 +344,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "leaudio_use_aggressive_params" + namespace: "bluetooth" + description: "use aggressive parameters for LE Audio device" + bug: "400607635" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/opp.aconfig b/flags/opp.aconfig index 73604961fe..cdba75b756 100644 --- a/flags/opp.aconfig +++ b/flags/opp.aconfig @@ -2,16 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.bt" flag { - name: "opp_ignore_content_observer_after_service_stop" - namespace: "bluetooth" - description: "When OppService.stop() is called, previously posted ContentObserver.onChange() should not run" - bug: "346467641" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "opp_set_insets_for_edge_to_edge" namespace: "bluetooth" description: "Set proper insets in BluetoothOppTransferHistory to adapt to edge-to-edge." @@ -41,3 +31,20 @@ flag { } } +flag { + name: "opp_device_picker_extra_intent_apis" + is_exported: true + namespace: "bluetooth" + description: "New API to get the original intent in Bluetooth Device Picker" + bug: "395796600" +} + +flag { + name: "send_opp_device_picker_extra_intent" + namespace: "bluetooth" + description: "Send the original intent when opening Bluetooth device picker" + bug: "397852103" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/pairing.aconfig b/flags/pairing.aconfig index f30410db17..faae7d2618 100644 --- a/flags/pairing.aconfig +++ b/flags/pairing.aconfig @@ -270,3 +270,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "unrelated_device_smp_cancellation" + namespace: "bluetooth" + description: "Don't cancel SMP pairing if encryption fails for unrelated device" + bug: "404658828" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/floss/OWNERS b/floss/OWNERS deleted file mode 100644 index a823e6e1dd..0000000000 --- a/floss/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_chromeos diff --git a/floss/pandora/OWNERS b/floss/pandora/OWNERS deleted file mode 100644 index ca4001b6d4..0000000000 --- a/floss/pandora/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Reviewers for /floss/pandora - -johnlai@google.com -chharry@google.com diff --git a/framework/OWNERS b/framework/OWNERS deleted file mode 100644 index 40a43c47cb..0000000000 --- a/framework/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -hallstrom@google.com -okamil@google.com -siyuanh@google.com -wescande@google.com diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt index cdb9bf0670..ef922c92e1 100644 --- a/framework/api/system-current.txt +++ b/framework/api/system-current.txt @@ -369,6 +369,7 @@ package android.bluetooth { public interface BluetoothDevicePicker { field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_DEVICE_SELECTED = "android.bluetooth.devicepicker.action.DEVICE_SELECTED"; field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LAUNCH = "android.bluetooth.devicepicker.action.LAUNCH"; + field @FlaggedApi("com.android.bluetooth.flags.opp_device_picker_extra_intent_apis") public static final String EXTRA_DEVICE_PICKER_ORIGINAL_SEND_INTENT = "android.bluetooth.extra.DEVICE_PICKER_ORIGINAL_SEND_INTENT"; field public static final String EXTRA_FILTER_TYPE = "android.bluetooth.devicepicker.extra.FILTER_TYPE"; field public static final String EXTRA_LAUNCH_CLASS = "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS"; field public static final String EXTRA_LAUNCH_PACKAGE = "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE"; diff --git a/framework/java/android/bluetooth/BluetoothDevicePicker.java b/framework/java/android/bluetooth/BluetoothDevicePicker.java index ee788dae18..7e7cd6d6b7 100644 --- a/framework/java/android/bluetooth/BluetoothDevicePicker.java +++ b/framework/java/android/bluetooth/BluetoothDevicePicker.java @@ -18,6 +18,7 @@ package android.bluetooth; import static android.Manifest.permission.BLUETOOTH_CONNECT; +import android.annotation.FlaggedApi; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -25,6 +26,8 @@ import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; +import com.android.bluetooth.flags.Flags; + /** * A helper to show a system "Device Picker" activity to the user. * @@ -64,6 +67,14 @@ public interface BluetoothDevicePicker { String EXTRA_LAUNCH_CLASS = "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS"; /** + * Extra for the original ACTION_SEND or ACTION_SEND_MULTIPLE intent that triggered the BT + * sharing. + */ + @FlaggedApi(Flags.FLAG_OPP_DEVICE_PICKER_EXTRA_INTENT_APIS) + String EXTRA_DEVICE_PICKER_ORIGINAL_SEND_INTENT = + "android.bluetooth.extra.DEVICE_PICKER_ORIGINAL_SEND_INTENT"; + + /** * Broadcast when one BT device is selected from BT device picker screen. Selected {@link * BluetoothDevice} is returned in extra data named {@link BluetoothDevice#EXTRA_DEVICE}. */ diff --git a/framework/java/android/bluetooth/OWNERS b/framework/java/android/bluetooth/OWNERS deleted file mode 100644 index 757ed73819..0000000000 --- a/framework/java/android/bluetooth/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -per-file BluetoothHearingAid.java=file:/OWNERS_hearingaid -per-file BluetoothCsipSetCoordinator.java=file:/OWNERS_leaudio -per-file BluetoothLeAudio*.java=file:/OWNERS_leaudio -per-file BluetoothLeBroadcast*.java=file:/OWNERS_leaudio -per-file BluetoothVolumeControl.java=file:/OWNERS_leaudio diff --git a/framework/tests/bumble/src/android/bluetooth/DckScanTest.kt b/framework/tests/bumble/src/android/bluetooth/DckScanTest.kt index 8660a4076b..845862b71a 100644 --- a/framework/tests/bumble/src/android/bluetooth/DckScanTest.kt +++ b/framework/tests/bumble/src/android/bluetooth/DckScanTest.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout import org.junit.Assume.assumeTrue +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -99,6 +100,7 @@ class DckScanTest( } @Test + @Ignore("b/404159990") fun scanForUuid_remoteFound() { // Assume isRemoteAdvertisingWithUuid is true to skip tests in which // device is not advertising with UUID diff --git a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java index 9b631dce91..6e6337279e 100644 --- a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java +++ b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java @@ -422,7 +422,6 @@ public class GattClientTest { } @Test - @RequiresFlagsEnabled(Flags.FLAG_GATT_FIX_MULTIPLE_DIRECT_CONNECT) public void connectMultiple_closeOne_shouldSuccess() { BluetoothGattCallback gattCallback = mock(BluetoothGattCallback.class); BluetoothGattCallback gattCallback2 = mock(BluetoothGattCallback.class); @@ -435,11 +434,11 @@ public class GattClientTest { BluetoothGatt gatt2 = device.connectGatt(mContext, false, gattCallback2); try { - gatt.disconnect(); - gatt.close(); - verify(gattCallback2, timeout(1000)) .onConnectionStateChange(eq(gatt2), eq(GATT_SUCCESS), eq(STATE_CONNECTED)); + + gatt.disconnect(); + gatt.close(); } finally { gatt2.disconnect(); gatt2.close(); @@ -696,7 +695,6 @@ public class GattClientTest { // Check if we can have 100 simultaneous clients @Test - @RequiresFlagsEnabled(Flags.FLAG_GATT_CLIENT_DYNAMIC_ALLOCATION) public void connectGatt_multipleClients() { registerGattService(); diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java index 38527362ba..3df5075936 100644 --- a/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java +++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHeadTrackerTest.java @@ -64,6 +64,7 @@ import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.bluetooth.flags.Flags; import com.android.compatibility.common.util.AdoptShellPermissionsRule; import org.hamcrest.CustomTypeSafeMatcher; @@ -353,6 +354,7 @@ public class HidHeadTrackerTest { BluetoothDevice.ACTION_UUID, BluetoothDevice.ACTION_ACL_CONNECTED, BluetoothDevice.ACTION_ACL_DISCONNECTED, + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, BluetoothDevice.ACTION_FOUND); } @@ -405,6 +407,7 @@ public class HidHeadTrackerTest { unregisterIntentActions( BluetoothDevice.ACTION_UUID, BluetoothDevice.ACTION_ACL_CONNECTED, + BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED, BluetoothDevice.ACTION_FOUND); } @@ -490,8 +493,10 @@ public class HidHeadTrackerTest { */ private void verifyTransportSwitch(BluetoothDevice device, int fromTransport, int toTransport) { assertThat(fromTransport).isNotEqualTo(toTransport); - verifyConnectionState(mBumbleDevice, equalTo(fromTransport), equalTo(STATE_DISCONNECTING)); - + if (!Flags.ignoreUnselectedHidTransportStates()) { + verifyConnectionState( + mBumbleDevice, equalTo(fromTransport), equalTo(STATE_DISCONNECTING)); + } // Capture the next intent with filter // Filter is necessary as otherwise it will corrupt all other unordered verifications final Intent[] savedIntent = {null}; diff --git a/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java b/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java index fb89c738ad..45c329e460 100644 --- a/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java +++ b/framework/tests/bumble/src/android/bluetooth/hid/HidHostDualModeTest.java @@ -66,6 +66,7 @@ import android.util.Log; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.bluetooth.flags.Flags; import com.android.compatibility.common.util.AdoptShellPermissionsRule; import org.hamcrest.CustomTypeSafeMatcher; @@ -503,8 +504,9 @@ public class HidHostDualModeTest { */ private void verifyTransportSwitch(BluetoothDevice device, int fromTransport, int toTransport) { assertThat(fromTransport).isNotEqualTo(toTransport); - verifyConnectionState(mDevice, equalTo(fromTransport), equalTo(STATE_DISCONNECTING)); - + if (!Flags.ignoreUnselectedHidTransportStates()) { + verifyConnectionState(mDevice, equalTo(fromTransport), equalTo(STATE_DISCONNECTING)); + } // Capture the next intent with filter // Filter is necessary as otherwise it will corrupt all other unordered verifications final Intent[] savedIntent = {null}; diff --git a/framework/tests/bumble/src/android/bluetooth/hid/OWNERS b/framework/tests/bumble/src/android/bluetooth/hid/OWNERS deleted file mode 100644 index 75539edb09..0000000000 --- a/framework/tests/bumble/src/android/bluetooth/hid/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 27441 -# Project owners -rwt@google.com diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/OWNERS b/framework/tests/bumble/src/android/bluetooth/pairing/OWNERS deleted file mode 100644 index 75539edb09..0000000000 --- a/framework/tests/bumble/src/android/bluetooth/pairing/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 27441 -# Project owners -rwt@google.com diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java b/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java index 37dc344cb5..ecb400deb3 100644 --- a/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java +++ b/framework/tests/bumble/src/android/bluetooth/pairing/PairingTest.java @@ -32,6 +32,7 @@ import android.bluetooth.BluetoothHidHost; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothStatusCodes; +import android.bluetooth.BluetoothSocket; import android.bluetooth.PandoraDevice; import android.bluetooth.StreamObserverSpliterator; import android.bluetooth.Utils; @@ -41,6 +42,7 @@ import android.bluetooth.test_utils.BlockingBluetoothAdapter; import android.bluetooth.test_utils.EnableBluetoothRule; import android.content.Context; import android.os.ParcelUuid; +import android.util.Log; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -80,6 +82,9 @@ import java.time.Duration; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.io.IOException; @RunWith(TestParameterInjector.class) public class PairingTest { @@ -87,6 +92,7 @@ public class PairingTest { private static final Duration BOND_INTENT_TIMEOUT = Duration.ofSeconds(10); private static final int TEST_DELAY_MS = 1000; + private static final int TEST_PSM = 5; private static final ParcelUuid BATTERY_UUID = ParcelUuid.fromString("0000180F-0000-1000-8000-00805F9B34FB"); @@ -836,6 +842,101 @@ public class PairingTest { intentReceiver.close(); } + /** + * Test pending LE L2CAP socket connection and LE pairing + * + * <p>Prerequisites: + * + * <ol> + * <li>Bumble and Android are not bonded + * </ol> + * + * <p>Steps: + * + * <ol> + * <li>Make Remote device non-connectable over LE + * <li>Initiate LE socket connection from DUT to Remote device + * <li>Initiate LE pairing from DUT to Remote device + * <li>Start LE Advertisement from Remote device after few seconds + * </ol> + * + * <p>Expectation: LE connection should be created and LE Pairing should succeed. + */ + @Test + public void testCreateLeSocket_BondLe() throws Exception { + IntentReceiver intentReceiver = new IntentReceiver.Builder(sTargetContext, + BluetoothDevice.ACTION_ACL_CONNECTED, + BluetoothDevice.ACTION_PAIRING_REQUEST, + BluetoothDevice.ACTION_BOND_STATE_CHANGED) + .build(); + + StreamObserver<PairingEventAnswer> pairingEventAnswerObserver = + mBumble.security() + .withDeadlineAfter(BOND_INTENT_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS) + .onPairing(mPairingEventStreamObserver); + + BluetoothSocket bluetoothSocket = mBumbleDevice.createL2capChannel(TEST_PSM); + + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.submit(() -> { + try { + bluetoothSocket.connect(); + } catch (IOException e) { + Log.e(TAG, "Exception during socket connection: " + e); + } + }); + executor.shutdown(); + + // Wait for LE L2CAP socket connection above to be called and reach BT stack + Thread.sleep(2000); + + mBumbleDevice.createBond(BluetoothDevice.TRANSPORT_LE); + + /* Make LE L2CAP socket connection and LE Bond calls to wait for few seconds + and start LE advertisement from remote device. */ + Thread.sleep(3000); + + // Start LE advertisement from Bumble + AdvertiseRequest.Builder advRequestBuilder = + AdvertiseRequest.newBuilder().setLegacy(true) + .setConnectable(true) + .setOwnAddressType(OwnAddressType.PUBLIC); + + StreamObserverSpliterator<AdvertiseResponse> responseObserver = + new StreamObserverSpliterator<>(); + mBumble.host().advertise(advRequestBuilder.build(), responseObserver); + + intentReceiver.verifyReceivedOrdered( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDING)); + + intentReceiver.verifyReceivedOrdered( + hasAction(BluetoothDevice.ACTION_ACL_CONNECTED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_LE)); + + intentReceiver.verifyReceivedOrdered( + hasAction(BluetoothDevice.ACTION_PAIRING_REQUEST), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra( + BluetoothDevice.EXTRA_PAIRING_VARIANT, + BluetoothDevice.PAIRING_VARIANT_CONSENT)); + mBumbleDevice.setPairingConfirmation(true); + + PairingEvent pairingEvent = mPairingEventStreamObserver.iterator().next(); + assertThat(pairingEvent.hasJustWorks()).isTrue(); + pairingEventAnswerObserver.onNext( + PairingEventAnswer.newBuilder().setEvent(pairingEvent).setConfirm(true).build()); + + intentReceiver.verifyReceivedOrdered( + hasAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED), + hasExtra(BluetoothDevice.EXTRA_DEVICE, mBumbleDevice), + hasExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_BONDED)); + + intentReceiver.close(); + } + /** Helper/testStep functions goes here */ /** diff --git a/framework/tests/bumble/src/android/bluetooth/service_discovery/OWNERS b/framework/tests/bumble/src/android/bluetooth/service_discovery/OWNERS deleted file mode 100644 index 353b5d03a5..0000000000 --- a/framework/tests/bumble/src/android/bluetooth/service_discovery/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 27441 -# Project owners -jpawlowski@google.com diff --git a/framework/tests/bumble/src/android/bluetooth/sockets/OWNERS b/framework/tests/bumble/src/android/bluetooth/sockets/OWNERS deleted file mode 100644 index f9b588d2e9..0000000000 --- a/framework/tests/bumble/src/android/bluetooth/sockets/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Project owners -bhaktha@google.com -poahlo@google.com diff --git a/framework/tests/metrics/OWNERS b/framework/tests/metrics/OWNERS deleted file mode 100644 index 01d41baf7e..0000000000 --- a/framework/tests/metrics/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 1099313 -# Project owners -ahujapalash@google.com diff --git a/pandora/OWNERS b/pandora/OWNERS deleted file mode 100644 index 14634f3b78..0000000000 --- a/pandora/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Project owners -girardier@google.com diff --git a/service/OWNERS b/service/OWNERS deleted file mode 100644 index 5b3f4bad26..0000000000 --- a/service/OWNERS +++ /dev/null @@ -1 +0,0 @@ -wescande@google.com diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java index 5f13b6266a..b797d6ceac 100644 --- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java @@ -1391,15 +1391,31 @@ class BluetoothManagerService { } // If we tried to enable BT while BT was in the process of shutting down, // wait for the BT process to fully tear down and then force a restart - // here. This is a bit of a hack (b/29363429). + // here. This is a bit of a hack (b/29363429). if (prevState == STATE_BLE_TURNING_OFF && newState == STATE_OFF) { - if (mEnable) { - Log.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); - if (!Flags.systemServerRemoveExtraThreadJump()) { - waitForState(STATE_OFF); + if (Flags.enableBleWhileDisablingAirplane()) { + if (mHandler.hasMessages(0, ON_AIRPLANE_MODE_CHANGED_TOKEN)) { + mHandler.removeCallbacksAndMessages(ON_AIRPLANE_MODE_CHANGED_TOKEN); + Log.d(TAG, "Handling delayed airplane mode event"); + handleAirplaneModeChanged(AirplaneModeListener.isOnOverrode()); + } + if (mEnable && !isBinding()) { + Log.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); + if (!Flags.systemServerRemoveExtraThreadJump()) { + waitForState(STATE_OFF); + } + mHandler.sendEmptyMessageDelayed( + MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs()); + } + } else { + if (mEnable) { + Log.d(TAG, "Entering STATE_OFF but mEnabled is true; restarting."); + if (!Flags.systemServerRemoveExtraThreadJump()) { + waitForState(STATE_OFF); + } + mHandler.sendEmptyMessageDelayed( + MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs()); } - mHandler.sendEmptyMessageDelayed( - MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs()); } } if (newState == STATE_ON || newState == STATE_BLE_ON) { diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java index 5e2b66ac06..e825940e90 100644 --- a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java +++ b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java @@ -617,6 +617,35 @@ public class BluetoothManagerServiceTest { assertThat(mLooper.nextMessage()).isNull(); // Must not create a MESSAGE_ENABLE } + @Test + @EnableFlags({ + Flags.FLAG_SYSTEM_SERVER_REMOVE_EXTRA_THREAD_JUMP, + Flags.FLAG_ENABLE_BLE_WHILE_DISABLING_AIRPLANE + }) + public void enableBle_whenDisableAirplaneIsDelayed_startBluetooth() throws Exception { + mManagerService.enable("enableBle_whenDisableAirplaneIsDelayed_startBluetooth"); + IBluetoothCallback btCallback = transition_offToOn(); + mManagerService.onAirplaneModeChanged(true); + assertThat(mManagerService.getState()).isEqualTo(STATE_TURNING_OFF); + + // Generate an event that will be delayed due to the TURNING_OFF state + mManagerService.onAirplaneModeChanged(false); + + transition_onToBleOn(btCallback); + mInOrder.verify(mAdapterBinder).bleOnToOff(any()); + assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_OFF); + + // As soon as we left BLE_ON, generate a call from 3p app that request to turn on Bluetooth + mManagerService.enableBle("enableBle_whenDisableAirplaneIsDelayed_startBluetooth", mBinder); + + // When all the profile are started, adapterService consider it is ON + btCallback.onBluetoothStateChange(STATE_BLE_TURNING_OFF, STATE_OFF); + syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); + + transition_offToOn(); + assertThat(mManagerService.getState()).isEqualTo(STATE_ON); + } + @SafeVarargs private void verifyIntentSent(Matcher<Intent>... matchers) { mInOrder.verify(mContext) diff --git a/sysprop/OWNERS b/sysprop/OWNERS deleted file mode 100644 index 5b3f4bad26..0000000000 --- a/sysprop/OWNERS +++ /dev/null @@ -1 +0,0 @@ -wescande@google.com diff --git a/system/BUILD.gn b/system/BUILD.gn index 48b6207a60..1d6325e6d9 100644 --- a/system/BUILD.gn +++ b/system/BUILD.gn @@ -41,7 +41,6 @@ group("bluetooth") { #if (use.test) { #group("bluetooth_tests") { #deps = [ - #"//bt/system/btcore:net_test_btcore", #"//bt/system/common:bluetooth_test_common", #"//bt/system/profile/avrcp:net_test_avrcp", #"//bt/system/service:bluetoothtbd_test", diff --git a/system/OWNERS b/system/OWNERS deleted file mode 100644 index b2acb91716..0000000000 --- a/system/OWNERS +++ /dev/null @@ -1,18 +0,0 @@ -# Reviewers for /system - -bhaktha@google.com -eruffieux@google.com -hallstrom@google.com -henrichataing@google.com -jpawlowski@google.com -okamil@google.com -poahlo@google.com -rongxuan@google.com -rwt@google.com -wescande@google.com - -# Reviewers for Channel Sounding related files -per-file /bta/ras/*=file:/OWNERS_channel_sounding -per-file /gd/hal/ranging_hal*=file:/OWNERS_channel_sounding -per-file /gd/hci/distance_measurement_*=file:/OWNERS_channel_sounding -per-file /main/shim/distance_measurement_manager*=file:/OWNERS_channel_sounding diff --git a/system/bta/Android.bp b/system/bta/Android.bp index 5a8f721ebd..22074caf65 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -1347,6 +1347,74 @@ cc_test { } cc_test { + name: "bluetooth_hearing_aid_test", + test_suites: ["general-tests"], + defaults: [ + "fluoride_bta_defaults", + "mts_defaults", + ], + host_supported: true, + include_dirs: [ + "packages/modules/Bluetooth/system", + "packages/modules/Bluetooth/system/bta/include", + "packages/modules/Bluetooth/system/bta/test/common", + "packages/modules/Bluetooth/system/stack/include", + ], + srcs: [ + ":TestCommonMockFunctions", + ":TestMockBtaHearingAidAudioSource", + ":TestMockDevice", + ":TestMockMainShim", + ":TestMockMainShimEntry", + ":TestMockStackBtmInterface", + ":TestMockStackBtmIso", + ":TestMockStackGapConnInterface", + ":TestMockStackL2cap", + ":TestStubOsi", + "gatt/database.cc", + "gatt/database_builder.cc", + "hearing_aid/hearing_aid.cc", + "hearing_aid/hearing_aid_test.cc", + "test/common/bta_gatt_api_mock.cc", + "test/common/bta_gatt_queue_mock.cc", + "test/common/btif_storage_mock.cc", + "test/common/btm_api_mock.cc", + ], + shared_libs: [ + "libbase", + "libcrypto", + "liblog", + ], + static_libs: [ + "bluetooth_flags_c_lib_for_test", + "libaconfig_storage_read_api_cc", + "libbluetooth-types", + "libbluetooth_crypto_toolbox", + "libbluetooth_gd", + "libbluetooth_log", + "libbt-audio-asrc", + "libbt-btu-main-thread", + "libbt-common", + "libbt-platform-protos-lite", + "libchrome", + "libevent", + "libg722codec", + "libgmock", + "server_configurable_flags", + ], + sanitize: { + cfi: true, + scs: true, + address: true, + all_undefined: true, + integer_overflow: true, + diag: { + undefined: true, + }, + }, +} + +cc_test { name: "bluetooth_hh_test", test_suites: ["general-tests"], defaults: [ diff --git a/system/bta/dm/bta_dm_act.cc b/system/bta/dm/bta_dm_act.cc index 48e0f16c99..ddc481171f 100644 --- a/system/bta/dm/bta_dm_act.cc +++ b/system/bta/dm/bta_dm_act.cc @@ -545,7 +545,7 @@ void bta_dm_remove_device(const RawAddress& target) { if (le_connected || bredr_connected) { // Wait for all transports to be disconnected - tBTA_DM_REMOVE_PENDNIG node = {pseudo_addr, identity_addr, le_connected, bredr_connected}; + tBTA_DM_REMOVE_PENDING node = {pseudo_addr, identity_addr, le_connected, bredr_connected}; bta_dm_cb.pending_removals.push_back(node); log::info( "Waiting for disconnection over LE:{}, BR/EDR:{} for pseudo address: {}, identity " diff --git a/system/bta/dm/bta_dm_disc.cc b/system/bta/dm/bta_dm_disc.cc index b3f010e86e..79d3421361 100644 --- a/system/bta/dm/bta_dm_disc.cc +++ b/system/bta/dm/bta_dm_disc.cc @@ -152,8 +152,7 @@ gatt_interface_t& get_gatt_interface() { return *gatt_interface; } void bta_dm_disc_gatt_cancel_open(const RawAddress& bd_addr) { get_gatt_interface().BTA_GATTC_CancelOpen(0, bd_addr, false); - if (com::android::bluetooth::flags::cancel_open_discovery_client() && - bta_dm_discovery_cb.client_if != BTA_GATTS_INVALID_IF) { + if (bta_dm_discovery_cb.client_if != BTA_GATTS_INVALID_IF) { get_gatt_interface().BTA_GATTC_CancelOpen(bta_dm_discovery_cb.client_if, bd_addr, true); } } @@ -559,11 +558,7 @@ static void bta_dm_gatt_disc_complete(tCONN_ID conn_id, tGATT_STATUS status) { } else { log::info("Discovery complete for invalid conn ID. Will pick up next job"); - if (com::android::bluetooth::flags::cancel_open_discovery_client()) { - bta_dm_close_gatt_conn(bta_dm_discovery_cb.conn_id); - } else { - bta_dm_discovery_cb.conn_id = GATT_INVALID_CONN_ID; - } + bta_dm_close_gatt_conn(bta_dm_discovery_cb.conn_id); if (bta_dm_discovery_cb.transports & BT_TRANSPORT_BR_EDR) { log::info("classic discovery still pending {}", bta_dm_discovery_cb.peer_bdaddr); return; diff --git a/system/bta/dm/bta_dm_int.h b/system/bta/dm/bta_dm_int.h index 521a8072de..26f7e4d97c 100644 --- a/system/bta/dm/bta_dm_int.h +++ b/system/bta/dm/bta_dm_int.h @@ -81,7 +81,7 @@ inline std::string device_info_text(tBTA_DM_DEV_INFO info) { #define BTA_DM_PM_EXECUTE 3 typedef uint8_t tBTA_DM_PM_REQ; -struct tBTA_DM_REMOVE_PENDNIG { +struct tBTA_DM_REMOVE_PENDING { RawAddress pseudo_addr; RawAddress identity_addr; bool le_connected; @@ -215,7 +215,7 @@ typedef struct { #endif alarm_t* switch_delay_timer; - std::list<tBTA_DM_REMOVE_PENDNIG> pending_removals; + std::list<tBTA_DM_REMOVE_PENDING> pending_removals; } tBTA_DM_CB; /* DI control block */ diff --git a/system/bta/gatt/bta_gattc_act.cc b/system/bta/gatt/bta_gattc_act.cc index 3b6069b1a1..346d7c3d6a 100644 --- a/system/bta/gatt/bta_gattc_act.cc +++ b/system/bta/gatt/bta_gattc_act.cc @@ -132,28 +132,17 @@ void bta_gattc_disable() { return; } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (!bta_gattc_cb.cl_rcb_map.empty()) { - bta_gattc_cb.state = BTA_GATTC_STATE_DISABLING; - } - - // An entry can be erased during deregister, use a copied collection - std::vector<tGATT_IF> gatt_ifs; - for (auto& [gatt_if, p_rcb] : bta_gattc_cb.cl_rcb_map) { - gatt_ifs.push_back(gatt_if); - } - for (auto& gatt_if : gatt_ifs) { - bta_gattc_deregister(bta_gattc_cb.cl_rcb_map[gatt_if].get()); - } - } else { - for (i = 0; i < BTA_GATTC_CL_MAX; i++) { - if (!bta_gattc_cb.cl_rcb[i].in_use) { - continue; - } + if (!bta_gattc_cb.cl_rcb_map.empty()) { + bta_gattc_cb.state = BTA_GATTC_STATE_DISABLING; + } - bta_gattc_cb.state = BTA_GATTC_STATE_DISABLING; - bta_gattc_deregister(&bta_gattc_cb.cl_rcb[i]); - } + // An entry can be erased during deregister, use a copied collection + std::vector<tGATT_IF> gatt_ifs; + for (auto& [gatt_if, p_rcb] : bta_gattc_cb.cl_rcb_map) { + gatt_ifs.push_back(gatt_if); + } + for (auto& gatt_if : gatt_ifs) { + bta_gattc_deregister(bta_gattc_cb.cl_rcb_map[gatt_if].get()); } /* no registered apps, indicate disable completed */ @@ -187,55 +176,25 @@ void bta_gattc_register(const Uuid& app_uuid, const std::string& name, tBTA_GATT bta_gattc_enable(); } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - client_if = GATT_Register(app_uuid, name, &bta_gattc_cl_cback, eatt_support); - if (client_if == 0) { - log::error("Register with GATT stack failed"); - status = GATT_ERROR; - } else { - auto p_rcb = std::make_unique<tBTA_GATTC_RCB>(); - p_rcb->in_use = true; - p_rcb->p_cback = p_cback; - p_rcb->app_uuid = app_uuid; - p_rcb->client_if = client_if; - bta_gattc_cb.cl_rcb_map.emplace(client_if, std::move(p_rcb)); - - log::debug( - "Registered GATT client interface {} with uuid={}, starting it on " - "main thread", - client_if, app_uuid.ToString()); - - do_in_main_thread(base::BindOnce(&bta_gattc_start_if, client_if)); - status = GATT_SUCCESS; - } + client_if = GATT_Register(app_uuid, name, &bta_gattc_cl_cback, eatt_support); + if (client_if == 0) { + log::error("Register with GATT stack failed"); + status = GATT_ERROR; } else { - for (uint8_t i = 0; i < BTA_GATTC_CL_MAX; i++) { - if (!bta_gattc_cb.cl_rcb[i].in_use) { - bta_gattc_cb.cl_rcb[i].client_if = - GATT_Register(app_uuid, "GattClient", &bta_gattc_cl_cback, eatt_support); - if (bta_gattc_cb.cl_rcb[i].client_if == 0) { - log::error("Register with GATT stack failed with index {}, trying next index", i); - status = GATT_ERROR; - } else { - bta_gattc_cb.cl_rcb[i].in_use = true; - bta_gattc_cb.cl_rcb[i].p_cback = p_cback; - bta_gattc_cb.cl_rcb[i].app_uuid = app_uuid; - - /* BTA use the same client interface as BTE GATT statck */ - client_if = bta_gattc_cb.cl_rcb[i].client_if; - - log::debug( - "Registered GATT client interface {} with uuid={}, starting it on " - "main thread", - client_if, app_uuid.ToString()); - - do_in_main_thread(base::BindOnce(&bta_gattc_start_if, client_if)); - - status = GATT_SUCCESS; - break; - } - } - } + auto p_rcb = std::make_unique<tBTA_GATTC_RCB>(); + p_rcb->in_use = true; + p_rcb->p_cback = p_cback; + p_rcb->app_uuid = app_uuid; + p_rcb->client_if = client_if; + bta_gattc_cb.cl_rcb_map.emplace(client_if, std::move(p_rcb)); + + log::debug( + "Registered GATT client interface {} with uuid={}, starting it on " + "main thread", + client_if, app_uuid.ToString()); + + do_in_main_thread(base::BindOnce(&bta_gattc_start_if, client_if)); + status = GATT_SUCCESS; } if (!cb.is_null()) { @@ -258,26 +217,13 @@ void bta_gattc_deregister(tBTA_GATTC_RCB* p_clreg) { continue; } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (bta_gattc_cb.bg_track[i].cif_set.contains(p_clreg->client_if)) { - bta_gattc_mark_bg_conn(p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false); - if (!GATT_CancelConnect(p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false)) { - log::warn( - "Unable to cancel GATT connection client_if:{} peer:{} " - "is_direct:{}", - p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false); - } - } - } else { - if (bta_gattc_cb.bg_track[i].cif_mask & - ((tBTA_GATTC_CIF_MASK)1 << (p_clreg->client_if - 1))) { - bta_gattc_mark_bg_conn(p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false); - if (!GATT_CancelConnect(p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false)) { - log::warn( - "Unable to cancel GATT connection client_if:{} peer:{} " - "is_direct:{}", - p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false); - } + if (bta_gattc_cb.bg_track[i].cif_set.contains(p_clreg->client_if)) { + bta_gattc_mark_bg_conn(p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false); + if (!GATT_CancelConnect(p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false)) { + log::warn( + "Unable to cancel GATT connection client_if:{} peer:{} " + "is_direct:{}", + p_clreg->client_if, bta_gattc_cb.bg_track[i].remote_bda, false); } } } @@ -288,38 +234,23 @@ void bta_gattc_deregister(tBTA_GATTC_RCB* p_clreg) { } /* close all CLCB related to this app */ - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (!p_clcb->in_use || p_clcb->p_rcb != p_clreg) { - continue; - } - p_clreg->dereg_pending = true; - - tBTA_GATTC_DATA gattc_data = { - .hdr = - { - .event = BTA_GATTC_API_CLOSE_EVT, - .layer_specific = static_cast<uint16_t>(p_clcb->bta_conn_id), - }, - }; - bta_gattc_close(p_clcb.get(), &gattc_data); - } - // deallocated clcbs will not be accessed. Let them be claened up. - bta_gattc_cleanup_clcb(); - } else { - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) { - if (!bta_gattc_cb.clcb[i].in_use || (bta_gattc_cb.clcb[i].p_rcb != p_clreg)) { - continue; - } - - p_clreg->dereg_pending = true; - - BT_HDR_RIGID buf; - buf.event = BTA_GATTC_API_CLOSE_EVT; - buf.layer_specific = static_cast<uint16_t>(bta_gattc_cb.clcb[i].bta_conn_id); - bta_gattc_close(&bta_gattc_cb.clcb[i], (tBTA_GATTC_DATA*)&buf); + for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (!p_clcb->in_use || p_clcb->p_rcb != p_clreg) { + continue; } + p_clreg->dereg_pending = true; + + tBTA_GATTC_DATA gattc_data = { + .hdr = + { + .event = BTA_GATTC_API_CLOSE_EVT, + .layer_specific = static_cast<uint16_t>(p_clcb->bta_conn_id), + }, + }; + bta_gattc_close(p_clcb.get(), &gattc_data); } + // deallocated clcbs will not be accessed. Let them be claened up. + bta_gattc_cleanup_clcb(); } /** process connect API request */ @@ -736,21 +667,12 @@ void bta_gattc_close(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) { /** when a SRCB finished discovery, tell all related clcb */ void bta_gattc_reset_discover_st(tBTA_GATTC_SERV* p_srcb, tGATT_STATUS status) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (p_clcb->p_srcb != p_srcb) { - continue; - } - p_clcb->status = status; - bta_gattc_sm_execute(p_clcb.get(), BTA_GATTC_DISCOVER_CMPL_EVT, NULL); - } - } else { - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) { - if (bta_gattc_cb.clcb[i].p_srcb == p_srcb) { - bta_gattc_cb.clcb[i].status = status; - bta_gattc_sm_execute(&bta_gattc_cb.clcb[i], BTA_GATTC_DISCOVER_CMPL_EVT, NULL); - } + for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (p_clcb->p_srcb != p_srcb) { + continue; } + p_clcb->status = status; + bta_gattc_sm_execute(p_clcb.get(), BTA_GATTC_DISCOVER_CMPL_EVT, NULL); } } @@ -777,23 +699,13 @@ void bta_gattc_disc_close(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data /** when a SRCB start discovery, tell all related clcb and set the state */ static void bta_gattc_set_discover_st(tBTA_GATTC_SERV* p_srcb) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (p_clcb->p_srcb != p_srcb) { - continue; - } - p_clcb->status = GATT_SUCCESS; - p_clcb->state = BTA_GATTC_DISCOVER_ST; - p_clcb->request_during_discovery = BTA_GATTC_DISCOVER_REQ_NONE; - } - } else { - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) { - if (bta_gattc_cb.clcb[i].p_srcb == p_srcb) { - bta_gattc_cb.clcb[i].status = GATT_SUCCESS; - bta_gattc_cb.clcb[i].state = BTA_GATTC_DISCOVER_ST; - bta_gattc_cb.clcb[i].request_during_discovery = BTA_GATTC_DISCOVER_REQ_NONE; - } + for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (p_clcb->p_srcb != p_srcb) { + continue; } + p_clcb->status = GATT_SUCCESS; + p_clcb->state = BTA_GATTC_DISCOVER_ST; + p_clcb->request_during_discovery = BTA_GATTC_DISCOVER_REQ_NONE; } } @@ -1422,12 +1334,8 @@ static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB* p_clreg) { memset(&cb_data, 0, sizeof(tBTA_GATTC)); GATT_Deregister(p_clreg->client_if); - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (bta_gattc_cb.cl_rcb_map.erase(p_clreg->client_if) == 0) { - log::warn("deregistered unknown rcb client_if={}", p_clreg->client_if); - } - } else { - memset(p_clreg, 0, sizeof(tBTA_GATTC_RCB)); + if (bta_gattc_cb.cl_rcb_map.erase(p_clreg->client_if) == 0) { + log::warn("deregistered unknown rcb client_if={}", p_clreg->client_if); } cb_data.reg_oper.client_if = client_if; @@ -1490,20 +1398,11 @@ void bta_gattc_process_api_refresh(const RawAddress& remote_bda) { if (p_srvc_cb->connected && p_srvc_cb->num_clcb != 0) { bool found = false; tBTA_GATTC_CLCB* p_clcb = &bta_gattc_cb.clcb[0]; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { - if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srvc_cb) { - p_clcb = p_clcb_i.get(); - found = true; - break; - } - } - } else { - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++, p_clcb++) { - if (p_clcb->in_use && p_clcb->p_srcb == p_srvc_cb) { - found = true; - break; - } + for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { + if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srvc_cb) { + p_clcb = p_clcb_i.get(); + found = true; + break; } } if (found) { @@ -1563,38 +1462,19 @@ static bool bta_gattc_process_srvc_chg_ind(tCONN_ID conn_id, tBTA_GATTC_RCB* p_c /* not an opened connection; or connection busy */ /* search for first available clcb and start discovery */ if (p_clcb == NULL || (p_clcb && p_clcb->p_q_cmd != NULL)) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { - if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srcb && p_clcb_i->p_q_cmd == NULL) { - p_clcb = p_clcb_i.get(); - break; - } - } - } else { - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) { - if (bta_gattc_cb.clcb[i].in_use && bta_gattc_cb.clcb[i].p_srcb == p_srcb && - bta_gattc_cb.clcb[i].p_q_cmd == NULL) { - p_clcb = &bta_gattc_cb.clcb[i]; - break; - } + for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { + if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srcb && p_clcb_i->p_q_cmd == NULL) { + p_clcb = p_clcb_i.get(); + break; } } } // Use a busy CLCB to start discovery if no CLCB is available, this will be queued. if (com::android::bluetooth::flags::start_discover_service_changed() && p_clcb == NULL) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { - if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srcb) { - p_clcb = p_clcb_i.get(); - break; - } - } - } else { - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) { - if (bta_gattc_cb.clcb[i].in_use && bta_gattc_cb.clcb[i].p_srcb == p_srcb) { - p_clcb = &bta_gattc_cb.clcb[i]; - break; - } + for (auto& p_clcb_i : bta_gattc_cb.clcb_set) { + if (p_clcb_i->in_use && p_clcb_i->p_srcb == p_srcb) { + p_clcb = p_clcb_i.get(); + break; } } } diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc index 23b150b829..523cf90fe5 100644 --- a/system/bta/gatt/bta_gattc_utils.cc +++ b/system/bta/gatt/bta_gattc_utils.cc @@ -25,7 +25,6 @@ #define LOG_TAG "bt_bta_gattc" #include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> #include <cstdint> @@ -58,23 +57,11 @@ static uint8_t ble_acceptlist_size() { * ******************************************************************************/ tBTA_GATTC_RCB* bta_gattc_cl_get_regcb(uint8_t client_if) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - auto it = bta_gattc_cb.cl_rcb_map.find(client_if); - if (it == bta_gattc_cb.cl_rcb_map.end()) { - return NULL; - } else { - return it->second.get(); - } - } else { - uint8_t i = 0; - tBTA_GATTC_RCB* p_clrcb = &bta_gattc_cb.cl_rcb[0]; - - for (i = 0; i < BTA_GATTC_CL_MAX; i++, p_clrcb++) { - if (p_clrcb->in_use && p_clrcb->client_if == client_if) { - return p_clrcb; - } - } + auto it = bta_gattc_cb.cl_rcb_map.find(client_if); + if (it == bta_gattc_cb.cl_rcb_map.end()) { return NULL; + } else { + return it->second.get(); } } /******************************************************************************* @@ -86,20 +73,7 @@ tBTA_GATTC_RCB* bta_gattc_cl_get_regcb(uint8_t client_if) { * Returns pointer to the regcb * ******************************************************************************/ -uint8_t bta_gattc_num_reg_app(void) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - return (uint8_t)bta_gattc_cb.cl_rcb_map.size(); - } else { - uint8_t i = 0, j = 0; - - for (i = 0; i < BTA_GATTC_CL_MAX; i++) { - if (bta_gattc_cb.cl_rcb[i].in_use) { - j++; - } - } - return j; - } -} +uint8_t bta_gattc_num_reg_app(void) { return (uint8_t)bta_gattc_cb.cl_rcb_map.size(); } /******************************************************************************* * * Function bta_gattc_find_clcb_by_cif @@ -111,21 +85,10 @@ uint8_t bta_gattc_num_reg_app(void) { ******************************************************************************/ tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_cif(uint8_t client_if, const RawAddress& remote_bda, tBT_TRANSPORT transport) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (p_clcb->in_use && p_clcb->p_rcb->client_if == client_if && - p_clcb->transport == transport && p_clcb->bda == remote_bda) { - return p_clcb.get(); - } - } - } else { - tBTA_GATTC_CLCB* p_clcb = &bta_gattc_cb.clcb[0]; - - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++, p_clcb++) { - if (p_clcb->in_use && p_clcb->p_rcb->client_if == client_if && - p_clcb->transport == transport && p_clcb->bda == remote_bda) { - return p_clcb; - } + for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (p_clcb->in_use && p_clcb->p_rcb->client_if == client_if && p_clcb->transport == transport && + p_clcb->bda == remote_bda) { + return p_clcb.get(); } } return NULL; @@ -140,19 +103,9 @@ tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_cif(uint8_t client_if, const RawAddress& * ******************************************************************************/ tBTA_GATTC_CLCB* bta_gattc_find_clcb_by_conn_id(tCONN_ID conn_id) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (p_clcb->in_use && p_clcb->bta_conn_id == conn_id) { - return p_clcb.get(); - } - } - } else { - tBTA_GATTC_CLCB* p_clcb = &bta_gattc_cb.clcb[0]; - - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++, p_clcb++) { - if (p_clcb->in_use && p_clcb->bta_conn_id == conn_id) { - return p_clcb; - } + for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (p_clcb != NULL && p_clcb->in_use && p_clcb->bta_conn_id == conn_id) { + return p_clcb.get(); } } return NULL; @@ -171,63 +124,30 @@ tBTA_GATTC_CLCB* bta_gattc_clcb_alloc(tGATT_IF client_if, const RawAddress& remo tBT_TRANSPORT transport) { tBTA_GATTC_CLCB* p_clcb = NULL; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - bta_gattc_cleanup_clcb(); - auto [p_clcb_i, b] = bta_gattc_cb.clcb_set.emplace(std::make_unique<tBTA_GATTC_CLCB>()); - p_clcb = p_clcb_i->get(); + bta_gattc_cleanup_clcb(); + auto [p_clcb_i, b] = bta_gattc_cb.clcb_set.emplace(std::make_unique<tBTA_GATTC_CLCB>()); + p_clcb = p_clcb_i->get(); - p_clcb->in_use = true; - p_clcb->status = GATT_SUCCESS; - p_clcb->transport = transport; - p_clcb->bda = remote_bda; - p_clcb->p_q_cmd = NULL; + p_clcb->in_use = true; + p_clcb->status = GATT_SUCCESS; + p_clcb->transport = transport; + p_clcb->bda = remote_bda; + p_clcb->p_q_cmd = NULL; - p_clcb->p_rcb = bta_gattc_cl_get_regcb(client_if); + p_clcb->p_rcb = bta_gattc_cl_get_regcb(client_if); - p_clcb->p_srcb = bta_gattc_find_srcb(remote_bda); - if (p_clcb->p_srcb == NULL) { - p_clcb->p_srcb = bta_gattc_srcb_alloc(remote_bda); - } + p_clcb->p_srcb = bta_gattc_find_srcb(remote_bda); + if (p_clcb->p_srcb == NULL) { + p_clcb->p_srcb = bta_gattc_srcb_alloc(remote_bda); + } - if (p_clcb->p_rcb != NULL && p_clcb->p_srcb != NULL) { - p_clcb->p_srcb->num_clcb++; - p_clcb->p_rcb->num_clcb++; - } else { - /* release this clcb if clcb or srcb allocation failed */ - bta_gattc_cb.clcb_set.erase(p_clcb_i); - p_clcb = NULL; - } + if (p_clcb->p_rcb != NULL && p_clcb->p_srcb != NULL) { + p_clcb->p_srcb->num_clcb++; + p_clcb->p_rcb->num_clcb++; } else { - for (int i_clcb = 0; i_clcb < BTA_GATTC_CLCB_MAX; i_clcb++) { - if (!bta_gattc_cb.clcb[i_clcb].in_use) { -#if (BTA_GATT_DEBUG == TRUE) - log::verbose("found clcb:{} available", i_clcb); -#endif - p_clcb = &bta_gattc_cb.clcb[i_clcb]; - p_clcb->in_use = true; - p_clcb->status = GATT_SUCCESS; - p_clcb->transport = transport; - p_clcb->bda = remote_bda; - p_clcb->p_q_cmd = NULL; - - p_clcb->p_rcb = bta_gattc_cl_get_regcb(client_if); - - p_clcb->p_srcb = bta_gattc_find_srcb(remote_bda); - if (p_clcb->p_srcb == NULL) { - p_clcb->p_srcb = bta_gattc_srcb_alloc(remote_bda); - } - - if (p_clcb->p_rcb != NULL && p_clcb->p_srcb != NULL) { - p_clcb->p_srcb->num_clcb++; - p_clcb->p_rcb->num_clcb++; - } else { - /* release this clcb if clcb or srcb allocation failed */ - p_clcb->in_use = false; - p_clcb = NULL; - } - break; - } - } + /* release this clcb if clcb or srcb allocation failed */ + bta_gattc_cb.clcb_set.erase(p_clcb_i); + p_clcb = NULL; } return p_clcb; } @@ -330,9 +250,7 @@ void bta_gattc_clcb_dealloc(tBTA_GATTC_CLCB* p_clcb) { p_clcb->status = GATT_SUCCESS; // in bta_gattc_sm_execute(), p_clcb is accessed again so we dealloc clcb later. // it will be claned up when the client is deregistered or a new clcb is allocated. - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - bta_gattc_cb.clcb_pending_dealloc.insert(p_clcb); - } + bta_gattc_cb.clcb_pending_dealloc.insert(p_clcb); } /******************************************************************************* @@ -643,39 +561,21 @@ bool bta_gattc_mark_bg_conn(tGATT_IF client_if, const RawAddress& remote_bda_ptr for (i = 0; i < ble_acceptlist_size(); i++, p_bg_tck++) { if (p_bg_tck->in_use && ((p_bg_tck->remote_bda == remote_bda_ptr) || (p_bg_tck->remote_bda.IsEmpty()))) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - auto& p_cif_set = p_bg_tck->cif_set; - if (add) { /* mask on the cif bit */ - p_cif_set.insert(client_if); - } else { - if (client_if != 0) { - p_cif_set.erase(client_if); - } else { - p_cif_set.clear(); - } - } - /* no BG connection for this device, make it available */ - if (p_bg_tck->cif_set.empty()) { - p_bg_tck->in_use = false; - p_bg_tck->remote_bda = RawAddress::kEmpty; - } + auto& p_cif_set = p_bg_tck->cif_set; + if (add) { /* mask on the cif bit */ + p_cif_set.insert(client_if); } else { - p_cif_mask = &p_bg_tck->cif_mask; - - if (add) { /* mask on the cif bit */ - *p_cif_mask |= (1 << (client_if - 1)); + if (client_if != 0) { + p_cif_set.erase(client_if); } else { - if (client_if != 0) { - *p_cif_mask &= (~(1 << (client_if - 1))); - } else { - *p_cif_mask = 0; - } - } - /* no BG connection for this device, make it available */ - if (p_bg_tck->cif_mask == 0) { - *p_bg_tck = tBTA_GATTC_BG_TCK{}; + p_cif_set.clear(); } } + /* no BG connection for this device, make it available */ + if (p_bg_tck->cif_set.empty()) { + p_bg_tck->in_use = false; + p_bg_tck->remote_bda = RawAddress::kEmpty; + } return true; } } @@ -688,12 +588,7 @@ bool bta_gattc_mark_bg_conn(tGATT_IF client_if, const RawAddress& remote_bda_ptr p_bg_tck->in_use = true; p_bg_tck->remote_bda = remote_bda_ptr; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - p_bg_tck->cif_set = {client_if}; - } else { - p_cif_mask = &p_bg_tck->cif_mask; - *p_cif_mask = ((tBTA_GATTC_CIF_MASK)1 << (client_if - 1)); - } + p_bg_tck->cif_set = {client_if}; return true; } } @@ -719,15 +614,8 @@ bool bta_gattc_check_bg_conn(tGATT_IF client_if, const RawAddress& remote_bda, u for (i = 0; i < ble_acceptlist_size() && !is_bg_conn; i++, p_bg_tck++) { if (p_bg_tck->in_use && (p_bg_tck->remote_bda == remote_bda || p_bg_tck->remote_bda.IsEmpty())) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (p_bg_tck->cif_set.contains(client_if) && role == HCI_ROLE_CENTRAL) { - is_bg_conn = true; - } - } else { - if (((p_bg_tck->cif_mask & ((tBTA_GATTC_CIF_MASK)1 << (client_if - 1))) != 0) && - role == HCI_ROLE_CENTRAL) { - is_bg_conn = true; - } + if (p_bg_tck->cif_set.contains(client_if) && role == HCI_ROLE_CENTRAL) { + is_bg_conn = true; } } } @@ -938,58 +826,28 @@ void bta_gatt_client_dump(int fd) { stream << " -- used: " << entry_count << "\n"; entry_count = 0; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - stream << " ->cl_rcb (dynamic)\n"; - for (auto& [i, p_cl_rcb] : bta_gattc_cb.cl_rcb_map) { - entry_count++; - stream << " client_if: " << +p_cl_rcb->client_if << " app uuids: " << p_cl_rcb->app_uuid - << " clcb_num: " << +p_cl_rcb->num_clcb; - stream << "\n"; - } - } else { - stream << " ->cl_rcb (BTA_GATTC_CL_MAX=" << BTA_GATTC_CL_MAX << ")\n"; - for (int i = 0; i < BTA_GATTC_CL_MAX; i++) { - tBTA_GATTC_RCB* p_cl_rcb = &bta_gattc_cb.cl_rcb[i]; - if (!p_cl_rcb->in_use) { - continue; - } - entry_count++; - stream << " client_if: " << +p_cl_rcb->client_if << " app uuids: " << p_cl_rcb->app_uuid - << " clcb_num: " << +p_cl_rcb->num_clcb; - stream << "\n"; - } + stream << " ->cl_rcb (dynamic)\n"; + for (auto& [i, p_cl_rcb] : bta_gattc_cb.cl_rcb_map) { + entry_count++; + stream << " client_if: " << +p_cl_rcb->client_if << " app uuids: " << p_cl_rcb->app_uuid + << " clcb_num: " << +p_cl_rcb->num_clcb; + stream << "\n"; } stream << " -- used: " << entry_count << "\n"; entry_count = 0; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - stream << " ->clcb (dynamic)\n"; - for (auto& p_clcb : bta_gattc_cb.clcb_set) { - if (!p_clcb->in_use) { - continue; - } - entry_count++; - stream << " conn_id: " << loghex(p_clcb->bta_conn_id) - << " address: " << p_clcb->bda.ToRedactedStringForLogging() - << " transport: " << bt_transport_text(p_clcb->transport) - << " state: " << bta_clcb_state_text(p_clcb->state); - stream << "\n"; - } - } else { - stream << " ->clcb (BTA_GATTC_CLCB_MAX=" << BTA_GATTC_CLCB_MAX << ")\n"; - for (size_t i = 0; i < BTA_GATTC_CLCB_MAX; i++) { - tBTA_GATTC_CLCB* p_clcb = &bta_gattc_cb.clcb[i]; - if (!p_clcb->in_use) { - continue; - } - entry_count++; - stream << " conn_id: " << loghex(p_clcb->bta_conn_id) - << " address: " << p_clcb->bda.ToRedactedStringForLogging() - << " transport: " << bt_transport_text(p_clcb->transport) - << " state: " << bta_clcb_state_text(p_clcb->state); - stream << "\n"; + stream << " ->clcb (dynamic)\n"; + for (auto& p_clcb : bta_gattc_cb.clcb_set) { + if (!p_clcb->in_use) { + continue; } + entry_count++; + stream << " conn_id: " << loghex(p_clcb->bta_conn_id) + << " address: " << p_clcb->bda.ToRedactedStringForLogging() + << " transport: " << bt_transport_text(p_clcb->transport) + << " state: " << bta_clcb_state_text(p_clcb->state); + stream << "\n"; } stream << " -- used: " << entry_count << "\n"; diff --git a/system/bta/hearing_aid/OWNERS b/system/bta/hearing_aid/OWNERS deleted file mode 100644 index bbaa85ddbb..0000000000 --- a/system/bta/hearing_aid/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_hearingaid diff --git a/system/bta/hearing_aid/hearing_aid_test.cc b/system/bta/hearing_aid/hearing_aid_test.cc new file mode 100644 index 0000000000..2090df9d81 --- /dev/null +++ b/system/bta/hearing_aid/hearing_aid_test.cc @@ -0,0 +1,474 @@ +/* + * 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. + */ + +#include <base/bind_helpers.h> +#include <base/functional/bind.h> +#include <base/strings/string_number_conversions.h> +#include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <osi/include/alarm.h> +#include <string.h> +#include <sys/socket.h> + +#include <variant> + +#include "bta/le_audio/le_audio_types.h" +#include "bta_gatt_api_mock.h" +#include "bta_gatt_queue_mock.h" +#include "bta_hearing_aid_api.h" +#include "btif_storage_mock.h" +#include "btm_api_mock.h" +#include "gatt/database_builder.h" +#include "hardware/bt_gatt_types.h" +#include "hci/controller_interface_mock.h" +#include "stack/include/bt_uuid16.h" +#include "stack/include/btm_status.h" +#include "test/common/mock_functions.h" +#include "test/mock/mock_main_shim_entry.h" +#include "test/mock/mock_stack_gap_conn_interface.h" +#include "test/mock/mock_stack_l2cap_interface.h" +#include "types/bt_transport.h" + +static std::map<const char*, bool> fake_osi_bool_props; + +namespace bluetooth { +namespace hearing_aid { +namespace internal { +namespace { + +using base::HexEncode; + +using namespace bluetooth::hearing_aid; + +using ::bluetooth::hearing_aid::ConnectionState; +using ::bluetooth::hearing_aid::HearingAidCallbacks; +using ::bluetooth::hearing_aid::HearingAidInterface; + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::DoAll; +using ::testing::DoDefault; +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::NiceMock; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::SaveArg; +using ::testing::Sequence; +using ::testing::SetArgPointee; +using ::testing::WithArg; + +RawAddress GetTestAddress(int index) { + CHECK_LT(index, UINT8_MAX); + RawAddress result = {{0xC0, 0xDE, 0xC0, 0xDE, 0x00, static_cast<uint8_t>(index)}}; + return result; +} + +static uint16_t GetTestConnId(const RawAddress& address) { + return address.address[RawAddress::kLength - 1]; +} + +class MockHearingAidCallbacks : public HearingAidCallbacks { +public: + MockHearingAidCallbacks() = default; + MockHearingAidCallbacks(const MockHearingAidCallbacks&) = delete; + ~MockHearingAidCallbacks() override = default; + + MOCK_METHOD((void), OnConnectionState, (ConnectionState state, const RawAddress& address), + (override)); + MOCK_METHOD((void), OnDeviceAvailable, + (uint8_t capabilities, uint64_t hiSyncId, const RawAddress& address), (override)); +}; + +class HearingAidTestBase : public ::testing::Test { +protected: + Uuid HEARING_AID_UUID = Uuid::FromString("FDF0"); + Uuid READ_ONLY_PROPERTIES_UUID = Uuid::FromString("6333651e-c481-4a3e-9169-7c902aad37bb"); + Uuid AUDIO_CONTROL_POINT_UUID = Uuid::FromString("f0d4de7e-4a88-476c-9d9f-1937b0996cc0"); + Uuid AUDIO_STATUS_UUID = Uuid::FromString("38663f1a-e711-4cac-b641-326b56404837"); + Uuid VOLUME_UUID = Uuid::FromString("00e4ca9e-ab14-41e4-8823-f9e70c7e91df"); + Uuid LE_PSM_UUID = Uuid::FromString("2d410339-82b6-42aa-b34e-e2e01df8cc1a"); + + void set_sample_database(uint16_t conn_id) { + static constexpr uint16_t kGapSvcStartHdl = 0x0001; + static constexpr uint16_t kGapDeviceNameValHdl = 0x0003; + static constexpr uint16_t kGapSvcEndHdl = kGapDeviceNameValHdl; + + static constexpr uint16_t kSvcStartHdl = 0x0010; + static constexpr uint16_t kReadOnlyProperties = 0x0012; + static constexpr uint16_t kAudioControlPoint = 0x0015; + static constexpr uint16_t kAudioStatusPoint = 0x0018; + static constexpr uint16_t kVolume = 0x001B; + static constexpr uint16_t kLePsm = 0x001E; + static constexpr uint16_t kSvcEndHdl = kLePsm; + + gatt::DatabaseBuilder bob; + + /* Generic Access Service */ + bob.AddService(kGapSvcStartHdl, kGapDeviceNameValHdl, Uuid::From16Bit(0x1800), true); + /* Device Name Char. */ + bob.AddCharacteristic(kGapDeviceNameValHdl - 1, kGapDeviceNameValHdl, Uuid::From16Bit(0x2a00), + GATT_CHAR_PROP_BIT_READ); + + /* ASHA Service */ + bob.AddService(kSvcStartHdl, kSvcEndHdl, HEARING_AID_UUID, true); + bob.AddCharacteristic(kReadOnlyProperties - 1, kReadOnlyProperties, READ_ONLY_PROPERTIES_UUID, + GATT_CHAR_PROP_BIT_READ); + bob.AddCharacteristic(kAudioControlPoint - 1, kAudioControlPoint, AUDIO_CONTROL_POINT_UUID, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_WRITE_NR); + bob.AddCharacteristic(kAudioStatusPoint - 1, kAudioStatusPoint, AUDIO_STATUS_UUID, + GATT_CHAR_PROP_BIT_READ | GATT_CHAR_PROP_BIT_NOTIFY); + bob.AddDescriptor(kAudioStatusPoint + 1, Uuid::From16Bit(GATT_UUID_CHAR_CLIENT_CONFIG)); + bob.AddCharacteristic(kVolume - 1, kVolume, VOLUME_UUID, GATT_CHAR_PROP_BIT_WRITE_NR); + bob.AddCharacteristic(kLePsm - 1, kLePsm, LE_PSM_UUID, GATT_CHAR_PROP_BIT_READ); + + services_map[conn_id] = bob.Build().Services(); + + ON_CALL(gatt_queue, ReadCharacteristic(conn_id, _, _, _)) + .WillByDefault(Invoke( + [this](uint16_t conn_id, uint16_t handle, GATT_READ_OP_CB cb, void* cb_data) { + auto* svc = gatt::FindService(services_map[conn_id], handle); + if (svc == nullptr) { + return; + } + + std::vector<uint8_t> value; + tGATT_STATUS status = GATT_SUCCESS; + + switch (handle) { + case kReadOnlyProperties: + value.resize(17); + value.assign(17, 0x01); + break; + case kAudioStatusPoint: + value.resize(1); + value.assign(1, 0); + break; + case kLePsm: + value.resize(2); + value.assign(2, 0x0080); + break; + /* passthrough */ + default: + status = GATT_READ_NOT_PERMIT; + break; + } + + if (cb) { + cb(conn_id, status, handle, value.size(), value.data(), cb_data); + } + })); + + /* default action for GetCharacteristic function call */ + ON_CALL(gatt_interface, GetCharacteristic(_, _)) + .WillByDefault( + Invoke([&](uint16_t conn_id, uint16_t handle) -> const gatt::Characteristic* { + std::list<gatt::Service>& services = services_map[conn_id]; + for (auto const& service : services) { + for (auto const& characteristic : service.characteristics) { + if (characteristic.value_handle == handle) { + return &characteristic; + } + } + } + + return nullptr; + })); + + ON_CALL(gatt_interface, ServiceSearchRequest(_, _)) + .WillByDefault(WithArg<0>( + Invoke([&](uint16_t conn_id) { InjectSearchCompleteEvent(conn_id); }))); + + /* default action for GetServices function call */ + ON_CALL(gatt_interface, GetServices(_)) + .WillByDefault(WithArg<0>(Invoke([&](uint16_t conn_id) -> std::list<gatt::Service>* { + return &services_map[conn_id]; + }))); + + /* default action for RegisterForNotifications function call */ + ON_CALL(gatt_interface, RegisterForNotifications(gatt_if, _, _)) + .WillByDefault(Return(GATT_SUCCESS)); + + /* default action for DeregisterForNotifications function call */ + ON_CALL(gatt_interface, DeregisterForNotifications(gatt_if, _, _)) + .WillByDefault(Return(GATT_SUCCESS)); + + /* default action for WriteDescriptor function call */ + ON_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _)) + .WillByDefault(Invoke([](uint16_t conn_id, uint16_t handle, std::vector<uint8_t> value, + tGATT_WRITE_TYPE /*write_type*/, GATT_WRITE_OP_CB cb, + void* cb_data) -> void { + if (cb) { + cb(conn_id, GATT_SUCCESS, handle, value.size(), value.data(), cb_data); + } + })); + } + + void SetUp(void) override { + fake_osi_bool_props.clear(); + bluetooth::manager::SetMockBtmInterface(&btm_interface); + bluetooth::storage::SetMockBtifStorageInterface(&btif_storage_interface_); + gatt::SetMockBtaGattInterface(&gatt_interface); + gatt::SetMockBtaGattQueue(&gatt_queue); + callbacks.reset(new MockHearingAidCallbacks()); + bluetooth::hci::testing::mock_controller_ = + std::make_unique<NiceMock<bluetooth::hci::testing::MockControllerInterface>>(); + bluetooth::testing::stack::l2cap::set_interface(&mock_stack_l2cap_interface_); + bluetooth::testing::stack::gap_conn::set_interface(&mock_stack_gap_conn_interface_); + + encryption_result = true; + + ON_CALL(mock_stack_l2cap_interface_, L2CA_UpdateBleConnParams(_, _, _, _, _, _, _)) + .WillByDefault(Invoke([&](const RawAddress& /*rem_bda*/, uint16_t min_int, + uint16_t /*max_int*/, uint16_t latency, uint16_t timeout, + uint16_t /*min_ce_len*/, uint16_t /*max_ce_len*/) { + req_int = min_int; + req_latency = latency; + req_timeout = timeout; + return true; + })); + + ON_CALL(mock_stack_gap_conn_interface_, GAP_ConnOpen(_, _, _, _, _, _, _, _, _, _, _)) + .WillByDefault(Invoke([&](const char* /* p_serv_name */, uint8_t /*service_id*/, + bool /*is_server*/, const RawAddress* p_rem_bda, + uint16_t /*psm*/, uint16_t /*le_mps*/, + tL2CAP_CFG_INFO* /*p_cfg*/, tL2CAP_ERTM_INFO* /*ertm_info*/, + uint16_t /*security*/, tGAP_CONN_CALLBACK* p_cb, + tBT_TRANSPORT /*transport*/) { + InjectConnUpdateEvent(p_rem_bda->address[5], req_int, req_latency, req_timeout); + + gap_conn_cb = p_cb; + if (gap_conn_cb) { + gap_conn_cb(0xFFFF, GAP_EVT_CONN_OPENED, nullptr); + } + return 1; + })); + + ON_CALL(mock_stack_gap_conn_interface_, GAP_ConnGetRemoteAddr(_)) + .WillByDefault(Invoke([&](uint16_t /*gap_handle*/) { return &test_address; })); + + /* by default connect only direct connection requests */ + ON_CALL(gatt_interface, Open(_, _, _, _)) + .WillByDefault(Invoke([&](tGATT_IF /*client_if*/, const RawAddress& remote_bda, + tBTM_BLE_CONN_TYPE connection_type, bool /*opportunistic*/) { + if (connection_type == BTM_BLE_DIRECT_CONNECTION) { + InjectConnectedEvent(remote_bda, GetTestConnId(remote_bda)); + } + })); + + ON_CALL(gatt_interface, Close(_)).WillByDefault(Invoke([&](uint16_t conn_id) { + /* We arrive here once, when we call Disconnect; and second time, after + * we send OnGattDisconnected - but device was already removed */ + if (connected_devices.count(conn_id) > 0) { + InjectDisconnectedEvent(conn_id); + } + })); + } + + void TearDown(void) override { + services_map.clear(); + gatt::SetMockBtaGattQueue(nullptr); + gatt::SetMockBtaGattInterface(nullptr); + bluetooth::manager::SetMockBtmInterface(nullptr); + bluetooth::hci::testing::mock_controller_.reset(); + bluetooth::testing::stack::l2cap::reset_interface(); + bluetooth::testing::stack::gap_conn::reset_interface(); + Mock::VerifyAndClearExpectations(&*callbacks); + Mock::VerifyAndClearExpectations(&gatt_queue); + Mock::VerifyAndClearExpectations(&gatt_interface); + Mock::VerifyAndClearExpectations(&btm_interface); + callbacks.reset(); + } + + void InjectConnectedEvent(const RawAddress& address, uint16_t conn_id, + tGATT_STATUS status = GATT_SUCCESS) { + tBTA_GATTC_OPEN event_data = { + .status = status, + .conn_id = conn_id, + .client_if = gatt_if, + .remote_bda = address, + .transport = BT_TRANSPORT_LE, + .mtu = 240, + }; + connected_devices[conn_id] = address; + gatt_callback(BTA_GATTC_OPEN_EVT, (tBTA_GATTC*)&event_data); + } + + void InjectConnUpdateEvent(uint16_t conn_id, uint16_t interval, uint16_t latency, + uint16_t timeout, tGATT_STATUS status = GATT_SUCCESS) { + tBTA_GATTC_CONN_UPDATE event_data = { + .conn_id = conn_id, + .interval = interval, + .latency = latency, + .timeout = timeout, + .status = status, + }; + + gatt_callback(BTA_GATTC_CONN_UPDATE_EVT, (tBTA_GATTC*)&event_data); + } + + void InjectDisconnectedEvent(uint16_t conn_id, + tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_LOCAL_HOST, + bool allow_fake_conn = false) { + if (!allow_fake_conn) { + ASSERT_NE(connected_devices.count(conn_id), 0u); + } + + tBTA_GATTC_CLOSE event_data = { + .conn_id = conn_id, + .status = GATT_SUCCESS, + .client_if = gatt_if, + .remote_bda = connected_devices[conn_id], + .reason = reason, + }; + + connected_devices.erase(conn_id); + gatt_callback(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC*)&event_data); + } + + void InjectSearchCompleteEvent(uint16_t conn_id) { + tBTA_GATTC_SEARCH_CMPL event_data = { + .conn_id = conn_id, + .status = GATT_SUCCESS, + }; + + gatt_callback(BTA_GATTC_SEARCH_CMPL_EVT, (tBTA_GATTC*)&event_data); + } + + void InjectNotificationEvent(const RawAddress& test_address, uint16_t conn_id, uint16_t handle, + std::vector<uint8_t> value, bool indicate = false) { + tBTA_GATTC_NOTIFY event_data = { + .conn_id = conn_id, + .bda = test_address, + .handle = handle, + .len = (uint8_t)value.size(), + .is_notify = !indicate, + }; + + ASSERT_TRUE(value.size() < GATT_MAX_ATTR_LEN); + std::copy(value.begin(), value.end(), event_data.value); + gatt_callback(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC*)&event_data); + } + + void SetEncryptionResult(const RawAddress& address, bool success) { + encryption_result = success; + + ON_CALL(btm_interface, BTM_IsEncrypted(address, _)).WillByDefault(Return(encryption_result)); + + ON_CALL(btm_interface, IsDeviceBonded(address, _)).WillByDefault(Return(true)); + } + + std::unique_ptr<MockHearingAidCallbacks> callbacks; + bluetooth::manager::MockBtmInterface btm_interface; + bluetooth::storage::MockBtifStorageInterface btif_storage_interface_; + gatt::MockBtaGattInterface gatt_interface; + gatt::MockBtaGattQueue gatt_queue; + tBTA_GATTC_CBACK* gatt_callback; + const uint8_t gatt_if = 0xfe; + std::map<uint8_t, RawAddress> connected_devices; + std::map<uint16_t, std::list<gatt::Service>> services_map; + bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_; + bluetooth::testing::stack::gap_conn::Mock mock_stack_gap_conn_interface_; + tGAP_CONN_CALLBACK* gap_conn_cb; + uint16_t req_int; + uint16_t req_latency; + uint16_t req_timeout; + bool encryption_result; + const RawAddress test_address = GetTestAddress(1); +}; + +class HearingAidTest : public HearingAidTestBase { + void SetUp(void) override { + HearingAidTestBase::SetUp(); + BtaAppRegisterCallback app_register_callback; + EXPECT_CALL(gatt_interface, AppRegister(_, _, _, _)) + .WillOnce(DoAll(SaveArg<1>(&gatt_callback), SaveArg<2>(&app_register_callback))); + HearingAid::Initialize(callbacks.get(), base::DoNothing()); + ASSERT_TRUE(gatt_callback); + ASSERT_TRUE(app_register_callback); + app_register_callback.Run(gatt_if, GATT_SUCCESS); + ASSERT_TRUE(HearingAid::IsHearingAidRunning()); + Mock::VerifyAndClearExpectations(&gatt_interface); + } + void TearDown(void) override { + EXPECT_CALL(gatt_interface, AppDeregister(gatt_if)); + if (HearingAid::IsHearingAidRunning()) { + HearingAid::CleanUp(); + } + ASSERT_FALSE(HearingAid::IsHearingAidRunning()); + gatt_callback = nullptr; + HearingAidTestBase::TearDown(); + } +}; + +/* Test that hearing aid is initialized and cleaned up */ +TEST_F(HearingAidTestBase, initialize) { + ASSERT_FALSE(HearingAid::IsHearingAidRunning()); + HearingAid::Initialize(callbacks.get(), base::DoNothing()); + ASSERT_TRUE(HearingAid::IsHearingAidRunning()); + HearingAid::CleanUp(); + ASSERT_FALSE(HearingAid::IsHearingAidRunning()); +} + +/* Test that connect cancellation works */ +TEST_F(HearingAidTest, disconnect_when_connecting) { + /* Override the default action to prevent us sending the connected event */ + EXPECT_CALL(gatt_interface, Open(gatt_if, test_address, BTM_BLE_DIRECT_CONNECTION, _)) + .WillOnce(Return()); + EXPECT_CALL(*callbacks, OnDeviceAvailable(_, _, test_address)).Times(0); + HearingAid::Connect(test_address); + + /* Single call from HearingAid:Disconnect*/ + EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(1); + EXPECT_CALL(gatt_interface, CancelOpen(_, test_address, _)).Times(AnyNumber()); + EXPECT_CALL(gatt_interface, Close(_)).Times(0); + HearingAid::Disconnect(test_address); +} + +/* Test that connect works and Connected state gets reported */ +TEST_F(HearingAidTest, connect) { + set_sample_database(1); + + EXPECT_CALL(gatt_interface, Open(gatt_if, test_address, BTM_BLE_DIRECT_CONNECTION, _)); + EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)); + EXPECT_CALL(*callbacks, OnDeviceAvailable(_, _, test_address)); + ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(Return(true)); + + HearingAid::Connect(test_address); +} + +/* Test that connected device can be disconnected */ +TEST_F(HearingAidTest, disconnect_when_connected) { + set_sample_database(1); + + ON_CALL(btm_interface, BTM_IsEncrypted(test_address, _)).WillByDefault(Return(true)); + EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::CONNECTED, test_address)).Times(1); + EXPECT_CALL(*callbacks, OnDeviceAvailable(_, _, test_address)); + HearingAid::Connect(test_address); + + /* First call from HearingAid:Disconnect. Second call from OnGattDisconnected*/ + EXPECT_CALL(*callbacks, OnConnectionState(ConnectionState::DISCONNECTED, test_address)).Times(2); + EXPECT_CALL(gatt_interface, Close(_)).Times(2); + HearingAid::Disconnect(test_address); +} + +} // namespace +} // namespace internal +} // namespace hearing_aid +} // namespace bluetooth diff --git a/system/bta/hh/bta_hh_le.cc b/system/bta/hh/bta_hh_le.cc index 6dc6365562..9cbd8569ab 100644 --- a/system/bta/hh/bta_hh_le.cc +++ b/system/bta/hh/bta_hh_le.cc @@ -925,14 +925,8 @@ static void bta_hh_le_dis_cback(const RawAddress& addr, tDIS_VALUE* p_dis_value) p_cb->dscp_info.version = p_dis_value->pnp_id.product_version; } - /* TODO(b/367910199): un-serialize once multiservice HoGP is implemented */ - if (com::android::bluetooth::flags::serialize_hogp_and_dis()) { - Uuid pri_srvc = Uuid::From16Bit(UUID_SERVCLASS_LE_HID); - BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, pri_srvc); - return; - } - - bta_hh_le_open_cmpl(p_cb); + Uuid pri_srvc = Uuid::From16Bit(UUID_SERVCLASS_LE_HID); + BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, pri_srvc); } /******************************************************************************* @@ -950,23 +944,16 @@ static void bta_hh_le_pri_service_discovery(tBTA_HH_DEV_CB* p_cb) { p_cb->disc_active |= (BTA_HH_LE_DISC_HIDS | BTA_HH_LE_DISC_DIS); - /* read DIS info */ + /* read DIS info. If failed, continue to discover HoGP services. */ if (!DIS_ReadDISInfo(p_cb->link_spec.addrt.bda, bta_hh_le_dis_cback, DIS_ATTR_PNP_ID_BIT)) { log::error("read DIS failed"); p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; - } else { - /* TODO(b/367910199): un-serialize once multiservice HoGP is implemented */ - if (com::android::bluetooth::flags::serialize_hogp_and_dis()) { - log::debug("Waiting for DIS result before starting HoGP service discovery"); - return; - } + Uuid pri_srvc = Uuid::From16Bit(UUID_SERVCLASS_LE_HID); + BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, pri_srvc); + return; } - /* in parallel */ - /* start primary service discovery for HID service */ - Uuid pri_srvc = Uuid::From16Bit(UUID_SERVCLASS_LE_HID); - BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, pri_srvc); - return; + log::debug("Waiting for DIS result before starting HoGP service discovery"); } /******************************************************************************* @@ -1860,17 +1847,13 @@ static void read_report_cb(tCONN_ID conn_id, tGATT_STATUS status, uint16_t handl log::warn("Unexpected Read response, w4_evt={}", bta_hh_event_text(p_dev_cb->w4_evt)); return; } - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - p_dev_cb->w4_evt = BTA_HH_EMPTY_EVT; - } + p_dev_cb->w4_evt = BTA_HH_EMPTY_EVT; uint8_t hid_handle = p_dev_cb->hid_handle; const gatt::Characteristic* p_char = BTA_GATTC_GetCharacteristic(conn_id, handle); if (p_char == nullptr) { log::error("Unknown handle"); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_read_report_reply(hid_handle, BTA_HH_ERR, nullptr); - } + send_read_report_reply(hid_handle, BTA_HH_ERR, nullptr); return; } @@ -1884,16 +1867,10 @@ static void read_report_cb(tCONN_ID conn_id, tGATT_STATUS status, uint16_t handl break; default: log::error("Unexpected Read UUID: {}", p_char->uuid.ToString()); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_read_report_reply(hid_handle, BTA_HH_ERR, nullptr); - } + send_read_report_reply(hid_handle, BTA_HH_ERR, nullptr); return; } - if (!com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - p_dev_cb->w4_evt = BTA_HH_EMPTY_EVT; - } - if (status != GATT_SUCCESS) { send_read_report_reply(hid_handle, BTA_HH_ERR, nullptr); return; @@ -1937,9 +1914,7 @@ static void bta_hh_le_get_rpt(tBTA_HH_DEV_CB* p_cb, tBTA_HH_RPT_TYPE r_type, uin if (p_rpt == nullptr) { log::error("no matching report"); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_read_report_reply(p_cb->hid_handle, BTA_HH_ERR, nullptr); - } + send_read_report_reply(p_cb->hid_handle, BTA_HH_ERR, nullptr); return; } @@ -1982,17 +1957,13 @@ static void write_report_cb(tCONN_ID conn_id, tGATT_STATUS status, uint16_t hand } log::verbose("w4_evt:{}", bta_hh_event_text(p_dev_cb->w4_evt)); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - p_dev_cb->w4_evt = BTA_HH_EMPTY_EVT; - } + p_dev_cb->w4_evt = BTA_HH_EMPTY_EVT; uint8_t hid_handle = p_dev_cb->hid_handle; const gatt::Characteristic* p_char = BTA_GATTC_GetCharacteristic(conn_id, handle); if (p_char == nullptr) { log::error("Unknown characteristic handle: {}", handle); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_write_report_reply(hid_handle, BTA_HH_ERR, cb_evt); - } + send_write_report_reply(hid_handle, BTA_HH_ERR, cb_evt); return; } @@ -2000,17 +1971,10 @@ static void write_report_cb(tCONN_ID conn_id, tGATT_STATUS status, uint16_t hand if (uuid16 != GATT_UUID_HID_REPORT && uuid16 != GATT_UUID_HID_BT_KB_INPUT && uuid16 != GATT_UUID_HID_BT_MOUSE_INPUT && uuid16 != GATT_UUID_HID_BT_KB_OUTPUT) { log::error("Unexpected characteristic UUID: {}", p_char->uuid.ToString()); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_write_report_reply(hid_handle, BTA_HH_ERR, cb_evt); - } + send_write_report_reply(hid_handle, BTA_HH_ERR, cb_evt); return; } - /* Set Report finished */ - if (!com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - p_dev_cb->w4_evt = BTA_HH_EMPTY_EVT; - } - if (status == GATT_SUCCESS) { send_write_report_reply(hid_handle, BTA_HH_OK, cb_evt); } else { @@ -2033,9 +1997,7 @@ static void bta_hh_le_write_rpt(tBTA_HH_DEV_CB* p_cb, tBTA_HH_RPT_TYPE r_type, B if (p_buf == NULL || p_buf->len == 0) { log::error("Illegal data"); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_write_report_reply(p_cb->hid_handle, BTA_HH_ERR, w4_evt); - } + send_write_report_reply(p_cb->hid_handle, BTA_HH_ERR, w4_evt); return; } @@ -2047,9 +2009,7 @@ static void bta_hh_le_write_rpt(tBTA_HH_DEV_CB* p_cb, tBTA_HH_RPT_TYPE r_type, B p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc.report, p_cb->mode, r_type, rpt_id); if (p_rpt == NULL) { log::error("no matching report"); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - send_write_report_reply(p_cb->hid_handle, BTA_HH_ERR, w4_evt); - } + send_write_report_reply(p_cb->hid_handle, BTA_HH_ERR, w4_evt); osi_free(p_buf); return; } diff --git a/system/bta/include/OWNERS b/system/bta/include/OWNERS deleted file mode 100644 index 9dfb8422a1..0000000000 --- a/system/bta/include/OWNERS +++ /dev/null @@ -1 +0,0 @@ -per-file bta_hearing_aid_api.h=file:/OWNERS_hearingaid diff --git a/system/bta/le_audio/OWNERS b/system/bta/le_audio/OWNERS deleted file mode 100644 index a7d77894ba..0000000000 --- a/system/bta/le_audio/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_leaudio diff --git a/system/bta/le_audio/metrics_collector.cc b/system/bta/le_audio/metrics_collector.cc index a4fb4a353d..24da2e085c 100644 --- a/system/bta/le_audio/metrics_collector.cc +++ b/system/bta/le_audio/metrics_collector.cc @@ -25,9 +25,9 @@ #include <unordered_map> #include <vector> -#include "common/metrics.h" #include "hardware/bt_le_audio.h" #include "le_audio_types.h" +#include "os/metrics.h" #include "types/raw_address.h" namespace bluetooth::le_audio { @@ -199,7 +199,7 @@ public: device_address[i] = device_metric->address_; } - bluetooth::common::LogLeAudioConnectionSessionReported( + bluetooth::os::LogMetricLeAudioConnectionSessionReported( group_size_, group_id_, connection_duration_nanos, device_connecting_offset_nanos, device_connected_offset_nanos, device_connection_duration_nanos, device_connection_statuses, device_disconnection_statuses, device_address, @@ -278,7 +278,7 @@ void MetricsCollector::OnBroadcastStateChanged(bool started) { broadcast_beginning_timepoint_ = std::chrono::high_resolution_clock::now(); } else { auto broadcast_ending_timepoint_ = std::chrono::high_resolution_clock::now(); - bluetooth::common::LogLeAudioBroadcastSessionReported( + bluetooth::os::LogMetricLeAudioBroadcastSessionReported( get_timedelta_nanos(broadcast_beginning_timepoint_, broadcast_ending_timepoint_)); broadcast_beginning_timepoint_ = kInvalidTimePoint; } diff --git a/system/bta/le_audio/metrics_collector_test.cc b/system/bta/le_audio/metrics_collector_test.cc index c94db90ad6..199199bf8a 100644 --- a/system/bta/le_audio/metrics_collector_test.cc +++ b/system/bta/le_audio/metrics_collector_test.cc @@ -23,7 +23,7 @@ #include <cstdint> #include <vector> -#include "common/metrics.h" +#include "os/metrics.h" #include "types/raw_address.h" using testing::_; @@ -57,9 +57,9 @@ std::vector<int64_t> last_streaming_duration_nanos; std::vector<int32_t> last_streaming_context_type; namespace bluetooth { -namespace common { +namespace os { -void LogLeAudioConnectionSessionReported( +void LogMetricLeAudioConnectionSessionReported( int32_t group_size, int32_t group_metric_id, int64_t connection_duration_nanos, const std::vector<int64_t>& device_connecting_offset_nanos, const std::vector<int64_t>& device_connected_offset_nanos, @@ -85,11 +85,11 @@ void LogLeAudioConnectionSessionReported( last_streaming_context_type = streaming_context_type; } -void LogLeAudioBroadcastSessionReported(int64_t duration_nanos) { +void LogMetricLeAudioBroadcastSessionReported(int64_t duration_nanos) { last_broadcast_duration_nanos = duration_nanos; } -} // namespace common +} // namespace os } // namespace bluetooth namespace bluetooth::le_audio { diff --git a/system/bta/le_audio/state_machine.cc b/system/bta/le_audio/state_machine.cc index e5ce67cf9d..b3f20fba39 100644 --- a/system/bta/le_audio/state_machine.cc +++ b/system/bta/le_audio/state_machine.cc @@ -681,8 +681,16 @@ public: kLogHciEvent, group->group_id_, leAudioDevice->address_, kLogSetDataPathOp + "cis_h:" + loghex(conn_handle) + " STATUS=" + loghex(status)); + /* Find ASE and later update state for the given cis.*/ + auto ase = leAudioDevice->GetFirstActiveAseByCisAndDataPathState(CisState::CONNECTED, + DataPathState::CONFIGURING); + if (status) { - log::error("failed to setup data path"); + log::error("Failed to setup data path for {}, cis handle: {:#x}, error: {:#x}", + leAudioDevice->address_, conn_handle, status); + if (ase && ase->cis_conn_hdl == conn_handle) { + ase->data_path_state = DataPathState::IDLE; + } StopStream(group); return; @@ -696,10 +704,6 @@ public: return; } - /* Update state for the given cis.*/ - auto ase = leAudioDevice->GetFirstActiveAseByCisAndDataPathState(CisState::CONNECTED, - DataPathState::CONFIGURING); - if (!ase || ase->cis_conn_hdl != conn_handle) { log::error("Cannot find ase by handle {}", conn_handle); return; diff --git a/system/bta/test/common/btif_storage_mock.cc b/system/bta/test/common/btif_storage_mock.cc index 7e4a4e5118..f5eb38939e 100644 --- a/system/bta/test/common/btif_storage_mock.cc +++ b/system/bta/test/common/btif_storage_mock.cc @@ -19,6 +19,7 @@ #include <bluetooth/log.h> +#include "bta_hearing_aid_api.h" #include "btif/include/btif_profile_storage.h" #include "btif/include/btif_storage.h" @@ -134,3 +135,17 @@ bt_status_t btif_storage_get_remote_device_property(const RawAddress* address, log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!"); return btif_storage_interface->GetRemoteDeviceProperty(address, property); } + +/** Get the hearing aid device properties. */ +bool btif_storage_get_hearing_aid_prop(const RawAddress& address, uint8_t* capabilities, + uint64_t* hi_sync_id, uint16_t* render_delay, + uint16_t* preparation_delay, uint16_t* codecs) { + log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!"); + return btif_storage_interface->GetHearingAidProp(address, capabilities, hi_sync_id, render_delay, + preparation_delay, codecs); +} + +void btif_storage_add_hearing_aid(const HearingDevice& dev_info) { + log::assert_that(btif_storage_interface != nullptr, "Mock storage module not set!"); + return btif_storage_interface->AddHearingAid(&dev_info); +} diff --git a/system/bta/test/common/btif_storage_mock.h b/system/bta/test/common/btif_storage_mock.h index 8809643f70..25f9bbdde0 100644 --- a/system/bta/test/common/btif_storage_mock.h +++ b/system/bta/test/common/btif_storage_mock.h @@ -21,6 +21,8 @@ #include "include/hardware/bluetooth.h" #include "types/raw_address.h" +struct HearingDevice; + namespace bluetooth { namespace storage { @@ -50,6 +52,11 @@ public: virtual bt_status_t GetRemoteDeviceProperty(const RawAddress* address, bt_property_t* property) = 0; + virtual bool GetHearingAidProp(const RawAddress& address, uint8_t* capabilities, + uint64_t* hi_sync_id, uint16_t* render_delay, + uint16_t* preparation_delay, uint16_t* codecs) = 0; + virtual void AddHearingAid(const HearingDevice* dev_info) = 0; + virtual ~BtifStorageInterface() = default; }; @@ -88,6 +95,11 @@ public: MOCK_METHOD((void), RemoveLeaudioHas, (const RawAddress& address), (override)); MOCK_METHOD((bt_status_t), GetRemoteDeviceProperty, (const RawAddress* address, bt_property_t* property), (override)); + MOCK_METHOD((bool), GetHearingAidProp, + (const RawAddress& address, uint8_t* capabilities, uint64_t* hi_sync_id, + uint16_t* render_delay, uint16_t* preparation_delay, uint16_t* codecs), + (override)); + MOCK_METHOD((void), AddHearingAid, (const HearingDevice* dev_info), (override)); }; /** diff --git a/system/btcore/Android.bp b/system/btcore/Android.bp index 6a0350afcb..b02ead0f8d 100644 --- a/system/btcore/Android.bp +++ b/system/btcore/Android.bp @@ -20,10 +20,8 @@ cc_library_static { "packages/modules/Bluetooth/system/gd", ], srcs: [ - "src/device_class.cc", "src/module.cc", "src/osi_module.cc", - "src/property.cc", ], header_libs: ["libbluetooth_headers"], static_libs: [ @@ -50,41 +48,3 @@ cc_library_headers { ], min_sdk_version: "30", } - -// Note: It's good to get the tests compiled both for the host and the target so -// we get to test with both Bionic libc and glibc -// libbtcore unit tests for target and host -cc_test { - name: "net_test_btcore", - test_suites: ["general-tests"], - defaults: [ - "fluoride_defaults", - "mts_defaults", - ], - local_include_dirs: ["include"], - include_dirs: ["packages/modules/Bluetooth/system"], - srcs: [ - "test/device_class_test.cc", - "test/property_test.cc", - ], - shared_libs: [ - "libbase", - "liblog", - ], - static_libs: [ - "libbluetooth-types", - "libbluetooth_log", - "libbtcore", - "libchrome", - "libcom.android.sysprop.bluetooth.wrapped", - "libosi", - ], - host_supported: true, - test_options: { - unit_test: true, - }, - sanitize: { - address: true, - }, - header_libs: ["libbluetooth_headers"], -} diff --git a/system/btcore/BUILD.gn b/system/btcore/BUILD.gn index 76853554cf..abfbd328ee 100644 --- a/system/btcore/BUILD.gn +++ b/system/btcore/BUILD.gn @@ -16,11 +16,9 @@ static_library("btcore") { sources = [ - "src/device_class.cc", "src/hal_util.cc", "src/module.cc", "src/osi_module.cc", - "src/property.cc", ] include_dirs = [ @@ -33,35 +31,3 @@ static_library("btcore") { "//bt/system/log:log_defaults", ] } - -if (use.test) { - executable("net_test_btcore") { - sources = [ - "test/device_class_test.cc", - "test/property_test.cc", - ] - - include_dirs = [ - "include", - "//bt/system", - ] - - deps = [ - "//bt/system/btcore", - "//bt/system/osi", - "//bt/system/types", - ] - - configs += [ - "//bt/system:external_gtest_main", - "//bt/system:target_defaults", - "//bt/system/log:log_defaults", - ] - - libs = [ - "pthread", - "rt", - "dl", - ] - } -} diff --git a/system/btcore/fuzzer/Android.bp b/system/btcore/fuzzer/Android.bp deleted file mode 100644 index 07b8c28f89..0000000000 --- a/system/btcore/fuzzer/Android.bp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2021 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 { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "system_bt_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["system_bt_license"], -} - -cc_defaults { - name: "btcore_fuzz_defaults", - shared_libs: [ - "libcrypto", - "libstatslog_bt", - ], - static_libs: [ - "libbase", - "libbluetooth-types", - "libbluetooth_crypto_toolbox", - "libbluetooth_log", - "libbt-common", - "libbtcore", - "libchrome", - "libcom.android.sysprop.bluetooth.wrapped", - "libevent", - "liblog", - "libmodpb64", - "libosi", - "libprotobuf-cpp-lite", - ], - include_dirs: [ - "packages/modules/Bluetooth/system", - ], - header_libs: [ - "libbluetooth_headers", - ], - fuzz_config: { - cc: ["android-bluetooth-security@google.com"], - componentid: 27441, - hotlists: [ - "4593311", - ], - description: "The fuzzer targets the APIs of libbtcore", - vector: "local_no_privileges_required", - service_privilege: "privileged", - users: "multi_user", - fuzzed_code_usage: "shipped", - }, -} - -cc_fuzz { - name: "btcore_device_class_fuzzer", - defaults: [ - "btcore_fuzz_defaults", - ], - srcs: [ - "btcore_device_class_fuzzer.cpp", - ], -} - -cc_fuzz { - name: "btcore_property_fuzzer", - defaults: [ - "btcore_fuzz_defaults", - ], - srcs: [ - "btcore_property_fuzzer.cpp", - ], - -} - -cc_fuzz { - name: "btcore_module_fuzzer", - enabled: false, - defaults: [ - "btcore_fuzz_defaults", - ], - srcs: [ - "btcore_module_fuzzer.cpp", - ], -} diff --git a/system/btcore/fuzzer/README.md b/system/btcore/fuzzer/README.md deleted file mode 100644 index cf770e4385..0000000000 --- a/system/btcore/fuzzer/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Fuzzers for libbtcore - -## Plugin Design Considerations -The fuzzer plugins for `libbtcore` are designed based on the understanding of the -source code and tries to achieve the following: - -##### Maximize code coverage -The configuration parameters are not hard-coded, but instead selected based on -incoming data. This ensures more code paths are reached by the fuzzers. - -Fuzzers assigns values to the following parameters to pass on to libbtcore: -1. Bluetooth Device Type (parameter name: `deviceType`) -2. Bluetooth Adapter Visibility Mode (parameter name: `mode`) -3. Bluetooth Address (parameter name: `btAddress`) -4. Bluetooth Device Class parameter (parameter name: `deviceClassT`) - -| Parameter| Valid Values| Configured Value| -|------------- |-------------| ----- | -| `deviceType` | 0.`BT_DEVICE_DEVTYPE_BREDR` 1.`BT_DEVICE_DEVTYPE_BLE` 2.`BT_DEVICE_DEVTYPE_DUAL` | Value obtained from FuzzedDataProvider | -| `mode` | 0.`BT_SCAN_MODE_NONE` 1.`BT_SCAN_MODE_CONNECTABLE` 2.`BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE` | Value obtained from FuzzedDataProvider | -| `btAddress` | Values inside array ranges from `0x0` to `0xFF` | Value obtained from FuzzedDataProvider | -| `deviceClassT` | Values inside array ranges from `0x0` to `0xFF` | Value obtained from FuzzedDataProvider | -This also ensures that the plugins are always deterministic for any given input. - -##### Maximize utilization of input data -The plugins feed the entire input data to the module. -This ensures that the plugins tolerates any kind of input (empty, huge, -malformed, etc) and doesn't `exit()` on any input and thereby increasing the -chance of identifying vulnerabilities. - -## Build - -This describes steps to build btcore_device_class_fuzzer, btcore_property_fuzzer and btcore_module_fuzzer binaries. - -### Android - -#### Steps to build -Build the fuzzer -``` - $ mm -j$(nproc) btcore_device_class_fuzzer - $ mm -j$(nproc) btcore_property_fuzzer - $ mm -j$(nproc) btcore_module_fuzzer -``` -### Steps to run - -To run on device -``` - $ adb sync data - $ adb shell /data/fuzz/arm64/btcore_device_class_fuzzer/btcore_device_class_fuzzer - $ adb shell /data/fuzz/arm64/btcore_property_fuzzer/btcore_property_fuzzer - $ adb shell /data/fuzz/arm64/btcore_module_fuzzer/btcore_module_fuzzer -``` - -## References: - * http://llvm.org/docs/LibFuzzer.html - * https://github.com/google/oss-fuzz diff --git a/system/btcore/fuzzer/btcore_device_class_fuzzer.cpp b/system/btcore/fuzzer/btcore_device_class_fuzzer.cpp deleted file mode 100644 index fef1e33370..0000000000 --- a/system/btcore/fuzzer/btcore_device_class_fuzzer.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2021 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 <fuzzer/FuzzedDataProvider.h> - -#include "btcore/include/device_class.h" - -class BTCoreDeviceClassFuzzer { -public: - void process(const uint8_t* data, size_t size); - -private: - std::unique_ptr<FuzzedDataProvider> mFdp = nullptr; -}; - -void BTCoreDeviceClassFuzzer::process(const uint8_t* data, size_t size) { - mFdp = std::make_unique<FuzzedDataProvider>(data, size); - size_t dcStreamSize = sizeof(bt_device_class_t) * sizeof(uint8_t); - - std::vector<uint8_t> dcStreamSrc(dcStreamSize, 0x0); - mFdp->ConsumeData(dcStreamSrc.data(), dcStreamSize); - - bt_device_class_t deviceClass; - device_class_from_stream(&deviceClass, dcStreamSrc.data()); - - std::vector<uint8_t> dcStreamDst(dcStreamSize, 0x0); - (void)device_class_to_stream(&deviceClass, dcStreamDst.data(), dcStreamSize); - - device_class_set_limited(&deviceClass, mFdp->ConsumeBool()); - (void)device_class_get_limited(&deviceClass); - - int val = mFdp->ConsumeIntegral<int>(); - device_class_set_major_device(&deviceClass, val); - (void)device_class_get_major_device(&deviceClass); - - val = mFdp->ConsumeIntegral<int>(); - device_class_set_minor_device(&deviceClass, val); - (void)device_class_get_minor_device(&deviceClass); - - device_class_set_information(&deviceClass, mFdp->ConsumeBool()); - (void)device_class_get_information(&deviceClass); - - bt_device_class_t deviceClassCopy; - (void)device_class_copy(&deviceClassCopy, &deviceClass); - (void)device_class_equals(&deviceClass, &deviceClassCopy); - - val = mFdp->ConsumeIntegralInRange(1, INT_MAX); - device_class_from_int(&deviceClass, val); - (void)device_class_to_int(&deviceClass); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - BTCoreDeviceClassFuzzer btCoreDeviceClassFuzzer; - btCoreDeviceClassFuzzer.process(data, size); - return 0; -} diff --git a/system/btcore/fuzzer/btcore_module_fuzzer.cpp b/system/btcore/fuzzer/btcore_module_fuzzer.cpp deleted file mode 100644 index adb21b724d..0000000000 --- a/system/btcore/fuzzer/btcore_module_fuzzer.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2021 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 "btcore/include/hal_util.h" -#include "btcore/include/module.h" - -extern const module_t osi_module; - -extern "C" { -struct android_namespace_t* android_get_exported_namespace(const char*) { return nullptr; } -} - -class BTCoreModuleFuzzer { -public: - void process(); -}; - -void BTCoreModuleFuzzer::process() { - const bt_interface_t* interface; - (void)hal_util_load_bt_library(&interface); - module_management_start(); - module_init(&osi_module); - (void)module_start_up(&osi_module); - (void)get_module(osi_module.name); - module_shut_down(&osi_module); - module_clean_up(&osi_module); - module_management_stop(); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t*, size_t) { - BTCoreModuleFuzzer btCoreModuleFuzzer; - btCoreModuleFuzzer.process(); - return 0; -} diff --git a/system/btcore/fuzzer/btcore_property_fuzzer.cpp b/system/btcore/fuzzer/btcore_property_fuzzer.cpp deleted file mode 100644 index eabcf505c3..0000000000 --- a/system/btcore/fuzzer/btcore_property_fuzzer.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2021 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 <fuzzer/FuzzedDataProvider.h> - -#include "btcore/include/property.h" -#include "types/bluetooth/uuid.h" -#include "types/raw_address.h" - -using bluetooth::Uuid; - -constexpr int32_t kRandomStringLength = 256; - -class BTCorePropertyFuzzer { -public: - void process(const uint8_t* data, size_t size); - -private: - std::unique_ptr<FuzzedDataProvider> mFdp = nullptr; -}; - -void BTCorePropertyFuzzer::process(const uint8_t* data, size_t size) { - mFdp = std::make_unique<FuzzedDataProvider>(data, size); - uint8_t addr[RawAddress::kLength]; - mFdp->ConsumeData(addr, sizeof(uint8_t) * RawAddress::kLength); - RawAddress btAddress = {addr}; - bt_property_t* property = property_new_addr(&btAddress); - property_as_addr(property); - property_free(property); - - bt_device_class_t deviceClass = {{mFdp->ConsumeIntegral<uint8_t>(), - mFdp->ConsumeIntegral<uint8_t>(), - mFdp->ConsumeIntegral<uint8_t>()}}; - property = property_new_device_class(&deviceClass); - - const bt_device_class_t* pDeviceClass = property_as_device_class(property); - (void)device_class_to_int(pDeviceClass); - property_free(property); - - bt_device_type_t deviceType = (bt_device_type_t)(mFdp->ConsumeIntegral<uint32_t>()); - property = property_new_device_type(deviceType); - (void)property_as_device_type(property); - property_free(property); - - uint32_t timeout = mFdp->ConsumeIntegral<uint32_t>(); - property = property_new_discoverable_timeout(timeout); - (void)property_as_discoverable_timeout(property); - property_free(property); - - std::string name = mFdp->ConsumeRandomLengthString(kRandomStringLength); - property = property_new_name(name.c_str()); - (void)property_as_name(property); - property_free(property); - - int8_t rssi = mFdp->ConsumeIntegral<int8_t>(); - property = property_new_rssi(rssi); - (void)property_as_rssi(property); - property_free(property); - - size_t uuidSize = sizeof(uint8_t) * bluetooth::Uuid::kNumBytes128; - uint8_t uuid[bluetooth::Uuid::kNumBytes128]; - mFdp->ConsumeData(uuid, uuidSize); - Uuid uuidBE = Uuid::From128BitBE(uuid); - property = property_new_uuids(&uuidBE, 1); - size_t uuidCount; - (void)property_as_uuids(property, &uuidCount); - property_free(property); - - mFdp->ConsumeData(uuid, uuidSize); - Uuid uuidLE = Uuid::From128BitLE(uuid); - Uuid uuids[] = {uuidBE, uuidLE}; - bt_property_t* propertySrc = property_new_uuids(uuids, std::size(uuids)); - bt_property_t propertyDest; - (void)property_copy(&propertyDest, propertySrc); - property_free(propertySrc); -} - -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - BTCorePropertyFuzzer btCorePropertyFuzzer; - btCorePropertyFuzzer.process(data, size); - return 0; -} diff --git a/system/btcore/include/device_class.h b/system/btcore/include/device_class.h deleted file mode 100644 index 6f56b119b0..0000000000 --- a/system/btcore/include/device_class.h +++ /dev/null @@ -1,95 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 <stdbool.h> -#include <stddef.h> -#include <stdint.h> - -// Provides Class Of Device primitive as specified in the bluetooth spec. -// [Class Of Device] -// (https://www.bluetooth.org/en-us/specification/assigned-numbers/baseband) - -// Device class may be defined in other structures. -// Only use defined methods to manipulate internals. -typedef struct bt_device_class_t { - uint8_t _[3]; // Do not access directly; use methods below. -} bt_device_class_t; - -// Copies the |data| class of device stream into device class |dc|. |dc| -// and |data| must not be NULL. -void device_class_from_stream(bt_device_class_t* dc, const uint8_t* data); - -// Serializes the device class |dc| to pointer argument |data| in big endian -// format. |len| must contain the buffer size of |data|. Returns the actual -// number of bytes copied into |data|. |dc| and |data| must not be NULL. -int device_class_to_stream(const bt_device_class_t* dc, uint8_t* data, size_t len); - -// Copies the |data| class of device integer into device class |dc|. |dc| -// must not be NULL. -void device_class_from_int(bt_device_class_t* dc, int data); - -// Returns the device class |dc| in integer format. |dc| must not be NULL. -int device_class_to_int(const bt_device_class_t* dc); - -// Compares and returns |true| if two device classes |p1| and |p2| are equal. -// False otherwise. -bool device_class_equals(const bt_device_class_t* p1, const bt_device_class_t* p2); - -// Copies and returns |true| if the device class was successfully copied from -// |p2| into |p1|. False otherwise. -bool device_class_copy(bt_device_class_t* dest, const bt_device_class_t* src); - -// Query, getters and setters for the major device class. |dc| must not be -// NULL. -int device_class_get_major_device(const bt_device_class_t* dc); -void device_class_set_major_device(bt_device_class_t* dc, int val); - -// Query, getters and setters for the minor device class. |dc| must not be NULL. -int device_class_get_minor_device(const bt_device_class_t* dc); -void device_class_set_minor_device(bt_device_class_t* dc, int val); - -// Query, getters and setters for the various major service class features. -// |dc| must not be NULL. -bool device_class_get_limited(const bt_device_class_t* dc); -void device_class_set_limited(bt_device_class_t* dc, bool set); - -bool device_class_get_positioning(const bt_device_class_t* dc); -void device_class_set_positioning(bt_device_class_t* dc, bool set); - -bool device_class_get_networking(const bt_device_class_t* dc); -void device_class_set_networking(bt_device_class_t* dc, bool set); - -bool device_class_get_rendering(const bt_device_class_t* dc); -void device_class_set_rendering(bt_device_class_t* dc, bool set); - -bool device_class_get_capturing(const bt_device_class_t* dc); -void device_class_set_capturing(bt_device_class_t* dc, bool set); - -bool device_class_get_object_transfer(const bt_device_class_t* dc); -void device_class_set_object_transfer(bt_device_class_t* dc, bool set); - -bool device_class_get_audio(const bt_device_class_t* dc); -void device_class_set_audio(bt_device_class_t* dc, bool set); - -bool device_class_get_telephony(const bt_device_class_t* dc); -void device_class_set_telephony(bt_device_class_t* dc, bool set); - -bool device_class_get_information(const bt_device_class_t* dc); -void device_class_set_information(bt_device_class_t* dc, bool set); diff --git a/system/btcore/include/device_features.h b/system/btcore/include/device_features.h deleted file mode 100644 index 13f8653635..0000000000 --- a/system/btcore/include/device_features.h +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 <stdint.h> - -// Represents a page of device feature enabled/disabled bits returned -// by the local controller. See the bluetooth spec for bit indexes. -typedef struct { - uint8_t as_array[8]; -} bt_device_features_t; diff --git a/system/btcore/include/event_mask.h b/system/btcore/include/event_mask.h deleted file mode 100644 index f811c78c8e..0000000000 --- a/system/btcore/include/event_mask.h +++ /dev/null @@ -1,28 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 <stdint.h> - -// Represents a mask which can be used to tell the controller which -// HCI events the stack wishes to be informed about. See the bluetooth -// spec for more information on what each bit means. -typedef struct { - const uint8_t as_array[8]; -} bt_event_mask_t; diff --git a/system/btcore/include/iac.h b/system/btcore/include/iac.h deleted file mode 100644 index 39c830e54b..0000000000 --- a/system/btcore/include/iac.h +++ /dev/null @@ -1,28 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 <stdint.h> - -// Inquiry Access Code -// [Bluetooth Baseband] -// (https://www.bluetooth.org/en-us/specification/assigned-numbers/baseband) -typedef struct { - uint8_t iac[3]; -} __attribute__((packed)) bt_inquiry_access_code_t; diff --git a/system/btcore/include/property.h b/system/btcore/include/property.h deleted file mode 100644 index f8e06150ad..0000000000 --- a/system/btcore/include/property.h +++ /dev/null @@ -1,76 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 <hardware/bluetooth.h> -#include <stdint.h> -#include <stdlib.h> - -#include "btcore/include/device_class.h" -#include "types/bluetooth/uuid.h" -#include "types/raw_address.h" - -// Copies an array of consecutive properties of |count| to a newly -// allocated array. |properties| must not be NULL. -bt_property_t* property_copy_array(const bt_property_t* properties, size_t count); - -// Copies |src| to |dest|. Returns the value of |dest|. -// |src| and |dest| must not be NULL. -bt_property_t* property_copy(bt_property_t* dest, const bt_property_t* src); - -// Returns true if the value of the two properties |p1| and |p2| are equal. -// |p1| and |p2| must not be NULL. -bool property_equals(const bt_property_t* p1, const bt_property_t* p2); - -// Property resource allocations. Caller is expected to free |property| -// using |property_free| or |property_free_array|. -// Parameter must not be NULL. A copy of the parameter is made and -// stored in the property. -bt_property_t* property_new_addr(const RawAddress* addr); -bt_property_t* property_new_device_class(const bt_device_class_t* dc); -bt_property_t* property_new_device_type(bt_device_type_t device_type); -bt_property_t* property_new_discoverable_timeout(const uint32_t timeout); -bt_property_t* property_new_name(const char* name); -bt_property_t* property_new_rssi(const int8_t rssi); -bt_property_t* property_new_uuids(const bluetooth::Uuid* uuid, size_t count); - -// Property resource frees both property and value. -void property_free(bt_property_t* property); -void property_free_array(bt_property_t* properties, size_t count); - -// Value check convenience methods. The contents of the property are -// checked for the respective validity and returns true, false otherwise. -// |property| must not be NULL. -bool property_is_addr(const bt_property_t* property); -bool property_is_device_class(const bt_property_t* property); -bool property_is_device_type(const bt_property_t* property); -bool property_is_discoverable_timeout(const bt_property_t* property); -bool property_is_name(const bt_property_t* property); -bool property_is_rssi(const bt_property_t* property); -bool property_is_uuids(const bt_property_t* property); - -// Value conversion convenience methods. The contents of the property are -// properly typed and returned to the caller. |property| must not be NULL. -const RawAddress* property_as_addr(const bt_property_t* property); -const bt_device_class_t* property_as_device_class(const bt_property_t* property); -bt_device_type_t property_as_device_type(const bt_property_t* property); -uint32_t property_as_discoverable_timeout(const bt_property_t* property); -const bt_bdname_t* property_as_name(const bt_property_t* property); -int8_t property_as_rssi(const bt_property_t* property); -const bluetooth::Uuid* property_as_uuids(const bt_property_t* property, size_t* count); diff --git a/system/btcore/include/version.h b/system/btcore/include/version.h deleted file mode 100644 index d7787c7409..0000000000 --- a/system/btcore/include/version.h +++ /dev/null @@ -1,29 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 <stdint.h> - -typedef struct { - uint8_t hci_version; - uint16_t hci_revision; - uint8_t lmp_version; - uint16_t manufacturer; - uint16_t lmp_subversion; -} bt_version_t; diff --git a/system/btcore/src/device_class.cc b/system/btcore/src/device_class.cc deleted file mode 100644 index 08d2ae191a..0000000000 --- a/system/btcore/src/device_class.cc +++ /dev/null @@ -1,167 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 "btcore/include/device_class.h" - -#include <arpa/inet.h> -#include <bluetooth/log.h> -#include <endian.h> -#include <string.h> - -using namespace bluetooth; - -typedef struct _bt_device_class_t { - uint32_t unused : 2; // LSBs - uint32_t minor_device : 6; - uint32_t major_device : 5; - uint32_t major_service : 11; // MSBs -} __attribute__((__packed__)) _bt_device_class_t; - -// Convenience to interpret raw device class bytes. -#define DC(x) ((_bt_device_class_t*)(x)) - -// Ensure the internal device class implementation and public one -// have equal size. -static_assert(sizeof(_bt_device_class_t) == sizeof(bt_device_class_t), - "Internal and external device class implementation should have the same size"); - -// [Major Service Classes] -// (https://www.bluetooth.org/en-us/specification/assigned-numbers/baseband) -enum { - DC_LIMITED_DISCOVERABLE_MODE = 0x0001, - DC_RESERVED14 = 0x0002, - DC_RESERVED15 = 0x0004, - DC_POSITIONING = 0x0008, - DC_NETWORKING = 0x0010, - DC_RENDERING = 0x0020, - DC_CAPTURING = 0x0040, - DC_OBJECT_TRANSFER = 0x0080, - DC_AUDIO = 0x0100, - DC_TELEPHONY = 0x0200, - DC_INFORMATION = 0x0400, -}; - -static bool device_class_get_major_service_(const bt_device_class_t* dc, int bitmask); -static void device_class_clr_major_service_(bt_device_class_t* dc, int bitmask); -static void device_class_set_major_service_(bt_device_class_t* dc, int bitmask); - -void device_class_from_stream(bt_device_class_t* dc, const uint8_t* data) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - log::assert_that(data != NULL, "assert failed: data != NULL"); - *dc = *(bt_device_class_t*)data; -} - -int device_class_to_stream(const bt_device_class_t* dc, uint8_t* data, size_t len) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - log::assert_that(data != NULL, "assert failed: data != NULL"); - log::assert_that(len >= sizeof(bt_device_class_t), - "assert failed: len >= sizeof(bt_device_class_t)"); - for (size_t i = 0; i < sizeof(bt_device_class_t); ++i) { - data[i] = dc->_[i]; - } - return sizeof(bt_device_class_t); -} - -void device_class_from_int(bt_device_class_t* dc, int data) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - log::assert_that(data != 0, "assert failed: data != 0"); - // Careful with endianess. - dc->_[0] = data & 0xff; - dc->_[1] = (data >> 8) & 0xff; - dc->_[2] = (data >> 16) & 0xff; -} - -int device_class_to_int(const bt_device_class_t* dc) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - // Careful with endianess. - int val = 0; - memcpy(&val, dc, sizeof(*dc)); - return static_cast<int>(le32toh(val) & 0xffffff); -} - -bool device_class_equals(const bt_device_class_t* p1, const bt_device_class_t* p2) { - log::assert_that(p1 != NULL, "assert failed: p1 != NULL"); - log::assert_that(p2 != NULL, "assert failed: p2 != NULL"); - return memcmp(p1, p2, sizeof(bt_device_class_t)) == 0; -} - -bool device_class_copy(bt_device_class_t* dest, const bt_device_class_t* src) { - log::assert_that(dest != NULL, "assert failed: dest != NULL"); - log::assert_that(src != NULL, "assert failed: src != NULL"); - return memcpy(dest, src, sizeof(bt_device_class_t)) == dest; -} - -int device_class_get_major_device(const bt_device_class_t* dc) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - return DC(dc)->major_device; -} - -void device_class_set_major_device(bt_device_class_t* dc, int val) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - DC(dc)->major_device = val; -} - -int device_class_get_minor_device(const bt_device_class_t* dc) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - return DC(dc)->minor_device; -} - -void device_class_set_minor_device(bt_device_class_t* dc, int val) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - DC(dc)->minor_device = val; -} - -bool device_class_get_information(const bt_device_class_t* dc) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - return device_class_get_major_service_(dc, DC_INFORMATION); -} - -void device_class_set_information(bt_device_class_t* dc, bool set) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - if (set) { - device_class_set_major_service_(dc, DC_INFORMATION); - } else { - device_class_clr_major_service_(dc, DC_INFORMATION); - } -} - -bool device_class_get_limited(const bt_device_class_t* dc) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - return device_class_get_major_service_(dc, DC_LIMITED_DISCOVERABLE_MODE); -} - -void device_class_set_limited(bt_device_class_t* dc, bool set) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - if (set) { - device_class_set_major_service_(dc, DC_LIMITED_DISCOVERABLE_MODE); - } else { - device_class_clr_major_service_(dc, DC_LIMITED_DISCOVERABLE_MODE); - } -} - -static bool device_class_get_major_service_(const bt_device_class_t* dc, int bitmask) { - return DC(dc)->major_service & bitmask; -} - -static void device_class_clr_major_service_(bt_device_class_t* dc, int bitmask) { - DC(dc)->major_service &= ~bitmask; -} - -static void device_class_set_major_service_(bt_device_class_t* dc, int bitmask) { - DC(dc)->major_service |= bitmask; -} diff --git a/system/btcore/src/property.cc b/system/btcore/src/property.cc deleted file mode 100644 index 0379229432..0000000000 --- a/system/btcore/src/property.cc +++ /dev/null @@ -1,217 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 "btcore/include/property.h" - -#include <bluetooth/log.h> -#include <string.h> - -#include "btcore/include/device_class.h" -#include "osi/include/allocator.h" -#include "osi/include/compat.h" -#include "types/bluetooth/uuid.h" -#include "types/raw_address.h" - -using bluetooth::Uuid; -using namespace bluetooth; - -static bt_property_t* property_new_(void* val, size_t len, bt_property_type_t type); - -bt_property_t* property_copy_array(const bt_property_t* properties, size_t count) { - log::assert_that(properties != NULL, "assert failed: properties != NULL"); - bt_property_t* clone = static_cast<bt_property_t*>(osi_calloc(sizeof(bt_property_t) * count)); - - memcpy(&clone[0], &properties[0], sizeof(bt_property_t) * count); - for (size_t i = 0; i < count; ++i) { - clone[i].val = osi_calloc(clone[i].len); - memcpy(clone[i].val, properties[i].val, clone[i].len); - } - - return clone; -} - -bt_property_t* property_copy(bt_property_t* dest, const bt_property_t* src) { - log::assert_that(dest != NULL, "assert failed: dest != NULL"); - log::assert_that(src != NULL, "assert failed: src != NULL"); - return (bt_property_t*)memcpy(dest, src, sizeof(bt_property_t)); -} - -bool property_equals(const bt_property_t* p1, const bt_property_t* p2) { - // Two null properties are not the same. May need to revisit that - // decision when we have a test case that exercises that condition. - if (!p1 || !p2 || p1->type != p2->type) { - return false; - } - - // Although the Bluetooth name is a 249-byte array, the implementation - // treats it like a variable-length array with its size specified in the - // property's `len` field. We special-case the equivalence of BDNAME - // types here by truncating the larger, zero-padded name to its string - // length and comparing against the shorter name. - // - // Note: it may be the case that both strings are zero-padded but that - // hasn't come up yet so this implementation doesn't handle it. - if (p1->type == BT_PROPERTY_BDNAME && p1->len != p2->len) { - const bt_property_t *shorter = p1, *longer = p2; - if (p1->len > p2->len) { - shorter = p2; - longer = p1; - } - return strlen((const char*)longer->val) == (size_t)shorter->len && - !memcmp(longer->val, shorter->val, shorter->len); - } - - return p1->len == p2->len && !memcmp(p1->val, p2->val, p1->len); -} - -bt_property_t* property_new_addr(const RawAddress* addr) { - log::assert_that(addr != NULL, "assert failed: addr != NULL"); - return property_new_((void*)addr, sizeof(RawAddress), BT_PROPERTY_BDADDR); -} - -bt_property_t* property_new_device_class(const bt_device_class_t* dc) { - log::assert_that(dc != NULL, "assert failed: dc != NULL"); - return property_new_((void*)dc, sizeof(bt_device_class_t), BT_PROPERTY_CLASS_OF_DEVICE); -} - -bt_property_t* property_new_device_type(bt_device_type_t type) { - return property_new_((void*)&type, sizeof(bt_device_type_t), BT_PROPERTY_TYPE_OF_DEVICE); -} - -bt_property_t* property_new_discoverable_timeout(const uint32_t timeout) { - return property_new_((void*)&timeout, sizeof(uint32_t), BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT); -} - -bt_property_t* property_new_name(const char* name) { - log::assert_that(name != NULL, "assert failed: name != NULL"); - return property_new_((void*)name, sizeof(bt_bdname_t), BT_PROPERTY_BDNAME); -} - -bt_property_t* property_new_rssi(int8_t rssi) { - return property_new_((void*)&rssi, sizeof(int8_t), BT_PROPERTY_REMOTE_RSSI); -} - -bt_property_t* property_new_uuids(const Uuid* uuid, size_t count) { - log::assert_that(uuid != NULL, "assert failed: uuid != NULL"); - return property_new_((void*)uuid, sizeof(Uuid) * count, BT_PROPERTY_UUIDS); -} - -void property_free(bt_property_t* property) { property_free_array(property, 1); } - -void property_free_array(bt_property_t* properties, size_t count) { - if (properties == NULL) { - return; - } - - for (size_t i = 0; i < count; ++i) { - osi_free(properties[i].val); - } - - osi_free(properties); -} - -bool property_is_addr(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_BDADDR; -} - -bool property_is_device_class(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_CLASS_OF_DEVICE; -} - -bool property_is_device_type(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_TYPE_OF_DEVICE; -} - -bool property_is_discoverable_timeout(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT; -} - -bool property_is_name(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_BDNAME; -} - -bool property_is_rssi(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_REMOTE_RSSI; -} - -bool property_is_uuids(const bt_property_t* property) { - log::assert_that(property != NULL, "assert failed: property != NULL"); - return property->type == BT_PROPERTY_UUIDS; -} - -// Convenience conversion methods to property values -const RawAddress* property_as_addr(const bt_property_t* property) { - log::assert_that(property_is_addr(property), "assert failed: property_is_addr(property)"); - return (const RawAddress*)property->val; -} - -const bt_device_class_t* property_as_device_class(const bt_property_t* property) { - log::assert_that(property_is_device_class(property), - "assert failed: property_is_device_class(property)"); - return (const bt_device_class_t*)property->val; -} - -bt_device_type_t property_as_device_type(const bt_property_t* property) { - log::assert_that(property_is_device_type(property), - "assert failed: property_is_device_type(property)"); - return *(const bt_device_type_t*)property->val; -} - -uint32_t property_as_discoverable_timeout(const bt_property_t* property) { - log::assert_that(property_is_discoverable_timeout(property), - "assert failed: property_is_discoverable_timeout(property)"); - return *(const uint32_t*)property->val; -} - -const bt_bdname_t* property_as_name(const bt_property_t* property) { - log::assert_that(property_is_name(property), "assert failed: property_is_name(property)"); - return (const bt_bdname_t*)property->val; -} - -int8_t property_as_rssi(const bt_property_t* property) { - log::assert_that(property_is_rssi(property), "assert failed: property_is_rssi(property)"); - return *(const int8_t*)property->val; -} - -const Uuid* property_as_uuids(const bt_property_t* property, size_t* count) { - log::assert_that(property_is_uuids(property), "assert failed: property_is_uuids(property)"); - *count = sizeof(Uuid) / property->len; - return (const Uuid*)property->val; -} - -static bt_property_t* property_new_(void* val, size_t len, bt_property_type_t type) { - bt_property_t* property = static_cast<bt_property_t*>(osi_calloc(sizeof(bt_property_t))); - - property->val = osi_calloc(len + 1); - if (type == BT_PROPERTY_BDNAME) { - osi_strlcpy((char*)property->val, (const char*)val, len); - } else { - memcpy(property->val, val, len); - } - - property->type = type; - property->len = len; - - return property; -} diff --git a/system/btcore/test/device_class_test.cc b/system/btcore/test/device_class_test.cc deleted file mode 100644 index ed5f9581f0..0000000000 --- a/system/btcore/test/device_class_test.cc +++ /dev/null @@ -1,224 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 "btcore/include/device_class.h" - -#include <arpa/inet.h> -#include <gtest/gtest.h> - -static ::testing::AssertionResult check_bitfield(const char* m_expr, const char* n_expr, int m, - int n) { - if (m == n) { - return ::testing::AssertionSuccess(); - } - - std::stringstream ss; - - ss.str(""); - ss << std::showbase << std::hex << std::setw(8) << std::setfill('0') << m; - std::string expected_str = ss.str(); - - ss.str(""); - ss << std::showbase << std::hex << std::setw(8) << std::setfill('0') << n; - std::string actual_str = ss.str(); - - return ::testing::AssertionFailure() - << m_expr << " and " << n_expr << " ( " << expected_str << " vs " << actual_str << " )"; -} - -class DeviceClassTest : public ::testing::Test {}; - -TEST_F(DeviceClassTest, cod_sizeof) { - uint8_t dc_stream[] = {0x00, 0x00, 0x00, 0x00}; - bt_device_class_t dc0; - device_class_from_stream(&dc0, dc_stream); - EXPECT_EQ((size_t)3, sizeof(dc0)); -} - -TEST_F(DeviceClassTest, simple) { - uint8_t dc_stream[][sizeof(bt_device_class_t)] = { - {0x00, 0x00, 0x00}, {0xff, 0xff, 0xff}, {0xaa, 0x55, 0xaa}, - {0x01, 0x23, 0x45}, {0x20, 0x07, 0x14}, - }; - - for (size_t i = 0; i < sizeof(dc_stream) / sizeof(bt_device_class_t); i++) { - bt_device_class_t dc; - device_class_from_stream(&dc, (uint8_t*)&dc_stream[i]); - - uint8_t* to_stream = (uint8_t*)&dc; - EXPECT_PRED_FORMAT2(check_bitfield, (unsigned)dc_stream[i][0], to_stream[0]); - EXPECT_PRED_FORMAT2(check_bitfield, (unsigned)dc_stream[i][1], to_stream[1]); - EXPECT_PRED_FORMAT2(check_bitfield, (unsigned)dc_stream[i][2], to_stream[2]); - } -} - -TEST_F(DeviceClassTest, to_stream) { - { - bt_device_class_t dc; - - uint8_t dc_stream0[] = {0x00, 0x00, 0x00, 0xaa}; - device_class_from_stream(&dc, dc_stream0); - - uint8_t dc_stream1[] = {0x00, 0x00, 0x00, 0x00}; - int rc = device_class_to_stream(&dc, dc_stream1, sizeof(dc_stream1)); - EXPECT_EQ(3, rc); - - uint32_t val = 0; - memcpy(&val, &dc, sizeof(dc)); - EXPECT_PRED_FORMAT2(check_bitfield, 0x00000000, val); - - EXPECT_PRED_FORMAT2(check_bitfield, 0x00, dc_stream1[0]); - EXPECT_PRED_FORMAT2(check_bitfield, 0x00, dc_stream1[1]); - EXPECT_PRED_FORMAT2(check_bitfield, 0x00, dc_stream1[2]); - } - - { - uint8_t dc_stream0[] = {0xaa, 0x55, 0xaa, 0x55}; - uint8_t dc_stream1[] = {0x00, 0x00, 0x00, 0x00}; - - bt_device_class_t dc; - device_class_from_stream(&dc, dc_stream0); - - int rc = device_class_to_stream(&dc, dc_stream1, sizeof(dc_stream1)); - EXPECT_EQ(3, rc); - uint32_t val = 0; - memcpy(&val, &dc, sizeof(dc)); - EXPECT_PRED_FORMAT2(check_bitfield, 0x00aa55aa, val); - - EXPECT_PRED_FORMAT2(check_bitfield, 0xaa, dc_stream1[0]); - EXPECT_PRED_FORMAT2(check_bitfield, 0x55, dc_stream1[1]); - EXPECT_PRED_FORMAT2(check_bitfield, 0xaa, dc_stream1[2]); - } - - { - uint8_t dc_stream0[] = {0x01, 0x23, 0x45, 0x67}; - uint8_t dc_stream1[] = {0x00, 0x00, 0x00, 0x00}; - - bt_device_class_t dc; - device_class_from_stream(&dc, dc_stream0); - - int rc = device_class_to_stream(&dc, dc_stream1, sizeof(dc_stream1)); - EXPECT_EQ(3, rc); - uint32_t val = 0; - memcpy(&val, &dc, sizeof(dc)); - EXPECT_PRED_FORMAT2(check_bitfield, 0x452301, val); - - EXPECT_PRED_FORMAT2(check_bitfield, 0x01, dc_stream1[0]); - EXPECT_PRED_FORMAT2(check_bitfield, 0x23, dc_stream1[1]); - EXPECT_PRED_FORMAT2(check_bitfield, 0x45, dc_stream1[2]); - } -} - -TEST_F(DeviceClassTest, limited_discoverable_mode) { - uint8_t dc_stream[] = {0x00, 0x00, 0x00}; - bt_device_class_t dc; - device_class_from_stream(&dc, dc_stream); - uint32_t test = 0; - memcpy(&test, &dc, sizeof(dc)); - - EXPECT_FALSE(device_class_get_limited(&dc)); - EXPECT_EQ((unsigned)0x00000000, test); - - device_class_set_limited(&dc, true); - test = 0; - memcpy(&test, &dc, sizeof(dc)); - EXPECT_TRUE(device_class_get_limited(&dc)); - EXPECT_EQ((unsigned)0x00002000, test); - - device_class_set_limited(&dc, false); - test = 0; - memcpy(&test, &dc, sizeof(dc)); - EXPECT_FALSE(device_class_get_limited(&dc)); - EXPECT_EQ((unsigned)0x00000000, test); - - device_class_set_limited(&dc, true); - test = 0; - memcpy(&test, &dc, sizeof(dc)); - EXPECT_PRED_FORMAT2(check_bitfield, 0x00002000, test); - - device_class_set_limited(&dc, false); - test = 0; - memcpy(&test, &dc, sizeof(dc)); - EXPECT_PRED_FORMAT2(check_bitfield, 0x00000000, test); -} - -TEST_F(DeviceClassTest, equals) { - uint8_t dc_stream0[] = {0x00, 0x01, 0x02}; - uint8_t dc_stream1[] = {0x00, 0x02, 0x03}; - - bt_device_class_t dc0; - device_class_from_stream(&dc0, dc_stream0); - bt_device_class_t dc1; - device_class_from_stream(&dc1, dc_stream1); - EXPECT_FALSE(device_class_equals(&dc0, &dc1)); -} - -TEST_F(DeviceClassTest, copy) { - uint8_t dc_stream0[] = {0xaa, 0x55, 0x33}; - bt_device_class_t dc0; - device_class_from_stream(&dc0, dc_stream0); - bt_device_class_t dc1; - EXPECT_TRUE(device_class_copy(&dc1, &dc0)); - EXPECT_TRUE(device_class_equals(&dc0, &dc1)); -} - -TEST_F(DeviceClassTest, from_int) { - bt_device_class_t dc1; - int cod1 = 0x5a020c; // 5898764 - device_class_from_int(&dc1, cod1); - - uint8_t dc_stream[] = {0x0c, 0x02, 0x5a}; - bt_device_class_t dc2; - device_class_from_stream(&dc2, dc_stream); - EXPECT_TRUE(device_class_equals(&dc1, &dc2)); -} - -TEST_F(DeviceClassTest, to_int) { - bt_device_class_t dc1 = {{0x0c, 0x02, 0x5a}}; - int cod1 = device_class_to_int(&dc1); - - EXPECT_EQ(dc1._[0], 0x0c); - EXPECT_EQ(dc1._[1], 0x02); - EXPECT_EQ(dc1._[2], 0x5a); - - bt_device_class_t dc2; - uint8_t dc_stream[] = {0x0c, 0x02, 0x5a}; - device_class_from_stream(&dc2, dc_stream); - - EXPECT_EQ(dc2._[0], 0x0c); - EXPECT_EQ(dc2._[1], 0x02); - EXPECT_EQ(dc2._[2], 0x5a); - - int cod2 = device_class_to_int(&dc2); - EXPECT_EQ(cod1, cod2); - EXPECT_EQ(cod1, 0x5a020c); // 5898764 -} - -TEST_F(DeviceClassTest, endian) { - bt_device_class_t dc; - int cod1 = 0x200714; // 2098964 - device_class_from_int(&dc, cod1); - - EXPECT_EQ(dc._[0], 0x14); - EXPECT_EQ(dc._[1], 0x07); - EXPECT_EQ(dc._[2], 0x20); - - int cod2 = device_class_to_int(&dc); - EXPECT_EQ(cod1, cod2); - EXPECT_EQ(cod2, 0x200714); // 2098964 -} diff --git a/system/btcore/test/property_test.cc b/system/btcore/test/property_test.cc deleted file mode 100644 index 7477bca1a9..0000000000 --- a/system/btcore/test/property_test.cc +++ /dev/null @@ -1,269 +0,0 @@ -/****************************************************************************** - * - * Copyright 2014 Google, Inc. - * - * 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 "btcore/include/property.h" - -#include <arpa/inet.h> -#include <gtest/gtest.h> - -#include "types/bluetooth/uuid.h" -#include "types/raw_address.h" - -using bluetooth::Uuid; - -class PropertyTest : public ::testing::Test {}; - -TEST_F(PropertyTest, addr) { - RawAddress addr0 = {{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}}; - bt_property_t* property = property_new_addr(&addr0); - - EXPECT_EQ(addr0.address[0], ((uint8_t*)property->val)[0]); - EXPECT_EQ(addr0.address[1], ((uint8_t*)property->val)[1]); - EXPECT_EQ(addr0.address[2], ((uint8_t*)property->val)[2]); - EXPECT_EQ(addr0.address[3], ((uint8_t*)property->val)[3]); - EXPECT_EQ(addr0.address[4], ((uint8_t*)property->val)[4]); - EXPECT_EQ(addr0.address[5], ((uint8_t*)property->val)[5]); - EXPECT_EQ(BT_PROPERTY_BDADDR, property->type); - EXPECT_EQ((int)sizeof(RawAddress), property->len); - - const RawAddress* addr1 = property_as_addr(property); - EXPECT_EQ(addr0.address[0], addr1->address[0]); - - property_free(property); -} - -TEST_F(PropertyTest, device_class) { - bt_device_class_t dc0 = {{0x01, 0x23, 0x45}}; - bt_property_t* property = property_new_device_class(&dc0); - - EXPECT_EQ(dc0._[0], ((uint8_t*)property->val)[0]); - EXPECT_EQ(dc0._[1], ((uint8_t*)property->val)[1]); - EXPECT_EQ(dc0._[2], ((uint8_t*)property->val)[2]); - EXPECT_EQ(BT_PROPERTY_CLASS_OF_DEVICE, property->type); - EXPECT_EQ((int)sizeof(bt_device_class_t), property->len); - - const bt_device_class_t* dc1 = property_as_device_class(property); - int dc_int = device_class_to_int(dc1); - EXPECT_EQ(0x452301, dc_int); - - property_free(property); -} - -TEST_F(PropertyTest, device_type) { - bt_device_type_t dt0 = (bt_device_type_t)1; - bt_property_t* property = property_new_device_type(dt0); - - EXPECT_EQ((int)dt0, *(int*)property->val); - EXPECT_EQ(BT_PROPERTY_TYPE_OF_DEVICE, property->type); - EXPECT_EQ((int)sizeof(bt_device_type_t), property->len); - - bt_device_type_t dt1 = property_as_device_type(property); - EXPECT_EQ(1, (int)dt1); - - property_free(property); -} - -TEST_F(PropertyTest, discovery_timeout) { - uint32_t timeout0 = 12345; - bt_property_t* property = property_new_discoverable_timeout(timeout0); - - EXPECT_EQ(timeout0, *(uint32_t*)property->val); - EXPECT_EQ(BT_PROPERTY_ADAPTER_DISCOVERABLE_TIMEOUT, property->type); - EXPECT_EQ((int)sizeof(uint32_t), property->len); - - uint32_t timeout1 = property_as_discoverable_timeout(property); - EXPECT_EQ(timeout0, timeout1); - - property_free(property); -} - -TEST_F(PropertyTest, name) { - const char* name0 = "My btcore name"; - bt_property_t* property = property_new_name(name0); - - EXPECT_EQ(0, strcmp((char*)name0, (char*)property->val)); - EXPECT_EQ(BT_PROPERTY_BDNAME, property->type); - EXPECT_EQ((int)sizeof(bt_bdname_t), property->len); - - const bt_bdname_t* name1 = property_as_name(property); - EXPECT_EQ(0, strcmp((char*)name0, (char*)name1->name)); - - property_free(property); -} - -TEST_F(PropertyTest, rssi) { - int8_t rssi0 = -56; - bt_property_t* property = property_new_rssi(rssi0); - - EXPECT_EQ(*(int8_t*)property->val, rssi0); - EXPECT_EQ(BT_PROPERTY_REMOTE_RSSI, property->type); - EXPECT_EQ((int)sizeof(int8_t), property->len); - - int8_t rss1 = property_as_rssi(property); - EXPECT_EQ(rssi0, rss1); - - property_free(property); -} - -TEST_F(PropertyTest, uuids) { - Uuid uuid0 = Uuid::From128BitBE({{ - 0x00, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - 0xaa, - 0xbb, - 0xcc, - 0xdd, - 0xee, - 0xff, - }}); - bt_property_t* property = property_new_uuids(&uuid0, 1); - - EXPECT_EQ(0, memcmp(uuid0.To128BitBE().data(), property->val, sizeof(Uuid))); - EXPECT_EQ(BT_PROPERTY_UUIDS, property->type); - EXPECT_EQ((int)sizeof(Uuid), property->len); - - size_t uuid_cnt1; - const Uuid* uuid1 = property_as_uuids(property, &uuid_cnt1); - EXPECT_EQ(uuid0, *uuid1); - - property_free(property); -} - -TEST_F(PropertyTest, copy) { - { - Uuid uuids[] = { - Uuid::From128BitBE({{ - 0x00, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x99, - 0xaa, - 0xbb, - 0xcc, - 0xdd, - 0xee, - 0xff, - }}), - Uuid::From128BitBE({{ - 0xf0, - 0xe1, - 0xd2, - 0xc3, - 0xf4, - 0xe5, - 0xd6, - 0xc7, - 0xf8, - 0xe9, - 0xda, - 0xcb, - 0xfc, - 0xed, - 0xde, - 0xcf, - }}), - }; - - bt_property_t* property0 = property_new_uuids(uuids, sizeof(uuids) / sizeof(Uuid)); - - bt_property_t property1; - property_copy(&property1, property0); - EXPECT_TRUE(property_equals(property0, &property1)); - - property_free(property0); - } -} - -TEST_F(PropertyTest, equals) { - { - RawAddress addr0 = {{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}}; - bt_property_t* property0 = property_new_addr(&addr0); - - bt_device_class_t dc0 = {{0x01, 0x23, 0x45}}; - bt_property_t* property1 = property_new_device_class(&dc0); - - EXPECT_FALSE(property_equals(property0, property1)); - - property_free(property0); - property_free(property1); - } - - { - RawAddress addr = {{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}}; - bt_property_t* property0 = property_new_addr(&addr); - bt_property_t* property1 = property_new_addr(&addr); - - EXPECT_TRUE(property_equals(property0, property1)); - - property_free(property0); - property_free(property1); - } - - { - RawAddress addr0 = {{0x1, 0x2, 0x3, 0x4, 0x5, 0x6}}; - bt_property_t* property0 = property_new_addr(&addr0); - - RawAddress addr1 = {{0x1, 0x2, 0x3, 0x4, 0x5, 0xff}}; - bt_property_t* property1 = property_new_addr(&addr1); - - EXPECT_FALSE(property_equals(property0, property1)); - - property_free(property0); - property_free(property1); - } - - { - const char* name0 = "My btcore name"; - bt_property_t* property0 = property_new_name(name0); - - const char* name1 = "My btcore name"; - bt_property_t* property1 = property_new_name(name1); - - EXPECT_TRUE(property_equals(property0, property1)); - - property_free(property0); - property_free(property1); - } - - { - const char* name0 = "My btcore name"; - bt_property_t* property0 = property_new_name(name0); - - const char* name1 = "My btcore name "; - bt_property_t* property1 = property_new_name(name1); - - EXPECT_FALSE(property_equals(property0, property1)); - - property_free(property0); - property_free(property1); - } -} diff --git a/system/btif/src/OWNERS b/system/btif/src/OWNERS deleted file mode 100644 index e495e167d4..0000000000 --- a/system/btif/src/OWNERS +++ /dev/null @@ -1 +0,0 @@ -per-file btif_hearing_aid.cc=file:/OWNERS_hearingaid diff --git a/system/btif/src/btif_a2dp_source.cc b/system/btif/src/btif_a2dp_source.cc index 20805a92bd..5bf2ef0d87 100644 --- a/system/btif/src/btif_a2dp_source.cc +++ b/system/btif/src/btif_a2dp_source.cc @@ -48,7 +48,6 @@ #include "btif_hf.h" #include "btm_iso_api.h" #include "common/message_loop_thread.h" -#include "common/metrics.h" #include "common/repeating_timer.h" #include "common/time_util.h" #include "hardware/bt_av.h" @@ -1302,8 +1301,7 @@ static void btm_read_rssi_cb(void* data) { return; } - bluetooth::shim::LogMetricReadRssiResult(result->rem_bda, - bluetooth::common::kUnknownConnectionHandle, + bluetooth::shim::LogMetricReadRssiResult(result->rem_bda, bluetooth::os::kUnknownConnectionHandle, result->hci_status, result->rssi); log::warn("device: {}, rssi: {}", result->rem_bda, result->rssi); @@ -1321,7 +1319,7 @@ static void btm_read_failed_contact_counter_cb(void* data) { return; } bluetooth::shim::LogMetricReadFailedContactCounterResult( - result->rem_bda, bluetooth::common::kUnknownConnectionHandle, result->hci_status, + result->rem_bda, bluetooth::os::kUnknownConnectionHandle, result->hci_status, result->failed_contact_counter); log::warn("device: {}, Failed Contact Counter: {}", result->rem_bda, @@ -1340,7 +1338,7 @@ static void btm_read_tx_power_cb(void* data) { return; } bluetooth::shim::LogMetricReadTxPowerLevelResult(result->rem_bda, - bluetooth::common::kUnknownConnectionHandle, + bluetooth::os::kUnknownConnectionHandle, result->hci_status, result->tx_power); log::warn("device: {}, Tx Power: {}", result->rem_bda, result->tx_power); diff --git a/system/btif/src/btif_dm.cc b/system/btif/src/btif_dm.cc index 44b876af4e..e10606b9dd 100644 --- a/system/btif/src/btif_dm.cc +++ b/system/btif/src/btif_dm.cc @@ -1486,7 +1486,7 @@ static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH* auto triple = eir_uuids_cache.try_emplace(bdaddr, std::set<Uuid>{}); uuid_iter = std::get<0>(triple); } - log::info("EIR UUIDs for {}:", bdaddr); + log::info("EIR UUIDs for {}", bdaddr); for (int i = 0; i < num_uuids; ++i) { Uuid uuid = Uuid::From16Bit(p_uuid16[i]); log::info("{}", uuid.ToString()); @@ -1609,7 +1609,7 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, if (results_for_bonding_device) { // success for SDP bluetooth::metrics::LogSDPComplete(bd_addr, tBTA_STATUS::BTA_SUCCESS); - log::info("SDP finished for {}:", bd_addr); + log::info("SDP finished for {}", bd_addr); pairing_cb.sdp_over_classic = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED; } @@ -1620,7 +1620,7 @@ static void btif_on_service_discovery_results(RawAddress bd_addr, bt_property_t& le_prop = uuid_props[1]; if ((result == BTA_SUCCESS) && !uuids_param.empty()) { - log::info("New UUIDs for {}:", bd_addr); + log::info("New UUIDs for {}", bd_addr); for (const auto& uuid : uuids_param) { if (btif_should_ignore_uuid(uuid)) { continue; @@ -1758,7 +1758,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid bool lea_supported = is_le_audio_capable_during_service_discovery(bd_addr); if (is_transport_le) { - log::info("New GATT over LE UUIDs for {}:", bd_addr); + log::info("New GATT over LE UUIDs for {}", bd_addr); BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using LE transport"); if (btif_is_gatt_service_discovery_post_pairing(bd_addr)) { pairing_cb.gatt_over_le = btif_dm_pairing_cb_t::ServiceDiscoveryState::FINISHED; @@ -1786,7 +1786,7 @@ static void btif_on_gatt_results(RawAddress bd_addr, std::vector<bluetooth::Uuid } } } else { - log::debug("New GATT over SDP UUIDs for {}:", bd_addr); + log::debug("New GATT over SDP UUIDs for {}", bd_addr); BTM_LogHistory(kBtmLogTag, bd_addr, "Discovered GATT services using SDP transport"); } diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc index 36ad3d148e..28ed77113d 100644 --- a/system/btif/src/btif_hh.cc +++ b/system/btif/src/btif_hh.cc @@ -749,9 +749,7 @@ static void hh_get_rpt_handler(tBTA_HH_HSDATA& hs_data) { HAL_CBACK(bt_hh_callbacks, handshake_cb, (RawAddress*)&(p_dev->link_spec.addrt.bda), p_dev->link_spec.addrt.type, p_dev->link_spec.transport, (bthh_status_t)hs_data.status); - if (com::android::bluetooth::flags::forward_get_set_report_failure_to_uhid()) { - bta_hh_co_get_rpt_rsp(p_dev->dev_handle, (tBTA_HH_STATUS)hs_data.status, NULL, 0); - } + bta_hh_co_get_rpt_rsp(p_dev->dev_handle, (tBTA_HH_STATUS)hs_data.status, NULL, 0); } } diff --git a/system/common/Android.bp b/system/common/Android.bp index c192613dbc..a22a733e75 100644 --- a/system/common/Android.bp +++ b/system/common/Android.bp @@ -32,17 +32,6 @@ cc_library_static { canonical_path_from_root: false, export_proto_headers: true, }, - target: { - android: { - srcs: [ - "metrics.cc", - ], - static_libs: ["libstatslog_bt"], - }, - host: { - srcs: ["metrics_linux.cc"], - }, - }, shared_libs: [ "libcrypto", "libcutils", diff --git a/system/common/BUILD.gn b/system/common/BUILD.gn index 50ec9c1dd0..5b0852bd36 100644 --- a/system/common/BUILD.gn +++ b/system/common/BUILD.gn @@ -19,8 +19,6 @@ static_library("common") { "address_obfuscator.cc", "le_conn_params.cc", "message_loop_thread.cc", - "metric_id_allocator.cc", - "metrics_linux.cc", "os_utils.cc", "repeating_timer.cc", "stop_watch_legacy.cc", diff --git a/system/common/metrics.cc b/system/common/metrics.cc deleted file mode 100644 index c6c0977115..0000000000 --- a/system/common/metrics.cc +++ /dev/null @@ -1,94 +0,0 @@ -/****************************************************************************** - * - * Copyright 2016 Google, Inc. - * - * 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 "common/metrics.h" - -#include <bluetooth/log.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/le/enums.pb.h> -#include <statslog_bt.h> - -#include <cstdint> -#include <vector> - -#include "main/shim/metric_id_api.h" -#include "types/raw_address.h" - -namespace bluetooth { -namespace common { - -void LogRemoteVersionInfo(uint16_t handle, uint8_t status, uint8_t version, - uint16_t manufacturer_name, uint16_t subversion) { - int ret = stats_write(BLUETOOTH_REMOTE_VERSION_INFO_REPORTED, handle, status, version, - manufacturer_name, subversion); - if (ret < 0) { - log::warn( - "failed for handle {}, status 0x{:x}, version 0x{:x}, " - "manufacturer_name 0x{:x}, subversion 0x{:x}, error {}", - handle, status, version, manufacturer_name, subversion, ret); - } -} - -void LogLeAudioConnectionSessionReported( - int32_t group_size, int32_t group_metric_id, int64_t connection_duration_nanos, - const std::vector<int64_t>& device_connecting_offset_nanos, - const std::vector<int64_t>& device_connected_offset_nanos, - const std::vector<int64_t>& device_connection_duration_nanos, - const std::vector<int32_t>& device_connection_status, - const std::vector<int32_t>& device_disconnection_status, - const std::vector<RawAddress>& device_address, - const std::vector<int64_t>& streaming_offset_nanos, - const std::vector<int64_t>& streaming_duration_nanos, - const std::vector<int32_t>& streaming_context_type) { - std::vector<int32_t> device_metric_id(device_address.size()); - for (uint64_t i = 0; i < device_address.size(); i++) { - if (!device_address[i].IsEmpty()) { - device_metric_id[i] = bluetooth::shim::AllocateIdFromMetricIdAllocator(device_address[i]); - } else { - device_metric_id[i] = 0; - } - } - int ret = stats_write(LE_AUDIO_CONNECTION_SESSION_REPORTED, group_size, group_metric_id, - connection_duration_nanos, device_connecting_offset_nanos, - device_connected_offset_nanos, device_connection_duration_nanos, - device_connection_status, device_disconnection_status, device_metric_id, - streaming_offset_nanos, streaming_duration_nanos, streaming_context_type); - if (ret < 0) { - log::warn( - "failed for group {}device_connecting_offset_nanos[{}], " - "device_connected_offset_nanos[{}], " - "device_connection_duration_nanos[{}], device_connection_status[{}], " - "device_disconnection_status[{}], device_metric_id[{}], " - "streaming_offset_nanos[{}], streaming_duration_nanos[{}], " - "streaming_context_type[{}]", - group_metric_id, device_connecting_offset_nanos.size(), - device_connected_offset_nanos.size(), device_connection_duration_nanos.size(), - device_connection_status.size(), device_disconnection_status.size(), - device_metric_id.size(), streaming_offset_nanos.size(), streaming_duration_nanos.size(), - streaming_context_type.size()); - } -} - -void LogLeAudioBroadcastSessionReported(int64_t duration_nanos) { - int ret = stats_write(LE_AUDIO_BROADCAST_SESSION_REPORTED, duration_nanos); - if (ret < 0) { - log::warn("failed for duration={}", duration_nanos); - } -} - -} // namespace common -} // namespace bluetooth diff --git a/system/common/metrics.h b/system/common/metrics.h deleted file mode 100644 index 4d5a234ce5..0000000000 --- a/system/common/metrics.h +++ /dev/null @@ -1,66 +0,0 @@ -/****************************************************************************** - * - * Copyright 2016 Google, Inc. - * - * 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 <cstdint> -#include <vector> - -#include "types/raw_address.h" - -namespace bluetooth { -namespace common { - -/** - * Unknown connection handle for metrics purpose - */ -static const uint32_t kUnknownConnectionHandle = 0xFFFF; - -/** - * Logs when we receive Bluetooth Read Remote Version Information Complete - * Event from the remote device, as documented by the Bluetooth Core HCI - * specification - * - * Reference: 5.0 Core Specification, Vol 2, Part E, Page 1118 - * - * @param handle handle of associated ACL connection - * @param status HCI command status of this event - * @param version version code from read remote version complete event - * @param manufacturer_name manufacturer code from read remote version complete - * event - * @param subversion subversion code from read remote version complete event - */ -void LogRemoteVersionInfo(uint16_t handle, uint8_t status, uint8_t version, - uint16_t manufacturer_name, uint16_t subversion); - -void LogLeAudioConnectionSessionReported( - int32_t group_size, int32_t group_metric_id, int64_t connection_duration_nanos, - const std::vector<int64_t>& device_connecting_offset_nanos, - const std::vector<int64_t>& device_connected_offset_nanos, - const std::vector<int64_t>& device_connection_duration_nanos, - const std::vector<int32_t>& device_connection_status, - const std::vector<int32_t>& device_disconnection_status, - const std::vector<RawAddress>& device_address, - const std::vector<int64_t>& streaming_offset_nanos, - const std::vector<int64_t>& streaming_duration_nanos, - const std::vector<int32_t>& streaming_context_type); - -void LogLeAudioBroadcastSessionReported(int64_t duration_nanos); - -} // namespace common -} // namespace bluetooth diff --git a/system/common/metrics_linux.cc b/system/common/metrics_linux.cc deleted file mode 100644 index e886c05bf3..0000000000 --- a/system/common/metrics_linux.cc +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - * - * Copyright 2018 Google, Inc. - * - * 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 <cstdint> -#include <vector> - -#include "common/metrics.h" -#include "types/raw_address.h" - -namespace bluetooth { -namespace common { - -void LogRemoteVersionInfo(uint16_t /* handle */, uint8_t /* status */, uint8_t /* version */, - uint16_t /* manufacturer_name */, uint16_t /* subversion */) {} - -void LogLeAudioConnectionSessionReported( - int32_t /* group_size */, int32_t /* group_metric_id */, - int64_t /* connection_duration_nanos */, - const std::vector<int64_t>& /* device_connecting_offset_nanos */, - const std::vector<int64_t>& /* device_connected_offset_nanos */, - const std::vector<int64_t>& /* device_connection_duration_nanos */, - const std::vector<int32_t>& /* device_connection_status */, - const std::vector<int32_t>& /* device_disconnection_status */, - const std::vector<RawAddress>& /* device_address */, - const std::vector<int64_t>& /* streaming_offset_nanos */, - const std::vector<int64_t>& /* streaming_duration_nanos */, - const std::vector<int32_t>& /* streaming_context_type */) {} - -void LogLeAudioBroadcastSessionReported(int64_t /* duration_nanos */) {} - -} // namespace common -} // namespace bluetooth diff --git a/system/gd/Android.bp b/system/gd/Android.bp index 9a6bd02551..45555f130a 100644 --- a/system/gd/Android.bp +++ b/system/gd/Android.bp @@ -194,7 +194,7 @@ cc_library_static { "packages/modules/Bluetooth/system", ], apex_available: ["com.android.bt"], - min_sdk_version: "31", + min_sdk_version: "33", static_libs: [ "bluetooth_flags_c_lib", "libchrome", diff --git a/system/gd/common/audit_log.cc b/system/gd/common/audit_log.cc index e83c2894f8..2e112a0c34 100644 --- a/system/gd/common/audit_log.cc +++ b/system/gd/common/audit_log.cc @@ -16,6 +16,8 @@ #include "common/audit_log.h" +#include <format> + #ifdef __ANDROID__ #include <log/log_event_list.h> #endif // __ANDROID__ @@ -43,8 +45,8 @@ void LogConnectionAdminAuditEvent([[maybe_unused]] const char* action, android_log_event_list(SEC_TAG_BLUETOOTH_CONNECTION) << address.ToRedactedStringForLogging() - << /* success */ int32_t(status == hci::ErrorCode::SUCCESS) << action << ": " - << ErrorCodeText(status) << LOG_ID_SECURITY; + << /* success */ int32_t(status == hci::ErrorCode::SUCCESS) + << std::format("{}: {}", action, ErrorCodeText(status)) << LOG_ID_SECURITY; #endif /* defined(__ANDROID__) && !defined (FUZZ_TARGET) */ } diff --git a/system/gd/hal/hci_hal_android_test.cc b/system/gd/hal/hci_hal_android_test.cc index 247f15745f..f128f508cf 100644 --- a/system/gd/hal/hci_hal_android_test.cc +++ b/system/gd/hal/hci_hal_android_test.cc @@ -20,6 +20,7 @@ #include <queue> #include <thread> +#include "com_android_bluetooth_flags.h" #include "hal/hci_backend.h" #include "hal/hci_hal.h" #include "os/thread.h" @@ -79,10 +80,13 @@ protected: } void TearDown() override { - fake_registry_.StopAll(); handler_->Clear(); - delete thread_; + if (com::android::bluetooth::flags::same_handler_for_all_modules()) { + handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout); + } + fake_registry_.StopAll(); delete handler_; + delete thread_; } HciHal* hal; diff --git a/system/gd/hal/hci_hal_host_test.cc b/system/gd/hal/hci_hal_host_test.cc index 4f4c6bc6d0..d4539d6c3b 100644 --- a/system/gd/hal/hci_hal_host_test.cc +++ b/system/gd/hal/hci_hal_host_test.cc @@ -32,6 +32,7 @@ #include <utility> #include <vector> +#include "com_android_bluetooth_flags.h" #include "hal/hci_hal.h" #include "hal/serialize_packet.h" #include "os/thread.h" @@ -155,12 +156,15 @@ protected: void TearDown() override { hal_->unregisterIncomingPacketCallback(); + handler_->Clear(); + if (com::android::bluetooth::flags::same_handler_for_all_modules()) { + handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout); + } fake_registry_.StopAll(); + delete handler_; close(fake_server_socket_); - handler_->Clear(); delete fake_server_; delete thread_; - delete handler_; } void SetFakeServerSocketToBlocking() { diff --git a/system/gd/hal/snoop_logger.h b/system/gd/hal/snoop_logger.h index b009add89c..b0783442b3 100644 --- a/system/gd/hal/snoop_logger.h +++ b/system/gd/hal/snoop_logger.h @@ -17,6 +17,7 @@ #pragma once #include <bluetooth/log.h> +#include <com_android_bluetooth_flags.h> #include <fstream> #include <string> @@ -195,6 +196,13 @@ public: }; SnoopLogger(os::Handler* handler); + ~SnoopLogger() { + if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { + GetHandler()->Clear(); + GetHandler()->WaitUntilStopped(std::chrono::milliseconds(2000)); + delete GetHandler(); + } + } // Returns the maximum number of packets per file // Changes to this value is only effective after restarting Bluetooth diff --git a/system/gd/hci/acl_manager.cc b/system/gd/hci/acl_manager.cc index 26b0ce70eb..280cc237b4 100644 --- a/system/gd/hci/acl_manager.cc +++ b/system/gd/hci/acl_manager.cc @@ -260,16 +260,6 @@ void AclManager::CreateLeConnection(AddressWithType address_with_type, bool is_d CallOn(pimpl_->le_impl_, &le_impl::create_le_connection, address_with_type, true, is_direct); } -void AclManager::SetLeSuggestedDefaultDataParameters(uint16_t octets, uint16_t time) { - CallOn(pimpl_->le_impl_, &le_impl::set_le_suggested_default_data_parameters, octets, time); -} - -void AclManager::LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, - uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) { - CallOn(pimpl_->le_impl_, &le_impl::LeSetDefaultSubrate, subrate_min, subrate_max, max_latency, - cont_num, sup_tout); -} - void AclManager::SetPrivacyPolicyForInitiatorAddress( LeAddressManager::AddressPolicy address_policy, AddressWithType fixed_address, std::chrono::milliseconds minimum_rotation_time, diff --git a/system/gd/hci/acl_manager.h b/system/gd/hci/acl_manager.h index 2b3d523b1f..f38f1ff689 100644 --- a/system/gd/hci/acl_manager.h +++ b/system/gd/hci/acl_manager.h @@ -86,12 +86,6 @@ public: // Generates OnLeConnectSuccess if connected, or OnLeConnectFail otherwise virtual void CreateLeConnection(AddressWithType address_with_type, bool is_direct); - // Ask the controller for specific data parameters - virtual void SetLeSuggestedDefaultDataParameters(uint16_t octets, uint16_t time); - - virtual void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout); - virtual void SetPrivacyPolicyForInitiatorAddress(LeAddressManager::AddressPolicy address_policy, AddressWithType fixed_address, std::chrono::milliseconds minimum_rotation_time, diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h index ce93f46e7e..122f76525e 100644 --- a/system/gd/hci/acl_manager/le_impl.h +++ b/system/gd/hci/acl_manager/le_impl.h @@ -1063,15 +1063,12 @@ public: add_device_to_accept_list(address_with_type); } - if (com::android::bluetooth::flags:: - improve_create_connection_for_already_connecting_device()) { - bool in_accept_list_due_to_direct_connect = - direct_connections_.find(address_with_type) != direct_connections_.end(); - - if (already_in_accept_list && (in_accept_list_due_to_direct_connect || !is_direct)) { - log::info("Device {} already in accept list. Stop here.", address_with_type); - return; - } + bool in_accept_list_due_to_direct_connect = + direct_connections_.find(address_with_type) != direct_connections_.end(); + + if (already_in_accept_list && (in_accept_list_due_to_direct_connect || !is_direct)) { + log::info("Device {} already in accept list. Stop here.", address_with_type); + return; } if (is_direct) { @@ -1149,25 +1146,6 @@ public: remove_device_from_accept_list(address_with_type); } - void set_le_suggested_default_data_parameters(uint16_t length, uint16_t time) { - auto packet = LeWriteSuggestedDefaultDataLengthBuilder::Create(length, time); - le_acl_connection_interface_->EnqueueCommand( - std::move(packet), handler_->BindOnce([](CommandCompleteView /* complete */) {})); - } - - void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout) { - le_acl_connection_interface_->EnqueueCommand( - LeSetDefaultSubrateBuilder::Create(subrate_min, subrate_max, max_latency, cont_num, - sup_tout), - handler_->BindOnce([](CommandCompleteView complete) { - auto complete_view = LeSetDefaultSubrateCompleteView::Create(complete); - log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); - ErrorCode status = complete_view.GetStatus(); - log::assert_that(status == ErrorCode::SUCCESS, "Status = {}", ErrorCodeText(status)); - })); - } - void clear_resolving_list() { le_address_manager_->ClearResolvingList(); } void set_privacy_policy_for_initiator_address(LeAddressManager::AddressPolicy address_policy, diff --git a/system/gd/hci/acl_manager/le_impl_test.cc b/system/gd/hci/acl_manager/le_impl_test.cc index 66bc16bbd8..f54d316d30 100644 --- a/system/gd/hci/acl_manager/le_impl_test.cc +++ b/system/gd/hci/acl_manager/le_impl_test.cc @@ -284,164 +284,6 @@ protected: hci_layer_->IncomingEvent(LeSetRandomAddressCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); } - void test_direct_connection_after_background_connection() { - set_random_device_address_policy(); - - hci::AddressWithType address({0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, - AddressType::PUBLIC_DEVICE_ADDRESS); - - // arrange: Create background connection. Remember that acl_manager adds device background list - le_impl_->add_device_to_background_connection_list(address); - le_impl_->create_le_connection(address, true, /* is_direct */ false); - hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST); - hci_layer_->IncomingEvent( - LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - auto raw_bg_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); - hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); - sync_handler(); - - // act: Create direct connection - le_impl_->create_le_connection(address, true, /* is_direct */ true); - auto cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); - if (cancel_connection.IsValid()) { - hci_layer_->IncomingEvent( - LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( - ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, - AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, - ClockAccuracy::PPM_30)); - } - auto raw_direct_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); - - // assert - auto bg_create_connection = - LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( - AclCommandView::Create(raw_bg_create_connection))); - EXPECT_TRUE(bg_create_connection.IsValid()); - auto direct_create_connection = - LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( - AclCommandView::Create(raw_direct_create_connection))); - EXPECT_TRUE(direct_create_connection.IsValid()); - log::info("Scan Interval {}", direct_create_connection.GetLeScanInterval()); - ASSERT_NE(direct_create_connection.GetLeScanInterval(), - bg_create_connection.GetLeScanInterval()); - - hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); - sync_handler(); - - // Check state is ARMED - ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); - - // Simulate timeout on direct connect. Verify background connect is still in place - EXPECT_CALL(mock_le_connection_callbacks_, - OnLeConnectFail(_, ErrorCode::CONNECTION_ACCEPT_TIMEOUT)) - .Times(1); - le_impl_->on_create_connection_timeout(address); - sync_handler(); - cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); - hci_layer_->IncomingEvent( - LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( - ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, - AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, - ClockAccuracy::PPM_30)); - EXPECT_TRUE(cancel_connection.IsValid()); - raw_bg_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); - bg_create_connection = LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( - AclCommandView::Create(raw_bg_create_connection))); - EXPECT_TRUE(bg_create_connection.IsValid()); - sync_handler(); - ASSERT_TRUE(le_impl_->create_connection_timeout_alarms_.empty()); - - hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); - sync_handler(); - - // Check state is ARMED - ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); - } - - void test_direct_connect_after_direct_connect() { - set_random_device_address_policy(); - - hci::AddressWithType address({0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, - AddressType::PUBLIC_DEVICE_ADDRESS); - - // Create first direct connection - le_impl_->create_le_connection(address, true, /* is_direct */ true); - hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST); - hci_layer_->IncomingEvent( - LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - auto raw_direct_1_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); - hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); - sync_handler(); - - // Check state is ARMED - ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); - - // assert - auto direct_1_create_connection = - LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( - AclCommandView::Create(raw_direct_1_create_connection))); - EXPECT_TRUE(direct_1_create_connection.IsValid()); - - log::info("Second direct connect to the same device"); - - // Create second direct connection - le_impl_->create_le_connection(address, true, /* is_direct */ true); - sync_handler(); - - CommandView cancel_connection = CommandView::Create( - PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>())); - - if (!com::android::bluetooth::flags:: - improve_create_connection_for_already_connecting_device()) { - cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); - if (cancel_connection.IsValid()) { - hci_layer_->IncomingEvent( - LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( - ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, - AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, - ClockAccuracy::PPM_30)); - } - - auto raw_direct_2_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); - - auto direct_2_create_connection = - LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( - AclCommandView::Create(raw_direct_2_create_connection))); - EXPECT_TRUE(direct_2_create_connection.IsValid()); - hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); - sync_handler(); - } else { - hci_layer_->AssertNoQueuedCommand(); - } - - log::info("Simulate timeout"); - - EXPECT_CALL(mock_le_connection_callbacks_, - OnLeConnectFail(_, ErrorCode::CONNECTION_ACCEPT_TIMEOUT)) - .Times(1); - le_impl_->on_create_connection_timeout(address); - sync_handler(); - cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); - EXPECT_TRUE(cancel_connection.IsValid()); - hci_layer_->IncomingEvent( - LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( - ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, - AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, - ClockAccuracy::PPM_30)); - sync_handler(); - ASSERT_TRUE(le_impl_->create_connection_timeout_alarms_.empty()); - - hci_layer_->GetCommand(OpCode::LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST); - hci_layer_->IncomingEvent( - LeRemoveDeviceFromFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); - hci_layer_->AssertNoQueuedCommand(); - ASSERT_EQ(ConnectabilityState::DISARMED, le_impl_->connectability_state_); - } - // Need to store the LeAclConnection so it is not immediately dropped => disconnected std::unique_ptr<LeAclConnection> create_enhanced_connection(std::string remote_address_string, int handle) { @@ -1475,29 +1317,6 @@ TEST_F(LeImplTest, cancel_connect) { ASSERT_TRUE(le_impl_->create_connection_timeout_alarms_.empty()); } -TEST_F(LeImplTest, set_le_suggested_default_data_parameters) { - le_impl_->set_le_suggested_default_data_parameters(kLength, kTime); - sync_handler(); - auto view = CreateLeConnectionManagementCommandView<LeWriteSuggestedDefaultDataLengthView>( - hci_layer_->GetCommand()); - ASSERT_TRUE(view.IsValid()); - ASSERT_EQ(kLength, view.GetTxOctets()); - ASSERT_EQ(kTime, view.GetTxTime()); -} - -TEST_F(LeImplTest, LeSetDefaultSubrate) { - le_impl_->LeSetDefaultSubrate(kIntervalMin, kIntervalMax, kLatency, kContinuationNumber, - kTimeout); - sync_handler(); - auto view = CreateAclCommandView<LeSetDefaultSubrateView>(hci_layer_->GetCommand()); - ASSERT_TRUE(view.IsValid()); - ASSERT_EQ(kIntervalMin, view.GetSubrateMin()); - ASSERT_EQ(kIntervalMax, view.GetSubrateMax()); - ASSERT_EQ(kLatency, view.GetMaxLatency()); - ASSERT_EQ(kContinuationNumber, view.GetContinuationNumber()); - ASSERT_EQ(kTimeout, view.GetSupervisionTimeout()); -} - enum class ConnectionCompleteType { CONNECTION_COMPLETE, ENHANCED_CONNECTION_COMPLETE }; class LeImplTestParameterizedByConnectionCompleteEventType @@ -1716,29 +1535,138 @@ TEST_F(LeImplTest, DisconnectionAcceptlistCallback) { } TEST_F(LeImplTest, direct_connection_after_background_connection) { - // TODO b/356593752 - remove when test removing flag - com::android::bluetooth::flags::provider_ - ->improve_create_connection_for_already_connecting_device(false); - test_direct_connection_after_background_connection(); -} + set_random_device_address_policy(); + + hci::AddressWithType address({0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, + AddressType::PUBLIC_DEVICE_ADDRESS); + + // arrange: Create background connection. Remember that acl_manager adds device background list + le_impl_->add_device_to_background_connection_list(address); + le_impl_->create_le_connection(address, true, /* is_direct */ false); + hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST); + hci_layer_->IncomingEvent( + LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + auto raw_bg_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); + hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); + sync_handler(); + + // act: Create direct connection + le_impl_->create_le_connection(address, true, /* is_direct */ true); + auto cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); + if (cancel_connection.IsValid()) { + hci_layer_->IncomingEvent( + LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( + ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, + AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, + ClockAccuracy::PPM_30)); + } + auto raw_direct_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); + + // assert + auto bg_create_connection = + LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( + AclCommandView::Create(raw_bg_create_connection))); + EXPECT_TRUE(bg_create_connection.IsValid()); + auto direct_create_connection = + LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( + AclCommandView::Create(raw_direct_create_connection))); + EXPECT_TRUE(direct_create_connection.IsValid()); + log::info("Scan Interval {}", direct_create_connection.GetLeScanInterval()); + ASSERT_NE(direct_create_connection.GetLeScanInterval(), bg_create_connection.GetLeScanInterval()); -TEST_F(LeImplTest, direct_connection_after_background_connection_with_improvement) { - com::android::bluetooth::flags::provider_ - ->improve_create_connection_for_already_connecting_device(true); - test_direct_connection_after_background_connection(); + hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); + sync_handler(); + + // Check state is ARMED + ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); + + // Simulate timeout on direct connect. Verify background connect is still in place + EXPECT_CALL(mock_le_connection_callbacks_, + OnLeConnectFail(_, ErrorCode::CONNECTION_ACCEPT_TIMEOUT)) + .Times(1); + le_impl_->on_create_connection_timeout(address); + sync_handler(); + cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); + hci_layer_->IncomingEvent( + LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( + ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, + AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, + ClockAccuracy::PPM_30)); + EXPECT_TRUE(cancel_connection.IsValid()); + raw_bg_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); + bg_create_connection = LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( + AclCommandView::Create(raw_bg_create_connection))); + EXPECT_TRUE(bg_create_connection.IsValid()); + sync_handler(); + ASSERT_TRUE(le_impl_->create_connection_timeout_alarms_.empty()); + + hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); + sync_handler(); + + // Check state is ARMED + ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); } TEST_F(LeImplTest, direct_connection_after_direct_connection) { - // TODO b/356593752 - remove when test removing flag - com::android::bluetooth::flags::provider_ - ->improve_create_connection_for_already_connecting_device(false); - test_direct_connect_after_direct_connect(); -} + set_random_device_address_policy(); -TEST_F(LeImplTest, direct_connection_after_direct_connection_with_improvement) { - com::android::bluetooth::flags::provider_ - ->improve_create_connection_for_already_connecting_device(true); - test_direct_connect_after_direct_connect(); + hci::AddressWithType address({0x21, 0x22, 0x23, 0x24, 0x25, 0x26}, + AddressType::PUBLIC_DEVICE_ADDRESS); + + // Create first direct connection + le_impl_->create_le_connection(address, true, /* is_direct */ true); + hci_layer_->GetCommand(OpCode::LE_ADD_DEVICE_TO_FILTER_ACCEPT_LIST); + hci_layer_->IncomingEvent( + LeAddDeviceToFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + auto raw_direct_1_create_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION); + hci_layer_->IncomingEvent(LeCreateConnectionStatusBuilder::Create(ErrorCode::SUCCESS, 0x01)); + sync_handler(); + + // Check state is ARMED + ASSERT_EQ(ConnectabilityState::ARMED, le_impl_->connectability_state_); + + // assert + auto direct_1_create_connection = + LeCreateConnectionView::Create(LeConnectionManagementCommandView::Create( + AclCommandView::Create(raw_direct_1_create_connection))); + EXPECT_TRUE(direct_1_create_connection.IsValid()); + + log::info("Second direct connect to the same device"); + + // Create second direct connection + le_impl_->create_le_connection(address, true, /* is_direct */ true); + sync_handler(); + + CommandView cancel_connection = CommandView::Create( + PacketView<packet::kLittleEndian>(std::make_shared<std::vector<uint8_t>>())); + + hci_layer_->AssertNoQueuedCommand(); + + log::info("Simulate timeout"); + + EXPECT_CALL(mock_le_connection_callbacks_, + OnLeConnectFail(_, ErrorCode::CONNECTION_ACCEPT_TIMEOUT)) + .Times(1); + le_impl_->on_create_connection_timeout(address); + sync_handler(); + cancel_connection = hci_layer_->GetCommand(OpCode::LE_CREATE_CONNECTION_CANCEL); + EXPECT_TRUE(cancel_connection.IsValid()); + hci_layer_->IncomingEvent( + LeCreateConnectionCancelCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + hci_layer_->IncomingLeMetaEvent(LeConnectionCompleteBuilder::Create( + ErrorCode::UNKNOWN_CONNECTION, kHciHandle, Role::CENTRAL, + AddressType::PUBLIC_DEVICE_ADDRESS, Address::kEmpty, 0x0000, 0x0000, 0x0000, + ClockAccuracy::PPM_30)); + sync_handler(); + ASSERT_TRUE(le_impl_->create_connection_timeout_alarms_.empty()); + + hci_layer_->GetCommand(OpCode::LE_REMOVE_DEVICE_FROM_FILTER_ACCEPT_LIST); + hci_layer_->IncomingEvent( + LeRemoveDeviceFromFilterAcceptListCompleteBuilder::Create(0x01, ErrorCode::SUCCESS)); + hci_layer_->AssertNoQueuedCommand(); + ASSERT_EQ(ConnectabilityState::DISARMED, le_impl_->connectability_state_); } TEST_F(LeImplTest, direct_connection_cancel_but_connected) { diff --git a/system/gd/hci/acl_manager/round_robin_scheduler.cc b/system/gd/hci/acl_manager/round_robin_scheduler.cc index ad0e6dc621..89897a82e5 100644 --- a/system/gd/hci/acl_manager/round_robin_scheduler.cc +++ b/system/gd/hci/acl_manager/round_robin_scheduler.cc @@ -17,7 +17,6 @@ #include "hci/acl_manager/round_robin_scheduler.h" #include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> #include <memory> #include <utility> @@ -65,10 +64,8 @@ void RoundRobinScheduler::Unregister(uint16_t handle) { log::assert_that(acl_queue_handlers_.count(handle) == 1, "assert failed: acl_queue_handlers_.count(handle) == 1"); - if (com::android::bluetooth::flags::drop_acl_fragment_on_disconnect()) { - // Drop the pending fragments and recalculate number_of_sent_packets_ - drop_packet_fragments(handle); - } + // Drop the pending fragments and recalculate number_of_sent_packets_ + drop_packet_fragments(handle); auto& acl_queue_handler = acl_queue_handlers_.find(handle)->second; log::info("unregistering acl_queue handle={}, sent_packets={}", handle, @@ -94,8 +91,7 @@ void RoundRobinScheduler::Unregister(uint16_t handle) { starting_point_ = acl_queue_handlers_.begin(); // Restart sending packets if we got acl credits - if (com::android::bluetooth::flags::drop_acl_fragment_on_disconnect() && - credits_reclaimed_from_zero) { + if (credits_reclaimed_from_zero) { start_round_robin(); } } diff --git a/system/gd/hci/acl_manager/round_robin_scheduler_test.cc b/system/gd/hci/acl_manager/round_robin_scheduler_test.cc index 25bd2aff80..b990332c9e 100644 --- a/system/gd/hci/acl_manager/round_robin_scheduler_test.cc +++ b/system/gd/hci/acl_manager/round_robin_scheduler_test.cc @@ -16,7 +16,6 @@ #include "hci/acl_manager/round_robin_scheduler.h" -#include <com_android_bluetooth_flags.h> #include <gtest/gtest.h> #include "common/bidi_queue.h" @@ -422,8 +421,6 @@ TEST_F(RoundRobinSchedulerTest, receive_le_credit_when_next_fragment_is_classic) } TEST_F(RoundRobinSchedulerTest, unregister_reclaim_credits) { - com::android::bluetooth::flags::provider_->drop_acl_fragment_on_disconnect(true); - uint16_t handle = 0x01; auto connection_queue = std::make_shared<AclConnection::Queue>(20); auto new_connection_queue = std::make_shared<AclConnection::Queue>(20); diff --git a/system/gd/hci/le_address_manager.cc b/system/gd/hci/le_address_manager.cc index 804f88985e..327ea0f0ca 100644 --- a/system/gd/hci/le_address_manager.cc +++ b/system/gd/hci/le_address_manager.cc @@ -165,12 +165,8 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddress( min_seconds.count(), max_seconds.count()); enqueue_command_.Run(std::move(packet)); } else { - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { - address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true); - address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false); - } else { - address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_); - } + address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true); + address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false); } set_random_address(); break; @@ -229,12 +225,8 @@ void LeAddressManager::SetPrivacyPolicyForInitiatorAddressForTest( min_seconds.count(), max_seconds.count()); enqueue_command_.Run(std::move(packet)); } else { - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { - address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true); - address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false); - } else { - address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_); - } + address_rotation_wake_alarm_ = std::make_unique<os::Alarm>(handler_, true); + address_rotation_non_wake_alarm_ = std::make_unique<os::Alarm>(handler_, false); set_random_address(); } break; @@ -422,31 +414,25 @@ void LeAddressManager::prepare_to_rotate() { } void LeAddressManager::schedule_rotate_random_address() { - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { - std::string client_name = "LeAddressManager"; - auto privateAddressIntervalRange = GetNextPrivateAddressIntervalRange(client_name); - address_rotation_wake_alarm_->Schedule( - common::BindOnce( - []() { log::info("deadline wakeup in schedule_rotate_random_address"); }), - privateAddressIntervalRange.max); - address_rotation_non_wake_alarm_->Schedule( - common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)), - privateAddressIntervalRange.min); - - auto now = std::chrono::system_clock::now(); - if (address_rotation_interval_min.has_value()) { - CheckAddressRotationHappenedInExpectedTimeInterval( - *address_rotation_interval_min, *address_rotation_interval_max, now, client_name); - } - - // Update the expected range here. - address_rotation_interval_min.emplace(now + privateAddressIntervalRange.min); - address_rotation_interval_max.emplace(now + privateAddressIntervalRange.max); - } else { - address_rotation_wake_alarm_->Schedule( - common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)), - GetNextPrivateAddressIntervalMs()); + std::string client_name = "LeAddressManager"; + auto privateAddressIntervalRange = GetNextPrivateAddressIntervalRange(client_name); + address_rotation_wake_alarm_->Schedule( + common::BindOnce( + []() { log::info("deadline wakeup in schedule_rotate_random_address"); }), + privateAddressIntervalRange.max); + address_rotation_non_wake_alarm_->Schedule( + common::BindOnce(&LeAddressManager::prepare_to_rotate, common::Unretained(this)), + privateAddressIntervalRange.min); + + auto now = std::chrono::system_clock::now(); + if (address_rotation_interval_min.has_value()) { + CheckAddressRotationHappenedInExpectedTimeInterval( + *address_rotation_interval_min, *address_rotation_interval_max, now, client_name); } + + // Update the expected range here. + address_rotation_interval_min.emplace(now + privateAddressIntervalRange.min); + address_rotation_interval_max.emplace(now + privateAddressIntervalRange.max); } void LeAddressManager::set_random_address() { diff --git a/system/gd/hci/le_advertising_manager.cc b/system/gd/hci/le_advertising_manager.cc index f9a80f44f8..d626b2ee15 100644 --- a/system/gd/hci/le_advertising_manager.cc +++ b/system/gd/hci/le_advertising_manager.cc @@ -374,39 +374,29 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb log::info("Reenable advertising"); if (was_rotating_address) { log::info("Scheduling address rotation for advertiser_id={}", advertiser_id); - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { - advertising_sets_[advertiser_id].address_rotation_wake_alarm_ = - std::make_unique<os::Alarm>(module_handler_, true); - advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_ = - std::make_unique<os::Alarm>(module_handler_, false); - - std::string client_name = "advertising_set_" + std::to_string(advertiser_id); - auto privateAddressIntervalRange = - le_address_manager_->GetNextPrivateAddressIntervalRange(client_name); - - advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule( - common::BindOnce( - []() { log::info("deadline wakeup in handle_set_terminated"); }), - privateAddressIntervalRange.max); - advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address_on_timer, - common::Unretained(this), advertiser_id), - privateAddressIntervalRange.min); - - // Update the expected range here. - auto now = std::chrono::system_clock::now(); - advertising_sets_[advertiser_id].address_rotation_interval_min.emplace( - now + privateAddressIntervalRange.min); - advertising_sets_[advertiser_id].address_rotation_interval_max.emplace( - now + privateAddressIntervalRange.max); - } else { - advertising_sets_[advertiser_id].address_rotation_wake_alarm_ = - std::make_unique<os::Alarm>(module_handler_); - advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address_on_timer, - common::Unretained(this), advertiser_id), - le_address_manager_->GetNextPrivateAddressIntervalMs()); - } + advertising_sets_[advertiser_id].address_rotation_wake_alarm_ = + std::make_unique<os::Alarm>(module_handler_, true); + advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_ = + std::make_unique<os::Alarm>(module_handler_, false); + + std::string client_name = "advertising_set_" + std::to_string(advertiser_id); + auto privateAddressIntervalRange = + le_address_manager_->GetNextPrivateAddressIntervalRange(client_name); + + advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule( + common::BindOnce([]() { log::info("deadline wakeup in handle_set_terminated"); }), + privateAddressIntervalRange.max); + advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule( + common::BindOnce(&impl::set_advertising_set_random_address_on_timer, + common::Unretained(this), advertiser_id), + privateAddressIntervalRange.min); + + // Update the expected range here. + auto now = std::chrono::system_clock::now(); + advertising_sets_[advertiser_id].address_rotation_interval_min.emplace( + now + privateAddressIntervalRange.min); + advertising_sets_[advertiser_id].address_rotation_interval_max.emplace( + now + privateAddressIntervalRange.max); } enable_advertiser(advertiser_id, true, 0, 0); } @@ -673,40 +663,31 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb !leaudio_requested_nrpa && (!controller_->IsRpaGenerationSupported())) { // start timer for random address log::info("Scheduling address rotation for advertiser_id={}", id); - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { - advertising_sets_[id].address_rotation_wake_alarm_ = - std::make_unique<os::Alarm>(module_handler_, true); - advertising_sets_[id].address_rotation_non_wake_alarm_ = - std::make_unique<os::Alarm>(module_handler_, false); - - std::string client_name = "advertising_set_" + std::to_string(id); - auto privateAddressIntervalRange = - le_address_manager_->GetNextPrivateAddressIntervalRange(client_name); - - advertising_sets_[id].address_rotation_wake_alarm_->Schedule( - common::BindOnce([]() { - log::info("deadline wakeup in create_extended_advertiser_with_id"); - }), - privateAddressIntervalRange.max); - advertising_sets_[id].address_rotation_non_wake_alarm_->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address_on_timer, - common::Unretained(this), id), - privateAddressIntervalRange.min); - - // Update the expected range here. - auto now = std::chrono::system_clock::now(); - advertising_sets_[id].address_rotation_interval_min.emplace( - now + privateAddressIntervalRange.min); - advertising_sets_[id].address_rotation_interval_max.emplace( - now + privateAddressIntervalRange.max); - } else { - advertising_sets_[id].address_rotation_wake_alarm_ = - std::make_unique<os::Alarm>(module_handler_); - advertising_sets_[id].address_rotation_wake_alarm_->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address_on_timer, - common::Unretained(this), id), - le_address_manager_->GetNextPrivateAddressIntervalMs()); - } + advertising_sets_[id].address_rotation_wake_alarm_ = + std::make_unique<os::Alarm>(module_handler_, true); + advertising_sets_[id].address_rotation_non_wake_alarm_ = + std::make_unique<os::Alarm>(module_handler_, false); + + std::string client_name = "advertising_set_" + std::to_string(id); + auto privateAddressIntervalRange = + le_address_manager_->GetNextPrivateAddressIntervalRange(client_name); + + advertising_sets_[id].address_rotation_wake_alarm_->Schedule( + common::BindOnce([]() { + log::info("deadline wakeup in create_extended_advertiser_with_id"); + }), + privateAddressIntervalRange.max); + advertising_sets_[id].address_rotation_non_wake_alarm_->Schedule( + common::BindOnce(&impl::set_advertising_set_random_address_on_timer, + common::Unretained(this), id), + privateAddressIntervalRange.min); + + // Update the expected range here. + auto now = std::chrono::system_clock::now(); + advertising_sets_[id].address_rotation_interval_min.emplace( + now + privateAddressIntervalRange.min); + advertising_sets_[id].address_rotation_interval_max.emplace( + now + privateAddressIntervalRange.max); } } if (config.advertising_type == AdvertisingType::ADV_IND || @@ -859,39 +840,31 @@ struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallb } log::info("Scheduling address rotation for advertiser_id={}", advertiser_id); - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation()) { - std::string client_name = "advertising_set_" + std::to_string(advertiser_id); - auto privateAddressIntervalRange = - le_address_manager_->GetNextPrivateAddressIntervalRange(client_name); - advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule( - common::BindOnce([]() { - log::info("deadline wakeup in set_advertising_set_random_address_on_timer"); - }), - privateAddressIntervalRange.max); - advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address_on_timer, - common::Unretained(this), advertiser_id), - privateAddressIntervalRange.min); - - auto now = std::chrono::system_clock::now(); - if (advertising_sets_[advertiser_id].address_rotation_interval_min.has_value()) { - le_address_manager_->CheckAddressRotationHappenedInExpectedTimeInterval( - *(advertising_sets_[advertiser_id].address_rotation_interval_min), - *(advertising_sets_[advertiser_id].address_rotation_interval_max), now, - client_name); - } - - // Update the expected range here. - advertising_sets_[advertiser_id].address_rotation_interval_min.emplace( - now + privateAddressIntervalRange.min); - advertising_sets_[advertiser_id].address_rotation_interval_max.emplace( - now + privateAddressIntervalRange.max); - } else { - advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule( - common::BindOnce(&impl::set_advertising_set_random_address_on_timer, - common::Unretained(this), advertiser_id), - le_address_manager_->GetNextPrivateAddressIntervalMs()); + std::string client_name = "advertising_set_" + std::to_string(advertiser_id); + auto privateAddressIntervalRange = + le_address_manager_->GetNextPrivateAddressIntervalRange(client_name); + advertising_sets_[advertiser_id].address_rotation_wake_alarm_->Schedule( + common::BindOnce([]() { + log::info("deadline wakeup in set_advertising_set_random_address_on_timer"); + }), + privateAddressIntervalRange.max); + advertising_sets_[advertiser_id].address_rotation_non_wake_alarm_->Schedule( + common::BindOnce(&impl::set_advertising_set_random_address_on_timer, + common::Unretained(this), advertiser_id), + privateAddressIntervalRange.min); + + auto now = std::chrono::system_clock::now(); + if (advertising_sets_[advertiser_id].address_rotation_interval_min.has_value()) { + le_address_manager_->CheckAddressRotationHappenedInExpectedTimeInterval( + *(advertising_sets_[advertiser_id].address_rotation_interval_min), + *(advertising_sets_[advertiser_id].address_rotation_interval_max), now, client_name); } + + // Update the expected range here. + advertising_sets_[advertiser_id].address_rotation_interval_min.emplace( + now + privateAddressIntervalRange.min); + advertising_sets_[advertiser_id].address_rotation_interval_max.emplace( + now + privateAddressIntervalRange.max); } void register_advertiser( diff --git a/system/gd/metrics/chromeos/OWNERS b/system/gd/metrics/chromeos/OWNERS deleted file mode 100644 index a823e6e1dd..0000000000 --- a/system/gd/metrics/chromeos/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_chromeos diff --git a/system/gd/metrics/counter_metrics.h b/system/gd/metrics/counter_metrics.h index 88c7711f92..2f25f03ed0 100644 --- a/system/gd/metrics/counter_metrics.h +++ b/system/gd/metrics/counter_metrics.h @@ -15,6 +15,8 @@ */ #pragma once +#include <com_android_bluetooth_flags.h> + #include <unordered_map> #include "module.h" @@ -26,6 +28,13 @@ namespace metrics { class CounterMetrics : public bluetooth::Module { public: CounterMetrics(os::Handler* handler) : Module(handler) {} + ~CounterMetrics() { + if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { + GetHandler()->Clear(); + GetHandler()->WaitUntilStopped(std::chrono::milliseconds(2000)); + delete GetHandler(); + } + } bool CacheCount(int32_t key, int64_t value); virtual bool Count(int32_t key, int64_t count); diff --git a/system/gd/metrics/linux/OWNERS b/system/gd/metrics/linux/OWNERS deleted file mode 100644 index a823e6e1dd..0000000000 --- a/system/gd/metrics/linux/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_chromeos diff --git a/system/gd/module.cc b/system/gd/module.cc index 8904f0c559..76b386bbfe 100644 --- a/system/gd/module.cc +++ b/system/gd/module.cc @@ -128,10 +128,18 @@ void ModuleRegistry::StopAll() { auto module = Get(*it); last_instance_ = "stopping " + module->ToString(); - // Clear the handler before stopping the module to allow it to shut down gracefully. - log::info("Stopping Handler of Module {}", module->ToString()); - module->handler_->Clear(); - module->handler_->WaitUntilStopped(kModuleStopTimeout); + /* + * b/393449774 since we have now shifted to a single handler for all modules, we don't need + * to clear the handler here, it will be done in the respective teardown. + * Since we have a single handler, we need to make sure that the handler instance is deleted + * only once, otherwise we will see a crash as a handler can only be cleared once. + */ + if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { + // Clear the handler before stopping the module to allow it to shut down gracefully. + log::info("Stopping Handler of Module {}", module->ToString()); + module->handler_->Clear(); + module->handler_->WaitUntilStopped(kModuleStopTimeout); + } log::info("Stopping Module {}", module->ToString()); module->Stop(); } @@ -144,7 +152,9 @@ void ModuleRegistry::StopAll() { auto instance = started_modules_.find(*it); log::assert_that(instance != started_modules_.end(), "assert failed: instance != started_modules_.end()"); - delete instance->second->handler_; + if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { + delete instance->second->handler_; + } delete instance->second; started_modules_.erase(instance); } @@ -165,4 +175,15 @@ os::Handler* ModuleRegistry::GetModuleHandler(const ModuleFactory* module) const return nullptr; } +// Override the StopAll method to use the test thread and handler. +// This function will take care of releasing the handler instances. +void TestModuleRegistry::StopAll() { + os::Handler* handler = GetTestHandler(); + handler->Clear(); + if (com::android::bluetooth::flags::same_handler_for_all_modules()) { + handler->WaitUntilStopped(kHandlerStopTimeout); + } + ModuleRegistry::StopAll(); // call the base class StopAll + delete handler; +} } // namespace bluetooth diff --git a/system/gd/module.h b/system/gd/module.h index 816d455482..57e4db7d16 100644 --- a/system/gd/module.h +++ b/system/gd/module.h @@ -32,6 +32,9 @@ #include "os/thread.h" namespace bluetooth { +// Timeout for waiting for a handler to stop, used in Handler::WaitUntilStopped() +constexpr std::chrono::milliseconds kHandlerStopTimeout = std::chrono::milliseconds(2000); + namespace shim { class Stack; } // namespace shim @@ -191,6 +194,9 @@ public: os::Thread& GetTestThread() { return test_thread; } os::Handler* GetTestHandler() { return test_handler_; } + // Override the StopAll method to use the test thread and handler. + void StopAll(); + bool SynchronizeModuleHandler(const ModuleFactory* module, std::chrono::milliseconds timeout) const { return SynchronizeHandler(GetTestModuleHandler(module), timeout); diff --git a/system/gd/module_unittest.cc b/system/gd/module_unittest.cc index d1bf59ac73..8fb140c229 100644 --- a/system/gd/module_unittest.cc +++ b/system/gd/module_unittest.cc @@ -22,6 +22,7 @@ #include <sstream> #include <string> +#include "com_android_bluetooth_flags.h" #include "gtest/gtest.h" #include "os/handler.h" #include "os/thread.h" @@ -41,9 +42,12 @@ protected: void TearDown() override { handler_->Clear(); + if (com::android::bluetooth::flags::same_handler_for_all_modules()) { + handler_->WaitUntilStopped(kHandlerStopTimeout); + } delete registry_; - delete thread_; delete handler_; + delete thread_; } ModuleRegistry* registry_; diff --git a/system/gd/os/android/metrics.cc b/system/gd/os/android/metrics.cc index 78b12c8d96..5f5b34ef2e 100644 --- a/system/gd/os/android/metrics.cc +++ b/system/gd/os/android/metrics.cc @@ -122,11 +122,9 @@ void LogMetricRemoteVersionInfo(uint16_t handle, uint8_t status, uint8_t version manufacturer_name, subversion); if (ret < 0) { log::warn( - "Failed for handle {}, status {}, version {}, manufacturer_name {}, subversion {}, " - "error " - "{}", - handle, common::ToHexString(status), common::ToHexString(version), - common::ToHexString(manufacturer_name), common::ToHexString(subversion), ret); + "failed for handle {}, status 0x{:x}, version 0x{:x}, " + "manufacturer_name 0x{:x}, subversion 0x{:x}, error {}", + handle, status, version, manufacturer_name, subversion, ret); } } @@ -526,5 +524,53 @@ void LogMetricRfcommConnectionAtClose(const Address& address, } } +void LogMetricLeAudioConnectionSessionReported( + int32_t group_size, int32_t group_metric_id, int64_t connection_duration_nanos, + const std::vector<int64_t>& device_connecting_offset_nanos, + const std::vector<int64_t>& device_connected_offset_nanos, + const std::vector<int64_t>& device_connection_duration_nanos, + const std::vector<int32_t>& device_connection_status, + const std::vector<int32_t>& device_disconnection_status, + const std::vector<RawAddress>& device_address, + const std::vector<int64_t>& streaming_offset_nanos, + const std::vector<int64_t>& streaming_duration_nanos, + const std::vector<int32_t>& streaming_context_type) { + std::vector<int32_t> device_metric_id(device_address.size()); + for (uint64_t i = 0; i < device_address.size(); i++) { + if (!device_address[i].IsEmpty()) { + device_metric_id[i] = + MetricIdManager::GetInstance().AllocateId(ToGdAddress(device_address[i])); + } else { + device_metric_id[i] = 0; + } + } + int ret = stats_write(LE_AUDIO_CONNECTION_SESSION_REPORTED, group_size, group_metric_id, + connection_duration_nanos, device_connecting_offset_nanos, + device_connected_offset_nanos, device_connection_duration_nanos, + device_connection_status, device_disconnection_status, device_metric_id, + streaming_offset_nanos, streaming_duration_nanos, streaming_context_type); + if (ret < 0) { + log::warn( + "failed for group {}device_connecting_offset_nanos[{}], " + "device_connected_offset_nanos[{}], " + "device_connection_duration_nanos[{}], device_connection_status[{}], " + "device_disconnection_status[{}], device_metric_id[{}], " + "streaming_offset_nanos[{}], streaming_duration_nanos[{}], " + "streaming_context_type[{}]", + group_metric_id, device_connecting_offset_nanos.size(), + device_connected_offset_nanos.size(), device_connection_duration_nanos.size(), + device_connection_status.size(), device_disconnection_status.size(), + device_metric_id.size(), streaming_offset_nanos.size(), streaming_duration_nanos.size(), + streaming_context_type.size()); + } +} + +void LogMetricLeAudioBroadcastSessionReported(int64_t duration_nanos) { + int ret = stats_write(LE_AUDIO_BROADCAST_SESSION_REPORTED, duration_nanos); + if (ret < 0) { + log::warn("failed for duration={}", duration_nanos); + } +} + } // namespace os } // namespace bluetooth diff --git a/system/gd/os/chromeos/metrics.cc b/system/gd/os/chromeos/metrics.cc index b7921fe308..de36a98780 100644 --- a/system/gd/os/chromeos/metrics.cc +++ b/system/gd/os/chromeos/metrics.cc @@ -216,5 +216,19 @@ void LogMetricRfcommConnectionAtClose( android::bluetooth::BtaStatus /* sdp_status */, bool /* is_server */, bool /* sdp_initiated */, int32_t /* sdp_duration_ms */) {} +void LogMetricLeAudioConnectionSessionReported( + int32_t /*group_size*/, int32_t /*group_metric_id*/, int64_t /*connection_duration_nanos*/, + const std::vector<int64_t>& /*device_connecting_offset_nanos*/, + const std::vector<int64_t>& /*device_connected_offset_nanos*/, + const std::vector<int64_t>& /*device_connection_duration_nanos*/, + const std::vector<int32_t>& /*device_connection_status*/, + const std::vector<int32_t>& /*device_disconnection_status*/, + const std::vector<RawAddress>& /*device_address*/, + const std::vector<int64_t>& /*streaming_offset_nanos*/, + const std::vector<int64_t>& /*streaming_duration_nanos*/, + const std::vector<int32_t>& /*streaming_context_type*/) {} + +void LogMetricLeAudioBroadcastSessionReported(int64_t /*duration_nanos*/) {} + } // namespace os } // namespace bluetooth diff --git a/system/gd/os/host/metrics.cc b/system/gd/os/host/metrics.cc index a333113238..518a29ffee 100644 --- a/system/gd/os/host/metrics.cc +++ b/system/gd/os/host/metrics.cc @@ -135,5 +135,19 @@ void LogMetricRfcommConnectionAtClose( android::bluetooth::BtaStatus /* sdp_status */, bool /* is_server */, bool /* sdp_initiated */, int32_t /* sdp_duration_ms */) {} +void LogMetricLeAudioConnectionSessionReported( + int32_t /*group_size*/, int32_t /*group_metric_id*/, int64_t /*connection_duration_nanos*/, + const std::vector<int64_t>& /*device_connecting_offset_nanos*/, + const std::vector<int64_t>& /*device_connected_offset_nanos*/, + const std::vector<int64_t>& /*device_connection_duration_nanos*/, + const std::vector<int32_t>& /*device_connection_status*/, + const std::vector<int32_t>& /*device_disconnection_status*/, + const std::vector<RawAddress>& /*device_address*/, + const std::vector<int64_t>& /*streaming_offset_nanos*/, + const std::vector<int64_t>& /*streaming_duration_nanos*/, + const std::vector<int32_t>& /*streaming_context_type*/) {} + +void LogMetricLeAudioBroadcastSessionReported(int64_t /*duration_nanos*/) {} + } // namespace os } // namespace bluetooth diff --git a/system/gd/os/linux/metrics.cc b/system/gd/os/linux/metrics.cc index 7b727f38f2..f5d1555075 100644 --- a/system/gd/os/linux/metrics.cc +++ b/system/gd/os/linux/metrics.cc @@ -122,5 +122,20 @@ void LogMetricRfcommConnectionAtClose(const Address& raw_address, void LogMetricBluetoothEvent(const Address& address, android::bluetooth::EventType event_type, android::bluetooth::State state) {} + +void LogMetricLeAudioConnectionSessionReported( + int32_t /*group_size*/, int32_t /*group_metric_id*/, int64_t /*connection_duration_nanos*/, + const std::vector<int64_t>& /*device_connecting_offset_nanos*/, + const std::vector<int64_t>& /*device_connected_offset_nanos*/, + const std::vector<int64_t>& /*device_connection_duration_nanos*/, + const std::vector<int32_t>& /*device_connection_status*/, + const std::vector<int32_t>& /*device_disconnection_status*/, + const std::vector<RawAddress>& /*device_address*/, + const std::vector<int64_t>& /*streaming_offset_nanos*/, + const std::vector<int64_t>& /*streaming_duration_nanos*/, + const std::vector<int32_t>& /*streaming_context_type*/) {} + +void LogMetricLeAudioBroadcastSessionReported(int64_t /*duration_nanos*/) {} + } // namespace os } // namespace bluetooth diff --git a/system/gd/os/linux_generic/alarm.cc b/system/gd/os/linux_generic/alarm.cc index 8846ede579..a378ca93cd 100644 --- a/system/gd/os/linux_generic/alarm.cc +++ b/system/gd/os/linux_generic/alarm.cc @@ -17,7 +17,6 @@ #include "os/alarm.h" #include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> #include <sys/timerfd.h> #include <unistd.h> @@ -41,8 +40,7 @@ using common::OnceClosure; Alarm::Alarm(Handler* handler) : Alarm(handler, true) {} Alarm::Alarm(Handler* handler, bool isWakeAlarm) : handler_(handler) { - int timerfd_flag = - com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation() ? TFD_NONBLOCK : 0; + int timerfd_flag = TFD_NONBLOCK; fd_ = TIMERFD_CREATE(isWakeAlarm ? ALARM_CLOCK : CLOCK_BOOTTIME, timerfd_flag); @@ -85,7 +83,7 @@ void Alarm::on_fire() { auto bytes_read = read(fd_, ×_invoked, sizeof(uint64_t)); lock.unlock(); - if (com::android::bluetooth::flags::non_wake_alarm_for_rpa_rotation() && bytes_read == -1) { + if (bytes_read == -1) { log::debug("No data to read."); if (errno == EAGAIN || errno == EWOULDBLOCK) { log::debug("Alarm is already canceled or rescheduled."); diff --git a/system/gd/os/metrics.h b/system/gd/os/metrics.h index aab82826e6..04b5c37e84 100644 --- a/system/gd/os/metrics.h +++ b/system/gd/os/metrics.h @@ -29,8 +29,8 @@ #include "types/raw_address.h" namespace bluetooth { - namespace os { + /** * Unknown connection handle for metrics purpose */ @@ -379,6 +379,19 @@ void LogMetricRfcommConnectionAtClose(const hci::Address& address, android::bluetooth::BtaStatus sdp_status, bool is_server, bool sdp_initiated, int32_t sdp_duration_ms); +void LogMetricLeAudioConnectionSessionReported( + int32_t group_size, int32_t group_metric_id, int64_t connection_duration_nanos, + const std::vector<int64_t>& device_connecting_offset_nanos, + const std::vector<int64_t>& device_connected_offset_nanos, + const std::vector<int64_t>& device_connection_duration_nanos, + const std::vector<int32_t>& device_connection_status, + const std::vector<int32_t>& device_disconnection_status, + const std::vector<RawAddress>& device_address, + const std::vector<int64_t>& streaming_offset_nanos, + const std::vector<int64_t>& streaming_duration_nanos, + const std::vector<int32_t>& streaming_context_type); + +void LogMetricLeAudioBroadcastSessionReported(int64_t duration_nanos); + } // namespace os - // } // namespace bluetooth diff --git a/system/gd/rust/linux/OWNERS b/system/gd/rust/linux/OWNERS deleted file mode 100644 index a823e6e1dd..0000000000 --- a/system/gd/rust/linux/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_chromeos diff --git a/system/gd/rust/topshim/OWNERS b/system/gd/rust/topshim/OWNERS deleted file mode 100644 index a823e6e1dd..0000000000 --- a/system/gd/rust/topshim/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_chromeos diff --git a/system/gd/storage/storage_module.cc b/system/gd/storage/storage_module.cc index 5e3459e109..5a81a5cf94 100644 --- a/system/gd/storage/storage_module.cc +++ b/system/gd/storage/storage_module.cc @@ -90,6 +90,13 @@ StorageModule::StorageModule(os::Handler* handler, std::string config_file_path, StorageModule::~StorageModule() { std::lock_guard<std::recursive_mutex> lock(mutex_); + + if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { + GetHandler()->Clear(); + GetHandler()->WaitUntilStopped(std::chrono::milliseconds(2000)); + delete GetHandler(); + } + pimpl_.reset(); } diff --git a/system/log/OWNERS b/system/log/OWNERS deleted file mode 100644 index d1c75de900..0000000000 --- a/system/log/OWNERS +++ /dev/null @@ -1 +0,0 @@ -henrichataing@google.com diff --git a/system/main/shim/acl.cc b/system/main/shim/acl.cc index b2f0e23975..b01bd1f92d 100644 --- a/system/main/shim/acl.cc +++ b/system/main/shim/acl.cc @@ -969,11 +969,6 @@ struct shim::Acl::impl { maximum_latency, minimum_remote_timeout, minimum_local_timeout); } - void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout) { - GetAclManager()->LeSetDefaultSubrate(subrate_min, subrate_max, max_latency, cont_num, sup_tout); - } - void LeSubrateRequest(HciHandle handle, uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) { if (IsLeAcl(handle)) { @@ -1573,12 +1568,6 @@ void shim::Acl::UpdateConnectionParameters(uint16_t handle, uint16_t conn_int_mi conn_int_max, conn_latency, conn_timeout, min_ce_len, max_ce_len); } -void shim::Acl::LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, - uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) { - handler_->CallOn(pimpl_.get(), &Acl::impl::LeSetDefaultSubrate, subrate_min, subrate_max, - max_latency, cont_num, sup_tout); -} - void shim::Acl::LeSubrateRequest(uint16_t hci_handle, uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout) { handler_->CallOn(pimpl_.get(), &Acl::impl::LeSubrateRequest, hci_handle, subrate_min, subrate_max, diff --git a/system/main/shim/acl.h b/system/main/shim/acl.h index bbe15d0acf..8253faf013 100644 --- a/system/main/shim/acl.h +++ b/system/main/shim/acl.h @@ -80,8 +80,6 @@ public: void RemoveFromAddressResolution(const hci::AddressWithType& address_with_type); void ClearAddressResolution(); - void LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout); void LeSubrateRequest(uint16_t hci_handle, uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num, uint16_t sup_tout); diff --git a/system/main/shim/acl_api.cc b/system/main/shim/acl_api.cc index 56e63e1977..0868f40f2c 100644 --- a/system/main/shim/acl_api.cc +++ b/system/main/shim/acl_api.cc @@ -166,12 +166,6 @@ void bluetooth::shim::ACL_ClearAddressResolution() { void bluetooth::shim::ACL_ClearFilterAcceptList() { Stack::GetInstance()->GetAcl()->ClearFilterAcceptList(); } -void bluetooth::shim::ACL_LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, - uint16_t max_latency, uint16_t cont_num, - uint16_t sup_tout) { - Stack::GetInstance()->GetAcl()->LeSetDefaultSubrate(subrate_min, subrate_max, max_latency, - cont_num, sup_tout); -} void bluetooth::shim::ACL_LeSubrateRequest(uint16_t hci_handle, uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, diff --git a/system/main/shim/acl_api.h b/system/main/shim/acl_api.h index 6b28988ca6..b0bac16e02 100644 --- a/system/main/shim/acl_api.h +++ b/system/main/shim/acl_api.h @@ -49,8 +49,6 @@ void ACL_AddToAddressResolution(const tBLE_BD_ADDR& legacy_address_with_type, void ACL_RemoveFromAddressResolution(const tBLE_BD_ADDR& legacy_address_with_type); void ACL_ClearAddressResolution(); void ACL_ClearFilterAcceptList(); -void ACL_LeSetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout); void ACL_SendConnectionParameterUpdateRequest(uint16_t handle, uint16_t conn_int_min, uint16_t conn_int_max, uint16_t conn_latency, uint16_t conn_timeout, uint16_t min_ce_len, diff --git a/system/main/shim/stack.cc b/system/main/shim/stack.cc index 14447d75b3..027361875d 100644 --- a/system/main/shim/stack.cc +++ b/system/main/shim/stack.cc @@ -67,9 +67,9 @@ namespace shim { struct Stack::impl { Acl* acl_ = nullptr; - metrics::CounterMetrics* counter_metrics_ = nullptr; - storage::StorageModule* storage_ = nullptr; - hal::SnoopLogger* snoop_logger_ = nullptr; + std::shared_ptr<metrics::CounterMetrics> counter_metrics_ = nullptr; + std::shared_ptr<storage::StorageModule> storage_ = nullptr; + std::shared_ptr<hal::SnoopLogger> snoop_logger_ = nullptr; }; Stack::Stack() { pimpl_ = std::make_shared<Stack::impl>(); } @@ -89,9 +89,16 @@ void Stack::StartEverything() { stack_thread_ = new os::Thread("gd_stack_thread", os::Thread::Priority::REAL_TIME); stack_handler_ = new os::Handler(stack_thread_); - pimpl_->counter_metrics_ = new metrics::CounterMetrics(new Handler(stack_thread_)); - pimpl_->storage_ = new storage::StorageModule(new Handler(stack_thread_)); - pimpl_->snoop_logger_ = new hal::SnoopLogger(new Handler(stack_thread_)); + if (com::android::bluetooth::flags::same_handler_for_all_modules()) { + pimpl_->counter_metrics_ = std::make_shared<metrics::CounterMetrics>(stack_handler_); + pimpl_->storage_ = std::make_shared<storage::StorageModule>(stack_handler_); + pimpl_->snoop_logger_ = std::make_shared<hal::SnoopLogger>(stack_handler_); + } else { + pimpl_->counter_metrics_ = + std::make_shared<metrics::CounterMetrics>(new Handler(stack_thread_)); + pimpl_->storage_ = std::make_shared<storage::StorageModule>(new Handler(stack_thread_)); + pimpl_->snoop_logger_ = std::make_shared<hal::SnoopLogger>(new Handler(stack_thread_)); + } #if TARGET_FLOSS modules.add<sysprops::SyspropsModule>(); @@ -119,6 +126,7 @@ void Stack::StartEverything() { } is_running_ = true; + log::info("GD stack is running"); std::promise<void> promise; auto future = promise.get_future(); @@ -168,8 +176,12 @@ void Stack::Stop() { log::assert_that(is_running_, "Gd stack not running"); is_running_ = false; + log::info("GD stack is not running"); stack_handler_->Clear(); + if(com::android::bluetooth::flags::same_handler_for_all_modules()) { + stack_handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout); + } WakelockManager::Get().Acquire(); @@ -217,19 +229,19 @@ Acl* Stack::GetAcl() const { metrics::CounterMetrics* Stack::GetCounterMetrics() const { std::lock_guard<std::recursive_mutex> lock(mutex_); log::assert_that(is_running_, "assert failed: is_running_"); - return pimpl_->counter_metrics_; + return pimpl_->counter_metrics_.get(); } storage::StorageModule* Stack::GetStorage() const { std::lock_guard<std::recursive_mutex> lock(mutex_); log::assert_that(is_running_, "assert failed: is_running_"); - return pimpl_->storage_; + return pimpl_->storage_.get(); } hal::SnoopLogger* Stack::GetSnoopLogger() const { std::lock_guard<std::recursive_mutex> lock(mutex_); log::assert_that(is_running_, "assert failed: is_running_"); - return pimpl_->snoop_logger_; + return pimpl_->snoop_logger_.get(); } os::Handler* Stack::GetHandler() { @@ -265,9 +277,15 @@ void Stack::handle_start_up(ModuleList* modules, std::promise<void> promise) { void Stack::handle_shut_down(std::promise<void> promise) { registry_.StopAll(); + pimpl_->snoop_logger_->Stop(); pimpl_->storage_->Stop(); pimpl_->counter_metrics_->Stop(); + + pimpl_->snoop_logger_.reset(); + pimpl_->storage_.reset(); + pimpl_->counter_metrics_.reset(); + promise.set_value(); } diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc index 69afab3bfa..1a4c292d55 100644 --- a/system/stack/acl/btm_acl.cc +++ b/system/stack/acl/btm_acl.cc @@ -41,7 +41,6 @@ #include "bta/gatt/bta_gattc_int.h" #include "bta/include/bta_dm_acl.h" #include "bta/sys/bta_sys.h" -#include "common/metrics.h" #include "device/include/device_iot_config.h" #include "device/include/interop.h" #include "hci/controller_interface.h" @@ -51,6 +50,7 @@ #include "main/shim/dumpsys.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" +#include "os/metrics.h" #include "os/parameter_provider.h" #include "osi/include/allocator.h" #include "osi/include/properties.h" @@ -785,10 +785,10 @@ static void btm_process_remote_version_complete(uint8_t status, uint16_t handle, p_acl_cb->remote_version_info.valid = true; BTM_update_version_info(p_acl_cb->RemoteAddress(), p_acl_cb->remote_version_info); - bluetooth::common::LogRemoteVersionInfo(handle, status, lmp_version, manufacturer, - lmp_subversion); + bluetooth::os::LogMetricRemoteVersionInfo(handle, status, lmp_version, manufacturer, + lmp_subversion); } else { - bluetooth::common::LogRemoteVersionInfo(handle, status, 0, 0, 0); + bluetooth::os::LogMetricRemoteVersionInfo(handle, status, 0, 0, 0); } } diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc index cef553c79e..679e937eef 100644 --- a/system/stack/btm/btm_ble_sec.cc +++ b/system/stack/btm/btm_ble_sec.cc @@ -1662,6 +1662,13 @@ static bool btm_ble_complete_evt_ignore(const tBTM_SEC_DEV_REC* p_dev_rec, p_dev_rec->bd_addr); l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle); return true; + } else if (com::android::bluetooth::flags::le_peripheral_enc_failure() && + !p_dev_rec->role_central) { + log::warn("Peripheral encryption request failed for the bonded device {} with reason {}", + p_dev_rec->bd_addr, smp_status_text(p_data->complt.reason)); + btm_sec_disconnect(p_dev_rec->ble_hci_handle, HCI_ERR_AUTH_FAILURE, + smp_status_text(p_data->complt.reason)); + return true; } } @@ -1702,6 +1709,11 @@ static void btm_ble_complete_evt(const RawAddress& bd_addr, tBTM_SEC_DEV_REC* p_ } BTM_BLE_SEC_CALLBACK(BTM_LE_COMPLT_EVT, bd_addr, p_data); + p_dev_rec = btm_find_dev(bd_addr); // BTM_LE_COMPLT_EVT event may have removed the device + if (p_dev_rec == nullptr) { + log::warn("Device record removed {}", bd_addr); + return; + } log::verbose("before update sec_level=0x{:x} sec_flags=0x{:x}", p_data->complt.sec_level, p_dev_rec->sec_rec.sec_flags); diff --git a/system/stack/btm/btm_dev.cc b/system/stack/btm/btm_dev.cc index 92dc80e1ab..992a59236c 100644 --- a/system/stack/btm/btm_dev.cc +++ b/system/stack/btm/btm_dev.cc @@ -326,6 +326,10 @@ static bool is_handle_equal(void* data, void* context) { * ******************************************************************************/ tBTM_SEC_DEV_REC* btm_find_dev_by_handle(uint16_t handle) { + if (handle == HCI_INVALID_HANDLE) { + return nullptr; + } + if (btm_sec_cb.sec_dev_rec == nullptr) { return nullptr; } @@ -335,7 +339,7 @@ tBTM_SEC_DEV_REC* btm_find_dev_by_handle(uint16_t handle) { return static_cast<tBTM_SEC_DEV_REC*>(list_node(n)); } - return NULL; + return nullptr; } static bool is_not_same_identity_or_pseudo_address(void* data, void* context) { diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc index 439fa7bc0f..4595794cab 100644 --- a/system/stack/btm/btm_sec.cc +++ b/system/stack/btm/btm_sec.cc @@ -38,7 +38,6 @@ #include "bta/dm/bta_dm_act.h" #include "bta/dm/bta_dm_sec_int.h" #include "btif/include/btif_storage.h" -#include "common/metrics.h" #include "common/time_util.h" #include "device/include/device_iot_config.h" #include "device/include/interop.h" @@ -49,6 +48,7 @@ #include "main/shim/helpers.h" #include "main/shim/metrics_api.h" #include "metrics/bluetooth_event.h" +#include "os/metrics.h" #include "osi/include/allocator.h" #include "osi/include/properties.h" #include "stack/btm/btm_ble_int.h" @@ -1924,9 +1924,8 @@ void btm_create_conn_cancel_complete(uint8_t status, const RawAddress bd_addr) { log::verbose("btm_create_conn_cancel_complete(): in State: {} status:{}", tBTM_SEC_CB::btm_pair_state_descr(btm_sec_cb.pairing_state), status); bluetooth::shim::LogMetricLinkLayerConnectionEvent( - &bd_addr, bluetooth::common::kUnknownConnectionHandle, - android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, - android::bluetooth::hci::CMD_CREATE_CONNECTION_CANCEL, + &bd_addr, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, + android::bluetooth::LINK_TYPE_ACL, android::bluetooth::hci::CMD_CREATE_CONNECTION_CANCEL, android::bluetooth::hci::EVT_COMMAND_COMPLETE, android::bluetooth::hci::BLE_EVT_UNKNOWN, status, android::bluetooth::hci::STATUS_UNKNOWN); @@ -3130,12 +3129,12 @@ void btm_sec_auth_complete(uint16_t handle, tHCI_STATUS status) { p_dev_rec->sec_rec.security_required &= ~BTM_SEC_OUT_AUTHENTICATE; if (status != HCI_SUCCESS) { - if ((status != HCI_ERR_PEER_USER) && (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST)) { + if (status != HCI_ERR_PEER_USER && status != HCI_ERR_CONN_CAUSE_LOCAL_HOST) { btm_sec_send_hci_disconnect( p_dev_rec, HCI_ERR_PEER_USER, p_dev_rec->hci_handle, "stack::btm::btm_sec::btm_sec_auth_retry Auth fail while bonding"); } - } else { + } else if (!com::android::bluetooth::flags::immediate_encryption_after_pairing()) { BTM_LogHistory(kBtmLogTag, p_dev_rec->bd_addr, "Bonding completed", hci_error_code_text(status)); @@ -3160,6 +3159,11 @@ void btm_sec_auth_complete(uint16_t handle, tHCI_STATUS status) { } l2cu_start_post_bond_timer(p_dev_rec->hci_handle); + } else { + BTM_LogHistory(kBtmLogTag, p_dev_rec->bd_addr, "Bonding completed", + hci_error_code_text(status)); + BTM_SetEncryption(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, NULL, NULL, BTM_BLE_SEC_NONE); + l2cu_start_post_bond_timer(p_dev_rec->hci_handle); } return; diff --git a/system/stack/btm/security_event_parser.cc b/system/stack/btm/security_event_parser.cc index deba84b67f..b107a69cb8 100644 --- a/system/stack/btm/security_event_parser.cc +++ b/system/stack/btm/security_event_parser.cc @@ -22,10 +22,10 @@ #include <string> #include "btm_sec.h" -#include "common/metrics.h" #include "hci/hci_packets.h" #include "main/shim/helpers.h" #include "main/shim/metrics_api.h" +#include "os/metrics.h" #include "stack/include/btm_sec_api_types.h" #include "stack/include/hci_error_code.h" #include "stack/include/sec_hci_link_interface.h" @@ -35,7 +35,6 @@ using namespace bluetooth; using namespace bluetooth::hci; using android::bluetooth::hci::CMD_UNKNOWN; using android::bluetooth::hci::STATUS_UNKNOWN; -using bluetooth::common::kUnknownConnectionHandle; namespace bluetooth::stack::btm { namespace { @@ -44,7 +43,7 @@ static void log_address_and_status(const Address& bda, EventCode event_code, uint32_t cmd = android::bluetooth::hci::CMD_UNKNOWN; uint16_t status = static_cast<uint16_t>(event_status); uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN; - uint16_t handle = bluetooth::common::kUnknownConnectionHandle; + uint16_t handle = bluetooth::os::kUnknownConnectionHandle; int64_t value = 0; bluetooth::shim::LogMetricClassicPairingEvent( ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code), status, reason, value); @@ -53,7 +52,7 @@ static void log_address(const Address& bda, EventCode event_code) { uint32_t cmd = android::bluetooth::hci::CMD_UNKNOWN; uint16_t status = android::bluetooth::hci::STATUS_UNKNOWN; uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN; - uint16_t handle = bluetooth::common::kUnknownConnectionHandle; + uint16_t handle = bluetooth::os::kUnknownConnectionHandle; int64_t value = 0; bluetooth::shim::LogMetricClassicPairingEvent( ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code), status, reason, value); diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc index ee3c9757a5..367c8a89c8 100644 --- a/system/stack/btu/btu_hcif.cc +++ b/system/stack/btu/btu_hcif.cc @@ -37,10 +37,10 @@ #include <cstdint> #include "btm_iso_api.h" -#include "common/metrics.h" #include "internal_include/bt_target.h" #include "main/shim/hci_layer.h" #include "main/shim/metrics_api.h" +#include "os/metrics.h" #include "osi/include/allocator.h" #include "stack/include/acl_api.h" #include "stack/include/acl_hci_link_interface.h" @@ -110,7 +110,7 @@ static void btu_hcif_log_event_metrics(uint8_t evt_code, const uint8_t* p_event) uint32_t cmd = android::bluetooth::hci::CMD_UNKNOWN; uint16_t status = android::bluetooth::hci::STATUS_UNKNOWN; uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN; - uint16_t handle = bluetooth::common::kUnknownConnectionHandle; + uint16_t handle = bluetooth::os::kUnknownConnectionHandle; int64_t value = 0; RawAddress bda = RawAddress::kEmpty; @@ -375,7 +375,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_CREATE_CONNECTION_CANCEL: STREAM_TO_BDADDR(bd_addr, p_cmd); bluetooth::shim::LogMetricLinkLayerConnectionEvent( - &bd_addr, bluetooth::common::kUnknownConnectionHandle, + &bd_addr, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); break; @@ -400,7 +400,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_ENH_ACCEPT_ESCO_CONNECTION: STREAM_TO_BDADDR(bd_addr, p_cmd); bluetooth::shim::LogMetricLinkLayerConnectionEvent( - &bd_addr, bluetooth::common::kUnknownConnectionHandle, + &bd_addr, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_INCOMING, android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); break; @@ -409,7 +409,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, STREAM_TO_BDADDR(bd_addr, p_cmd); STREAM_TO_UINT8(reason, p_cmd); bluetooth::shim::LogMetricLinkLayerConnectionEvent( - &bd_addr, bluetooth::common::kUnknownConnectionHandle, + &bd_addr, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_INCOMING, android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, kUnknownBleEvt, cmd_status, reason); break; @@ -439,7 +439,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, // - When doing non-acceptlist connection // - When there is an error in command status bluetooth::shim::LogMetricLinkLayerConnectionEvent( - bd_addr_p, bluetooth::common::kUnknownConnectionHandle, + bd_addr_p, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); } @@ -464,7 +464,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, // - When doing non-acceptlist connection // - When there is an error in command status bluetooth::shim::LogMetricLinkLayerConnectionEvent( - bd_addr_p, bluetooth::common::kUnknownConnectionHandle, + bd_addr_p, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); } @@ -474,7 +474,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, if (cmd_status != HCI_SUCCESS && !is_cmd_status) { // Only log errors to prevent log spam due to acceptlist connections bluetooth::shim::LogMetricLinkLayerConnectionEvent( - nullptr, bluetooth::common::kUnknownConnectionHandle, + nullptr, bluetooth::os::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); } @@ -482,14 +482,14 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_READ_LOCAL_OOB_DATA: case HCI_READ_LOCAL_OOB_EXTENDED_DATA: bluetooth::shim::LogMetricClassicPairingEvent( - RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, + RawAddress::kEmpty, bluetooth::os::kUnknownConnectionHandle, opcode, hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, 0); break; case HCI_WRITE_SIMPLE_PAIRING_MODE: { uint8_t simple_pairing_mode; STREAM_TO_UINT8(simple_pairing_mode, p_cmd); bluetooth::shim::LogMetricClassicPairingEvent( - RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, + RawAddress::kEmpty, bluetooth::os::kUnknownConnectionHandle, opcode, hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, simple_pairing_mode); break; } @@ -497,7 +497,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, uint8_t secure_conn_host_support; STREAM_TO_UINT8(secure_conn_host_support, p_cmd); bluetooth::shim::LogMetricClassicPairingEvent( - RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, + RawAddress::kEmpty, bluetooth::os::kUnknownConnectionHandle, opcode, hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, secure_conn_host_support); break; } @@ -521,7 +521,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, STREAM_TO_BDADDR(bd_addr, p_cmd); STREAM_TO_UINT8(delete_all_flag, p_cmd); bluetooth::shim::LogMetricClassicPairingEvent( - bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, cmd_status, + bd_addr, bluetooth::os::kUnknownConnectionHandle, opcode, hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, delete_all_flag); break; } @@ -538,15 +538,15 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_REM_OOB_DATA_REQ_NEG_REPLY: STREAM_TO_BDADDR(bd_addr, p_cmd); bluetooth::shim::LogMetricClassicPairingEvent( - bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, cmd_status, + bd_addr, bluetooth::os::kUnknownConnectionHandle, opcode, hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, 0); break; case HCI_IO_CAP_REQ_NEG_REPLY: STREAM_TO_BDADDR(bd_addr, p_cmd); STREAM_TO_UINT8(reason, p_cmd); bluetooth::shim::LogMetricClassicPairingEvent(bd_addr, - bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, cmd_status, reason, 0); + bluetooth::os::kUnknownConnectionHandle, opcode, + hci_event, cmd_status, reason, 0); break; } } @@ -611,8 +611,8 @@ static void btu_hcif_log_command_complete_metrics(uint16_t opcode, const uint8_t case HCI_WRITE_SECURE_CONNS_SUPPORT: STREAM_TO_UINT8(status, p_return_params); bluetooth::shim::LogMetricClassicPairingEvent(RawAddress::kEmpty, - bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, status, reason, 0); + bluetooth::os::kUnknownConnectionHandle, opcode, + hci_event, status, reason, 0); break; case HCI_READ_ENCR_KEY_SIZE: { uint16_t handle; @@ -637,8 +637,8 @@ static void btu_hcif_log_command_complete_metrics(uint16_t opcode, const uint8_t STREAM_TO_UINT8(status, p_return_params); STREAM_TO_BDADDR(bd_addr, p_return_params); bluetooth::shim::LogMetricClassicPairingEvent(bd_addr, - bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, status, reason, 0); + bluetooth::os::kUnknownConnectionHandle, opcode, + hci_event, status, reason, 0); break; } } diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h index 0d27726af4..b602636f0d 100644 --- a/system/stack/eatt/eatt_impl.h +++ b/system/stack/eatt/eatt_impl.h @@ -723,11 +723,7 @@ struct eatt_impl { tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(channel->bda_, BT_TRANSPORT_LE); log::warn("disconnecting channel {:#x} for {}", channel->cid_, channel->bda_); - if (com::android::bluetooth::flags::gatt_disconnect_fix()) { - EattExtension::GetInstance()->Disconnect(channel->bda_, channel->cid_); - } else { - gatt_disconnect(p_tcb); - } + EattExtension::GetInstance()->Disconnect(channel->bda_, channel->cid_); } void start_indication_confirm_timer(const RawAddress& bd_addr, uint16_t cid) { diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc index 7b1a425f5e..bb5641d97d 100644 --- a/system/stack/gatt/gatt_api.cc +++ b/system/stack/gatt/gatt_api.cc @@ -75,8 +75,7 @@ static tGATT_HDL_LIST_ELEM& gatt_add_an_item_to_list(uint16_t s_handle) { return *rit; } -static tGATT_IF GATT_Register_Dynamic(const Uuid& app_uuid128, const std::string& name, - tGATT_CBACK* p_cb_info, bool eatt_support); +static tGATT_IF GATT_FindNextFreeClRcbId(); /***************************************************************************** * @@ -1229,63 +1228,6 @@ void GATT_SetIdleTimeout(const RawAddress& bd_addr, uint16_t idle_tout, tBT_TRAN ******************************************************************************/ tGATT_IF GATT_Register(const Uuid& app_uuid128, const std::string& name, tGATT_CBACK* p_cb_info, bool eatt_support) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - return GATT_Register_Dynamic(app_uuid128, name, p_cb_info, eatt_support); - } - tGATT_REG* p_reg; - uint8_t i_gatt_if = 0; - tGATT_IF gatt_if = 0; - - for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) { - if (p_reg->in_use && p_reg->app_uuid128 == app_uuid128) { - log::error("Application already registered, uuid={}", app_uuid128.ToString()); - return 0; - } - } - - if (stack_config_get_interface()->get_pts_use_eatt_for_all_services()) { - log::info("PTS: Force to use EATT for servers"); - eatt_support = true; - } - - for (i_gatt_if = 0, p_reg = gatt_cb.cl_rcb; i_gatt_if < GATT_MAX_APPS; i_gatt_if++, p_reg++) { - if (!p_reg->in_use) { - *p_reg = {}; - i_gatt_if++; /* one based number */ - p_reg->app_uuid128 = app_uuid128; - gatt_if = p_reg->gatt_if = (tGATT_IF)i_gatt_if; - p_reg->app_cb = *p_cb_info; - p_reg->in_use = true; - p_reg->eatt_support = eatt_support; - p_reg->name = name; - log::info("Allocated name:{} uuid:{} gatt_if:{} eatt_support:{}", name, - app_uuid128.ToString(), gatt_if, eatt_support); - return gatt_if; - } - } - - log::error("Unable to register GATT client, MAX client reached: {}", GATT_MAX_APPS); - return 0; -} - -static tGATT_IF GATT_FindNextFreeClRcbId() { - tGATT_IF gatt_if = gatt_cb.last_gatt_if; - for (int i = 0; i < GATT_IF_MAX; i++) { - if (++gatt_if > GATT_IF_MAX) { - gatt_if = static_cast<tGATT_IF>(1); - } - if (!gatt_cb.cl_rcb_map.contains(gatt_if)) { - gatt_cb.last_gatt_if = gatt_if; - return gatt_if; - } - } - log::error("Unable to register GATT client, MAX client reached: {}", gatt_cb.cl_rcb_map.size()); - - return GATT_IF_INVALID; -} - -static tGATT_IF GATT_Register_Dynamic(const Uuid& app_uuid128, const std::string& name, - tGATT_CBACK* p_cb_info, bool eatt_support) { for (auto& [gatt_if, p_reg] : gatt_cb.cl_rcb_map) { if (p_reg->app_uuid128 == app_uuid128) { log::error("Application already registered, uuid={}", app_uuid128.ToString()); @@ -1322,6 +1264,22 @@ static tGATT_IF GATT_Register_Dynamic(const Uuid& app_uuid128, const std::string return gatt_if; } +static tGATT_IF GATT_FindNextFreeClRcbId() { + tGATT_IF gatt_if = gatt_cb.last_gatt_if; + for (int i = 0; i < GATT_IF_MAX; i++) { + if (++gatt_if > GATT_IF_MAX) { + gatt_if = static_cast<tGATT_IF>(1); + } + if (!gatt_cb.cl_rcb_map.contains(gatt_if)) { + gatt_cb.last_gatt_if = gatt_if; + return gatt_if; + } + } + log::error("Unable to register GATT client, MAX client reached: {}", gatt_cb.cl_rcb_map.size()); + + return GATT_IF_INVALID; +} + /******************************************************************************* * * Function GATT_Deregister @@ -1385,11 +1343,7 @@ void GATT_Deregister(tGATT_IF gatt_if) { connection_manager::on_app_deregistered(gatt_if); - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - gatt_cb.cl_rcb_map.erase(gatt_if); - } else { - *p_reg = {}; - } + gatt_cb.cl_rcb_map.erase(gatt_if); } /******************************************************************************* diff --git a/system/stack/gatt/gatt_auth.cc b/system/stack/gatt/gatt_auth.cc index 7c4b935389..34dcb4886b 100644 --- a/system/stack/gatt/gatt_auth.cc +++ b/system/stack/gatt/gatt_auth.cc @@ -23,7 +23,6 @@ ******************************************************************************/ #include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> #include <string.h> #include "gatt_api.h" @@ -216,17 +215,9 @@ void gatt_notify_enc_cmpl(const RawAddress& bd_addr) { return; } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_rcb] : gatt_cb.cl_rcb_map) { - if (p_rcb->app_cb.p_enc_cmpl_cb) { - (*p_rcb->app_cb.p_enc_cmpl_cb)(p_rcb->gatt_if, bd_addr); - } - } - } else { - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - if (gatt_cb.cl_rcb[i].in_use && gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb) { - (*gatt_cb.cl_rcb[i].app_cb.p_enc_cmpl_cb)(gatt_cb.cl_rcb[i].gatt_if, bd_addr); - } + for (auto& [i, p_rcb] : gatt_cb.cl_rcb_map) { + if (p_rcb->app_cb.p_enc_cmpl_cb) { + (*p_rcb->app_cb.p_enc_cmpl_cb)(p_rcb->gatt_if, bd_addr); } } diff --git a/system/stack/gatt/gatt_cl.cc b/system/stack/gatt/gatt_cl.cc index 9a241813ae..8496d425a1 100644 --- a/system/stack/gatt/gatt_cl.cc +++ b/system/stack/gatt/gatt_cl.cc @@ -25,7 +25,6 @@ #define LOG_TAG "bluetooth" #include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> #include <string.h> #include "gatt_int.h" @@ -708,17 +707,9 @@ static void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_c // notification/indication // Note: need to do the indication count and start timer first then do // callback - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { - tcb.ind_count++; - } - } - } else { - for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { - if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { - tcb.ind_count++; - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + tcb.ind_count++; } } @@ -738,19 +729,10 @@ static void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_c gatt_cl_complete.att_value = value; gatt_cl_complete.cid = cid; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { - conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status, &gatt_cl_complete); - } - } - } else { - for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { - if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { - conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status, &gatt_cl_complete); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status, &gatt_cl_complete); } } @@ -788,19 +770,10 @@ static void gatt_process_notification(tGATT_TCB& tcb, uint16_t cid, uint8_t op_c gatt_cl_complete.att_value = value; gatt_cl_complete.cid = cid; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { - conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status, &gatt_cl_complete); - } - } - } else { - for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { - if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { - conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status, &gatt_cl_complete); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_cmpl_cb) { + conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_cmpl_cb)(conn_id, event, encrypt_status, &gatt_cl_complete); } } } diff --git a/system/stack/gatt/gatt_main.cc b/system/stack/gatt/gatt_main.cc index 56d268422c..5228369a26 100644 --- a/system/stack/gatt/gatt_main.cc +++ b/system/stack/gatt/gatt_main.cc @@ -282,7 +282,7 @@ bool gatt_disconnect(tGATT_TCB* p_tcb) { return true; } - if (com::android::bluetooth::flags::gatt_disconnect_fix() && p_tcb->eatt) { + if (p_tcb->eatt) { /* ATT is fixed channel and it is expected to drop ACL. * Make sure all EATT channels are disconnected before doing that. */ @@ -624,21 +624,10 @@ static void gatt_channel_congestion(tGATT_TCB* p_tcb, bool congested) { gatt_cl_send_next_cmd_inq(*p_tcb); } /* notifying all applications for the connection up event */ - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_congestion_cb) { - conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_congestion_cb)(conn_id, congested); - } - } - } else { - for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { - if (p_reg->in_use) { - if (p_reg->app_cb.p_congestion_cb) { - conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_congestion_cb)(conn_id, congested); - } - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_congestion_cb) { + conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_congestion_cb)(conn_id, congested); } } } @@ -658,20 +647,10 @@ void gatt_notify_phy_updated(tHCI_STATUS status, uint16_t handle, uint8_t tx_phy // TODO: Clean up this status conversion. tGATT_STATUS gatt_status = static_cast<tGATT_STATUS>(status); - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_phy_update_cb) { - tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_phy_update_cb)(p_reg->gatt_if, conn_id, tx_phy, rx_phy, gatt_status); - } - } - } else { - for (int i = 0; i < GATT_MAX_APPS; i++) { - tGATT_REG* p_reg = &gatt_cb.cl_rcb[i]; - if (p_reg->in_use && p_reg->app_cb.p_phy_update_cb) { - tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_phy_update_cb)(p_reg->gatt_if, conn_id, tx_phy, rx_phy, gatt_status); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_phy_update_cb) { + tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_phy_update_cb)(p_reg->gatt_if, conn_id, tx_phy, rx_phy, gatt_status); } } } @@ -684,22 +663,11 @@ void gatt_notify_conn_update(const RawAddress& remote, uint16_t interval, uint16 return; } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_conn_update_cb) { - tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_conn_update_cb)(p_reg->gatt_if, conn_id, interval, latency, timeout, - static_cast<tGATT_STATUS>(status)); - } - } - } else { - for (int i = 0; i < GATT_MAX_APPS; i++) { - tGATT_REG* p_reg = &gatt_cb.cl_rcb[i]; - if (p_reg->in_use && p_reg->app_cb.p_conn_update_cb) { - tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_conn_update_cb)(p_reg->gatt_if, conn_id, interval, latency, timeout, - static_cast<tGATT_STATUS>(status)); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_conn_update_cb) { + tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_conn_update_cb)(p_reg->gatt_if, conn_id, interval, latency, timeout, + static_cast<tGATT_STATUS>(status)); } } } @@ -717,22 +685,11 @@ void gatt_notify_subrate_change(uint16_t handle, uint16_t subrate_factor, uint16 return; } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_subrate_chg_cb) { - tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_subrate_chg_cb)(p_reg->gatt_if, conn_id, subrate_factor, latency, - cont_num, timeout, static_cast<tGATT_STATUS>(status)); - } - } - } else { - for (int i = 0; i < GATT_MAX_APPS; i++) { - tGATT_REG* p_reg = &gatt_cb.cl_rcb[i]; - if (p_reg->in_use && p_reg->app_cb.p_subrate_chg_cb) { - tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_subrate_chg_cb)(p_reg->gatt_if, conn_id, subrate_factor, latency, - cont_num, timeout, static_cast<tGATT_STATUS>(status)); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_subrate_chg_cb) { + tCONN_ID conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_subrate_chg_cb)(p_reg->gatt_if, conn_id, subrate_factor, latency, cont_num, + timeout, static_cast<tGATT_STATUS>(status)); } } } @@ -976,37 +933,19 @@ static void gatt_send_conn_cback(tGATT_TCB* p_tcb) { /* notifying all applications for the connection up event */ - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (!p_reg->in_use) { - continue; - } - - if (apps.find(p_reg->gatt_if) != apps.end()) { - gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true); - } - - if (p_reg->app_cb.p_conn_cb) { - conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, kGattConnected, - GATT_CONN_OK, p_tcb->transport); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (!p_reg->in_use) { + continue; } - } else { - for (i = 0, p_reg = gatt_cb.cl_rcb; i < GATT_MAX_APPS; i++, p_reg++) { - if (!p_reg->in_use) { - continue; - } - if (apps.find(p_reg->gatt_if) != apps.end()) { - gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true); - } + if (apps.find(p_reg->gatt_if) != apps.end()) { + gatt_update_app_use_link_flag(p_reg->gatt_if, p_tcb, true, true); + } - if (p_reg->app_cb.p_conn_cb) { - conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); - (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, kGattConnected, - GATT_CONN_OK, p_tcb->transport); - } + if (p_reg->app_cb.p_conn_cb) { + conn_id = gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if); + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, p_tcb->peer_bda, conn_id, kGattConnected, + GATT_CONN_OK, p_tcb->transport); } } diff --git a/system/stack/gatt/gatt_sr.cc b/system/stack/gatt/gatt_sr.cc index 133080c1db..f23b8b5dc9 100644 --- a/system/stack/gatt/gatt_sr.cc +++ b/system/stack/gatt/gatt_sr.cc @@ -407,27 +407,14 @@ static void gatt_process_exec_write_req(tGATT_TCB& tcb, uint16_t cid, uint8_t op trans_id = gatt_sr_enqueue_cmd(tcb, cid, op_code, 0); gatt_sr_copy_prep_cnt_to_cback_cnt(tcb); - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - auto prep_cnt_it = tcb.prep_cnt_map.begin(); - while (prep_cnt_it != tcb.prep_cnt_map.end()) { - gatt_if = prep_cnt_it->first; - conn_id = gatt_create_conn_id(tcb.tcb_idx, gatt_if); - tGATTS_DATA gatts_data; - gatts_data.exec_write = flag; - gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE_EXEC, &gatts_data); - prep_cnt_it = tcb.prep_cnt_map.erase(prep_cnt_it); - } - } else { - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - if (tcb.prep_cnt[i]) { - gatt_if = (tGATT_IF)(i + 1); - conn_id = gatt_create_conn_id(tcb.tcb_idx, gatt_if); - tGATTS_DATA gatts_data; - gatts_data.exec_write = flag; - gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE_EXEC, &gatts_data); - tcb.prep_cnt[i] = 0; - } - } + auto prep_cnt_it = tcb.prep_cnt_map.begin(); + while (prep_cnt_it != tcb.prep_cnt_map.end()) { + gatt_if = prep_cnt_it->first; + conn_id = gatt_create_conn_id(tcb.tcb_idx, gatt_if); + tGATTS_DATA gatts_data; + gatts_data.exec_write = flag; + gatt_sr_send_req_callback(conn_id, trans_id, GATTS_REQ_TYPE_WRITE_EXEC, &gatts_data); + prep_cnt_it = tcb.prep_cnt_map.erase(prep_cnt_it); } } else { /* nothing needs to be executed , send response now */ log::warn("gatt_process_exec_write_req: no prepare write pending"); @@ -911,19 +898,10 @@ static void gatts_process_mtu_req(tGATT_TCB& tcb, uint16_t cid, uint16_t len, ui gatts_data.mtu = tcb.payload_size; /* Notify all registered application with new MTU size. Use a transaction ID */ /* of 0, as no response is allowed from applications */ - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use) { - tCONN_ID conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); - gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, &gatts_data); - } - } - } else { - for (int i = 0; i < GATT_MAX_APPS; i++) { - if (gatt_cb.cl_rcb[i].in_use) { - tCONN_ID conn_id = gatt_create_conn_id(tcb.tcb_idx, gatt_cb.cl_rcb[i].gatt_if); - gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, &gatts_data); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use) { + tCONN_ID conn_id = gatt_create_conn_id(tcb.tcb_idx, p_reg->gatt_if); + gatt_sr_send_req_callback(conn_id, 0, GATTS_REQ_TYPE_MTU, &gatts_data); } } } diff --git a/system/stack/gatt/gatt_utils.cc b/system/stack/gatt/gatt_utils.cc index f401be9285..28590330c5 100644 --- a/system/stack/gatt/gatt_utils.cc +++ b/system/stack/gatt/gatt_utils.cc @@ -24,7 +24,6 @@ #define LOG_TAG "gatt_utils" #include <bluetooth/log.h> -#include <com_android_bluetooth_flags.h> #include <cstdint> #include <deque> @@ -1043,21 +1042,12 @@ tGATT_REG* gatt_get_regcb(tGATT_IF gatt_if) { uint8_t ii = (uint8_t)gatt_if; tGATT_REG* p_reg = NULL; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - auto it = gatt_cb.cl_rcb_map.find(gatt_if); - if (it == gatt_cb.cl_rcb_map.end()) { - log::warn("unknown gatt_if = {}", ii); - return NULL; - } - p_reg = it->second.get(); - } else { - // Index for cl_rcb is always 1 less than gatt_if. - if (ii < 1 || ii > GATT_MAX_APPS) { - log::warn("gatt_if out of range = {}", ii); - return NULL; - } - p_reg = &gatt_cb.cl_rcb[ii - 1]; + auto it = gatt_cb.cl_rcb_map.find(gatt_if); + if (it == gatt_cb.cl_rcb_map.end()) { + log::warn("unknown gatt_if = {}", ii); + return NULL; } + p_reg = it->second.get(); if (!p_reg->in_use) { log::warn("gatt_if found but not in use."); @@ -1332,16 +1322,8 @@ tGATT_TCB* gatt_find_tcb_by_cid(uint16_t lcid) { } void gatt_sr_copy_prep_cnt_to_cback_cnt(tGATT_TCB& tcb) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, cnt] : tcb.prep_cnt_map) { - tcb.sr_cmd.cback_cnt_map[i] = 1; - } - } else { - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - if (tcb.prep_cnt[i]) { - tcb.sr_cmd.cback_cnt[i] = 1; - } - } + for (auto& [i, cnt] : tcb.prep_cnt_map) { + tcb.sr_cmd.cback_cnt_map[i] = 1; } } @@ -1385,17 +1367,7 @@ bool gatt_sr_is_cback_cnt_zero(tGATT_TCB& tcb, uint16_t cid) { sr_cmd_p = &channel->server_outstanding_cmd_; } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - return sr_cmd_p->cback_cnt_map.empty(); - } - - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - if (sr_cmd_p->cback_cnt[i] != 0) { - return false; - } - } - - return true; + return sr_cmd_p->cback_cnt_map.empty(); } /******************************************************************************* @@ -1407,18 +1379,7 @@ bool gatt_sr_is_cback_cnt_zero(tGATT_TCB& tcb, uint16_t cid) { * Returns True no prepare write request * ******************************************************************************/ -bool gatt_sr_is_prep_cnt_zero(tGATT_TCB& tcb) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - return tcb.prep_cnt_map.empty(); - } else { - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - if (tcb.prep_cnt[i]) { - return false; - } - } - return true; - } -} +bool gatt_sr_is_prep_cnt_zero(tGATT_TCB& tcb) { return tcb.prep_cnt_map.empty(); } /******************************************************************************* * @@ -1430,31 +1391,15 @@ bool gatt_sr_is_prep_cnt_zero(tGATT_TCB& tcb) { * ******************************************************************************/ void gatt_sr_reset_cback_cnt(tGATT_TCB& tcb, uint16_t cid) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (cid == tcb.att_lcid) { - tcb.sr_cmd.cback_cnt_map.clear(); - } else { - EattChannel* channel = EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); - if (channel == nullptr) { - log::warn("{}, cid 0x{:02x} already disconnected", tcb.peer_bda, cid); - return; - } - channel->server_outstanding_cmd_.cback_cnt_map.clear(); - } + if (cid == tcb.att_lcid) { + tcb.sr_cmd.cback_cnt_map.clear(); } else { - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - if (cid == tcb.att_lcid) { - tcb.sr_cmd.cback_cnt[i] = 0; - } else { - EattChannel* channel = - EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); - if (channel == nullptr) { - log::warn("{}, cid 0x{:02x} already disconnected", tcb.peer_bda, cid); - return; - } - channel->server_outstanding_cmd_.cback_cnt[i] = 0; - } + EattChannel* channel = EattExtension::GetInstance()->FindEattChannelByCid(tcb.peer_bda, cid); + if (channel == nullptr) { + log::warn("{}, cid 0x{:02x} already disconnected", tcb.peer_bda, cid); + return; } + channel->server_outstanding_cmd_.cback_cnt_map.clear(); } } @@ -1540,25 +1485,13 @@ void gatt_sr_update_cback_cnt(tGATT_TCB& tcb, uint16_t cid, tGATT_IF gatt_if, bo gatt_sr_reset_cback_cnt(tcb, cid); } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (is_inc) { - sr_cmd_p->cback_cnt_map[gatt_if]++; - } else { - auto cback_cnt_it = sr_cmd_p->cback_cnt_map.find(gatt_if); - if (cback_cnt_it != sr_cmd_p->cback_cnt_map.end()) { - if ((--cback_cnt_it->second) <= 0) { - sr_cmd_p->cback_cnt_map.erase(cback_cnt_it); - } - } - } + if (is_inc) { + sr_cmd_p->cback_cnt_map[gatt_if]++; } else { - uint8_t idx = ((uint8_t)gatt_if) - 1; - - if (is_inc) { - sr_cmd_p->cback_cnt[idx]++; - } else { - if (sr_cmd_p->cback_cnt[idx]) { - sr_cmd_p->cback_cnt[idx]--; + auto cback_cnt_it = sr_cmd_p->cback_cnt_map.find(gatt_if); + if (cback_cnt_it != sr_cmd_p->cback_cnt_map.end()) { + if ((--cback_cnt_it->second) <= 0) { + sr_cmd_p->cback_cnt_map.erase(cback_cnt_it); } } } @@ -1582,23 +1515,13 @@ void gatt_sr_update_prep_cnt(tGATT_TCB& tcb, tGATT_IF gatt_if, bool is_inc, bool if (is_reset_first) { gatt_sr_reset_prep_cnt(tcb); } - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - if (is_inc) { - tcb.prep_cnt_map[gatt_if]++; - } else { - auto prep_cnt_i = tcb.prep_cnt_map.find(gatt_if); - if (prep_cnt_i != tcb.prep_cnt_map.end()) { - if (--prep_cnt_i->second <= 0) { - tcb.prep_cnt_map.erase(prep_cnt_i); - } - } - } + if (is_inc) { + tcb.prep_cnt_map[gatt_if]++; } else { - if (is_inc) { - tcb.prep_cnt[idx]++; - } else { - if (tcb.prep_cnt[idx]) { - tcb.prep_cnt[idx]--; + auto prep_cnt_i = tcb.prep_cnt_map.find(gatt_if); + if (prep_cnt_i != tcb.prep_cnt_map.end()) { + if (--prep_cnt_i->second <= 0) { + tcb.prep_cnt_map.erase(prep_cnt_i); } } } @@ -1806,24 +1729,12 @@ static void gatt_disconnect_complete_notify_user(const RawAddress& bda, tGATT_DI tBT_TRANSPORT transport) { tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(bda, transport); - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (p_reg->in_use && p_reg->app_cb.p_conn_cb) { - tCONN_ID conn_id = - p_tcb ? gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if) : GATT_INVALID_CONN_ID; - (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, kGattDisconnected, reason, - transport); - } - } - } else { - for (uint8_t i = 0; i < GATT_MAX_APPS; i++) { - tGATT_REG* p_reg = &gatt_cb.cl_rcb[i]; - if (p_reg->in_use && p_reg->app_cb.p_conn_cb) { - tCONN_ID conn_id = - p_tcb ? gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if) : GATT_INVALID_CONN_ID; - (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, kGattDisconnected, reason, - transport); - } + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (p_reg->in_use && p_reg->app_cb.p_conn_cb) { + tCONN_ID conn_id = + p_tcb ? gatt_create_conn_id(p_tcb->tcb_idx, p_reg->gatt_if) : GATT_INVALID_CONN_ID; + (*p_reg->app_cb.p_conn_cb)(p_reg->gatt_if, bda, conn_id, kGattDisconnected, reason, + transport); } } } @@ -1943,41 +1854,22 @@ uint16_t gatt_get_mtu_pref(const tGATT_REG* p_reg, const RawAddress& bda) { uint16_t gatt_get_apps_preferred_mtu(const RawAddress& bda) { uint16_t preferred_mtu = 0; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (!p_reg->in_use) { - continue; - } - - preferred_mtu = std::max(preferred_mtu, gatt_get_mtu_pref(p_reg.get(), bda)); + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (!p_reg->in_use) { + continue; } - } else { - for (auto& reg : gatt_cb.cl_rcb) { - if (!reg.in_use) { - continue; - } - preferred_mtu = std::max(preferred_mtu, gatt_get_mtu_pref(®, bda)); - } + preferred_mtu = std::max(preferred_mtu, gatt_get_mtu_pref(p_reg.get(), bda)); } return preferred_mtu; } void gatt_remove_apps_mtu_prefs(const RawAddress& bda) { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { - if (!p_reg->in_use) { - continue; - } - p_reg.get()->mtu_prefs.erase(bda); - } - } else { - for (auto& reg : gatt_cb.cl_rcb) { - if (!reg.in_use) { - continue; - } - reg.mtu_prefs.erase(bda); + for (auto& [i, p_reg] : gatt_cb.cl_rcb_map) { + if (!p_reg->in_use) { + continue; } + p_reg.get()->mtu_prefs.erase(bda); } } diff --git a/system/stack/include/l2cap_interface.h b/system/stack/include/l2cap_interface.h index b1551d2a80..19f85d4d2e 100644 --- a/system/stack/include/l2cap_interface.h +++ b/system/stack/include/l2cap_interface.h @@ -871,21 +871,6 @@ public: virtual void L2CA_SetEcosystemBaseInterval(uint32_t base_interval) = 0; /******************************************************************************* - * - * Function L2CA_SetDefaultSubrate - * - * Description BLE Set Default Subrate. - * - * Parameters: Subrate parameters - * - * Return value: void - * - ******************************************************************************/ - virtual void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, - uint16_t max_latency, uint16_t cont_num, - uint16_t timeout) = 0; - - /******************************************************************************* ** ** Function L2CA_SetMediaStreamChannel ** diff --git a/system/stack/l2cap/internal/l2c_api.h b/system/stack/l2cap/internal/l2c_api.h index dd1dbf52e6..989eccf50e 100644 --- a/system/stack/l2cap/internal/l2c_api.h +++ b/system/stack/l2cap/internal/l2c_api.h @@ -719,20 +719,6 @@ void L2CA_SetEcosystemBaseInterval(uint32_t base_interval); /******************************************************************************* * - * Function L2CA_SetDefaultSubrate - * - * Description BLE Set Default Subrate. - * - * Parameters: Subrate parameters - * - * Return value: void - * - ******************************************************************************/ -void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t timeout); - -/******************************************************************************* - * * Function L2CA_SubrateRequest * * Description BLE Subrate request. diff --git a/system/stack/l2cap/l2c_api.h b/system/stack/l2cap/l2c_api.h index 1ff09ffaf5..f9eab67d55 100644 --- a/system/stack/l2cap/l2c_api.h +++ b/system/stack/l2cap/l2c_api.h @@ -73,8 +73,6 @@ public: uint8_t* p_chnl_mask) override; [[nodiscard]] bool L2CA_SetAclPriority(const RawAddress& bd_addr, tL2CAP_PRIORITY priority) override; - void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t timeout) override; void L2CA_AdjustConnectionIntervals(uint16_t* min_interval, uint16_t* max_interval, uint16_t floor_interval) override; void L2CA_SetEcosystemBaseInterval(uint32_t base_interval) override; diff --git a/system/stack/l2cap/l2c_ble_conn_params.cc b/system/stack/l2cap/l2c_ble_conn_params.cc index 49eeb06155..392b1f5a74 100644 --- a/system/stack/l2cap/l2c_ble_conn_params.cc +++ b/system/stack/l2cap/l2c_ble_conn_params.cc @@ -510,26 +510,6 @@ static void l2cble_start_subrate_change(tL2C_LCB* p_lcb) { /******************************************************************************* * - * Function L2CA_SetDefaultSubrate - * - * Description BLE Set Default Subrate - * - * Parameters: Subrate parameters - * - * Return value: void - * - ******************************************************************************/ -void L2CA_SetDefaultSubrate(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t timeout) { - log::verbose("subrate_min={}, subrate_max={}, max_latency={}, cont_num={}, timeout={}", - subrate_min, subrate_max, max_latency, cont_num, timeout); - - bluetooth::shim::ACL_LeSetDefaultSubrate(subrate_min, subrate_max, max_latency, cont_num, - timeout); -} - -/******************************************************************************* - * * Function L2CA_SubrateRequest * * Description BLE Subrate request. diff --git a/system/stack/l2cap/l2cap_api.cc b/system/stack/l2cap/l2cap_api.cc index 0752c995c0..e9ccaeaf2a 100644 --- a/system/stack/l2cap/l2cap_api.cc +++ b/system/stack/l2cap/l2cap_api.cc @@ -234,13 +234,6 @@ void bluetooth::stack::l2cap::Impl::L2CA_SetEcosystemBaseInterval(uint32_t base_ return ::L2CA_IsLinkEstablished(bd_addr, transport); } -void bluetooth::stack::l2cap::Impl::L2CA_SetDefaultSubrate(uint16_t subrate_min, - uint16_t subrate_max, - uint16_t max_latency, uint16_t cont_num, - uint16_t timeout) { - ::L2CA_SetDefaultSubrate(subrate_min, subrate_max, max_latency, cont_num, timeout); -} - [[nodiscard]] bool bluetooth::stack::l2cap::Impl::L2CA_SubrateRequest( const RawAddress& rem_bda, uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num, uint16_t timeout) { diff --git a/system/stack/mmc/OWNERS b/system/stack/mmc/OWNERS deleted file mode 100644 index a823e6e1dd..0000000000 --- a/system/stack/mmc/OWNERS +++ /dev/null @@ -1 +0,0 @@ -include /OWNERS_chromeos diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc index 5d7a2eeea8..17f4908b3e 100644 --- a/system/stack/test/eatt/eatt_test.cc +++ b/system/stack/test/eatt/eatt_test.cc @@ -660,7 +660,6 @@ TEST_F(EattTest, ChannelUnavailableWhileReconfiguring) { } TEST_F(EattTest, DisconnectChannelOnIndicationConfirmationTimeout) { - com::android::bluetooth::flags::provider_->gatt_disconnect_fix(true); ConnectDeviceEattSupported(1); eatt_instance_->StartIndicationConfirmationTimer(test_address, test_local_cids[0]); diff --git a/system/stack/test/gatt/gatt_sr_test.cc b/system/stack/test/gatt/gatt_sr_test.cc index 9c5eb868d0..4106579080 100644 --- a/system/stack/test/gatt/gatt_sr_test.cc +++ b/system/stack/test/gatt/gatt_sr_test.cc @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <com_android_bluetooth_flags.h> #include <gtest/gtest.h> #include <cstdint> @@ -141,25 +140,16 @@ protected: tcb_.att_lcid = L2CAP_ATT_CID; el_.gatt_if = 1; - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - gatt_cb.cl_rcb_map.emplace(el_.gatt_if, std::make_unique<tGATT_REG>()); - tGATT_REG* p_reg = gatt_cb.cl_rcb_map[el_.gatt_if].get(); - p_reg->in_use = true; - p_reg->gatt_if = el_.gatt_if; - p_reg->app_cb.p_req_cb = ApplicationRequestCallback; - } else { - gatt_cb.cl_rcb[el_.gatt_if - 1].in_use = true; - gatt_cb.cl_rcb[el_.gatt_if - 1].app_cb.p_req_cb = ApplicationRequestCallback; - } + gatt_cb.cl_rcb_map.emplace(el_.gatt_if, std::make_unique<tGATT_REG>()); + tGATT_REG* p_reg = gatt_cb.cl_rcb_map[el_.gatt_if].get(); + p_reg->in_use = true; + p_reg->gatt_if = el_.gatt_if; + p_reg->app_cb.p_req_cb = ApplicationRequestCallback; test_state_ = TestMutables(); } - void TearDown() override { - if (com::android::bluetooth::flags::gatt_client_dynamic_allocation()) { - gatt_cb.cl_rcb_map.erase(el_.gatt_if); - } - } + void TearDown() override { gatt_cb.cl_rcb_map.erase(el_.gatt_if); } tGATT_TCB tcb_; tGATT_SRV_LIST_ELEM el_; diff --git a/system/stack/test/sdp/stack_sdp_test.cc b/system/stack/test/sdp/stack_sdp_test.cc index 314ea02334..c792a5fd0d 100644 --- a/system/stack/test/sdp/stack_sdp_test.cc +++ b/system/stack/test/sdp/stack_sdp_test.cc @@ -62,7 +62,6 @@ protected: fake_osi_ = std::make_unique<test::fake::FakeOsi>(); bluetooth::testing::stack::l2cap::set_interface(&mock_stack_l2cap_interface_); - tL2CAP_APPL_INFO l2cap_callbacks{}; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_RegisterWithSecurity(_, _, _, _, _, _, _)) .WillOnce(DoAll(SaveArg<1>(&l2cap_callbacks), ::testing::ReturnArg<0>())); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_Deregister(_)); @@ -73,6 +72,7 @@ protected: fake_osi_.reset(); } + tL2CAP_APPL_INFO l2cap_callbacks{}; bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_; std::unique_ptr<test::fake::FakeOsi> fake_osi_; }; diff --git a/system/test/Android.bp b/system/test/Android.bp index 106dedb7bd..9dab01352f 100644 --- a/system/test/Android.bp +++ b/system/test/Android.bp @@ -88,6 +88,13 @@ filegroup { } filegroup { + name: "TestMockBtaHearingAidAudioSource", + srcs: [ + "mock/mock_bta_hearing_aid_audio_source.cc", + ], +} + +filegroup { name: "TestMockBtaHf", srcs: [ "mock/mock_bta_hf*.cc", @@ -184,6 +191,7 @@ filegroup { ":TestMockBtaHas", ":TestMockBtaHd", ":TestMockBtaHearingAid", + ":TestMockBtaHearingAidAudioSource", ":TestMockBtaHf", ":TestMockBtaHh", ":TestMockBtaJv", @@ -457,6 +465,13 @@ filegroup { } filegroup { + name: "TestMockStackGapConnInterface", + srcs: [ + "mock/mock_stack_gap_conn_interface.cc", + ], +} + +filegroup { name: "TestMockStackConnMgr", srcs: [ "mock/mock_stack_connection_manager.cc", diff --git a/system/test/mock/mock_bta_hearing_aid_audio_source.cc b/system/test/mock/mock_bta_hearing_aid_audio_source.cc new file mode 100644 index 0000000000..c68fad3b8d --- /dev/null +++ b/system/test/mock/mock_bta_hearing_aid_audio_source.cc @@ -0,0 +1,43 @@ +/* + * Copyright 2021 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. + */ + +/* + * Generated mock file from original source file + * Functions generated:8 + */ + +#include <base/functional/callback.h> + +#include <cstdint> + +#include "bta/include/bta_gatt_queue.h" +#include "bta/include/bta_hearing_aid_api.h" +#include "test/common/mock_functions.h" +#include "types/raw_address.h" + +void HearingAidAudioSource::Start(const CodecConfiguration& /*codecConfiguration*/, + HearingAidAudioReceiver* /*audioReceiver*/, + uint16_t /*remote_delay_ms*/) { + inc_func_call_count(__func__); +} + +void HearingAidAudioSource::Stop() { inc_func_call_count(__func__); } + +void HearingAidAudioSource::Initialize() { inc_func_call_count(__func__); } + +void HearingAidAudioSource::CleanUp() { inc_func_call_count(__func__); } + +void HearingAidAudioSource::DebugDump(int /*fd*/) { inc_func_call_count(__func__); } diff --git a/system/test/mock/mock_main_shim_acl.cc b/system/test/mock/mock_main_shim_acl.cc index a98e809ad0..5f58860347 100644 --- a/system/test/mock/mock_main_shim_acl.cc +++ b/system/test/mock/mock_main_shim_acl.cc @@ -129,12 +129,6 @@ void shim::Acl::DisconnectLe(uint16_t /* handle */, tHCI_STATUS /* reason */, inc_func_call_count(__func__); } -void shim::Acl::LeSetDefaultSubrate(uint16_t /* subrate_min */, uint16_t /* subrate_max */, - uint16_t /* max_latency */, uint16_t /* cont_num */, - uint16_t /* sup_tout */) { - inc_func_call_count(__func__); -} - void shim::Acl::LeSubrateRequest(uint16_t /* hci_handle */, uint16_t /* subrate_min */, uint16_t /* subrate_max */, uint16_t /* max_latency */, uint16_t /* cont_num */, uint16_t /* sup_tout */) { diff --git a/system/test/mock/mock_main_shim_acl_api.cc b/system/test/mock/mock_main_shim_acl_api.cc index 3d257f1a5a..e3a3c9d63b 100644 --- a/system/test/mock/mock_main_shim_acl_api.cc +++ b/system/test/mock/mock_main_shim_acl_api.cc @@ -73,12 +73,6 @@ void bluetooth::shim::ACL_RemoveFromAddressResolution( inc_func_call_count(__func__); } void bluetooth::shim::ACL_ClearAddressResolution() { inc_func_call_count(__func__); } -void bluetooth::shim::ACL_LeSetDefaultSubrate(uint16_t /* subrate_min */, - uint16_t /* subrate_max */, - uint16_t /* max_latency */, uint16_t /* cont_num */, - uint16_t /* sup_tout */) { - inc_func_call_count(__func__); -} void bluetooth::shim::ACL_LeSubrateRequest(uint16_t /* hci_handle */, uint16_t /* subrate_min */, uint16_t /* subrate_max */, uint16_t /* max_latency */, uint16_t /* cont_num */, uint16_t /* sup_tout */) { diff --git a/system/test/mock/mock_main_shim_acl_api.h b/system/test/mock/mock_main_shim_acl_api.h index ab891673e3..29e27dbad3 100644 --- a/system/test/mock/mock_main_shim_acl_api.h +++ b/system/test/mock/mock_main_shim_acl_api.h @@ -84,21 +84,6 @@ struct ACL_ClearFilterAcceptList { }; extern struct ACL_ClearFilterAcceptList ACL_ClearFilterAcceptList; -// Name: ACL_LeSetDefaultSubrate -// Params: -// Return: void -struct ACL_LeSetDefaultSubrate { - std::function<void(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout)> - body{[](uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout) {}}; - void operator()(uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, - uint16_t cont_num, uint16_t sup_tout) { - body(subrate_min, subrate_max, max_latency, cont_num, sup_tout); - } -}; -extern struct ACL_LeSetDefaultSubrate ACL_LeSetDefaultSubrate; - // Name: ACL_LeSubrateRequest // Params: // Return: void diff --git a/system/test/mock/mock_stack_gap_conn.cc b/system/test/mock/mock_stack_gap_conn_interface.cc index 07193c9130..2ef1a3ef8f 100644 --- a/system/test/mock/mock_stack_gap_conn.cc +++ b/system/test/mock/mock_stack_gap_conn_interface.cc @@ -1,5 +1,5 @@ /* - * Copyright 2021 The Android Open Source Project + * 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. @@ -14,19 +14,39 @@ * limitations under the License. */ -/* - * Generated mock file from original source file - * Functions generated:13 - */ +#include "test/mock/mock_stack_gap_conn_interface.h" -#include "gap_api.h" -#include "stack/include/bt_hdr.h" +#include "stack/include/gap_api.h" #include "test/common/mock_functions.h" -#include "types/raw_address.h" -const RawAddress* GAP_ConnGetRemoteAddr(uint16_t /* gap_handle */) { +namespace { +bluetooth::testing::stack::gap_conn::Mock mock_gap_conn_interface; +bluetooth::testing::stack::gap_conn::Interface* interface_ = &mock_gap_conn_interface; +} // namespace + +void bluetooth::testing::stack::gap_conn::reset_interface() { + interface_ = &mock_gap_conn_interface; +} + +void bluetooth::testing::stack::gap_conn::set_interface( + bluetooth::testing::stack::gap_conn::Interface* interface) { + interface_ = interface; +} + +uint16_t GAP_ConnOpen(const char* p_serv_name, uint8_t service_id, bool is_server, + const RawAddress* p_rem_bda, uint16_t psm, uint16_t le_mps, + tL2CAP_CFG_INFO* p_cfg, tL2CAP_ERTM_INFO* ertm_info, uint16_t security, + tGAP_CONN_CALLBACK* p_cb, tBT_TRANSPORT transport) { inc_func_call_count(__func__); - return nullptr; + + return interface_->GAP_ConnOpen(p_serv_name, service_id, is_server, p_rem_bda, psm, le_mps, p_cfg, + ertm_info, security, p_cb, transport); +} + +const RawAddress* GAP_ConnGetRemoteAddr(uint16_t gap_handle) { + inc_func_call_count(__func__); + + return interface_->GAP_ConnGetRemoteAddr(gap_handle); } int GAP_GetRxQueueCnt(uint16_t /* handle */, uint32_t* /* p_rx_queue_count */) { inc_func_call_count(__func__); @@ -44,14 +64,6 @@ uint16_t GAP_ConnGetRemMtuSize(uint16_t /* gap_handle */) { inc_func_call_count(__func__); return 0; } -uint16_t GAP_ConnOpen(const char* /* p_serv_name */, uint8_t /* service_id */, bool /* is_server */, - const RawAddress* /* p_rem_bda */, uint16_t /* psm */, uint16_t /* le_mps */, - tL2CAP_CFG_INFO* /* p_cfg */, tL2CAP_ERTM_INFO* /* ertm_info */, - uint16_t /* security */, tGAP_CONN_CALLBACK* /* p_cb */, - tBT_TRANSPORT /* transport */) { - inc_func_call_count(__func__); - return 0; -} uint16_t GAP_ConnReadData(uint16_t /* gap_handle */, uint8_t* /* p_data */, uint16_t /* max_len */, uint16_t* /* p_len */) { inc_func_call_count(__func__); @@ -74,3 +86,8 @@ bool GAP_IsTransportLe(uint16_t /* gap_handle */) { return false; } void GAP_Init(void) { inc_func_call_count(__func__); } + +bluetooth::testing::stack::gap_conn::Interface& +bluetooth::testing::stack::gap_conn::get_interface() { + return *interface_; +} diff --git a/system/test/mock/mock_stack_gap_conn_interface.h b/system/test/mock/mock_stack_gap_conn_interface.h new file mode 100644 index 0000000000..c1ed7caea3 --- /dev/null +++ b/system/test/mock/mock_stack_gap_conn_interface.h @@ -0,0 +1,63 @@ +/* + * Copyright 2024 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 <gmock/gmock.h> + +#include <vector> + +#include "stack/include/gap_api.h" + +namespace bluetooth { +namespace testing { +namespace stack { +namespace gap_conn { + +class Interface { +public: + virtual ~Interface() = default; + + virtual uint16_t GAP_ConnOpen(const char* p_serv_name, uint8_t service_id, bool is_server, + const RawAddress* p_rem_bda, uint16_t psm, uint16_t le_mps, + tL2CAP_CFG_INFO* p_cfg, tL2CAP_ERTM_INFO* ertm_info, + uint16_t security, tGAP_CONN_CALLBACK* p_cb, + tBT_TRANSPORT transport) = 0; + + virtual const RawAddress* GAP_ConnGetRemoteAddr(uint16_t gap_handle) = 0; +}; + +class Mock : public Interface { +public: + ~Mock() = default; + + MOCK_METHOD(uint16_t, GAP_ConnOpen, + (const char* p_serv_name, uint8_t service_id, bool is_server, + const RawAddress* p_rem_bda, uint16_t psm, uint16_t le_mps, tL2CAP_CFG_INFO* p_cfg, + tL2CAP_ERTM_INFO* ertm_info, uint16_t security, tGAP_CONN_CALLBACK* p_cb, + tBT_TRANSPORT transport)); + + MOCK_METHOD(const RawAddress*, GAP_ConnGetRemoteAddr, (uint16_t gap_handle)); +}; + +void reset_interface(); +void set_interface(Interface* interface_); +Interface& get_interface(); + +} // namespace gap_conn +} // namespace stack +} // namespace testing +} // namespace bluetooth diff --git a/system/test/mock/mock_stack_l2cap_interface.h b/system/test/mock/mock_stack_l2cap_interface.h index a5cd536777..b2fc3e0ae9 100644 --- a/system/test/mock/mock_stack_l2cap_interface.h +++ b/system/test/mock/mock_stack_l2cap_interface.h @@ -64,9 +64,6 @@ public: MOCK_METHOD(bool, L2CA_GetPeerFeatures, (const RawAddress& bd_addr, uint32_t* p_ext_feat, uint8_t* p_chnl_mask)); MOCK_METHOD(bool, L2CA_SetAclPriority, (const RawAddress& bd_addr, tL2CAP_PRIORITY priority)); - MOCK_METHOD(void, L2CA_SetDefaultSubrate, - (uint16_t subrate_min, uint16_t subrate_max, uint16_t max_latency, uint16_t cont_num, - uint16_t timeout)); MOCK_METHOD(void, L2CA_AdjustConnectionIntervals, (uint16_t* min_interval, uint16_t* max_interval, uint16_t floor_interval)); MOCK_METHOD(void, L2CA_SetEcosystemBaseInterval, (uint32_t base_interval)); diff --git a/system/test/suite/adapter/adapter_unittest.cc b/system/test/suite/adapter/adapter_unittest.cc index 8e2769ea10..e295cb473d 100644 --- a/system/test/suite/adapter/adapter_unittest.cc +++ b/system/test/suite/adapter/adapter_unittest.cc @@ -16,8 +16,11 @@ * ******************************************************************************/ +#include <bluetooth/log.h> + #include "adapter/bluetooth_test.h" -#include "btcore/include/property.h" +#include "osi/include/allocator.h" +#include "osi/include/compat.h" #include "types/bt_transport.h" #include "types/raw_address.h" @@ -58,6 +61,52 @@ TEST_F(BluetoothTest, AdapterRepeatedEnableDisable) { } } +static bt_property_t* property_new_name(const char* name) { + bluetooth::log::assert_that(name != NULL, "assert failed: name != NULL"); + bt_property_t* property = static_cast<bt_property_t*>(osi_calloc(sizeof(bt_property_t))); + + property->val = osi_calloc(sizeof(bt_bdname_t) + 1); + osi_strlcpy((char*)property->val, name, sizeof(bt_bdname_t)); + + property->type = BT_PROPERTY_BDNAME; + property->len = sizeof(bt_bdname_t); + + return property; +} + +static void property_free(bt_property_t* property) { + if (property == NULL) { + return; + } + + osi_free(property->val); + osi_free(property); +} + +static const bt_bdname_t* property_as_name(const bt_property_t* property) { + bluetooth::log::assert_that(property->type == BT_PROPERTY_BDNAME, + "assert failed: property_is_name(property)"); + return (const bt_bdname_t*)property->val; +} + +static bool property_equals(const bt_property_t* p1, const bt_property_t* p2) { + if (!p1 || !p2 || p1->type != p2->type) { + return false; + } + + if (p1->type == BT_PROPERTY_BDNAME && p1->len != p2->len) { + const bt_property_t *shorter = p1, *longer = p2; + if (p1->len > p2->len) { + shorter = p2; + longer = p1; + } + return strlen((const char*)longer->val) == (size_t)shorter->len && + !memcmp(longer->val, shorter->val, shorter->len); + } + + return p1->len == p2->len && !memcmp(p1->val, p2->val, p1->len); +} + TEST_F(BluetoothTest, AdapterSetGetName) { bt_property_t* new_name = property_new_name("BluetoothTestName1"); diff --git a/system/test/suite/adapter/bluetooth_test.cc b/system/test/suite/adapter/bluetooth_test.cc index 4855b7a72d..490675a9f6 100644 --- a/system/test/suite/adapter/bluetooth_test.cc +++ b/system/test/suite/adapter/bluetooth_test.cc @@ -19,10 +19,11 @@ #include "adapter/bluetooth_test.h" #include <binder/ProcessState.h> +#include <bluetooth/log.h> #include <mutex> -#include "btcore/include/property.h" +#include "osi/include/allocator.h" #include "types/raw_address.h" extern bt_interface_t bluetoothInterface; @@ -35,6 +36,31 @@ namespace bttest { static BluetoothTest* instance = nullptr; +static void property_free_array(bt_property_t* properties, size_t count) { + if (properties == NULL) { + return; + } + + for (size_t i = 0; i < count; ++i) { + osi_free(properties[i].val); + } + + osi_free(properties); +} + +static bt_property_t* property_copy_array(const bt_property_t* properties, size_t count) { + bluetooth::log::assert_that(properties != NULL, "assert failed: properties != NULL"); + bt_property_t* clone = static_cast<bt_property_t*>(osi_calloc(sizeof(bt_property_t) * count)); + + memcpy(&clone[0], &properties[0], sizeof(bt_property_t) * count); + for (size_t i = 0; i < count; ++i) { + clone[i].val = osi_calloc(clone[i].len); + memcpy(clone[i].val, properties[i].val, clone[i].len); + } + + return clone; +} + void AdapterStateChangedCallback(bt_state_t new_state) { instance->state_ = new_state; semaphore_post(instance->adapter_state_changed_callback_sem_); diff --git a/tools/OWNERS b/tools/OWNERS deleted file mode 100644 index b2869ce1bc..0000000000 --- a/tools/OWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# Reviewers for /tools - -jpawlowski@google.com -siyuanh@google.com -wescande@google.com diff --git a/tools/lint/OWNERS b/tools/lint/OWNERS deleted file mode 100644 index 9dd2d58fe3..0000000000 --- a/tools/lint/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# Additional OWNERS for Bluetooth AndroidLint -salsavage@google.com diff --git a/tools/rootcanal/OWNERS b/tools/rootcanal/OWNERS deleted file mode 100644 index 22b124fd96..0000000000 --- a/tools/rootcanal/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Reviewers for /tools/rootcanal - -henrichataing@google.com |