diff options
225 files changed, 8574 insertions, 8002 deletions
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/com_android_bluetooth_hfp.cpp b/android/app/jni/com_android_bluetooth_hfp.cpp index 18a0bf1950..f321497e16 100644 --- a/android/app/jni/com_android_bluetooth_hfp.cpp +++ b/android/app/jni/com_android_bluetooth_hfp.cpp @@ -651,7 +651,8 @@ static jboolean isVoiceRecognitionSupportedNative(JNIEnv* env, jobject /* object return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } -static jboolean startVoiceRecognitionNative(JNIEnv* env, jobject /* object */, jbyteArray address) { +static jboolean startVoiceRecognitionNative(JNIEnv* env, jobject /* object */, jbyteArray address, + jboolean sendResult) { std::shared_lock<std::shared_timed_mutex> lock(interface_mutex); if (!sBluetoothHfpInterface) { log::warn("sBluetoothHfpInterface is null"); @@ -663,7 +664,7 @@ static jboolean startVoiceRecognitionNative(JNIEnv* env, jobject /* object */, j jniThrowIOException(env, EINVAL); return JNI_FALSE; } - bt_status_t status = sBluetoothHfpInterface->StartVoiceRecognition((RawAddress*)addr); + bt_status_t status = sBluetoothHfpInterface->StartVoiceRecognition((RawAddress*)addr, sendResult); if (status != BT_STATUS_SUCCESS) { log::error("Failed to start voice recognition, status: {}", bt_status_text(status)); } @@ -986,7 +987,7 @@ int register_com_android_bluetooth_hfp(JNIEnv* env) { {"disconnectAudioNative", "([B)Z", (void*)disconnectAudioNative}, {"isNoiseReductionSupportedNative", "([B)Z", (void*)isNoiseReductionSupportedNative}, {"isVoiceRecognitionSupportedNative", "([B)Z", (void*)isVoiceRecognitionSupportedNative}, - {"startVoiceRecognitionNative", "([B)Z", (void*)startVoiceRecognitionNative}, + {"startVoiceRecognitionNative", "([BZ)Z", (void*)startVoiceRecognitionNative}, {"stopVoiceRecognitionNative", "([B)Z", (void*)stopVoiceRecognitionNative}, {"setVolumeNative", "(II[B)Z", (void*)setVolumeNative}, {"notifyDeviceStatusNative", "(IIII[B)Z", (void*)notifyDeviceStatusNative}, 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/a2dp/A2dpService.java b/android/app/src/com/android/bluetooth/a2dp/A2dpService.java index 9481146d61..de37a5b3f4 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; @@ -168,7 +161,7 @@ public class A2dpService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothA2dpBinder(this); + return new A2dpServiceBinder(this); } @Override @@ -203,6 +196,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 +998,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 +1336,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/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/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/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/btservice/BluetoothSocketManagerBinder.java b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java index c9b1c82473..f9dcbe8017 100644 --- a/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java +++ b/android/app/src/com/android/bluetooth/btservice/BluetoothSocketManagerBinder.java @@ -286,7 +286,7 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { || !Utils.callerIsSystemOrActiveOrManagedUser( service, TAG, "getL2capLocalChannelId") || !Utils.checkConnectPermissionForDataDelivery( - service, source, "BluetoothSocketManagerBinder getL2capLocalChannelId")) { + service, source, TAG, "getL2capLocalChannelId")) { return INVALID_CID; } service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); @@ -300,7 +300,7 @@ class BluetoothSocketManagerBinder extends IBluetoothSocketManager.Stub { || !Utils.callerIsSystemOrActiveOrManagedUser( service, TAG, "getL2capRemoteChannelId") || !Utils.checkConnectPermissionForDataDelivery( - service, source, "BluetoothSocketManagerBinder getL2capRemoteChannelId")) { + service, source, TAG, "getL2capRemoteChannelId")) { return INVALID_CID; } service.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); 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/btservice/RemoteDevices.java b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java index c7e1b3eccd..c49659863b 100644 --- a/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/android/app/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -1609,6 +1609,28 @@ public class RemoteDevices { // Bond loss detected, add to the count. mAdapterService.getDatabase().updateKeyMissingCount(bluetoothDevice, true); + // Some apps are not able to handle the key missing broadcast, so we need to remove + // the bond to prevent them from misbehaving. + // TODO (b/402854328): Remove when the misbehaving apps are updated + if (bondLossIopFixNeeded(bluetoothDevice)) { + DeviceProperties deviceProperties = getDeviceProperties(bluetoothDevice); + if (deviceProperties == null) { + return; + } + String[] packages = deviceProperties.getPackages(); + if (packages.length == 0) { + return; + } + + Log.w( + TAG, + "Removing " + + bluetoothDevice + + " on behalf of: " + + Arrays.toString(packages)); + bluetoothDevice.removeBond(); + } + if (Flags.keyMissingPublic()) { mAdapterService.sendOrderedBroadcast( intent, @@ -1641,28 +1663,6 @@ public class RemoteDevices { new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, Utils.getTempBroadcastOptions()); } - - // Some apps are not able to handle the key missing broadcast, so we need to remove - // the bond to prevent them from misbehaving. - // TODO (b/402854328): Remove when the misbehaving apps are updated - if (bondLossIopFixNeeded(bluetoothDevice)) { - DeviceProperties deviceProperties = getDeviceProperties(bluetoothDevice); - if (deviceProperties == null) { - return; - } - String[] packages = deviceProperties.getPackages(); - if (packages.length == 0) { - return; - } - - Log.w( - TAG, - "Removing device: " - + bluetoothDevice - + "on behalf of: " - + Arrays.toString(packages)); - bluetoothDevice.removeBond(); - } } } @@ -2050,7 +2050,7 @@ public class RemoteDevices { } private static final String[] BOND_LOSS_IOP_PACKAGES = { - "com.sjm.crmd.patientApp_Android_", "com.abbott.crm.ngq.patient.", + "com.sjm.crmd.patientApp_Android", "com.abbott.crm.ngq.patient", }; private static final Set<String> BOND_LOSS_IOP_DEVICE_NAMES = Set.of("CM", "DM"); @@ -2084,8 +2084,11 @@ public class RemoteDevices { Log.w( TAG, "bondLossIopFixNeeded(): " + + " IOP fix needed for " + + device + + " name: " + deviceName - + " IOP fix needed for package " + + " package: " + packageName); return true; } 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/gatt/AppAdvertiseStats.java b/android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java index d997426cd5..44154b31bd 100644 --- a/android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java +++ b/android/app/src/com/android/bluetooth/gatt/AppAdvertiseStats.java @@ -35,7 +35,6 @@ import androidx.annotation.VisibleForTesting; import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.btservice.MetricsLogger; -import com.android.bluetooth.flags.Flags; import java.time.Duration; import java.time.Instant; @@ -219,15 +218,13 @@ class AppAdvertiseStats { } void recordAdvertiseErrorCount(int status) { - if (Flags.bleScanAdvMetricsRedesign()) { - BluetoothStatsLog.write( - BluetoothStatsLog.LE_ADV_ERROR_REPORTED, - new int[] {mAppUid}, - new String[] {mAppName}, - BluetoothStatsLog.LE_ADV_ERROR_REPORTED__LE_ADV_OP_CODE__ERROR_CODE_ON_START, - convertStatusCode(status), - getAttributionTag()); - } + BluetoothStatsLog.write( + BluetoothStatsLog.LE_ADV_ERROR_REPORTED, + new int[] {mAppUid}, + new String[] {mAppName}, + BluetoothStatsLog.LE_ADV_ERROR_REPORTED__LE_ADV_OP_CODE__ERROR_CODE_ON_START, + convertStatusCode(status), + getAttributionTag()); MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.LE_ADV_ERROR_ON_START_COUNT, 1); } @@ -421,23 +418,21 @@ class AppAdvertiseStats { } private void recordAdvertiseEnableCount(boolean enable, int instanceCount, long durationMs) { - if (Flags.bleScanAdvMetricsRedesign()) { - MetricsLogger.getInstance() - .logAdvStateChanged( - new int[] {mAppUid}, - new String[] {mAppName}, - enable /* enabled */, - convertAdvInterval(mInterval), - convertTxPowerLevel(mTxPowerLevel), - mConnectable, - mPeriodicAdvertisingEnabled, - mScanResponseData != null && mScannable /* hasScanResponse */, - !mLegacy /* isExtendedAdv */, - instanceCount, - durationMs, - mAppImportance, - getAttributionTag()); - } + MetricsLogger.getInstance() + .logAdvStateChanged( + new int[] {mAppUid}, + new String[] {mAppName}, + enable /* enabled */, + convertAdvInterval(mInterval), + convertTxPowerLevel(mTxPowerLevel), + mConnectable, + mPeriodicAdvertisingEnabled, + mScanResponseData != null && mScannable /* hasScanResponse */, + !mLegacy /* isExtendedAdv */, + instanceCount, + durationMs, + mAppImportance, + getAttributionTag()); if (enable) { MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.LE_ADV_COUNT_ENABLE, 1); if (mConnectable) { diff --git a/android/app/src/com/android/bluetooth/gatt/GattService.java b/android/app/src/com/android/bluetooth/gatt/GattService.java index c0ca2969d4..6e5faefa9f 100644 --- a/android/app/src/com/android/bluetooth/gatt/GattService.java +++ b/android/app/src/com/android/bluetooth/gatt/GattService.java @@ -24,8 +24,8 @@ import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED; import static android.bluetooth.BluetoothUtils.toAnonymizedAddress; import static com.android.bluetooth.Utils.callbackToApp; -import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser; import static com.android.bluetooth.Utils.checkCallerTargetSdk; +import static com.android.bluetooth.Utils.checkConnectPermissionForDataDelivery; import static com.android.bluetooth.util.AttributionSourceUtil.getLastAttributionTag; import static java.util.Objects.requireNonNull; @@ -43,7 +43,6 @@ import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.BluetoothStatusCodes; -import android.bluetooth.IBluetoothGatt; import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothGattServerCallback; import android.companion.CompanionDeviceManager; @@ -212,7 +211,7 @@ public class GattService extends ProfileService { @Override protected IProfileServiceBinder initBinder() { - return new BluetoothGattBinder(this); + return new GattServiceBinder(this); } @Override @@ -263,6 +262,10 @@ public class GattService extends ProfileService { return mScanController; } + CompanionDeviceManager getCompanionDeviceManager() { + return mCompanionDeviceManager; + } + @Override protected void setTestModeEnabled(boolean enableTestMode) { if (mScanController != null) { @@ -330,479 +333,6 @@ public class GattService extends ProfileService { } } - /** Handlers for incoming service calls */ - @VisibleForTesting - static class BluetoothGattBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder { - private GattService mService; - - BluetoothGattBinder(GattService svc) { - mService = svc; - } - - @Override - public void cleanup() { - mService = null; - } - - private GattService getService() { - // Cache mService because it can change while getService is called - GattService service = mService; - - if (service == null || !service.isAvailable()) { - Log.e(TAG, "getService() - Service requested, but not available!"); - return null; - } - - return service; - } - - @Override - public List<BluetoothDevice> getDevicesMatchingConnectionStates( - int[] states, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return Collections.emptyList(); - } - return service.getDevicesMatchingConnectionStates(states, source); - } - - @Override - public void registerClient( - ParcelUuid uuid, - IBluetoothGattCallback callback, - boolean eattSupport, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.registerClient(uuid.getUuid(), callback, eattSupport, source); - } - - @Override - public void unregisterClient(int clientIf, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.unregisterClient( - clientIf, source, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT); - } - - @Override - public void clientConnect( - int clientIf, - String address, - int addressType, - boolean isDirect, - int transport, - boolean opportunistic, - int phy, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.clientConnect( - clientIf, - address, - addressType, - isDirect, - transport, - opportunistic, - phy, - source); - } - - @Override - public void clientDisconnect(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.clientDisconnect(clientIf, address, source); - } - - @Override - public void clientSetPreferredPhy( - int clientIf, - String address, - int txPhy, - int rxPhy, - int phyOptions, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.clientSetPreferredPhy(clientIf, address, txPhy, rxPhy, phyOptions, source); - } - - @Override - public void clientReadPhy(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.clientReadPhy(clientIf, address, source); - } - - @Override - public void refreshDevice(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.refreshDevice(clientIf, address, source); - } - - @Override - public void discoverServices(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.discoverServices(clientIf, address, source); - } - - @Override - public void discoverServiceByUuid( - int clientIf, String address, ParcelUuid uuid, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.discoverServiceByUuid(clientIf, address, uuid.getUuid(), source); - } - - @Override - public void readCharacteristic( - int clientIf, String address, int handle, int authReq, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.readCharacteristic(clientIf, address, handle, authReq, source); - } - - @Override - public void readUsingCharacteristicUuid( - int clientIf, - String address, - ParcelUuid uuid, - int startHandle, - int endHandle, - int authReq, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.readUsingCharacteristicUuid( - clientIf, address, uuid.getUuid(), startHandle, endHandle, authReq, source); - } - - @Override - public int writeCharacteristic( - int clientIf, - String address, - int handle, - int writeType, - int authReq, - byte[] value, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - return service.writeCharacteristic( - clientIf, address, handle, writeType, authReq, value, source); - } - - @Override - public void readDescriptor( - int clientIf, String address, int handle, int authReq, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.readDescriptor(clientIf, address, handle, authReq, source); - } - - @Override - public int writeDescriptor( - int clientIf, - String address, - int handle, - int authReq, - byte[] value, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - return service.writeDescriptor(clientIf, address, handle, authReq, value, source); - } - - @Override - public void beginReliableWrite(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.beginReliableWrite(clientIf, address, source); - } - - @Override - public void endReliableWrite( - int clientIf, String address, boolean execute, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.endReliableWrite(clientIf, address, execute, source); - } - - @Override - public void registerForNotification( - int clientIf, - String address, - int handle, - boolean enable, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.registerForNotification(clientIf, address, handle, enable, source); - } - - @Override - public void readRemoteRssi(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.readRemoteRssi(clientIf, address, source); - } - - @Override - public void configureMTU(int clientIf, String address, int mtu, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.configureMTU(clientIf, address, mtu, source); - } - - @Override - public void connectionParameterUpdate( - int clientIf, String address, int connectionPriority, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.connectionParameterUpdate(clientIf, address, connectionPriority, source); - } - - @Override - public void leConnectionUpdate( - int clientIf, - String address, - int minConnectionInterval, - int maxConnectionInterval, - int peripheralLatency, - int supervisionTimeout, - int minConnectionEventLen, - int maxConnectionEventLen, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.leConnectionUpdate( - clientIf, - address, - minConnectionInterval, - maxConnectionInterval, - peripheralLatency, - supervisionTimeout, - minConnectionEventLen, - maxConnectionEventLen, - source); - } - - @Override - public int subrateModeRequest( - int clientIf, BluetoothDevice device, int subrateMode, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; - } - if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "subrateModeRequest")) { - return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED; - } - - if (!Utils.checkConnectPermissionForDataDelivery( - service, source, "GattService subrateModeRequest")) { - return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION; - } - - Utils.enforceCdmAssociationIfNotBluetoothPrivileged( - service, service.mCompanionDeviceManager, source, device); - - if (subrateMode < BluetoothGatt.SUBRATE_REQUEST_MODE_BALANCED - || subrateMode > BluetoothGatt.SUBRATE_REQUEST_MODE_LOW_POWER) { - throw new IllegalArgumentException("Subrate Mode not within valid range"); - } - - requireNonNull(device); - String address = device.getAddress(); - if (!BluetoothAdapter.checkBluetoothAddress(address)) { - throw new IllegalArgumentException("Invalid device address: " + address); - } - - return service.subrateModeRequest(clientIf, device, subrateMode); - } - - @Override - public void registerServer( - ParcelUuid uuid, - IBluetoothGattServerCallback callback, - boolean eattSupport, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.registerServer(uuid.getUuid(), callback, eattSupport, source); - } - - @Override - public void unregisterServer(int serverIf, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.unregisterServer(serverIf, source); - } - - @Override - public void serverConnect( - int serverIf, - String address, - int addressType, - boolean isDirect, - int transport, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.serverConnect(serverIf, address, addressType, isDirect, transport, source); - } - - @Override - public void serverDisconnect(int serverIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.serverDisconnect(serverIf, address, source); - } - - @Override - public void serverSetPreferredPhy( - int serverIf, - String address, - int txPhy, - int rxPhy, - int phyOptions, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.serverSetPreferredPhy(serverIf, address, txPhy, rxPhy, phyOptions, source); - } - - @Override - public void serverReadPhy(int clientIf, String address, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.serverReadPhy(clientIf, address, source); - } - - @Override - public void addService(int serverIf, BluetoothGattService svc, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.addService(serverIf, svc, source); - } - - @Override - public void removeService(int serverIf, int handle, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.removeService(serverIf, handle, source); - } - - @Override - public void clearServices(int serverIf, AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.clearServices(serverIf, source); - } - - @Override - public void sendResponse( - int serverIf, - String address, - int requestId, - int status, - int offset, - byte[] value, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.sendResponse(serverIf, address, requestId, status, offset, value, source); - } - - @Override - public int sendNotification( - int serverIf, - String address, - int handle, - boolean confirm, - byte[] value, - AttributionSource source) { - GattService service = getService(); - if (service == null) { - return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; - } - return service.sendNotification(serverIf, address, handle, confirm, value, source); - } - - @Override - public void disconnectAll(AttributionSource source) { - GattService service = getService(); - if (service == null) { - return; - } - service.disconnectAll(source); - } - } - /************************************************************************** * Callback functions - CLIENT *************************************************************************/ @@ -1426,8 +956,8 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) List<BluetoothDevice> getDevicesMatchingConnectionStates( int[] states, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService getDevicesMatchingConnectionStates")) { + if (!checkConnectPermissionForDataDelivery( + this, source, TAG, "getDevicesMatchingConnectionStates")) { return Collections.emptyList(); } @@ -1492,8 +1022,7 @@ public class GattService extends ProfileService { IBluetoothGattCallback callback, boolean eatt_support, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService registerClient")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "registerClient")) { return; } if (Flags.gattClientDynamicAllocation() @@ -1522,8 +1051,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void unregisterClient(int clientIf, AttributionSource source, ContextMap.RemoveReason reason) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService unregisterClient")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "unregisterClient")) { return; } @@ -1551,8 +1079,7 @@ public class GattService extends ProfileService { boolean opportunistic, int phy, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService clientConnect")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "clientConnect")) { return; } @@ -1642,8 +1169,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void clientDisconnect(int clientIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService clientDisconnect")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "clientDisconnect")) { return; } @@ -1681,8 +1207,7 @@ public class GattService extends ProfileService { int rxPhy, int phyOptions, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService clientSetPreferredPhy")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "clientSetPreferredPhy")) { return; } @@ -1705,8 +1230,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void clientReadPhy(int clientIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService clientReadPhy")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "clientReadPhy")) { return; } @@ -1724,8 +1248,8 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) synchronized List<ParcelUuid> getRegisteredServiceUuids(AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService getRegisteredServiceUuids")) { + if (!checkConnectPermissionForDataDelivery( + this, source, TAG, "getRegisteredServiceUuids")) { return Collections.emptyList(); } return mHandleMap.getEntries().stream() @@ -1735,8 +1259,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) List<String> getConnectedDevices(AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService getConnectedDevices")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "getConnectedDevices")) { return Collections.emptyList(); } @@ -1749,8 +1272,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void refreshDevice(int clientIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService refreshDevice")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "refreshDevice")) { return; } @@ -1760,8 +1282,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void discoverServices(int clientIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService discoverServices")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "discoverServices")) { return; } @@ -1786,8 +1307,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void discoverServiceByUuid(int clientIf, String address, UUID uuid, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService discoverServiceByUuid")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "discoverServiceByUuid")) { return; } @@ -1807,8 +1327,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void readCharacteristic( int clientIf, String address, int handle, int authReq, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService readCharacteristic")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "readCharacteristic")) { return; } @@ -1848,8 +1367,8 @@ public class GattService extends ProfileService { int endHandle, int authReq, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService readUsingCharacteristicUuid")) { + if (!checkConnectPermissionForDataDelivery( + this, source, TAG, "readUsingCharacteristicUuid")) { return; } @@ -1895,8 +1414,7 @@ public class GattService extends ProfileService { int authReq, byte[] value, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService writeCharacteristic")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "writeCharacteristic")) { return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION; } @@ -1941,8 +1459,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void readDescriptor( int clientIf, String address, int handle, int authReq, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService readDescriptor")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "readDescriptor")) { return; } @@ -1979,8 +1496,7 @@ public class GattService extends ProfileService { int authReq, byte[] value, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService writeDescriptor")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "writeDescriptor")) { return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION; } Log.v(TAG, "writeDescriptor() - address=" + toAnonymizedAddress(address)); @@ -2002,8 +1518,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void beginReliableWrite(int clientIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService beginReliableWrite")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "beginReliableWrite")) { return; } @@ -2013,8 +1528,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void endReliableWrite(int clientIf, String address, boolean execute, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService endReliableWrite")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "endReliableWrite")) { return; } @@ -2035,8 +1549,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void registerForNotification( int clientIf, String address, int handle, boolean enable, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService registerForNotification")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "registerForNotification")) { return; } @@ -2074,8 +1587,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void readRemoteRssi(int clientIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService readRemoteRssi")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "readRemoteRssi")) { return; } @@ -2085,8 +1597,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void configureMTU(int clientIf, String address, int mtu, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService configureMTU")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "configureMTU")) { return; } @@ -2104,8 +1615,8 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void connectionParameterUpdate( int clientIf, String address, int connectionPriority, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService connectionParameterUpdate")) { + if (!checkConnectPermissionForDataDelivery( + this, source, TAG, "connectionParameterUpdate")) { return; } @@ -2152,8 +1663,7 @@ public class GattService extends ProfileService { int minConnectionEventLen, int maxConnectionEventLen, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService leConnectionUpdate")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "leConnectionUpdate")) { return; } @@ -2186,7 +1696,7 @@ public class GattService extends ProfileService { maxConnectionEventLen); } - private int subrateModeRequest(int clientIf, BluetoothDevice device, int subrateMode) { + int subrateModeRequest(int clientIf, BluetoothDevice device, int subrateMode) { int subrateMin; int subrateMax; int maxLatency; @@ -2614,8 +2124,7 @@ public class GattService extends ProfileService { IBluetoothGattServerCallback callback, boolean eatt_support, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService registerServer")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "registerServer")) { return; } @@ -2627,8 +2136,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void unregisterServer(int serverIf, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService unregisterServer")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "unregisterServer")) { return; } @@ -2648,8 +2156,7 @@ public class GattService extends ProfileService { boolean isDirect, int transport, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService serverConnect")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "serverConnect")) { return; } @@ -2662,8 +2169,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void serverDisconnect(int serverIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService serverDisconnect")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "serverDisconnect")) { return; } @@ -2686,8 +2192,7 @@ public class GattService extends ProfileService { int rxPhy, int phyOptions, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService serverSetPreferredPhy")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "serverSetPreferredPhy")) { return; } @@ -2710,8 +2215,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void serverReadPhy(int serverIf, String address, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService serverReadPhy")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "serverReadPhy")) { return; } @@ -2729,7 +2233,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void addService(int serverIf, BluetoothGattService service, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery(this, source, "GattService addService")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "addService")) { return; } @@ -2774,8 +2278,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void removeService(int serverIf, int handle, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService removeService")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "removeService")) { return; } @@ -2786,8 +2289,7 @@ public class GattService extends ProfileService { @RequiresPermission(BLUETOOTH_CONNECT) void clearServices(int serverIf, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService clearServices")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "clearServices")) { return; } @@ -2804,8 +2306,7 @@ public class GattService extends ProfileService { int offset, byte[] value, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService sendResponse")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "sendResponse")) { return; } @@ -2854,8 +2355,7 @@ public class GattService extends ProfileService { boolean confirm, byte[] value, AttributionSource source) { - if (!Utils.checkConnectPermissionForDataDelivery( - this, source, "GattService sendNotification")) { + if (!checkConnectPermissionForDataDelivery(this, source, TAG, "sendNotification")) { return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION; } diff --git a/android/app/src/com/android/bluetooth/gatt/GattServiceBinder.java b/android/app/src/com/android/bluetooth/gatt/GattServiceBinder.java new file mode 100644 index 0000000000..7be287677c --- /dev/null +++ b/android/app/src/com/android/bluetooth/gatt/GattServiceBinder.java @@ -0,0 +1,499 @@ +/* + * 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.gatt; + +import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser; +import static com.android.bluetooth.Utils.checkConnectPermissionForDataDelivery; + +import static java.util.Objects.requireNonNull; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothGattService; +import android.bluetooth.BluetoothStatusCodes; +import android.bluetooth.IBluetoothGatt; +import android.bluetooth.IBluetoothGattCallback; +import android.bluetooth.IBluetoothGattServerCallback; +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; + +/** Handlers for incoming service calls */ +class GattServiceBinder extends IBluetoothGatt.Stub implements IProfileServiceBinder { + private static final String TAG = + GattServiceConfig.TAG_PREFIX + GattServiceBinder.class.getSimpleName(); + + private GattService mService; + + GattServiceBinder(GattService svc) { + mService = svc; + } + + @Override + public void cleanup() { + mService = null; + } + + private GattService getService() { + GattService service = mService; + + if (!Utils.checkServiceAvailable(service, TAG)) { + return null; + } + + return service; + } + + @Override + public List<BluetoothDevice> getDevicesMatchingConnectionStates( + int[] states, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return Collections.emptyList(); + } + return service.getDevicesMatchingConnectionStates(states, source); + } + + @Override + public void registerClient( + ParcelUuid uuid, + IBluetoothGattCallback callback, + boolean eattSupport, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.registerClient(uuid.getUuid(), callback, eattSupport, source); + } + + @Override + public void unregisterClient(int clientIf, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.unregisterClient( + clientIf, source, ContextMap.RemoveReason.REASON_UNREGISTER_CLIENT); + } + + @Override + public void clientConnect( + int clientIf, + String address, + int addressType, + boolean isDirect, + int transport, + boolean opportunistic, + int phy, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.clientConnect( + clientIf, address, addressType, isDirect, transport, opportunistic, phy, source); + } + + @Override + public void clientDisconnect(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.clientDisconnect(clientIf, address, source); + } + + @Override + public void clientSetPreferredPhy( + int clientIf, + String address, + int txPhy, + int rxPhy, + int phyOptions, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.clientSetPreferredPhy(clientIf, address, txPhy, rxPhy, phyOptions, source); + } + + @Override + public void clientReadPhy(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.clientReadPhy(clientIf, address, source); + } + + @Override + public void refreshDevice(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.refreshDevice(clientIf, address, source); + } + + @Override + public void discoverServices(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.discoverServices(clientIf, address, source); + } + + @Override + public void discoverServiceByUuid( + int clientIf, String address, ParcelUuid uuid, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.discoverServiceByUuid(clientIf, address, uuid.getUuid(), source); + } + + @Override + public void readCharacteristic( + int clientIf, String address, int handle, int authReq, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.readCharacteristic(clientIf, address, handle, authReq, source); + } + + @Override + public void readUsingCharacteristicUuid( + int clientIf, + String address, + ParcelUuid uuid, + int startHandle, + int endHandle, + int authReq, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.readUsingCharacteristicUuid( + clientIf, address, uuid.getUuid(), startHandle, endHandle, authReq, source); + } + + @Override + public int writeCharacteristic( + int clientIf, + String address, + int handle, + int writeType, + int authReq, + byte[] value, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + return service.writeCharacteristic( + clientIf, address, handle, writeType, authReq, value, source); + } + + @Override + public void readDescriptor( + int clientIf, String address, int handle, int authReq, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.readDescriptor(clientIf, address, handle, authReq, source); + } + + @Override + public int writeDescriptor( + int clientIf, + String address, + int handle, + int authReq, + byte[] value, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + return service.writeDescriptor(clientIf, address, handle, authReq, value, source); + } + + @Override + public void beginReliableWrite(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.beginReliableWrite(clientIf, address, source); + } + + @Override + public void endReliableWrite( + int clientIf, String address, boolean execute, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.endReliableWrite(clientIf, address, execute, source); + } + + @Override + public void registerForNotification( + int clientIf, String address, int handle, boolean enable, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.registerForNotification(clientIf, address, handle, enable, source); + } + + @Override + public void readRemoteRssi(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.readRemoteRssi(clientIf, address, source); + } + + @Override + public void configureMTU(int clientIf, String address, int mtu, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.configureMTU(clientIf, address, mtu, source); + } + + @Override + public void connectionParameterUpdate( + int clientIf, String address, int connectionPriority, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.connectionParameterUpdate(clientIf, address, connectionPriority, source); + } + + @Override + public void leConnectionUpdate( + int clientIf, + String address, + int minConnectionInterval, + int maxConnectionInterval, + int peripheralLatency, + int supervisionTimeout, + int minConnectionEventLen, + int maxConnectionEventLen, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.leConnectionUpdate( + clientIf, + address, + minConnectionInterval, + maxConnectionInterval, + peripheralLatency, + supervisionTimeout, + minConnectionEventLen, + maxConnectionEventLen, + source); + } + + @Override + public int subrateModeRequest( + int clientIf, BluetoothDevice device, int subrateMode, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED; + } + if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "subrateModeRequest")) { + return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED; + } + if (!checkConnectPermissionForDataDelivery(service, source, TAG, "subrateModeRequest")) { + return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION; + } + + Utils.enforceCdmAssociationIfNotBluetoothPrivileged( + service, service.getCompanionDeviceManager(), source, device); + + if (subrateMode < BluetoothGatt.SUBRATE_REQUEST_MODE_BALANCED + || subrateMode > BluetoothGatt.SUBRATE_REQUEST_MODE_LOW_POWER) { + throw new IllegalArgumentException("Subrate Mode not within valid range"); + } + + requireNonNull(device); + String address = device.getAddress(); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + throw new IllegalArgumentException("Invalid device address: " + address); + } + + return service.subrateModeRequest(clientIf, device, subrateMode); + } + + @Override + public void registerServer( + ParcelUuid uuid, + IBluetoothGattServerCallback callback, + boolean eattSupport, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.registerServer(uuid.getUuid(), callback, eattSupport, source); + } + + @Override + public void unregisterServer(int serverIf, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.unregisterServer(serverIf, source); + } + + @Override + public void serverConnect( + int serverIf, + String address, + int addressType, + boolean isDirect, + int transport, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.serverConnect(serverIf, address, addressType, isDirect, transport, source); + } + + @Override + public void serverDisconnect(int serverIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.serverDisconnect(serverIf, address, source); + } + + @Override + public void serverSetPreferredPhy( + int serverIf, + String address, + int txPhy, + int rxPhy, + int phyOptions, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.serverSetPreferredPhy(serverIf, address, txPhy, rxPhy, phyOptions, source); + } + + @Override + public void serverReadPhy(int clientIf, String address, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.serverReadPhy(clientIf, address, source); + } + + @Override + public void addService(int serverIf, BluetoothGattService svc, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.addService(serverIf, svc, source); + } + + @Override + public void removeService(int serverIf, int handle, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.removeService(serverIf, handle, source); + } + + @Override + public void clearServices(int serverIf, AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.clearServices(serverIf, source); + } + + @Override + public void sendResponse( + int serverIf, + String address, + int requestId, + int status, + int offset, + byte[] value, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.sendResponse(serverIf, address, requestId, status, offset, value, source); + } + + @Override + public int sendNotification( + int serverIf, + String address, + int handle, + boolean confirm, + byte[] value, + AttributionSource source) { + GattService service = getService(); + if (service == null) { + return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND; + } + return service.sendNotification(serverIf, address, handle, confirm, value, source); + } + + @Override + public void disconnectAll(AttributionSource source) { + GattService service = getService(); + if (service == null) { + return; + } + service.disconnectAll(source); + } +} 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/hfp/HeadsetNativeInterface.java b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java index 9d819eca5b..5afe78cee5 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetNativeInterface.java @@ -357,10 +357,11 @@ public class HeadsetNativeInterface { * Start voice recognition * * @param device target headset + * @param sendResult whether a BVRA response should be sent * @return True on success, False on failure */ - boolean startVoiceRecognition(BluetoothDevice device) { - return startVoiceRecognitionNative(getByteAddress(device)); + boolean startVoiceRecognition(BluetoothDevice device, boolean sendResult) { + return startVoiceRecognitionNative(getByteAddress(device), sendResult); } /** @@ -561,7 +562,7 @@ public class HeadsetNativeInterface { private native boolean isVoiceRecognitionSupportedNative(byte[] address); - private native boolean startVoiceRecognitionNative(byte[] address); + private native boolean startVoiceRecognitionNative(byte[] address, boolean sendResult); private native boolean stopVoiceRecognitionNative(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/hfp/HeadsetStateMachine.java b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index 4e8e14a547..6ea741af32 100644 --- a/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/android/app/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -1044,7 +1044,8 @@ class HeadsetStateMachine extends StateMachine { + " is not currentDevice"); break; } - if (!mNativeInterface.startVoiceRecognition(mDevice)) { + if (!mNativeInterface.startVoiceRecognition( + mDevice, /* sendResult */ true)) { stateLogW("Failed to start voice recognition"); break; } @@ -1122,6 +1123,9 @@ class HeadsetStateMachine extends StateMachine { ? HeadsetHalConstants.AT_RESPONSE_OK : HeadsetHalConstants.AT_RESPONSE_ERROR, 0); + if (Utils.isScoManagedByAudioEnabled()) { + mNativeInterface.startVoiceRecognition(mDevice, /* sendResult */ false); + } break; } case DIALING_OUT_RESULT: 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/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..1eac6ed336 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 @@ -713,207 +709,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 +777,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 +867,6 @@ public class HidHostService extends ProfileService { /** * @see BluetoothHidHost#getPreferredTransport */ - @VisibleForTesting int getPreferredTransport(BluetoothDevice device) { Log.d(TAG, "getPreferredTransport: device=" + device); @@ -1082,7 +875,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 +886,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 +897,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 +910,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 +927,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 +943,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 +1180,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_scan/AppScanStats.java b/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java index b5090eabd7..07d7209b40 100644 --- a/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java +++ b/android/app/src/com/android/bluetooth/le_scan/AppScanStats.java @@ -34,7 +34,6 @@ import com.android.bluetooth.BluetoothStatsLog; import com.android.bluetooth.Utils.TimeProvider; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.MetricsLogger; -import com.android.bluetooth.flags.Flags; import com.android.bluetooth.util.WorkSourceUtil; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -395,24 +394,22 @@ class AppScanStats { private void recordScanAppCountMetricsStart(LastScan scan) { MetricsLogger logger = MetricsLogger.getInstance(); logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_TOTAL_ENABLE, 1); - if (Flags.bleScanAdvMetricsRedesign()) { - logger.logAppScanStateChanged( - mWorkSourceUtil.getUids(), - mWorkSourceUtil.getTags(), - true /* enabled */, - scan.isFilterScan, - scan.isCallbackScan, - convertScanCallbackType(scan.scanCallbackType), - convertScanType(scan), - convertScanMode(scan.scanMode), - scan.reportDelayMillis, - 0 /* app_scan_duration_ms */, - mOngoingScans.size(), - sIsScreenOn, - isAppDead, - mAppImportance, - scan.getAttributionTag()); - } + logger.logAppScanStateChanged( + mWorkSourceUtil.getUids(), + mWorkSourceUtil.getTags(), + true /* enabled */, + scan.isFilterScan, + scan.isCallbackScan, + convertScanCallbackType(scan.scanCallbackType), + convertScanType(scan), + convertScanMode(scan.scanMode), + scan.reportDelayMillis, + 0 /* app_scan_duration_ms */, + mOngoingScans.size(), + sIsScreenOn, + isAppDead, + mAppImportance, + scan.getAttributionTag()); if (scan.isAutoBatchScan) { logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_AUTO_BATCH_ENABLE, 1); } else if (scan.isBatchScan) { @@ -429,24 +426,22 @@ class AppScanStats { private void recordScanAppCountMetricsStop(LastScan scan) { MetricsLogger logger = MetricsLogger.getInstance(); logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_TOTAL_DISABLE, 1); - if (Flags.bleScanAdvMetricsRedesign()) { - logger.logAppScanStateChanged( - mWorkSourceUtil.getUids(), - mWorkSourceUtil.getTags(), - false /* enabled */, - scan.isFilterScan, - scan.isCallbackScan, - convertScanCallbackType(scan.scanCallbackType), - convertScanType(scan), - convertScanMode(scan.scanMode), - scan.reportDelayMillis, - scan.duration, - mOngoingScans.size(), - sIsScreenOn, - isAppDead, - mAppImportance, - scan.getAttributionTag()); - } + logger.logAppScanStateChanged( + mWorkSourceUtil.getUids(), + mWorkSourceUtil.getTags(), + false /* enabled */, + scan.isFilterScan, + scan.isCallbackScan, + convertScanCallbackType(scan.scanCallbackType), + convertScanType(scan), + convertScanMode(scan.scanMode), + scan.reportDelayMillis, + scan.duration, + mOngoingScans.size(), + sIsScreenOn, + isAppDead, + mAppImportance, + scan.getAttributionTag()); if (scan.isAutoBatchScan) { logger.cacheCount(BluetoothProtoEnums.LE_SCAN_COUNT_AUTO_BATCH_DISABLE, 1); } else if (scan.isBatchScan) { @@ -523,49 +518,43 @@ class AppScanStats { } synchronized void recordScanTimeoutCountMetrics(int scannerId, long scanTimeoutMillis) { - if (Flags.bleScanAdvMetricsRedesign()) { - BluetoothStatsLog.write( - BluetoothStatsLog.LE_SCAN_ABUSED, - mWorkSourceUtil.getUids(), - mWorkSourceUtil.getTags(), - convertScanType(getScanFromScannerId(scannerId)), - BluetoothStatsLog.LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_SCAN_TIMEOUT, - scanTimeoutMillis, - getAttributionTagFromScannerId(scannerId)); - } + BluetoothStatsLog.write( + BluetoothStatsLog.LE_SCAN_ABUSED, + mWorkSourceUtil.getUids(), + mWorkSourceUtil.getTags(), + convertScanType(getScanFromScannerId(scannerId)), + BluetoothStatsLog.LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_SCAN_TIMEOUT, + scanTimeoutMillis, + getAttributionTagFromScannerId(scannerId)); MetricsLogger.getInstance() .cacheCount(BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_SCAN_TIMEOUT, 1); } synchronized void recordHwFilterNotAvailableCountMetrics( int scannerId, long numOfFilterSupported) { - if (Flags.bleScanAdvMetricsRedesign()) { - BluetoothStatsLog.write( - BluetoothStatsLog.LE_SCAN_ABUSED, - mWorkSourceUtil.getUids(), - mWorkSourceUtil.getTags(), - convertScanType(getScanFromScannerId(scannerId)), - BluetoothStatsLog.LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_HW_FILTER_NA, - numOfFilterSupported, - getAttributionTagFromScannerId(scannerId)); - } + BluetoothStatsLog.write( + BluetoothStatsLog.LE_SCAN_ABUSED, + mWorkSourceUtil.getUids(), + mWorkSourceUtil.getTags(), + convertScanType(getScanFromScannerId(scannerId)), + BluetoothStatsLog.LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_HW_FILTER_NA, + numOfFilterSupported, + getAttributionTagFromScannerId(scannerId)); MetricsLogger.getInstance() .cacheCount(BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_HW_FILTER_NOT_AVAILABLE, 1); } synchronized void recordTrackingHwFilterNotAvailableCountMetrics( int scannerId, long numOfTrackableAdv) { - if (Flags.bleScanAdvMetricsRedesign()) { - BluetoothStatsLog.write( - BluetoothStatsLog.LE_SCAN_ABUSED, - mWorkSourceUtil.getUids(), - mWorkSourceUtil.getTags(), - convertScanType(getScanFromScannerId(scannerId)), - BluetoothStatsLog - .LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_TRACKING_HW_FILTER_NA, - numOfTrackableAdv, - getAttributionTagFromScannerId(scannerId)); - } + BluetoothStatsLog.write( + BluetoothStatsLog.LE_SCAN_ABUSED, + mWorkSourceUtil.getUids(), + mWorkSourceUtil.getTags(), + convertScanType(getScanFromScannerId(scannerId)), + BluetoothStatsLog + .LE_SCAN_ABUSED__LE_SCAN_ABUSE_REASON__REASON_TRACKING_HW_FILTER_NA, + numOfTrackableAdv, + getAttributionTagFromScannerId(scannerId)); MetricsLogger.getInstance() .cacheCount( BluetoothProtoEnums.LE_SCAN_ABUSE_COUNT_TRACKING_HW_FILTER_NOT_AVAILABLE, @@ -608,10 +597,6 @@ class AppScanStats { return false; } recordScanRadioDurationMetrics(timeProvider); - if (!Flags.bleScanAdvMetricsRedesign()) { - sRadioStartTime = 0; - sIsRadioStarted = false; - } } return true; } @@ -627,21 +612,19 @@ class AppScanStats { double scanWeight = getScanWeight(sRadioScanMode) * 0.01; long weightedDuration = (long) (radioScanDuration * scanWeight); - if (Flags.bleScanAdvMetricsRedesign()) { - logger.logRadioScanStopped( - getRadioScanUids(), - getRadioScanTags(), - sRadioScanType, - convertScanMode(sRadioScanMode), - sRadioScanIntervalMs, - sRadioScanWindowMs, - sIsScreenOn, - radioScanDuration, - sRadioScanAppImportance, - getRadioScanAttributionTag()); - sRadioStartTime = 0; - sIsRadioStarted = false; - } + logger.logRadioScanStopped( + getRadioScanUids(), + getRadioScanTags(), + sRadioScanType, + convertScanMode(sRadioScanMode), + sRadioScanIntervalMs, + sRadioScanWindowMs, + sIsScreenOn, + radioScanDuration, + sRadioScanAppImportance, + getRadioScanAttributionTag()); + sRadioStartTime = 0; + sIsRadioStarted = false; if (weightedDuration > 0) { logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RADIO_DURATION_REGULAR, weightedDuration); if (sIsScreenOn) { @@ -711,16 +694,14 @@ class AppScanStats { if (!sIsRadioStarted) { return; } - if (Flags.bleScanAdvMetricsRedesign()) { - BluetoothStatsLog.write( - BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED, - getRadioScanUids(), - getRadioScanTags(), - 1 /* num_results */, - BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED__LE_SCAN_TYPE__SCAN_TYPE_REGULAR, - sIsScreenOn, - getRadioScanAttributionTag()); - } + BluetoothStatsLog.write( + BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED, + getRadioScanUids(), + getRadioScanTags(), + 1 /* num_results */, + BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED__LE_SCAN_TYPE__SCAN_TYPE_REGULAR, + sIsScreenOn, + getRadioScanAttributionTag()); MetricsLogger logger = MetricsLogger.getInstance(); logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_REGULAR, 1); if (sIsScreenOn) { @@ -736,16 +717,14 @@ class AppScanStats { synchronized (sLock) { isScreenOn = sIsScreenOn; } - if (Flags.bleScanAdvMetricsRedesign()) { - BluetoothStatsLog.write( - BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED, - getRadioScanUids(), - getRadioScanTags(), - numRecords, - BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED__LE_SCAN_TYPE__SCAN_TYPE_BATCH, - sIsScreenOn, - getRadioScanAttributionTag()); - } + BluetoothStatsLog.write( + BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED, + getRadioScanUids(), + getRadioScanTags(), + numRecords, + BluetoothStatsLog.LE_SCAN_RESULT_RECEIVED__LE_SCAN_TYPE__SCAN_TYPE_BATCH, + sIsScreenOn, + getRadioScanAttributionTag()); MetricsLogger logger = MetricsLogger.getInstance(); logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH_BUNDLE, 1); logger.cacheCount(BluetoothProtoEnums.LE_SCAN_RESULTS_COUNT_BATCH, numRecords); diff --git a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java index f2e1799921..e31bd36bbb 100644 --- a/android/app/src/com/android/bluetooth/le_scan/ScanManager.java +++ b/android/app/src/com/android/bluetooth/le_scan/ScanManager.java @@ -1292,18 +1292,6 @@ public class ScanManager { && client.mSettings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { Log.d(TAG, "start gattClientScanNative from startRegularScan()"); mNativeInterface.gattClientScan(true); - if (!Flags.bleScanAdvMetricsRedesign()) { - if (client.mStats != null - && !AppScanStats.recordScanRadioStart( - client.mScanModeApp, - client.mScannerId, - client.mStats, - getScanWindowMillis(client.mSettings), - getScanIntervalMillis(client.mSettings), - mTimeProvider)) { - Log.w(TAG, "Scan radio already started"); - } - } } } 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/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/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/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/BluetoothOppService.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppService.java index 2548b9e45c..46bed2ba89 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; } 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/pan/PanService.java b/android/app/src/com/android/bluetooth/pan/PanService.java index 311e100b52..5c2dfdf2a7 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; @@ -60,7 +55,6 @@ 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; @@ -154,7 +148,7 @@ public class PanService extends ProfileService { @Override public IProfileServiceBinder initBinder() { - return new BluetoothPanBinder(this); + return new PanServiceBinder(this); } public static synchronized PanService getPanService() { @@ -276,137 +270,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"); 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/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/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/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/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/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 01d195cc43..a5b7dc36c8 100644 --- a/android/app/tests/unit/Android.bp +++ b/android/app/tests/unit/Android.bp @@ -35,6 +35,7 @@ android_test { "androidx.room_room-runtime", "androidx.room_room-testing", "androidx.test.espresso.intents", + "androidx.test.ext.junit", "androidx.test.ext.truth", "androidx.test.rules", "androidx.test.uiautomator_uiautomator", @@ -45,6 +46,7 @@ android_test { "gson", "guava-android-testlib", "mmslib", + "mockito-kotlin2", "mockito-target-extended", "modules-utils-handlerexecutor", "platform-parametric-runner-lib", @@ -61,8 +63,11 @@ android_test { jarjar_rules: ":bluetooth-jarjar-rules", asset_dirs: ["src/com/android/bluetooth/btservice/storage/schemas"], - // Include all test java files. - srcs: ["src/**/*.java"], + // Include all test java and kotlin files. + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], jacoco: { include_filter: ["android.bluetooth.*"], exclude_filter: [], 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..efd371dfdd 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 @@ -34,41 +34,41 @@ 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 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); - verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, null); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { mBinder.getConnectionState(mDevice, null); - verify(mService).getConnectionState(mDevice); } 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/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/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/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/gatt/AdvertiseBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java deleted file mode 100644 index c78c9ec6db..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * 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.gatt; - -import static com.android.bluetooth.TestUtils.MockitoRule; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.bluetooth.BluetoothManager; -import android.bluetooth.le.AdvertiseData; -import android.bluetooth.le.AdvertisingSetParameters; -import android.bluetooth.le.IAdvertisingSetCallback; -import android.bluetooth.le.PeriodicAdvertisingParameters; -import android.content.AttributionSource; - -import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.bluetooth.btservice.AdapterService; - -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 AdvertiseBinder}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AdvertiseBinderTest { - @Rule public final MockitoRule mMockitoRule = new MockitoRule(); - - @Mock private AdapterService mAdapterService; - @Mock private AdvertiseManager mAdvertiseManager; - - private final AttributionSource mAttributionSource = - InstrumentationRegistry.getInstrumentation() - .getTargetContext() - .getSystemService(BluetoothManager.class) - .getAdapter() - .getAttributionSource(); - private AdvertiseBinder mBinder; - - @Before - public void setUp() { - doAnswer( - invocation -> { - ((Runnable) invocation.getArgument(0)).run(); - return null; - }) - .when(mAdvertiseManager) - .doOnAdvertiseThread(any()); - mBinder = new AdvertiseBinder(mAdapterService, mAdvertiseManager); - } - - @Test - public void startAdvertisingSet() { - AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build(); - AdvertiseData advertiseData = new AdvertiseData.Builder().build(); - AdvertiseData scanResponse = new AdvertiseData.Builder().build(); - PeriodicAdvertisingParameters periodicParameters = - new PeriodicAdvertisingParameters.Builder().build(); - AdvertiseData periodicData = new AdvertiseData.Builder().build(); - int duration = 1; - int maxExtAdvEvents = 2; - int serverIf = 3; - IAdvertisingSetCallback callback = mock(IAdvertisingSetCallback.class); - - mBinder.startAdvertisingSet( - parameters, - advertiseData, - scanResponse, - periodicParameters, - periodicData, - duration, - maxExtAdvEvents, - serverIf, - callback, - mAttributionSource); - - verify(mAdvertiseManager) - .startAdvertisingSet( - parameters, - advertiseData, - scanResponse, - periodicParameters, - periodicData, - duration, - maxExtAdvEvents, - serverIf, - callback, - mAttributionSource); - } - - @Test - public void stopAdvertisingSet() { - IAdvertisingSetCallback callback = mock(IAdvertisingSetCallback.class); - - mBinder.stopAdvertisingSet(callback, mAttributionSource); - - verify(mAdvertiseManager).stopAdvertisingSet(callback); - } - - @Test - public void setAdvertisingData() { - int advertiserId = 1; - AdvertiseData data = new AdvertiseData.Builder().build(); - - mBinder.setAdvertisingData(advertiserId, data, mAttributionSource); - verify(mAdvertiseManager).setAdvertisingData(advertiserId, data); - } - - @Test - public void setAdvertisingParameters() { - int advertiserId = 1; - AdvertisingSetParameters parameters = new AdvertisingSetParameters.Builder().build(); - - mBinder.setAdvertisingParameters(advertiserId, parameters, mAttributionSource); - verify(mAdvertiseManager).setAdvertisingParameters(advertiserId, parameters); - } - - @Test - public void setPeriodicAdvertisingData() { - int advertiserId = 1; - AdvertiseData data = new AdvertiseData.Builder().build(); - - mBinder.setPeriodicAdvertisingData(advertiserId, data, mAttributionSource); - verify(mAdvertiseManager).setPeriodicAdvertisingData(advertiserId, data); - } - - @Test - public void setPeriodicAdvertisingEnable() { - int advertiserId = 1; - boolean enable = true; - - mBinder.setPeriodicAdvertisingEnable(advertiserId, enable, mAttributionSource); - verify(mAdvertiseManager).setPeriodicAdvertisingEnable(advertiserId, enable); - } - - @Test - public void setPeriodicAdvertisingParameters() { - int advertiserId = 1; - PeriodicAdvertisingParameters parameters = - new PeriodicAdvertisingParameters.Builder().build(); - - mBinder.setPeriodicAdvertisingParameters(advertiserId, parameters, mAttributionSource); - verify(mAdvertiseManager).setPeriodicAdvertisingParameters(advertiserId, parameters); - } - - @Test - public void setScanResponseData() { - int advertiserId = 1; - AdvertiseData data = new AdvertiseData.Builder().build(); - - mBinder.setScanResponseData(advertiserId, data, mAttributionSource); - verify(mAdvertiseManager).setScanResponseData(advertiserId, data); - } - - @Test - public void getOwnAddress() { - int advertiserId = 1; - - mBinder.getOwnAddress(advertiserId, mAttributionSource); - verify(mAdvertiseManager).getOwnAddress(advertiserId); - } - - @Test - public void enableAdvertisingSet() { - int advertiserId = 1; - boolean enable = true; - int duration = 3; - int maxExtAdvEvents = 4; - - mBinder.enableAdvertisingSet( - advertiserId, enable, duration, maxExtAdvEvents, mAttributionSource); - verify(mAdvertiseManager) - .enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents); - } - - @Test - public void cleanUp_doesNotCrash() { - mBinder.cleanup(); - } -} diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.kt b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.kt new file mode 100644 index 0000000000..6b038678f0 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AdvertiseBinderTest.kt @@ -0,0 +1,201 @@ +/* + * 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.gatt + +import android.bluetooth.BluetoothManager +import android.bluetooth.le.AdvertiseData +import android.bluetooth.le.AdvertisingSetParameters +import android.bluetooth.le.IAdvertisingSetCallback +import android.bluetooth.le.PeriodicAdvertisingParameters +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.bluetooth.TestUtils.MockitoRule +import com.android.bluetooth.btservice.AdapterService +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.any +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.kotlin.whenever + +/** Test cases for [AdvertiseBinder] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class AdvertiseBinderTest { + + @get:Rule val mockitoRule = MockitoRule() + + @Mock private lateinit var adapterService: AdapterService + @Mock private lateinit var advertiseManager: AdvertiseManager + + private val attributionSource = + InstrumentationRegistry.getInstrumentation() + .targetContext + .getSystemService(BluetoothManager::class.java) + .adapter + .attributionSource + + private lateinit var binder: AdvertiseBinder + + @Before + fun setUp() { + doAnswer { invocation -> + (invocation.getArgument(0) as Runnable).run() + null + } + .whenever(advertiseManager) + .doOnAdvertiseThread(any()) + binder = AdvertiseBinder(adapterService, advertiseManager) + } + + @Test + fun startAdvertisingSet() { + val parameters = AdvertisingSetParameters.Builder().build() + val advertiseData = AdvertiseData.Builder().build() + val scanResponse = AdvertiseData.Builder().build() + val periodicParameters = PeriodicAdvertisingParameters.Builder().build() + val periodicData = AdvertiseData.Builder().build() + val duration = 1 + val maxExtAdvEvents = 2 + val serverIf = 3 + val callback = mock(IAdvertisingSetCallback::class.java) + + binder.startAdvertisingSet( + parameters, + advertiseData, + scanResponse, + periodicParameters, + periodicData, + duration, + maxExtAdvEvents, + serverIf, + callback, + attributionSource, + ) + verify(advertiseManager) + .startAdvertisingSet( + parameters, + advertiseData, + scanResponse, + periodicParameters, + periodicData, + duration, + maxExtAdvEvents, + serverIf, + callback, + attributionSource, + ) + } + + @Test + fun stopAdvertisingSet() { + val callback = mock(IAdvertisingSetCallback::class.java) + + binder.stopAdvertisingSet(callback, attributionSource) + verify(advertiseManager).stopAdvertisingSet(callback) + } + + @Test + fun setAdvertisingData() { + val advertiserId = 1 + val data = AdvertiseData.Builder().build() + + binder.setAdvertisingData(advertiserId, data, attributionSource) + verify(advertiseManager).setAdvertisingData(advertiserId, data) + } + + @Test + fun setAdvertisingParameters() { + val advertiserId = 1 + val parameters = AdvertisingSetParameters.Builder().build() + + binder.setAdvertisingParameters(advertiserId, parameters, attributionSource) + verify(advertiseManager).setAdvertisingParameters(advertiserId, parameters) + } + + @Test + fun setPeriodicAdvertisingData() { + val advertiserId = 1 + val data = AdvertiseData.Builder().build() + + binder.setPeriodicAdvertisingData(advertiserId, data, attributionSource) + verify(advertiseManager).setPeriodicAdvertisingData(advertiserId, data) + } + + @Test + fun setPeriodicAdvertisingEnable() { + val advertiserId = 1 + val enable = true + + binder.setPeriodicAdvertisingEnable(advertiserId, enable, attributionSource) + verify(advertiseManager).setPeriodicAdvertisingEnable(advertiserId, enable) + } + + @Test + fun setPeriodicAdvertisingParameters() { + val advertiserId = 1 + val parameters = PeriodicAdvertisingParameters.Builder().build() + + binder.setPeriodicAdvertisingParameters(advertiserId, parameters, attributionSource) + verify(advertiseManager).setPeriodicAdvertisingParameters(advertiserId, parameters) + } + + @Test + fun setScanResponseData() { + val advertiserId = 1 + val data = AdvertiseData.Builder().build() + + binder.setScanResponseData(advertiserId, data, attributionSource) + verify(advertiseManager).setScanResponseData(advertiserId, data) + } + + @Test + fun getOwnAddress() { + val advertiserId = 1 + + binder.getOwnAddress(advertiserId, attributionSource) + verify(advertiseManager).getOwnAddress(advertiserId) + } + + @Test + fun enableAdvertisingSet() { + val advertiserId = 1 + val enable = true + val duration = 3 + val maxExtAdvEvents = 4 + + binder.enableAdvertisingSet( + advertiserId, + enable, + duration, + maxExtAdvEvents, + attributionSource, + ) + verify(advertiseManager) + .enableAdvertisingSet(advertiserId, enable, duration, maxExtAdvEvents) + } + + @Test + fun cleanup_doesNotCrash() { + binder.cleanup() + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java index 01c4000778..e2bcfb75e2 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/AppAdvertiseStatsTest.java @@ -305,7 +305,6 @@ public class AppAdvertiseStatsTest { } @Test - @EnableFlags(Flags.FLAG_BLE_SCAN_ADV_METRICS_REDESIGN) public void testAdvertiseCounterMetrics() { int appUid = 0; int id = 1; diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java deleted file mode 100644 index a65c805a47..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.gatt; - -import static com.android.bluetooth.TestUtils.MockitoRule; -import static com.android.bluetooth.TestUtils.getTestDevice; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -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.BluetoothManager; -import android.bluetooth.le.DistanceMeasurementMethod; -import android.bluetooth.le.DistanceMeasurementParams; -import android.bluetooth.le.IDistanceMeasurementCallback; -import android.content.AttributionSource; -import android.os.ParcelUuid; - -import androidx.test.filters.SmallTest; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.bluetooth.btservice.AdapterService; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; - -import java.util.Collections; -import java.util.UUID; - -/** Test cases for {@link DistanceMeasurementBinder}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class DistanceMeasurementBinderTest { - @Rule public final MockitoRule mMockitoRule = new MockitoRule(); - - @Mock private DistanceMeasurementManager mDistanceMeasurementManager; - @Mock private AdapterService mAdapterService; - - private final AttributionSource mAttributionSource = - InstrumentationRegistry.getInstrumentation() - .getTargetContext() - .getSystemService(BluetoothManager.class) - .getAdapter() - .getAttributionSource(); - - private DistanceMeasurementBinder mBinder; - - @Before - public void setUp() throws Throwable { - mBinder = new DistanceMeasurementBinder(mAdapterService, mDistanceMeasurementManager); - when(mDistanceMeasurementManager.getSupportedDistanceMeasurementMethods()) - .thenReturn(Collections.emptyList()); - when(mDistanceMeasurementManager.runOnDistanceMeasurementThreadAndWaitForResult(any())) - .thenAnswer( - invocationOnMock -> { - DistanceMeasurementManager.GetResultTask task = - invocationOnMock.getArgument(0); - return task.getResult(); - }); - doAnswer( - invocation -> { - ((Runnable) (invocation.getArgument(0))).run(); - return null; - }) - .when(mDistanceMeasurementManager) - .postOnDistanceMeasurementThread(any()); - } - - @Test - public void getSupportedDistanceMeasurementMethods() { - mBinder.getSupportedDistanceMeasurementMethods(mAttributionSource); - verify(mDistanceMeasurementManager).getSupportedDistanceMeasurementMethods(); - } - - @Test - public void startDistanceMeasurement() { - UUID uuid = UUID.randomUUID(); - BluetoothDevice device = getTestDevice(3); - DistanceMeasurementParams params = - new DistanceMeasurementParams.Builder(device) - .setDurationSeconds(123) - .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW) - .build(); - IDistanceMeasurementCallback callback = mock(IDistanceMeasurementCallback.class); - mBinder.startDistanceMeasurement( - new ParcelUuid(uuid), params, callback, mAttributionSource); - verify(mDistanceMeasurementManager).startDistanceMeasurement(uuid, params, callback); - } - - @Test - public void stopDistanceMeasurement() { - UUID uuid = UUID.randomUUID(); - BluetoothDevice device = getTestDevice(3); - int method = DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI; - mBinder.stopDistanceMeasurement(new ParcelUuid(uuid), device, method, mAttributionSource); - verify(mDistanceMeasurementManager).stopDistanceMeasurement(uuid, device, method, false); - } -} diff --git a/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.kt b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.kt new file mode 100644 index 0000000000..b20eda9a17 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/gatt/DistanceMeasurementBinderTest.kt @@ -0,0 +1,112 @@ +/* + * 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.gatt + +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.bluetooth.le.DistanceMeasurementMethod +import android.bluetooth.le.DistanceMeasurementParams +import android.bluetooth.le.IDistanceMeasurementCallback +import android.os.ParcelUuid +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.bluetooth.TestUtils.MockitoRule +import com.android.bluetooth.TestUtils.getTestDevice +import com.android.bluetooth.btservice.AdapterService +import com.android.bluetooth.gatt.DistanceMeasurementManager.GetResultTask +import java.util.UUID +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.any +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.mock +import org.mockito.Mockito.verify +import org.mockito.kotlin.whenever + +/** Test cases for [DistanceMeasurementBinder] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class DistanceMeasurementBinderTest { + + @get:Rule val mockitoRule = MockitoRule() + + @Mock private lateinit var distanceMeasurementManager: DistanceMeasurementManager + @Mock private lateinit var adapterService: AdapterService + + private val attributionSource = + InstrumentationRegistry.getInstrumentation() + .targetContext + .getSystemService(BluetoothManager::class.java) + .adapter + .attributionSource + + private lateinit var binder: DistanceMeasurementBinder + + @Before + fun setUp() { + binder = DistanceMeasurementBinder(adapterService, distanceMeasurementManager) + doReturn(emptyList<DistanceMeasurementMethod>()) + .whenever(distanceMeasurementManager) + .getSupportedDistanceMeasurementMethods() + doAnswer { invocationOnMock -> + val task = invocationOnMock.getArgument<GetResultTask<*>>(0) + task.result + } + .whenever(distanceMeasurementManager) + .runOnDistanceMeasurementThreadAndWaitForResult(any<GetResultTask<*>>()) + doAnswer { invocation -> + (invocation.getArgument(0) as Runnable).run() + null + } + .whenever(distanceMeasurementManager) + .postOnDistanceMeasurementThread(any()) + } + + @Test + fun getSupportedDistanceMeasurementMethods() { + binder.getSupportedDistanceMeasurementMethods(attributionSource) + verify(distanceMeasurementManager).supportedDistanceMeasurementMethods + } + + @Test + fun startDistanceMeasurement() { + val uuid = UUID.randomUUID() + val device: BluetoothDevice = getTestDevice(3) + val params = + DistanceMeasurementParams.Builder(device) + .setDurationSeconds(123) + .setFrequency(DistanceMeasurementParams.REPORT_FREQUENCY_LOW) + .build() + val callback = mock(IDistanceMeasurementCallback::class.java) + binder.startDistanceMeasurement(ParcelUuid(uuid), params, callback, attributionSource) + verify(distanceMeasurementManager).startDistanceMeasurement(uuid, params, callback) + } + + @Test + fun stopDistanceMeasurement() { + val uuid = UUID.randomUUID() + val device: BluetoothDevice = getTestDevice(3) + val method = DistanceMeasurementMethod.DISTANCE_MEASUREMENT_METHOD_RSSI + binder.stopDistanceMeasurement(ParcelUuid(uuid), device, method, attributionSource) + verify(distanceMeasurementManager).stopDistanceMeasurement(uuid, device, method, false) + } +} 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 36e96f6007..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 { @@ -53,13 +54,12 @@ public class GattServiceBinderTest { @Mock private GattService mService; private AttributionSource mAttributionSource; - - private GattService.BluetoothGattBinder mBinder; + private GattServiceBinder mBinder; @Before public void setUp() throws Exception { when(mService.isAvailable()).thenReturn(true); - mBinder = new GattService.BluetoothGattBinder(mService); + mBinder = new GattServiceBinder(mService); mAttributionSource = new AttributionSource.Builder(1).build(); } @@ -68,7 +68,6 @@ public class GattServiceBinderTest { int[] states = new int[] {STATE_CONNECTED}; mBinder.getDevicesMatchingConnectionStates(states, mAttributionSource); - verify(mService).getDevicesMatchingConnectionStates(states, mAttributionSource); } @@ -79,7 +78,6 @@ public class GattServiceBinderTest { boolean eattSupport = true; mBinder.registerClient(new ParcelUuid(uuid), callback, eattSupport, mAttributionSource); - verify(mService).registerClient(uuid, callback, eattSupport, mAttributionSource); } @@ -88,7 +86,6 @@ public class GattServiceBinderTest { int clientIf = 3; mBinder.unregisterClient(clientIf, mAttributionSource); - verify(mService) .unregisterClient( clientIf, @@ -115,7 +112,6 @@ public class GattServiceBinderTest { opportunistic, phy, mAttributionSource); - verify(mService) .clientConnect( clientIf, @@ -134,7 +130,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.clientDisconnect(clientIf, address, mAttributionSource); - verify(mService).clientDisconnect(clientIf, address, mAttributionSource); } @@ -148,7 +143,6 @@ public class GattServiceBinderTest { mBinder.clientSetPreferredPhy( clientIf, address, txPhy, rxPhy, phyOptions, mAttributionSource); - verify(mService) .clientSetPreferredPhy( clientIf, address, txPhy, rxPhy, phyOptions, mAttributionSource); @@ -160,7 +154,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.clientReadPhy(clientIf, address, mAttributionSource); - verify(mService).clientReadPhy(clientIf, address, mAttributionSource); } @@ -170,7 +163,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.refreshDevice(clientIf, address, mAttributionSource); - verify(mService).refreshDevice(clientIf, address, mAttributionSource); } @@ -180,7 +172,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.discoverServices(clientIf, address, mAttributionSource); - verify(mService).discoverServices(clientIf, address, mAttributionSource); } @@ -191,7 +182,6 @@ public class GattServiceBinderTest { UUID uuid = UUID.randomUUID(); mBinder.discoverServiceByUuid(clientIf, address, new ParcelUuid(uuid), mAttributionSource); - verify(mService).discoverServiceByUuid(clientIf, address, uuid, mAttributionSource); } @@ -203,7 +193,6 @@ public class GattServiceBinderTest { int authReq = 3; mBinder.readCharacteristic(clientIf, address, handle, authReq, mAttributionSource); - verify(mService).readCharacteristic(clientIf, address, handle, authReq, mAttributionSource); } @@ -224,7 +213,6 @@ public class GattServiceBinderTest { endHandle, authReq, mAttributionSource); - verify(mService) .readUsingCharacteristicUuid( clientIf, @@ -247,7 +235,6 @@ public class GattServiceBinderTest { mBinder.writeCharacteristic( clientIf, address, handle, writeType, authReq, value, mAttributionSource); - verify(mService) .writeCharacteristic( clientIf, address, handle, writeType, authReq, value, mAttributionSource); @@ -261,7 +248,6 @@ public class GattServiceBinderTest { int authReq = 3; mBinder.readDescriptor(clientIf, address, handle, authReq, mAttributionSource); - verify(mService).readDescriptor(clientIf, address, handle, authReq, mAttributionSource); } @@ -274,7 +260,6 @@ public class GattServiceBinderTest { byte[] value = new byte[] {4, 5}; mBinder.writeDescriptor(clientIf, address, handle, authReq, value, mAttributionSource); - verify(mService) .writeDescriptor(clientIf, address, handle, authReq, value, mAttributionSource); } @@ -285,7 +270,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.beginReliableWrite(clientIf, address, mAttributionSource); - verify(mService).beginReliableWrite(clientIf, address, mAttributionSource); } @@ -296,7 +280,6 @@ public class GattServiceBinderTest { boolean execute = true; mBinder.endReliableWrite(clientIf, address, execute, mAttributionSource); - verify(mService).endReliableWrite(clientIf, address, execute, mAttributionSource); } @@ -308,7 +291,6 @@ public class GattServiceBinderTest { boolean enable = true; mBinder.registerForNotification(clientIf, address, handle, enable, mAttributionSource); - verify(mService) .registerForNotification(clientIf, address, handle, enable, mAttributionSource); } @@ -319,7 +301,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.readRemoteRssi(clientIf, address, mAttributionSource); - verify(mService).readRemoteRssi(clientIf, address, mAttributionSource); } @@ -330,7 +311,6 @@ public class GattServiceBinderTest { int mtu = 2; mBinder.configureMTU(clientIf, address, mtu, mAttributionSource); - verify(mService).configureMTU(clientIf, address, mtu, mAttributionSource); } @@ -342,7 +322,6 @@ public class GattServiceBinderTest { mBinder.connectionParameterUpdate( clientIf, address, connectionPriority, mAttributionSource); - verify(mService) .connectionParameterUpdate( clientIf, address, connectionPriority, mAttributionSource); @@ -369,7 +348,6 @@ public class GattServiceBinderTest { minConnectionEventLen, maxConnectionEventLen, mAttributionSource); - verify(mService) .leConnectionUpdate( clientIf, @@ -390,7 +368,6 @@ public class GattServiceBinderTest { boolean eattSupport = true; mBinder.registerServer(new ParcelUuid(uuid), callback, eattSupport, mAttributionSource); - verify(mService).registerServer(uuid, callback, eattSupport, mAttributionSource); } @@ -399,7 +376,6 @@ public class GattServiceBinderTest { int serverIf = 3; mBinder.unregisterServer(serverIf, mAttributionSource); - verify(mService).unregisterServer(serverIf, mAttributionSource); } @@ -413,7 +389,6 @@ public class GattServiceBinderTest { mBinder.serverConnect( serverIf, address, addressType, isDirect, transport, mAttributionSource); - verify(mService) .serverConnect( serverIf, address, addressType, isDirect, transport, mAttributionSource); @@ -425,7 +400,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.serverDisconnect(serverIf, address, mAttributionSource); - verify(mService).serverDisconnect(serverIf, address, mAttributionSource); } @@ -439,7 +413,6 @@ public class GattServiceBinderTest { mBinder.serverSetPreferredPhy( serverIf, address, txPhy, rxPhy, phyOptions, mAttributionSource); - verify(mService) .serverSetPreferredPhy( serverIf, address, txPhy, rxPhy, phyOptions, mAttributionSource); @@ -451,7 +424,6 @@ public class GattServiceBinderTest { String address = REMOTE_DEVICE_ADDRESS; mBinder.serverReadPhy(serverIf, address, mAttributionSource); - verify(mService).serverReadPhy(serverIf, address, mAttributionSource); } @@ -461,7 +433,6 @@ public class GattServiceBinderTest { BluetoothGattService svc = mock(BluetoothGattService.class); mBinder.addService(serverIf, svc, mAttributionSource); - verify(mService).addService(serverIf, svc, mAttributionSource); } @@ -471,7 +442,6 @@ public class GattServiceBinderTest { int handle = 2; mBinder.removeService(serverIf, handle, mAttributionSource); - verify(mService).removeService(serverIf, handle, mAttributionSource); } @@ -480,7 +450,6 @@ public class GattServiceBinderTest { int serverIf = 1; mBinder.clearServices(serverIf, mAttributionSource); - verify(mService).clearServices(serverIf, mAttributionSource); } @@ -495,7 +464,6 @@ public class GattServiceBinderTest { mBinder.sendResponse( serverIf, address, requestId, status, offset, value, mAttributionSource); - verify(mService) .sendResponse( serverIf, address, requestId, status, offset, value, mAttributionSource); @@ -510,7 +478,6 @@ public class GattServiceBinderTest { byte[] value = new byte[] {5, 6}; mBinder.sendNotification(serverIf, address, handle, confirm, value, mAttributionSource); - verify(mService) .sendNotification(serverIf, address, handle, confirm, value, mAttributionSource); } @@ -518,12 +485,11 @@ public class GattServiceBinderTest { @Test public void disconnectAll() throws Exception { mBinder.disconnectAll(mAttributionSource); - verify(mService).disconnectAll(mAttributionSource); } @Test - public void cleanUp_doesNotCrash() { + public void cleanup_doesNotCrash() { mBinder.cleanup(); } } 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/hfp/HeadsetServiceAndStateMachineTest.java b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java index 3edd1ff9ea..835f03aaa2 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/hfp/HeadsetServiceAndStateMachineTest.java @@ -172,7 +172,9 @@ public class HeadsetServiceAndStateMachineTest { doReturn(true).when(mNativeInterface).disconnectAudio(any(BluetoothDevice.class)); doReturn(true).when(mNativeInterface).setActiveDevice(any(BluetoothDevice.class)); doReturn(true).when(mNativeInterface).sendBsir(any(BluetoothDevice.class), anyBoolean()); - doReturn(true).when(mNativeInterface).startVoiceRecognition(any(BluetoothDevice.class)); + doReturn(true) + .when(mNativeInterface) + .startVoiceRecognition(any(BluetoothDevice.class), anyBoolean()); doReturn(true).when(mNativeInterface).stopVoiceRecognition(any(BluetoothDevice.class)); doReturn(true) .when(mNativeInterface) @@ -1138,7 +1140,7 @@ public class HeadsetServiceAndStateMachineTest { mTestLooper.dispatchAll(); verify(mNativeInterface).setActiveDevice(deviceA); assertThat(mHeadsetService.getActiveDevice()).isEqualTo(deviceA); - verify(mNativeInterface).startVoiceRecognition(deviceA); + verify(mNativeInterface).startVoiceRecognition(deviceA, true); verify(mAudioManager).setA2dpSuspended(true); verify(mAudioManager).setLeAudioSuspended(true); verify(mNativeInterface).connectAudio(deviceA); @@ -1685,6 +1687,7 @@ public class HeadsetServiceAndStateMachineTest { // has not add verification AudioDeviceInfo because it is final, unless add a wrapper mHeadsetService.startVoiceRecognition(device); mTestLooper.dispatchAll(); + verify(mNativeInterface).startVoiceRecognition(device, false); verify(mAudioManager, times(0)).setA2dpSuspended(true); verify(mAudioManager, times(0)).setLeAudioSuspended(true); verify(mNativeInterface, times(0)).connectAudio(device); @@ -1695,7 +1698,7 @@ public class HeadsetServiceAndStateMachineTest { assertThat(device).isNotNull(); assertThat(mHeadsetService.startVoiceRecognition(device)).isTrue(); mTestLooper.dispatchAll(); - verify(mNativeInterface).startVoiceRecognition(device); + verify(mNativeInterface).startVoiceRecognition(device, true); verify(mAudioManager).setA2dpSuspended(true); verify(mAudioManager).setLeAudioSuspended(true); verify(mNativeInterface).connectAudio(device); @@ -1727,7 +1730,7 @@ public class HeadsetServiceAndStateMachineTest { mHeadsetService.startVoiceRecognition(device); mTestLooper.dispatchAll(); // has not add verification AudioDeviceInfo because it is final, unless add a wrapper - verify(mNativeInterface).startVoiceRecognition(device); + verify(mNativeInterface).startVoiceRecognition(device, true); verify(mAudioManager, times(0)).setA2dpSuspended(true); verify(mAudioManager, times(0)).setLeAudioSuspended(true); verify(mNativeInterface, times(0)).connectAudio(device); 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..3a1a31d085 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 @@ -35,6 +35,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 { @@ -44,11 +45,11 @@ public class HeadsetClientServiceBinderTest { 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 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..e2b15ebdba 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 @@ -36,9 +36,11 @@ 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(); @@ -46,98 +48,90 @@ public class HidHostServiceBinderTest { 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); - verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { mBinder.disconnect(mDevice, null); - verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { mBinder.getConnectedDevices(null); - 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, null); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { mBinder.getConnectionState(mDevice, null); - verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { mBinder.getConnectionPolicy(mDevice, null); - verify(mService).getConnectionPolicy(mDevice); } @Test public void setPreferredTransport_callsServiceMethod() { int preferredTransport = BluetoothDevice.TRANSPORT_AUTO; - mBinder.setPreferredTransport(mDevice, preferredTransport, null); + mBinder.setPreferredTransport(mDevice, preferredTransport, null); verify(mService).setPreferredTransport(mDevice, preferredTransport); } @Test public void getPreferredTransport_callsServiceMethod() { mBinder.getPreferredTransport(mDevice, null); - verify(mService).getPreferredTransport(mDevice); } @Test public void getProtocolMode_callsServiceMethod() { mBinder.getProtocolMode(mDevice, null); - verify(mService).getProtocolMode(mDevice); } @Test public void virtualUnplug_callsServiceMethod() { mBinder.virtualUnplug(mDevice, null); - verify(mService).virtualUnplug(mDevice); } @Test public void setProtocolMode_callsServiceMethod() { int protocolMode = 1; - mBinder.setProtocolMode(mDevice, protocolMode, null); + mBinder.setProtocolMode(mDevice, protocolMode, null); verify(mService).setProtocolMode(mDevice, protocolMode); } @@ -146,8 +140,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, null); verify(mService).getReport(mDevice, reportType, reportId, bufferSize); } @@ -155,31 +149,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, null); verify(mService).setReport(mDevice, reportType, report); } @Test public void sendData_callsServiceMethod() { String report = "test_report"; - mBinder.sendData(mDevice, report, null); + mBinder.sendData(mDevice, report, null); verify(mService).sendData(mDevice, report); } @Test public void setIdleTime_callsServiceMethod() { byte idleTime = 1; - mBinder.setIdleTime(mDevice, idleTime, null); + mBinder.setIdleTime(mDevice, idleTime, null); verify(mService).setIdleTime(mDevice, idleTime); } @Test public void getIdleTime_callsServiceMethod() { mBinder.getIdleTime(mDevice, null); - 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_scan/ScanBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanBinderTest.java deleted file mode 100644 index 61c7df48b8..0000000000 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanBinderTest.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright (C) 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bluetooth.le_scan; - -import static 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 android.app.PendingIntent; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothManager; -import android.bluetooth.le.IPeriodicAdvertisingCallback; -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.Intent; -import android.os.WorkSource; - -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.mockito.Mock; - -import java.util.ArrayList; -import java.util.List; - -/** Test cases for {@link ScanBinder}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ScanBinderTest { - @Rule public final MockitoRule mMockitoRule = new MockitoRule(); - - @Mock private ScanController mScanController; - - private final AttributionSource mAttributionSource = - InstrumentationRegistry.getInstrumentation() - .getTargetContext() - .getSystemService(BluetoothManager.class) - .getAdapter() - .getAttributionSource(); - private final BluetoothDevice mDevice = getTestDevice(89); - private ScanBinder mBinder; - - @Before - public void setUp() { - mBinder = new ScanBinder(mScanController); - } - - @Test - public void registerScanner() { - IScannerCallback callback = mock(IScannerCallback.class); - WorkSource workSource = mock(WorkSource.class); - - mBinder.registerScanner(callback, workSource, mAttributionSource); - verify(mScanController).registerScanner(callback, workSource, mAttributionSource); - } - - @Test - public void unregisterScanner() { - int scannerId = 1; - - mBinder.unregisterScanner(scannerId, mAttributionSource); - verify(mScanController).unregisterScanner(scannerId, mAttributionSource); - } - - @Test - public void startScan() { - int scannerId = 1; - ScanSettings settings = new ScanSettings.Builder().build(); - List<ScanFilter> filters = new ArrayList<>(); - - mBinder.startScan(scannerId, settings, filters, mAttributionSource); - verify(mScanController).startScan(scannerId, settings, filters, mAttributionSource); - } - - @Test - public void startScanForIntent() { - PendingIntent intent = - PendingIntent.getBroadcast( - InstrumentationRegistry.getInstrumentation().getTargetContext(), - 0, - new Intent(), - PendingIntent.FLAG_IMMUTABLE); - ScanSettings settings = new ScanSettings.Builder().build(); - List<ScanFilter> filters = new ArrayList<>(); - - mBinder.startScanForIntent(intent, settings, filters, mAttributionSource); - verify(mScanController) - .registerPiAndStartScan(intent, settings, filters, mAttributionSource); - } - - @Test - public void stopScan_withScannerId() { - int scannerId = 1; - - mBinder.stopScan(scannerId, mAttributionSource); - verify(mScanController).stopScan(scannerId, mAttributionSource); - } - - @Test - public void stopScan_withIntent() { - PendingIntent intent = - PendingIntent.getBroadcast( - InstrumentationRegistry.getInstrumentation().getTargetContext(), - 0, - new Intent(), - PendingIntent.FLAG_IMMUTABLE); - - mBinder.stopScanForIntent(intent, mAttributionSource); - verify(mScanController).stopScan(intent, mAttributionSource); - } - - @Test - public void flushPendingBatchResults() { - int scannerId = 1; - - mBinder.flushPendingBatchResults(scannerId, mAttributionSource); - verify(mScanController).flushPendingBatchResults(scannerId, mAttributionSource); - } - - @Test - public void registerSync() { - ScanResult scanResult = new ScanResult(mDevice, null, 0, 0); - int skip = 1; - int timeout = 2; - IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class); - - mBinder.registerSync(scanResult, skip, timeout, callback, mAttributionSource); - verify(mScanController) - .registerSync(scanResult, skip, timeout, callback, mAttributionSource); - } - - @Test - public void unregisterSync() { - IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class); - - mBinder.unregisterSync(callback, mAttributionSource); - verify(mScanController).unregisterSync(callback, mAttributionSource); - } - - @Test - public void transferSync() { - int serviceData = 1; - int syncHandle = 2; - - mBinder.transferSync(mDevice, serviceData, syncHandle, mAttributionSource); - verify(mScanController).transferSync(mDevice, serviceData, syncHandle, mAttributionSource); - } - - @Test - public void transferSetInfo() { - int serviceData = 1; - int advHandle = 2; - IPeriodicAdvertisingCallback callback = mock(IPeriodicAdvertisingCallback.class); - - mBinder.transferSetInfo(mDevice, serviceData, advHandle, callback, mAttributionSource); - verify(mScanController) - .transferSetInfo(mDevice, serviceData, advHandle, callback, mAttributionSource); - } - - @Test - public void numHwTrackFiltersAvailable() { - mBinder.numHwTrackFiltersAvailable(mAttributionSource); - verify(mScanController).numHwTrackFiltersAvailable(mAttributionSource); - } - - @Test - public void cleanup_doesNotCrash() { - mBinder.cleanup(); - } -} diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanBinderTest.kt b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanBinderTest.kt new file mode 100644 index 0000000000..3424c48a54 --- /dev/null +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanBinderTest.kt @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bluetooth.le_scan + +import android.app.PendingIntent +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothManager +import android.bluetooth.le.IPeriodicAdvertisingCallback +import android.bluetooth.le.IScannerCallback +import android.bluetooth.le.ScanFilter +import android.bluetooth.le.ScanResult +import android.bluetooth.le.ScanSettings +import android.content.Intent +import android.os.WorkSource +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.bluetooth.TestUtils.MockitoRule +import com.android.bluetooth.TestUtils.getTestDevice +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.mock +import org.mockito.Mockito.verify + +/** Test cases for [ScanBinder] */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class ScanBinderTest { + + @get:Rule val mockitoRule = MockitoRule() + + @Mock private lateinit var scanController: ScanController + + private val attributionSource = + InstrumentationRegistry.getInstrumentation() + .targetContext + .getSystemService(BluetoothManager::class.java) + .adapter + .attributionSource + private val device: BluetoothDevice = getTestDevice(89) + private lateinit var binder: ScanBinder + + @Before + fun setUp() { + binder = ScanBinder(scanController) + } + + @Test + fun registerScanner() { + val callback = mock(IScannerCallback::class.java) + val workSource = mock(WorkSource::class.java) + + binder.registerScanner(callback, workSource, attributionSource) + verify(scanController).registerScanner(callback, workSource, attributionSource) + } + + @Test + fun unregisterScanner() { + val scannerId = 1 + + binder.unregisterScanner(scannerId, attributionSource) + verify(scanController).unregisterScanner(scannerId, attributionSource) + } + + @Test + fun startScan() { + val scannerId = 1 + val settings = ScanSettings.Builder().build() + val filters = listOf<ScanFilter>() + + binder.startScan(scannerId, settings, filters, attributionSource) + verify(scanController).startScan(scannerId, settings, filters, attributionSource) + } + + @Test + fun startScanForIntent() { + val intent = + PendingIntent.getBroadcast( + InstrumentationRegistry.getInstrumentation().targetContext, + 0, + Intent(), + PendingIntent.FLAG_IMMUTABLE, + ) + val settings = ScanSettings.Builder().build() + val filters = listOf<ScanFilter>() + + binder.startScanForIntent(intent, settings, filters, attributionSource) + verify(scanController).registerPiAndStartScan(intent, settings, filters, attributionSource) + } + + @Test + fun stopScan_withScannerId() { + val scannerId = 1 + + binder.stopScan(scannerId, attributionSource) + verify(scanController).stopScan(scannerId, attributionSource) + } + + @Test + fun stopScan_withIntent() { + val intent = + PendingIntent.getBroadcast( + InstrumentationRegistry.getInstrumentation().targetContext, + 0, + Intent(), + PendingIntent.FLAG_IMMUTABLE, + ) + + binder.stopScanForIntent(intent, attributionSource) + verify(scanController).stopScan(intent, attributionSource) + } + + @Test + fun flushPendingBatchResults() { + val scannerId = 1 + + binder.flushPendingBatchResults(scannerId, attributionSource) + verify(scanController).flushPendingBatchResults(scannerId, attributionSource) + } + + @Test + fun registerSync() { + val scanResult = mock(ScanResult::class.java) + val skip = 1 + val timeout = 2 + val callback = mock(IPeriodicAdvertisingCallback::class.java) + + binder.registerSync(scanResult, skip, timeout, callback, attributionSource) + verify(scanController).registerSync(scanResult, skip, timeout, callback, attributionSource) + } + + @Test + fun unregisterSync() { + val callback = mock(IPeriodicAdvertisingCallback::class.java) + + binder.unregisterSync(callback, attributionSource) + verify(scanController).unregisterSync(callback, attributionSource) + } + + @Test + fun transferSync() { + val serviceData = 1 + val syncHandle = 2 + + binder.transferSync(device, serviceData, syncHandle, attributionSource) + verify(scanController).transferSync(device, serviceData, syncHandle, attributionSource) + } + + @Test + fun transferSetInfo() { + val serviceData = 1 + val advHandle = 2 + val callback = mock(IPeriodicAdvertisingCallback::class.java) + + binder.transferSetInfo(device, serviceData, advHandle, callback, attributionSource) + verify(scanController) + .transferSetInfo(device, serviceData, advHandle, callback, attributionSource) + } + + @Test + fun numHwTrackFiltersAvailable() { + binder.numHwTrackFiltersAvailable(attributionSource) + verify(scanController).numHwTrackFiltersAvailable(attributionSource) + } + + @Test + fun cleanup_doesNotCrash() { + binder.cleanup() + } +} diff --git a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java index 7059517c66..54a3679ee6 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/le_scan/ScanManagerTest.java @@ -1227,7 +1227,6 @@ public class ScanManagerTest { } @Test - @EnableFlags(Flags.FLAG_BLE_SCAN_ADV_METRICS_REDESIGN) public void testMetricsAppScanScreenOn() { // Set filtered scan flag final boolean isFiltered = true; @@ -1324,7 +1323,6 @@ public class ScanManagerTest { } @Test - @EnableFlags(Flags.FLAG_BLE_SCAN_ADV_METRICS_REDESIGN) public void testMetricsRadioScanScreenOnOffMultiScan() { // Set filtered scan flag final boolean isFiltered = true; 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..b3125460c4 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 @@ -35,77 +35,73 @@ 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 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); - verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { mBinder.getConnectedDevices(null); - verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, null); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { mBinder.getConnectionState(mDevice, null); - verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { mBinder.getConnectionPolicy(mDevice, null); - verify(mService).getConnectionPolicy(mDevice); } @Test public void getState_callsServiceMethod() { mBinder.getState(null); - verify(mService).getState(); } @Test public void isConnected_callsServiceStaticMethod() { mBinder.isConnected(mDevice, null); - verify(mService).getConnectionState(mDevice); } @@ -116,7 +112,7 @@ public class BluetoothMapServiceBinderTest { } @Test - public void cleanUp_doesNotCrash() { + public void cleanup_doesNotCrash() { mBinder.cleanup(); } } 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..a67a5c3981 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 @@ -35,70 +35,67 @@ 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 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); - verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { mBinder.disconnect(mDevice, null); - verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { mBinder.getConnectedDevices(null); - verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, null); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { mBinder.getConnectionState(mDevice, null); - verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void getConnectionPolicy_callsServiceMethod() { mBinder.getConnectionPolicy(mDevice, null); - verify(mService).getConnectionPolicy(mDevice); } @@ -106,13 +103,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, null); 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/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/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/pan/PanServiceBinderTest.java b/android/app/tests/unit/src/com/android/bluetooth/pan/PanServiceBinderTest.java index d798666437..1ba4881c90 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 @@ -35,75 +35,72 @@ 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 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); - verify(mService).connect(mDevice); } @Test public void disconnect_callsServiceMethod() { mBinder.disconnect(mDevice, null); - verify(mService).disconnect(mDevice); } @Test public void getConnectedDevices_callsServiceMethod() { mBinder.getConnectedDevices(null); - verify(mService).getConnectedDevices(); } @Test public void getDevicesMatchingConnectionStates_callsServiceMethod() { int[] states = new int[] {STATE_CONNECTED}; - mBinder.getDevicesMatchingConnectionStates(states, null); + mBinder.getDevicesMatchingConnectionStates(states, null); verify(mService).getDevicesMatchingConnectionStates(states); } @Test public void getConnectionState_callsServiceMethod() { mBinder.getConnectionState(mDevice, null); - verify(mService).getConnectionState(mDevice); } @Test public void setConnectionPolicy_callsServiceMethod() { int connectionPolicy = CONNECTION_POLICY_ALLOWED; - mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); + mBinder.setConnectionPolicy(mDevice, connectionPolicy, null); verify(mService).setConnectionPolicy(mDevice, connectionPolicy); } @Test public void isTetheringOn_callsServiceMethod() { mBinder.isTetheringOn(null); - 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..2397f3897a 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 @@ -35,20 +35,22 @@ 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 BluetoothDevice mDevice = getTestDevice(60); - BluetoothPbapService.PbapBinder mBinder; + private BluetoothPbapServiceBinder mBinder; @Before public void setUp() { - mBinder = new BluetoothPbapService.PbapBinder(mService); + mBinder = new BluetoothPbapServiceBinder(mService); } @Test 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/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/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/flags/gap.aconfig b/flags/gap.aconfig index d12e294ec7..2b833af69a 100644 --- a/flags/gap.aconfig +++ b/flags/gap.aconfig @@ -30,13 +30,6 @@ flag { } flag { - name: "ble_scan_adv_metrics_redesign" - namespace: "bluetooth" - description: "Reimplement BLE scan and advertisement metrics logging." - bug: "328303508" -} - -flag { name: "msft_addr_tracking_quirk" namespace: "bluetooth" description: "Scanning with MSFT paddress tracking for Realtek BT controllers" @@ -64,16 +57,6 @@ flag { } 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" @@ -104,13 +87,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" @@ -206,16 +182,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" diff --git a/flags/hci.aconfig b/flags/hci.aconfig index cdd5ca3ab6..6f6b1e70df 100644 --- a/flags/hci.aconfig +++ b/flags/hci.aconfig @@ -2,13 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.bt" flag { - name: "encryption_change_v2" - namespace: "bluetooth" - description: "Enable encryption change V2 event" - bug: "366018699" -} - -flag { name: "dont_send_hci_disconnect_repeatedly" namespace: "bluetooth" description: "Prevent BT from sending repeated HCI disconnect command" diff --git a/flags/hid.aconfig b/flags/hid.aconfig index d40e55821f..ee5728c94f 100644 --- a/flags/hid.aconfig +++ b/flags/hid.aconfig @@ -118,3 +118,13 @@ 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 + } +} diff --git a/flags/l2cap.aconfig b/flags/l2cap.aconfig index fec2edd6d5..f82b596674 100644 --- a/flags/l2cap.aconfig +++ b/flags/l2cap.aconfig @@ -9,16 +9,6 @@ flag { } flag { - name: "l2cap_le_do_not_adjust_min_interval" - namespace: "bluetooth" - description: "Do not adjust min_interval in connection update request" - bug: "346960036" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "l2cap_fcs_option_fix" namespace: "bluetooth" description: "Use fcs_option for non BASIC mode " 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 bc57bafe9e..f30410db17 100644 --- a/flags/pairing.aconfig +++ b/flags/pairing.aconfig @@ -102,16 +102,6 @@ flag { } flag { - name: "smp_state_machine_stuck_after_disconnection_fix" - namespace: "bluetooth" - description: "Fix state machine stuck after pairing device disconnection" - bug: "376306092" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "prevent_service_connections_on_remove_bond" namespace: "bluetooth" description: "Disable service connections on remove bond" @@ -260,3 +250,23 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "le_peripheral_enc_failure" + namespace: "bluetooth" + description: "Don't remove bond on LE encryption failure in peripheral role with bonded device, instead just disconnect the link" + bug: "403313352" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "immediate_encryption_after_pairing" + namespace: "bluetooth" + description: "Encrypt BR/EDR link immediately after pairing" + bug: "402510244" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/flags/security.aconfig b/flags/security.aconfig index c83dc02b24..8d5a8c1f31 100644 --- a/flags/security.aconfig +++ b/flags/security.aconfig @@ -2,13 +2,6 @@ package: "com.android.bluetooth.flags" container: "com.android.bt" flag { - name: "key_missing_classic_device" - namespace: "bluetooth" - description: "Key missing broadcast for Classic devices" - bug: "333634398" -} - -flag { name: "key_missing_ble_peripheral" namespace: "bluetooth" description: "Key missing broadcast for LE devices in peripheral role" @@ -66,16 +59,6 @@ flag { } flag { - name: "sec_disconnect_on_le_key_missing" - namespace: "bluetooth" - description: "Disconnect LE link when keys are missing during encryption" - bug: "376680866" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "guard_bonded_device_properties" namespace: "bluetooth" description: "Don't update device properties for bonded devices from the device discovery results" diff --git a/flags/system_service.aconfig b/flags/system_service.aconfig index ea40eb33de..c1cf4f6bd8 100644 --- a/flags/system_service.aconfig +++ b/flags/system_service.aconfig @@ -2,6 +2,16 @@ package: "com.android.bluetooth.flags" container: "com.android.bt" flag { + name: "enable_ble_while_disabling_airplane" + namespace: "bluetooth" + description: "Fix a race between enabling and handling airplane event" + bug: "402563502" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "kill_instead_of_exit" namespace: "bluetooth" description: "There is no value in pretending we are exiting properly. This is a kill and we should express it this way when finishing the process" 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/BluetoothAdapter.java b/framework/java/android/bluetooth/BluetoothAdapter.java index 40d1153581..1b37497323 100644 --- a/framework/java/android/bluetooth/BluetoothAdapter.java +++ b/framework/java/android/bluetooth/BluetoothAdapter.java @@ -3686,6 +3686,9 @@ public final class BluetoothAdapter { if (mBluetoothLeScanner != null) { mBluetoothLeScanner.cleanup(); } + if (mDistanceMeasurementManager != null) { + mDistanceMeasurementManager.cleanup(); + } } } finally { mServiceLock.writeLock().unlock(); 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/le/DistanceMeasurementManager.java b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java index 3b3c1126ff..94715301f0 100644 --- a/framework/java/android/bluetooth/le/DistanceMeasurementManager.java +++ b/framework/java/android/bluetooth/le/DistanceMeasurementManager.java @@ -24,6 +24,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.RequiresNoPermission; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; @@ -271,6 +272,16 @@ public final class DistanceMeasurementManager { return Collections.emptySet(); } + /** + * Clear session map. Should be called when bluetooth is down. + * + * @hide + */ + @RequiresNoPermission + public void cleanup() { + mSessionMap.clear(); + } + @SuppressLint("AndroidFrameworkBluetoothPermission") private final IDistanceMeasurementCallback mCallbackWrapper = new IDistanceMeasurementCallback.Stub() { diff --git a/framework/tests/bumble/src/android/bluetooth/GattClientTest.java b/framework/tests/bumble/src/android/bluetooth/GattClientTest.java index 9b631dce91..29a9668a07 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); diff --git a/framework/tests/bumble/src/android/bluetooth/pairing/OobPairingTest.java b/framework/tests/bumble/src/android/bluetooth/pairing/OobPairingTest.java index bdfa42dd65..0b0da56f7b 100644 --- a/framework/tests/bumble/src/android/bluetooth/pairing/OobPairingTest.java +++ b/framework/tests/bumble/src/android/bluetooth/pairing/OobPairingTest.java @@ -19,6 +19,8 @@ package android.bluetooth.pairing; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; +import static com.google.common.truth.Truth.assertThat; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter.OobDataCallback; import android.bluetooth.BluetoothDevice; @@ -340,11 +342,14 @@ public class OobPairingTest { while (true) { if (scanningResponseIterator.hasNext()) { ScanningResponse scanningResponse = scanningResponseIterator.next(); - // select first available device + // select first available device with Random address type deviceAddr = scanningResponse.getRandom(); - break; + if (deviceAddr != null) { + break; + } } } + assertThat(deviceAddr).isNotNull(); ConnectLEResponse leConn = mBumble.hostBlocking() diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java index 08a949d81a..b797d6ceac 100644 --- a/service/src/com/android/server/bluetooth/BluetoothManagerService.java +++ b/service/src/com/android/server/bluetooth/BluetoothManagerService.java @@ -78,7 +78,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.sysprop.BluetoothProperties; -import android.util.proto.ProtoOutputStream; import androidx.annotation.RequiresApi; @@ -95,7 +94,6 @@ import kotlin.Unit; import kotlin.time.TimeSource; import java.io.FileDescriptor; -import java.io.FileOutputStream; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -140,10 +138,17 @@ class BluetoothManagerService { // Delay for retrying enable and disable in msec @VisibleForTesting static final int ENABLE_DISABLE_DELAY_MS = 300 * HW_MULTIPLIER; + // TODO: b/402209603 remove along with system_server_remove_extra_thread_jump @VisibleForTesting static final int MESSAGE_ENABLE = 1; + // TODO: b/402209603 remove along with system_server_remove_extra_thread_jump @VisibleForTesting static final int MESSAGE_DISABLE = 2; + + // TODO: b/402209603 remove along with system_server_remove_extra_thread_jump @VisibleForTesting static final int MESSAGE_HANDLE_ENABLE_DELAYED = 3; + + // TODO: b/402209603 remove along with system_server_remove_extra_thread_jump @VisibleForTesting static final int MESSAGE_HANDLE_DISABLE_DELAYED = 4; + @VisibleForTesting static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40; @VisibleForTesting static final int MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED = 41; @VisibleForTesting static final int MESSAGE_RESTART_BLUETOOTH_SERVICE = 42; @@ -327,8 +332,9 @@ class BluetoothManagerService { } else if (state != STATE_ON && state != STATE_OFF && state != STATE_BLE_ON) { // Bluetooth is turning state return ADD_PROXY_DELAY_MS; - } else if (mHandler.hasMessages(MESSAGE_ENABLE) - || mHandler.hasMessages(MESSAGE_DISABLE) + } else if ((!Flags.systemServerRemoveExtraThreadJump() + && (mHandler.hasMessages(MESSAGE_ENABLE) + || mHandler.hasMessages(MESSAGE_DISABLE))) || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED) || mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) || mHandler.hasMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE) @@ -1038,6 +1044,42 @@ class BluetoothManagerService { return true; } + private static CompletableFuture<Void> createDeathNotifier(IBinder binder) { + CompletableFuture<Void> deathNotifier = new CompletableFuture<>(); + try { + binder.linkToDeath( + () -> { + Log.i(TAG, "Successfully received Bluetooth death"); + deathNotifier.complete(null); + }, + 0); + } catch (RemoteException e) { + Log.e(TAG, "listenBinderDeath(): Failed to linkToDeath", e); + deathNotifier.complete(null); + } + return deathNotifier; + } + + private static void killBluetoothProcess( + AdapterBinder adapter, CompletableFuture<Void> deathNotifier) { + try { + // Force kill Bluetooth to make sure its process is not reused. + // Note: In a perfect world, we should be able to re-init the same process. + // Unfortunately, this requires an heavy rework of the Bluetooth app + // TODO: b/339501753 - Properly stop Bluetooth without killing it + adapter.killBluetoothProcess(); + + deathNotifier.get(2_000, TimeUnit.MILLISECONDS); + } catch (android.os.DeadObjectException e) { + // Reduce exception to info and skip waiting (Bluetooth is dead as wanted) + Log.i(TAG, "killBluetoothProcess(): Bluetooth already dead 💀"); + } catch (RemoteException e) { + Log.e(TAG, "killBluetoothProcess(): Unable to call killBluetoothProcess", e); + } catch (TimeoutException | InterruptedException | ExecutionException e) { + Log.e(TAG, "killBluetoothProcess(): Bluetooth death not received after > 2000ms", e); + } + } + void unbindAndFinish() { Log.d(TAG, "unbindAndFinish(): mAdapter=" + mAdapter + " isBinding=" + isBinding()); @@ -1054,40 +1096,13 @@ class BluetoothManagerService { Log.e(TAG, "unbindAndFinish(): Unable to unregister BluetoothCallback", e); } - CompletableFuture<Void> binderDead = new CompletableFuture<>(); - try { - mAdapter.getAdapterBinder() - .asBinder() - .linkToDeath( - () -> { - Log.i(TAG, "Successfully received Bluetooth death"); - binderDead.complete(null); - }, - 0); - } catch (RemoteException e) { - Log.e(TAG, "unbindAndFinish(): Failed to linkToDeath", e); - binderDead.complete(null); - } + CompletableFuture<Void> deathNotifier = + createDeathNotifier(mAdapter.getAdapterBinder().asBinder()); // Unbind first to avoid receiving unwanted "onServiceDisconnected" mContext.unbindService(mConnection); - try { - // Force kill Bluetooth to make sure its process is not reused. - // Note: In a perfect world, we should be able to re-init the same process. - // Unfortunately, this requires an heavy rework of the Bluetooth app - // TODO: b/339501753 - Properly stop Bluetooth without killing it - mAdapter.killBluetoothProcess(); - - binderDead.get(2_000, TimeUnit.MILLISECONDS); - } catch (android.os.DeadObjectException e) { - // Reduce exception to info and skip waiting (Bluetooth is dead as wanted) - Log.i(TAG, "unbindAndFinish(): Bluetooth already dead 💀"); - } catch (RemoteException e) { - Log.e(TAG, "unbindAndFinish(): Unable to call killBluetoothProcess", e); - } catch (TimeoutException | InterruptedException | ExecutionException e) { - Log.e(TAG, "unbindAndFinish(): Bluetooth death not received after > 2000ms", e); - } + killBluetoothProcess(mAdapter, deathNotifier); long timeSpentForShutdown = System.currentTimeMillis() - currentTimeMs; mShutdownLatencyHistogram.logSample((float) timeSpentForShutdown); @@ -1228,10 +1243,10 @@ class BluetoothManagerService { } if (Flags.setComponentAvailableFix()) { - mHandler - .obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED, - componentName.getPackageName()) - .sendToTarget(); + mHandler.obtainMessage( + MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED, + componentName.getPackageName()) + .sendToTarget(); } else { mHandler.sendEmptyMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED); } @@ -1252,8 +1267,12 @@ class BluetoothManagerService { public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_ENABLE: - int quietEnable = msg.arg1; - int isBle = msg.arg2; + if (Flags.systemServerRemoveExtraThreadJump()) { + break; + } + + boolean quietEnable = msg.arg1 != 0; + boolean isBle = msg.arg2 != 0; Log.d( TAG, @@ -1265,6 +1284,10 @@ class BluetoothManagerService { break; case MESSAGE_DISABLE: + if (Flags.systemServerRemoveExtraThreadJump()) { + break; + } + Log.d(TAG, "MESSAGE_DISABLE: mAdapter=" + mAdapter); handleDisableMessage(); @@ -1311,6 +1334,15 @@ class BluetoothManagerService { case MESSAGE_BLUETOOTH_SERVICE_CONNECTED: IBinder service = (IBinder) msg.obj; + + // Handle case where disable was called before binding complete. + if (Flags.systemServerRemoveExtraThreadJump() && !isBinding() && !mEnable) { + Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: after cancelling binding"); + AdapterBinder adapter = + BluetoothServerProxy.getInstance().createAdapterBinder(service); + killBluetoothProcess(adapter, createDeathNotifier(service)); + break; + } Log.d(TAG, "MESSAGE_BLUETOOTH_SERVICE_CONNECTED: service=" + service); // Remove timeout @@ -1330,7 +1362,7 @@ class BluetoothManagerService { offToBleOn(); sendBluetoothServiceUpCallback(); - if (!mEnable) { + if (!Flags.systemServerRemoveExtraThreadJump() && !mEnable) { waitForState(STATE_ON); onToBleOn(); } @@ -1359,13 +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."); - waitForState(STATE_OFF); - mHandler.sendEmptyMessageDelayed( - MESSAGE_RESTART_BLUETOOTH_SERVICE, getServiceRestartMs()); + 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()); + } } } if (newState == STATE_ON || newState == STATE_BLE_ON) { @@ -1524,17 +1574,19 @@ class BluetoothManagerService { return mHandler.hasMessages(MESSAGE_TIMEOUT_BIND); } - private void handleEnableMessage(int quietEnable, int isBle) { + private void handleEnableMessage(boolean quietEnable, boolean isBle) { + String logHeader = "handleEnableMessage(" + quietEnable + ", " + isBle + "): "; if (mShutdownInProgress) { - Log.d(TAG, "Skip Bluetooth Enable in device shutdown process"); + Log.d(TAG, logHeader + "Skip Bluetooth Enable in device shutdown process"); return; } - if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) - || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + if (!Flags.systemServerRemoveExtraThreadJump() + && (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED))) { // We are handling enable or disable right now, wait for it. mHandler.sendMessageDelayed( - mHandler.obtainMessage(MESSAGE_ENABLE, quietEnable, isBle), + mHandler.obtainMessage(MESSAGE_ENABLE, quietEnable ? 1 : 0, isBle ? 1 : 0), ENABLE_DISABLE_DELAY_MS); return; } @@ -1542,27 +1594,27 @@ class BluetoothManagerService { mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); mEnable = true; - if (isBle == 0) { + if (!isBle) { setBluetoothPersistedState(BLUETOOTH_ON_BLUETOOTH); } if (mState.oneOf(STATE_BLE_TURNING_ON, STATE_TURNING_ON, STATE_ON)) { - Log.i(TAG, "MESSAGE_ENABLE: already enabled. Current state=" + mState); + Log.i(TAG, logHeader + "Already enabled. Current state=" + mState); return; } - if (mState.oneOf(STATE_BLE_ON) && isBle == 1) { - Log.i(TAG, "MESSAGE_ENABLE: Already in BLE_ON while being requested to go to BLE_ON"); + if (mState.oneOf(STATE_BLE_ON) && isBle) { + Log.i(TAG, logHeader + "Already in BLE_ON while being requested to go to BLE_ON"); return; } if (mState.oneOf(STATE_BLE_ON)) { - Log.i(TAG, "MESSAGE_ENABLE: Bluetooth transition from STATE_BLE_ON to STATE_ON"); + Log.i(TAG, logHeader + "Bluetooth transition from STATE_BLE_ON to STATE_ON"); bleOnToOn(); return; } - if (mAdapter != null) { + if (!Flags.systemServerRemoveExtraThreadJump() && mAdapter != null) { // TODO: b/339548431 - Adapt this after removal of Flags.explicitKillFromSystemServer // // We need to wait until transitioned to STATE_OFF and the previous Bluetooth process @@ -1581,14 +1633,15 @@ class BluetoothManagerService { return; } - mQuietEnable = (quietEnable == 1); + mQuietEnable = quietEnable; handleEnable(); } private void handleDisableMessage() { - if (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) - || isBinding() - || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED)) { + if (!Flags.systemServerRemoveExtraThreadJump() + && (mHandler.hasMessages(MESSAGE_HANDLE_DISABLE_DELAYED) + || isBinding() + || mHandler.hasMessages(MESSAGE_HANDLE_ENABLE_DELAYED))) { // We are handling enable or disable right now, wait for it. mHandler.sendEmptyMessageDelayed(MESSAGE_DISABLE, ENABLE_DISABLE_DELAY_MS); return; @@ -1596,10 +1649,25 @@ class BluetoothManagerService { mHandler.removeMessages(MESSAGE_RESTART_BLUETOOTH_SERVICE); - if (mEnable && mAdapter != null) { + if (Flags.systemServerRemoveExtraThreadJump() && isBinding()) { + Log.d(TAG, "Disable while binding"); + mEnable = false; + mContext.unbindService(mConnection); + mHandler.removeMessages(MESSAGE_TIMEOUT_BIND); + mHandler.removeMessages(MESSAGE_BLUETOOTH_SERVICE_CONNECTED); + } else if (Flags.systemServerRemoveExtraThreadJump() + && mState.oneOf(STATE_BLE_TURNING_ON)) { + Log.d(TAG, "Disable while BLE_TURNING_ON"); + mEnable = false; + bluetoothStateChangeHandler(STATE_BLE_TURNING_ON, STATE_OFF); + } else if (mEnable && mAdapter != null) { mWaitForDisableRetry = 0; - mHandler.sendEmptyMessageDelayed( - MESSAGE_HANDLE_DISABLE_DELAYED, ENABLE_DISABLE_DELAY_MS); + if (Flags.systemServerRemoveExtraThreadJump()) { + handleDisableDelayed(false); + } else { + mHandler.sendEmptyMessageDelayed( + MESSAGE_HANDLE_DISABLE_DELAYED, ENABLE_DISABLE_DELAY_MS); + } } else { mEnable = false; onToBleOn(); @@ -1648,6 +1716,10 @@ class BluetoothManagerService { Log.e(TAG, "Wait for STATE_OFF timeout"); } } + if (Flags.systemServerRemoveExtraThreadJump()) { + handleEnable(); + return; + } // Either state is changed to STATE_OFF or reaches the maximum retry, we // should move forward to the next step. mWaitForEnableRetry = 0; @@ -1656,6 +1728,9 @@ class BluetoothManagerService { } private void handleDisableDelayed(boolean disabling) { + if (Flags.systemServerRemoveExtraThreadJump() && disabling) { + return; + } if (!disabling) { // The Bluetooth is turning on, wait for STATE_ON if (!mState.oneOf(STATE_ON)) { @@ -1673,10 +1748,12 @@ class BluetoothManagerService { mWaitForDisableRetry = 0; mEnable = false; onToBleOn(); - // Wait for state exiting STATE_ON - Message disableDelayedMsg = - mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); - mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + if (!Flags.systemServerRemoveExtraThreadJump()) { + // Wait for state exiting STATE_ON + Message disableDelayedMsg = + mHandler.obtainMessage(MESSAGE_HANDLE_DISABLE_DELAYED, 1, 0); + mHandler.sendMessageDelayed(disableDelayedMsg, ENABLE_DISABLE_DELAY_MS); + } } else { // The Bluetooth is turning off, wait for exiting STATE_ON if (mState.oneOf(STATE_ON)) { @@ -1837,8 +1914,13 @@ class BluetoothManagerService { } private void sendDisableMsg(int reason, String packageName) { - mHandler.sendEmptyMessage(MESSAGE_DISABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + mHandler.sendEmptyMessage(MESSAGE_DISABLE); + } ActiveLogs.add(reason, false, packageName, false); + if (Flags.systemServerRemoveExtraThreadJump()) { + handleDisableMessage(); + } } private void sendEnableMsg(boolean quietMode, int reason) { @@ -1850,9 +1932,14 @@ class BluetoothManagerService { } private void sendEnableMsg(boolean quietMode, int reason, String packageName, boolean isBle) { - mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, isBle ? 1 : 0).sendToTarget(); + if (!Flags.systemServerRemoveExtraThreadJump()) { + mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, isBle ? 1 : 0).sendToTarget(); + } ActiveLogs.add(reason, true, packageName, isBle); mLastEnabledTime = SystemClock.elapsedRealtime(); + if (Flags.systemServerRemoveExtraThreadJump()) { + handleEnableMessage(quietMode, isBle); + } } private void addCrashLog() { @@ -2254,33 +2341,34 @@ class BluetoothManagerService { } /** - * In case of a Bluetooth crash, mark it's enabled components as non longer available to - * trigger the PACKAGE_CHANGED intent. This should not be needed in a normal shutdown as the - * Bluetooth clean its components on its own + * In case of a Bluetooth crash, mark it's enabled components as non longer available to trigger + * the PACKAGE_CHANGED intent. This should not be needed in a normal shutdown as the Bluetooth + * clean its components on its own */ private void disableBluetoothComponents(String packageName) { PackageManager pm = mContext.getPackageManager(); PackageInfo packageInfo = null; try { - packageInfo = pm.getPackageInfo( - packageName, - PackageManager.GET_SERVICES | - PackageManager.GET_ACTIVITIES | - PackageManager.GET_RECEIVERS | - PackageManager.GET_PROVIDERS); + packageInfo = + pm.getPackageInfo( + packageName, + PackageManager.GET_SERVICES + | PackageManager.GET_ACTIVITIES + | PackageManager.GET_RECEIVERS + | PackageManager.GET_PROVIDERS); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "Package not found: " + packageName, e); return; } // Refer to updateOppLauncherComponentState() - List<String> baseBluetoothOppActivities = List.of( - "com.android.bluetooth.opp.BluetoothOppLauncherActivity", - "com.android.bluetooth.opp.BluetoothOppBtEnableActivity", - "com.android.bluetooth.opp.BluetoothOppBtEnablingActivity", - "com.android.bluetooth.opp.BluetoothOppBtErrorActivity" - ); + List<String> baseBluetoothOppActivities = + List.of( + "com.android.bluetooth.opp.BluetoothOppLauncherActivity", + "com.android.bluetooth.opp.BluetoothOppBtEnableActivity", + "com.android.bluetooth.opp.BluetoothOppBtEnablingActivity", + "com.android.bluetooth.opp.BluetoothOppBtErrorActivity"); disableComponents(pm, packageInfo.activities, packageName, baseBluetoothOppActivities); disableComponents(pm, packageInfo.services, packageName, null); @@ -2295,17 +2383,20 @@ class BluetoothManagerService { } Arrays.stream(components) - .filter(componentInfo -> !componentInfo.enabled) - .map(componentInfo -> new ComponentName(packageName, componentInfo.name)) - .filter(componentName -> - (componentsToKeep == null || - !componentsToKeep.contains(componentName.getClassName()))) - .forEach(componentName -> { - pm.setComponentEnabledSetting( - componentName, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED, - PackageManager.DONT_KILL_APP); - Log.i(TAG, "Disabled component: " + componentName.flattenToString()); - }); + .filter(componentInfo -> !componentInfo.enabled) + .map(componentInfo -> new ComponentName(packageName, componentInfo.name)) + .filter( + componentName -> + (componentsToKeep == null + || !componentsToKeep.contains( + componentName.getClassName()))) + .forEach( + componentName -> { + pm.setComponentEnabledSetting( + componentName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + Log.i(TAG, "Disabled component: " + componentName.flattenToString()); + }); } } diff --git a/service/tests/Android.bp b/service/tests/Android.bp index 217bd36f70..8bdefcc8ac 100644 --- a/service/tests/Android.bp +++ b/service/tests/Android.bp @@ -26,6 +26,7 @@ android_test { ], static_libs: [ + "androidx.test.espresso.intents", "androidx.test.rules", "flag-junit", "frameworks-base-testutils", diff --git a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java index 480af98329..e825940e90 100644 --- a/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java +++ b/service/tests/src/com/android/server/bluetooth/BluetoothManagerServiceTest.java @@ -22,6 +22,9 @@ import static android.bluetooth.BluetoothAdapter.STATE_ON; import static android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF; import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction; +import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra; + import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_BLUETOOTH_SERVICE_CONNECTED; import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED; import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_BLUETOOTH_STATE_CHANGE; @@ -33,6 +36,7 @@ import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_RESTO import static com.android.server.bluetooth.BluetoothManagerService.MESSAGE_TIMEOUT_BIND; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; @@ -41,13 +45,16 @@ import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.validateMockitoUsage; import static org.mockito.Mockito.verify; import android.annotation.SuppressLint; +import android.app.AppOpsManager; import android.app.PropertyInvalidatedCache; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.IBluetooth; import android.bluetooth.IBluetoothCallback; import android.bluetooth.IBluetoothManager; @@ -55,7 +62,6 @@ import android.bluetooth.IBluetoothManagerCallback; import android.bluetooth.IBluetoothStateChangeCallback; import android.content.ComponentName; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; @@ -63,21 +69,29 @@ import android.os.Message; import android.os.UserHandle; import android.os.UserManager; import android.os.test.TestLooper; +import android.permission.PermissionManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.bluetooth.flags.Flags; + +import org.hamcrest.Matcher; +import org.hamcrest.core.AllOf; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.Spy; +import org.mockito.hamcrest.MockitoHamcrest; import platform.test.runner.parameterized.ParameterizedAndroidJunit4; import platform.test.runner.parameterized.Parameters; @@ -93,7 +107,8 @@ public class BluetoothManagerServiceTest { @Parameters(name = "{0}") public static List<FlagsParameterization> getParams() { - return FlagsParameterization.allCombinationsOf(); + return FlagsParameterization.allCombinationsOf( + Flags.FLAG_SYSTEM_SERVER_REMOVE_EXTRA_THREAD_JUMP); } public BluetoothManagerServiceTest(FlagsParameterization flags) { @@ -103,24 +118,26 @@ public class BluetoothManagerServiceTest { private static final int STATE_BLE_TURNING_ON = 14; // can't find the symbol because hidden api private static final int STATE_BLE_TURNING_OFF = 16; // can't find the symbol because hidden api - BluetoothManagerService mManagerService; + private final Context mTargetContext = + InstrumentationRegistry.getInstrumentation().getTargetContext(); - @Spy - private final Context mContext = - new ContextWrapper(InstrumentationRegistry.getInstrumentation().getTargetContext()); - - @Spy BluetoothServerProxy mBluetoothServerProxy; + @Mock BluetoothServerProxy mBluetoothServerProxy; + @Mock Context mContext; @Mock UserManager mUserManager; @Mock UserHandle mUserHandle; - @Mock IBinder mBinder; @Mock IBluetoothManagerCallback mManagerCallback; @Mock IBluetoothStateChangeCallback mStateChangeCallback; - @Mock IBluetooth mAdapterService; @Mock AdapterBinder mAdapterBinder; + @Mock AppOpsManager mAppOpsManager; + @Mock PermissionManager mPermissionManager; + + private int mPersistedState = BluetoothManagerService.BLUETOOTH_OFF; - TestLooper mLooper; + private InOrder mInOrder; + private TestLooper mLooper; + private BluetoothManagerService mManagerService; private static class ServerQuery extends PropertyInvalidatedCache.QueryHandler<IBluetoothManager, Integer> { @@ -146,6 +163,7 @@ public class BluetoothManagerServiceTest { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mInOrder = inOrder(mContext, mManagerCallback, mAdapterBinder); PropertyInvalidatedCache<IBluetoothManager, Integer> testCache = new PropertyInvalidatedCache<>( @@ -163,24 +181,37 @@ public class BluetoothManagerServiceTest { doReturn("00:11:22:33:44:55") .when(mBluetoothServerProxy) .settingsSecureGetString(any(), eq(Settings.Secure.BLUETOOTH_ADDRESS)); - // Set persisted state to BLUETOOTH_OFF to not generate unwanted behavior when starting test - doReturn(BluetoothManagerService.BLUETOOTH_OFF) + doAnswer( + inv -> { + return mPersistedState; + }) .when(mBluetoothServerProxy) .getBluetoothPersistedState(any(), anyInt()); doAnswer( inv -> { - doReturn(inv.getArguments()[1]) - .when(mBluetoothServerProxy) - .getBluetoothPersistedState(any(), anyInt()); + mPersistedState = inv.getArgument(1); return null; }) .when(mBluetoothServerProxy) .setBluetoothPersistedState(any(), anyInt()); - // Test is not allowed to send broadcast as Bluetooth. doNothing Prevent SecurityException - doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any(), any()); + doAnswer( + inv -> { + IBinder.DeathRecipient recipient = inv.getArgument(0); + recipient.binderDied(); + return null; + }) + .when(mBinder) + .linkToDeath(any(), anyInt()); + + doReturn(BluetoothManagerServiceTest.class.getSimpleName()).when(mContext).getPackageName(); + doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt()); + doReturn(mTargetContext.getContentResolver()).when(mContext).getContentResolver(); + doReturn(mTargetContext.getPackageManager()).when(mContext).getPackageManager(); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); + doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class); + doReturn(mPermissionManager).when(mContext).getSystemService(PermissionManager.class); doReturn(mBinder).when(mManagerCallback).asBinder(); @@ -233,9 +264,13 @@ public class BluetoothManagerServiceTest { IntStream.of(what) .forEach( w -> { + String log = "Expecting message " + w + ": "; + Message msg = mLooper.nextMessage(); - assertThat(msg).isNotNull(); - assertThat(msg.what).isEqualTo(w); + assertWithMessage(log + "but got null").that(msg).isNotNull(); + assertWithMessage(log + "but got " + msg.what) + .that(msg.what) + .isEqualTo(w); msg.getTarget().dispatchMessage(msg); }); } @@ -270,7 +305,9 @@ public class BluetoothManagerServiceTest { // called from SYSTEM user, should try to toggle Bluetooth off mManagerService.onUserRestrictionsChanged(UserHandle.SYSTEM); - syncHandler(MESSAGE_DISABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_DISABLE); + } } @Test @@ -283,8 +320,11 @@ public class BluetoothManagerServiceTest { anyInt(), any(UserHandle.class)); mManagerService.enableBle("enable_bindFailure_removesTimeout", mBinder); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } verify(mContext).unbindService(any()); + mInOrder.verify(mContext).unbindService(any()); // TODO(b/280518177): Failed to start should be noted / reported in metrics // Maybe show a popup or a crash notification @@ -294,7 +334,9 @@ public class BluetoothManagerServiceTest { @Test public void enable_bindTimeout() throws Exception { mManagerService.enableBle("enable_bindTimeout", mBinder); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } mLooper.moveTimeForward(120_000); // 120 seconds syncHandler(MESSAGE_TIMEOUT_BIND); @@ -312,7 +354,7 @@ public class BluetoothManagerServiceTest { ArgumentCaptor<BluetoothManagerService.BluetoothServiceConnection> captor = ArgumentCaptor.forClass(BluetoothManagerService.BluetoothServiceConnection.class); - verify(mContext) + mInOrder.verify(mContext) .bindServiceAsUser( any(Intent.class), captor.capture(), anyInt(), any(UserHandle.class)); assertThat(captor.getAllValues()).hasSize(1); @@ -324,11 +366,10 @@ public class BluetoothManagerServiceTest { return serviceConnection; } - private static IBluetoothCallback captureBluetoothCallback(AdapterBinder adapterBinder) - throws Exception { + private IBluetoothCallback captureBluetoothCallback() throws Exception { ArgumentCaptor<IBluetoothCallback> captor = ArgumentCaptor.forClass(IBluetoothCallback.class); - verify(adapterBinder).registerCallback(captor.capture(), any()); + mInOrder.verify(mAdapterBinder).registerCallback(captor.capture(), any()); assertThat(captor.getAllValues()).hasSize(1); return captor.getValue(); } @@ -337,11 +378,10 @@ public class BluetoothManagerServiceTest { // Binding of IBluetooth acceptBluetoothBinding(); - // TODO(b/280518177): This callback is too early, bt is not ON nor BLE_ON - verify(mManagerCallback).onBluetoothServiceUp(any()); - - IBluetoothCallback btCallback = captureBluetoothCallback(mAdapterBinder); - verify(mAdapterBinder).offToBleOn(anyBoolean(), any()); + IBluetoothCallback btCallback = captureBluetoothCallback(); + mInOrder.verify(mAdapterBinder).offToBleOn(anyBoolean(), any()); + verifyBleStateIntentSent(STATE_OFF, STATE_BLE_TURNING_ON); + mInOrder.verify(mManagerCallback).onBluetoothServiceUp(any()); assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_ON); @@ -349,30 +389,30 @@ public class BluetoothManagerServiceTest { // trigger the stateChangeCallback from native btCallback.onBluetoothStateChange(STATE_BLE_TURNING_ON, STATE_BLE_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); + verifyBleStateIntentSent(STATE_BLE_TURNING_ON, STATE_BLE_ON); return btCallback; } private IBluetoothCallback transition_offToOn() throws Exception { IBluetoothCallback btCallback = transition_offToBleOn(); - verify(mAdapterBinder).bleOnToOn(any()); + mInOrder.verify(mAdapterBinder).bleOnToOn(any()); // AdapterService go to turning_on and start all profile on its own btCallback.onBluetoothStateChange(STATE_BLE_ON, STATE_TURNING_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); + verifyBleStateIntentSent(STATE_BLE_ON, STATE_TURNING_ON); + verifyStateIntentSent(STATE_OFF, STATE_TURNING_ON); // When all the profile are started, adapterService consider it is ON btCallback.onBluetoothStateChange(STATE_TURNING_ON, STATE_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); - - // Check that we sent 6 intent, 4 for BLE: BLE_TURNING_ON + BLE_ON + TURNING_ON + ON - // and 2 for classic: TURNING_ON + ON - // TODO(b/280518177): assert the intent are the correct one - verify(mContext, times(6)).sendBroadcastAsUser(any(), any(), any(), any()); + verifyBleStateIntentSent(STATE_TURNING_ON, STATE_ON); + verifyStateIntentSent(STATE_TURNING_ON, STATE_ON); return btCallback; } private void transition_onToBleOn(IBluetoothCallback btCallback) throws Exception { - verify(mAdapterBinder).onToBleOn(any()); + mInOrder.verify(mAdapterBinder).onToBleOn(any()); btCallback.onBluetoothStateChange(STATE_TURNING_OFF, STATE_BLE_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); @@ -380,7 +420,7 @@ public class BluetoothManagerServiceTest { private void transition_onToOff(IBluetoothCallback btCallback) throws Exception { transition_onToBleOn(btCallback); - verify(mAdapterBinder).bleOnToOff(any()); + mInOrder.verify(mAdapterBinder).bleOnToOff(any()); // When all the profile are started, adapterService consider it is ON btCallback.onBluetoothStateChange(STATE_BLE_TURNING_OFF, STATE_OFF); @@ -390,62 +430,70 @@ public class BluetoothManagerServiceTest { @Test public void enable_whileTurningToBleOn_shouldEnable() throws Exception { mManagerService.enableBle("enable_whileTurningToBleOn_shouldEnable", mBinder); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } acceptBluetoothBinding(); - IBluetoothCallback btCallback = captureBluetoothCallback(mAdapterBinder); + IBluetoothCallback btCallback = captureBluetoothCallback(); assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_ON); // receive enable when Bluetooth is in BLE_TURNING_ON mManagerService.enable("enable_whileTurningToBleOn_shouldEnable"); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } btCallback.onBluetoothStateChange(STATE_BLE_TURNING_ON, STATE_BLE_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); - verify(mAdapterBinder).bleOnToOn(any()); + mInOrder.verify(mAdapterBinder).bleOnToOn(any()); } @Test public void enable_whileNotYetBoundToBle_shouldEnable() throws Exception { mManagerService.enableBle("enable_whileTurningToBleOn_shouldEnable", mBinder); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } assertThat(mManagerService.getState()).isEqualTo(STATE_OFF); // receive enable when Bluetooth is OFF and not yet binded mManagerService.enable("enable_whileTurningToBleOn_shouldEnable"); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } acceptBluetoothBinding(); - IBluetoothCallback btCallback = captureBluetoothCallback(mAdapterBinder); + IBluetoothCallback btCallback = captureBluetoothCallback(); assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_ON); btCallback.onBluetoothStateChange(STATE_BLE_TURNING_ON, STATE_BLE_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); - verify(mAdapterBinder).bleOnToOn(any()); + mInOrder.verify(mAdapterBinder).bleOnToOn(any()); } @Test public void offToBleOn() throws Exception { mManagerService.enableBle("test_offToBleOn", mBinder); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } transition_offToBleOn(); - // Check that we sent 2 intent, one for BLE_TURNING_ON, one for BLE_ON - // TODO(b/280518177): assert the intent are the correct one - verify(mContext, times(2)).sendBroadcastAsUser(any(), any(), any(), any()); - // Check that there was no transition to STATE_ON - verify(mAdapterBinder, times(0)).bleOnToOn(any()); + mInOrder.verify(mAdapterBinder, never()).bleOnToOn(any()); assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_ON); } @Test public void offToOn() throws Exception { mManagerService.enable("test_offToOn"); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } transition_offToOn(); @@ -455,13 +503,15 @@ public class BluetoothManagerServiceTest { @Test public void crash_whileTransitionState_canRecover() throws Exception { mManagerService.enableBle("crash_whileTransitionState_canRecover", mBinder); - syncHandler(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } BluetoothManagerService.BluetoothServiceConnection serviceConnection = acceptBluetoothBinding(); - IBluetoothCallback btCallback = captureBluetoothCallback(mAdapterBinder); - verify(mAdapterBinder).offToBleOn(anyBoolean(), any()); + IBluetoothCallback btCallback = captureBluetoothCallback(); + mInOrder.verify(mAdapterBinder).offToBleOn(anyBoolean(), any()); btCallback.onBluetoothStateChange(STATE_OFF, STATE_BLE_TURNING_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_ON); @@ -484,42 +534,136 @@ public class BluetoothManagerServiceTest { @Test public void disableAirplane_whenNothing_startBluetooth() throws Exception { - doReturn(BluetoothManagerService.BLUETOOTH_ON_BLUETOOTH) - .when(mBluetoothServerProxy) - .getBluetoothPersistedState(any(), anyInt()); + if (!Flags.systemServerRemoveExtraThreadJump()) { + mPersistedState = BluetoothManagerService.BLUETOOTH_ON_BLUETOOTH; + } mManagerService.enable("disableAirplane_whenNothing_startBluetooth"); - discardMessage(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + discardMessage(MESSAGE_ENABLE); + } else { + transition_offToOn(); + } + + assertThat(mLooper.nextMessage()).isNull(); mManagerService.onAirplaneModeChanged(false); - discardMessage(MESSAGE_ENABLE); + if (!Flags.systemServerRemoveExtraThreadJump()) { + discardMessage(MESSAGE_ENABLE); + } } @Test - public void disableAirplane_whenFactoryReset_doesNotStartBluetooth() throws Exception { - doAnswer( - invocation -> { - IBinder.DeathRecipient recipient = invocation.getArgument(0); - recipient.binderDied(); - return null; - }) - .when(mBinder) - .linkToDeath(any(), anyInt()); + @EnableFlags(Flags.FLAG_SYSTEM_SERVER_REMOVE_EXTRA_THREAD_JUMP) + public void disable_whenBinding_bluetoothShouldStop_new() throws Exception { + mManagerService.enable("disable_whenBinding_bluetoothShouldStop_new"); + mInOrder.verify(mContext).bindServiceAsUser(any(), any(), anyInt(), any()); + mManagerService.disable("disable_whenBinding_bluetoothShouldStop_new", true); + mInOrder.verify(mContext).unbindService(any()); + assertThat(mManagerService.getState()).isEqualTo(STATE_OFF); + } - mManagerService.enable("test_offToOn"); + @Test + @EnableFlags(Flags.FLAG_SYSTEM_SERVER_REMOVE_EXTRA_THREAD_JUMP) + public void disable_whenTurningBleOn_bluetoothShouldStop() throws Exception { + mManagerService.enable("disable_whenBinding_bluetoothShouldStop_new"); + acceptBluetoothBinding(); + assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_ON); + mManagerService.disable("disable_whenBinding_bluetoothShouldStop_new", true); + mInOrder.verify(mContext).unbindService(any()); + assertThat(mManagerService.getState()).isEqualTo(STATE_OFF); + } + + @Test + @DisableFlags(Flags.FLAG_SYSTEM_SERVER_REMOVE_EXTRA_THREAD_JUMP) + public void disable_whenBinding_bluetoothShouldStop_old() throws Exception { + mManagerService.enable("disable_whenBinding_bluetoothShouldStop_old"); syncHandler(MESSAGE_ENABLE); + mManagerService.disable("disable_whenBinding_bluetoothShouldStop_old", true); + syncHandler(MESSAGE_DISABLE); + + IBluetoothCallback btCallback = transition_offToBleOn(); + assertThat(mManagerService.getState()).isEqualTo(STATE_BLE_TURNING_OFF); + + btCallback.onBluetoothStateChange(STATE_BLE_TURNING_OFF, STATE_OFF); + syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); + + mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS); + syncHandler(MESSAGE_DISABLE); + + assertThat(mManagerService.getState()).isEqualTo(STATE_OFF); + } + + @Test + public void disableAirplane_whenFactoryReset_doesNotStartBluetooth() throws Exception { + mManagerService.enable("disableAirplane_whenFactoryReset_doesNotStartBluetooth"); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_ENABLE); + } IBluetoothCallback btCallback = transition_offToOn(); assertThat(mManagerService.getState()).isEqualTo(STATE_ON); mManagerService.mHandler.sendEmptyMessage(MESSAGE_RESTORE_USER_SETTING_OFF); syncHandler(MESSAGE_RESTORE_USER_SETTING_OFF); - syncHandler(MESSAGE_DISABLE); - mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS); - syncHandler(MESSAGE_HANDLE_DISABLE_DELAYED); - mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS); - syncHandler(MESSAGE_HANDLE_DISABLE_DELAYED); + if (!Flags.systemServerRemoveExtraThreadJump()) { + syncHandler(MESSAGE_DISABLE); + mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS); + syncHandler(MESSAGE_HANDLE_DISABLE_DELAYED); + mLooper.moveTimeForward(BluetoothManagerService.ENABLE_DISABLE_DELAY_MS); + syncHandler(MESSAGE_HANDLE_DISABLE_DELAYED); + } transition_onToOff(btCallback); mManagerService.onAirplaneModeChanged(false); 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) + .sendBroadcastAsUser( + MockitoHamcrest.argThat(AllOf.allOf(matchers)), any(), any(), any()); + } + + private void verifyBleStateIntentSent(int from, int to) { + verifyIntentSent( + hasAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED), + hasExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, from), + hasExtra(BluetoothAdapter.EXTRA_STATE, to)); + } + + private void verifyStateIntentSent(int from, int to) { + verifyIntentSent( + hasAction(BluetoothAdapter.ACTION_STATE_CHANGED), + hasExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, from), + hasExtra(BluetoothAdapter.EXTRA_STATE, to)); + } } diff --git a/system/BUILD.gn b/system/BUILD.gn index b86ca5f392..48b6207a60 100644 --- a/system/BUILD.gn +++ b/system/BUILD.gn @@ -76,7 +76,6 @@ config("target_defaults") { "//bt/system", "//bt/flags/exported_include", "//bt/sysprop/exported_include", - "//bt/system/linux_include", "//bt/system/include", "//bt/system/gd", "//bt/system/pdl/hci/include", diff --git a/system/bta/Android.bp b/system/bta/Android.bp index 52d34d59cc..5a8f721ebd 100644 --- a/system/bta/Android.bp +++ b/system/bta/Android.bp @@ -231,6 +231,7 @@ cc_test { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackA2dp", ":TestMockStackAcl", @@ -243,7 +244,6 @@ cc_test { ":TestMockStackGatt", ":TestMockStackHid", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackPan", ":TestMockStackRfcomm", ":TestMockStackRnr", @@ -339,10 +339,10 @@ cc_test { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackBtm", ":TestMockStackL2cap", - ":TestMockStackMetrics", "test/gatt/database_builder_sample_device_test.cc", "test/gatt/database_builder_test.cc", "test/gatt/database_test.cc", diff --git a/system/bta/BUILD.gn b/system/bta/BUILD.gn index 9c28016617..f621539151 100644 --- a/system/bta/BUILD.gn +++ b/system/bta/BUILD.gn @@ -143,7 +143,6 @@ static_library("bta") { "sys", "//bt/system/", "//bt/system/include", - "//bt/system/linux_include", "//bt/system/bta", "//bt/system/bta/aics/include", "//bt/system/gd", diff --git a/system/bta/gatt/bta_gattc_utils.cc b/system/bta/gatt/bta_gattc_utils.cc index 23b150b829..1541f391ca 100644 --- a/system/bta/gatt/bta_gattc_utils.cc +++ b/system/bta/gatt/bta_gattc_utils.cc @@ -142,7 +142,7 @@ 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) { + if (p_clcb != NULL && p_clcb->in_use && p_clcb->bta_conn_id == conn_id) { return p_clcb.get(); } } diff --git a/system/bta/le_audio/broadcaster/broadcaster_test.cc b/system/bta/le_audio/broadcaster/broadcaster_test.cc index a211a810ad..16177230d1 100644 --- a/system/bta/le_audio/broadcaster/broadcaster_test.cc +++ b/system/bta/le_audio/broadcaster/broadcaster_test.cc @@ -63,6 +63,8 @@ using testing::Test; using namespace bluetooth::le_audio; using namespace bluetooth; +using bluetooth::hci::testing::mock_controller_; + using bluetooth::le_audio::DsaMode; using bluetooth::le_audio::LeAudioCodecConfiguration; using bluetooth::le_audio::LeAudioSourceAudioHalClient; @@ -287,8 +289,8 @@ protected: init_message_loop_thread(); reset_mock_function_count_map(); - bluetooth::hci::testing::mock_controller_ = &mock_controller_; - ON_CALL(mock_controller_, SupportsBleIsochronousBroadcaster).WillByDefault(Return(true)); + mock_controller_ = std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + ON_CALL(*mock_controller_, SupportsBleIsochronousBroadcaster).WillByDefault(Return(true)); iso_manager_ = bluetooth::hci::IsoManager::GetInstance(); ASSERT_NE(iso_manager_, nullptr); @@ -366,7 +368,7 @@ protected: ContentControlIdKeeper::GetInstance()->Stop(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.release(); delete mock_audio_source_; iso_active_callback = nullptr; delete mock_audio_source_; @@ -436,7 +438,6 @@ protected: protected: MockLeAudioBroadcasterCallbacks mock_broadcaster_callbacks_; - bluetooth::hci::testing::MockControllerInterface mock_controller_; bluetooth::hci::IsoManager* iso_manager_; MockIsoManager* mock_iso_manager_; bluetooth::hci::iso_manager::BigCallbacks* big_callbacks_ = nullptr; diff --git a/system/bta/le_audio/codec_manager_test.cc b/system/bta/le_audio/codec_manager_test.cc index 9ac1fde07d..c69464b72e 100644 --- a/system/bta/le_audio/codec_manager_test.cc +++ b/system/bta/le_audio/codec_manager_test.cc @@ -300,10 +300,12 @@ public: bluetooth::legacy::hci::testing::SetMock(legacy_hci_mock_); - ON_CALL(controller_interface, SupportsBleIsochronousBroadcaster).WillByDefault(Return(true)); - ON_CALL(controller_interface, IsSupported(OpCode::CONFIGURE_DATA_PATH)) + bluetooth::hci::testing::mock_controller_ = + std::make_unique<NiceMock<bluetooth::hci::testing::MockControllerInterface>>(); + ON_CALL(*bluetooth::hci::testing::mock_controller_, SupportsBleIsochronousBroadcaster) + .WillByDefault(Return(true)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, IsSupported(OpCode::CONFIGURE_DATA_PATH)) .WillByDefault(Return(true)); - bluetooth::hci::testing::mock_controller_ = &controller_interface; codec_manager = CodecManager::GetInstance(); @@ -311,9 +313,11 @@ public: RegisterSinkHalClientMock(); } - virtual void TearDown() override { codec_manager->Stop(); } + virtual void TearDown() override { + codec_manager->Stop(); + bluetooth::hci::testing::mock_controller_.release(); + } - NiceMock<bluetooth::hci::testing::MockControllerInterface> controller_interface; CodecManager* codec_manager; bluetooth::legacy::hci::testing::MockInterface legacy_hci_mock_; diff --git a/system/bta/le_audio/devices_test.cc b/system/bta/le_audio/devices_test.cc index adc90e5213..cd711065c4 100644 --- a/system/bta/le_audio/devices_test.cc +++ b/system/bta/le_audio/devices_test.cc @@ -514,7 +514,9 @@ protected: desired_group_size_ = -1; bluetooth::manager::SetMockBtmInterface(&btm_interface_); - bluetooth::hci::testing::mock_controller_ = &controller_interface_; + + bluetooth::hci::testing::mock_controller_ = + std::make_unique<NiceMock<bluetooth::hci::testing::MockControllerInterface>>(); auto codec_location = ::bluetooth::le_audio::types::CodecLocation::HOST; bluetooth::le_audio::AudioSetConfigurationProvider::Initialize(codec_location); @@ -740,6 +742,8 @@ protected: if (codec_manager_) { codec_manager_->Stop(); } + + bluetooth::hci::testing::mock_controller_.reset(); } LeAudioDevice* AddTestDevice(int snk_ase_num, int src_ase_num, int snk_ase_num_cached = 0, @@ -1417,7 +1421,6 @@ protected: LeAudioDeviceGroup* group_ = nullptr; bluetooth::manager::MockBtmInterface btm_interface_; MockCsisClient mock_csis_client_module_; - NiceMock<bluetooth::hci::testing::MockControllerInterface> controller_interface_; bluetooth::le_audio::CodecManager* codec_manager_; MockCodecManager* mock_codec_manager_; diff --git a/system/bta/le_audio/le_audio_client_test.cc b/system/bta/le_audio/le_audio_client_test.cc index a3c52dc9e6..02104518d2 100644 --- a/system/bta/le_audio/le_audio_client_test.cc +++ b/system/bta/le_audio/le_audio_client_test.cc @@ -1477,11 +1477,13 @@ protected: init_message_loop_thread(); reset_mock_function_count_map(); - ON_CALL(controller_, SupportsBleConnectedIsochronousStreamCentral).WillByDefault(Return(true)); - ON_CALL(controller_, SupportsBleConnectedIsochronousStreamPeripheral) + hci::testing::mock_controller_ = + std::make_unique<NiceMock<bluetooth::hci::testing::MockControllerInterface>>(); + ON_CALL(*hci::testing::mock_controller_, SupportsBleConnectedIsochronousStreamCentral) .WillByDefault(Return(true)); - ON_CALL(controller_, SupportsBle2mPhy).WillByDefault(Return(true)); - bluetooth::hci::testing::mock_controller_ = &controller_; + ON_CALL(*hci::testing::mock_controller_, SupportsBleConnectedIsochronousStreamPeripheral) + .WillByDefault(Return(true)); + ON_CALL(*hci::testing::mock_controller_, SupportsBle2mPhy).WillByDefault(Return(true)); bluetooth::manager::SetMockBtmInterface(&mock_btm_interface_); gatt::SetMockBtaGattInterface(&mock_gatt_interface_); gatt::SetMockBtaGattQueue(&mock_gatt_queue_); @@ -1595,7 +1597,7 @@ protected: } iso_manager_->Stop(); - bluetooth::hci::testing::mock_controller_ = nullptr; + hci::testing::mock_controller_.reset(); } protected: @@ -2761,7 +2763,6 @@ protected: /* Audio track metadata */ char* test_tags_ptr_ = nullptr; - NiceMock<bluetooth::hci::testing::MockControllerInterface> controller_; }; class UnicastTest : public UnicastTestNoInit { diff --git a/system/bta/le_audio/state_machine_test.cc b/system/bta/le_audio/state_machine_test.cc index d17bfda188..e0860515fb 100644 --- a/system/bta/le_audio/state_machine_test.cc +++ b/system/bta/le_audio/state_machine_test.cc @@ -261,7 +261,9 @@ protected: bluetooth::manager::SetMockBtmInterface(&btm_interface); gatt::SetMockBtaGattInterface(&gatt_interface); gatt::SetMockBtaGattQueue(&gatt_queue); - bluetooth::hci::testing::mock_controller_ = &controller_; + + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); overwrite_cis_status_idx_ = 0; use_cis_retry_cnt_ = false; @@ -649,7 +651,7 @@ protected: cached_remote_qos_configuration_for_ase_.clear(); LeAudioGroupStateMachine::Cleanup(); ::bluetooth::le_audio::AudioSetConfigurationProvider::Cleanup(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.release(); } std::shared_ptr<LeAudioDevice> PrepareConnectedDevice(uint8_t id, @@ -1714,7 +1716,6 @@ protected: std::vector<RawAddress> addresses_; std::map<uint8_t, std::unique_ptr<LeAudioDeviceGroup>> le_audio_device_groups_; bool group_create_command_disallowed_ = false; - bluetooth::hci::testing::MockControllerInterface controller_; }; class StateMachineTest : public StateMachineTestBase { diff --git a/system/bta/test/bta_ag_sco_test.cc b/system/bta/test/bta_ag_sco_test.cc index 16caac7665..a90198c54a 100644 --- a/system/bta/test/bta_ag_sco_test.cc +++ b/system/bta/test/bta_ag_sco_test.cc @@ -43,14 +43,14 @@ protected: this->codec = codec; return enh_esco_params_t{}; }; - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); } void TearDown() override { test::mock::device_esco_parameters::esco_parameters_for_codec = {}; - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } esco_codec_t codec; - bluetooth::hci::testing::MockControllerInterface controller_; }; TEST_P(BtaAgScoParameterSelectionTest, create_sco_cvsd) { diff --git a/system/bta/test/bta_ag_test.cc b/system/bta/test/bta_ag_test.cc index 5fa3884669..2114f7e773 100644 --- a/system/bta/test/bta_ag_test.cc +++ b/system/bta/test/bta_ag_test.cc @@ -65,7 +65,8 @@ protected: void SetUp() override { reset_mock_function_count_map(); fake_osi_ = std::make_unique<test::fake::FakeOsi>(); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); main_thread_start_up(); post_on_bt_main([]() { log::info("Main thread started up"); }); @@ -85,7 +86,7 @@ protected: bta_sys_deregister(BTA_ID_AG); post_on_bt_main([]() { log::info("Main thread shutting down"); }); main_thread_shut_down(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } std::unique_ptr<test::fake::FakeOsi> fake_osi_; @@ -93,7 +94,6 @@ protected: uint32_t tmp_num = 0xFFFF; RawAddress addr; esco_codec_t codec; - bluetooth::hci::testing::MockControllerInterface controller_; }; class BtaAgSwbTest : public BtaAgTest { diff --git a/system/bta/test/bta_dm_test.cc b/system/bta/test/bta_dm_test.cc index 90d41d5e6f..b49008b739 100644 --- a/system/bta/test/bta_dm_test.cc +++ b/system/bta/test/bta_dm_test.cc @@ -65,10 +65,10 @@ class BtaDmTest : public BtaWithContextTest { protected: void SetUp() override { BtaWithContextTest::SetUp(); - ON_CALL(controller_, LeRand).WillByDefault([](bluetooth::hci::LeRandCallback cb) { - cb(0x1234); - }); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + ON_CALL(*bluetooth::hci::testing::mock_controller_, LeRand) + .WillByDefault([](bluetooth::hci::LeRandCallback cb) { cb(0x1234); }); BTA_dm_init(); bluetooth::legacy::testing::bta_dm_init_cb(); @@ -82,9 +82,8 @@ protected: void TearDown() override { bluetooth::legacy::testing::bta_dm_deinit_cb(); BtaWithContextTest::TearDown(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } - bluetooth::hci::testing::MockControllerInterface controller_; }; class BtaDmCustomAlarmTest : public BtaDmTest { diff --git a/system/bta/test/bta_sdp_test.cc b/system/bta/test/bta_sdp_test.cc index 06e8ff86aa..77f1762cc0 100644 --- a/system/bta/test/bta_sdp_test.cc +++ b/system/bta/test/bta_sdp_test.cc @@ -30,17 +30,16 @@ class BtaSdpTest : public BtaWithHwOnTest { protected: void SetUp() override { BtaWithHwOnTest::SetUp(); - ON_CALL(controller_, LeRand).WillByDefault([](bluetooth::hci::LeRandCallback cb) { - cb(0x1234); - }); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + ON_CALL(*bluetooth::hci::testing::mock_controller_, LeRand) + .WillByDefault([](bluetooth::hci::LeRandCallback cb) { cb(0x1234); }); } void TearDown() override { BtaWithHwOnTest::TearDown(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } - bluetooth::hci::testing::MockControllerInterface controller_; }; class BtaSdpRegisteredTest : public BtaSdpTest { diff --git a/system/bta/test/bta_test_fixtures.h b/system/bta/test/bta_test_fixtures.h index c259d0b916..e41e64d496 100644 --- a/system/bta/test/bta_test_fixtures.h +++ b/system/bta/test/bta_test_fixtures.h @@ -60,7 +60,8 @@ protected: ASSERT_NE(get_btm_client_interface().lifecycle.btm_init, nullptr); ASSERT_NE(get_btm_client_interface().lifecycle.btm_free, nullptr); - bluetooth::hci::testing::mock_controller_ = &mock_controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); bluetooth::testing::stack::rnr::set_interface(&mock_stack_rnr_interface_); test::mock::stack_gatt_api::GATT_Register.body = @@ -85,12 +86,11 @@ protected: mock_btm_client_interface.eir.BTM_WriteEIR = {}; bluetooth::testing::stack::rnr::reset_interface(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); BtaWithFakesTest::TearDown(); } - bluetooth::hci::testing::MockControllerInterface mock_controller_; bluetooth::testing::stack::rnr::Mock mock_stack_rnr_interface_; }; 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_hf.cc b/system/btif/src/btif_hf.cc index c5dc2bca6a..178a1672e6 100644 --- a/system/btif/src/btif_hf.cc +++ b/system/btif/src/btif_hf.cc @@ -880,7 +880,7 @@ public: bt_status_t DisconnectAudio(RawAddress* bd_addr) override; bt_status_t isNoiseReductionSupported(RawAddress* bd_addr) override; bt_status_t isVoiceRecognitionSupported(RawAddress* bd_addr) override; - bt_status_t StartVoiceRecognition(RawAddress* bd_addr) override; + bt_status_t StartVoiceRecognition(RawAddress* bd_addr, bool sendResult) override; bt_status_t StopVoiceRecognition(RawAddress* bd_addr) override; bt_status_t VolumeControl(bthf_volume_type_t type, int volume, RawAddress* bd_addr) override; bt_status_t DeviceStatusNotification(bthf_network_state_t ntk_state, bthf_service_type_t svc_type, @@ -1023,7 +1023,7 @@ bt_status_t HeadsetInterface::isVoiceRecognitionSupported(RawAddress* bd_addr) { return BT_STATUS_SUCCESS; } -bt_status_t HeadsetInterface::StartVoiceRecognition(RawAddress* bd_addr) { +bt_status_t HeadsetInterface::StartVoiceRecognition(RawAddress* bd_addr, bool sendResult) { CHECK_BTHF_INIT(); int idx = btif_hf_idx_by_bdaddr(bd_addr); if ((idx < 0) || (idx >= BTA_AG_MAX_NUM_CLIENTS)) { @@ -1039,9 +1039,11 @@ bt_status_t HeadsetInterface::StartVoiceRecognition(RawAddress* bd_addr) { return BT_STATUS_UNSUPPORTED; } btif_hf_cb[idx].is_during_voice_recognition = true; - tBTA_AG_RES_DATA ag_res = {}; - ag_res.state = true; - BTA_AgResult(btif_hf_cb[idx].handle, BTA_AG_BVRA_RES, ag_res); + if (sendResult) { + tBTA_AG_RES_DATA ag_res = {}; + ag_res.state = true; + BTA_AgResult(btif_hf_cb[idx].handle, BTA_AG_BVRA_RES, ag_res); + } return BT_STATUS_SUCCESS; } diff --git a/system/btif/test/btif_core_test.cc b/system/btif/test/btif_core_test.cc index 7342a17b99..3073bd194c 100644 --- a/system/btif/test/btif_core_test.cc +++ b/system/btif/test/btif_core_test.cc @@ -207,7 +207,8 @@ class BtifCoreTest : public ::testing::Test { protected: void SetUp() override { callback_map_.clear(); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); bluetooth::testing::set_hal_cbacks(&callbacks); auto promise = std::promise<void>(); auto future = promise.get_future(); @@ -223,17 +224,17 @@ protected: callback_map_["callback_thread_event"] = [&promise]() { promise.set_value(); }; CleanCoreInterface(); ASSERT_EQ(std::future_status::ready, future.wait_for(timeout_time)); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); callback_map_.erase("callback_thread_event"); } - bluetooth::hci::testing::MockControllerInterface controller_; }; class BtifCoreWithControllerTest : public BtifCoreTest { protected: void SetUp() override { BtifCoreTest::SetUp(); - ON_CALL(controller_, SupportsSniffSubrating).WillByDefault(Return(true)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, SupportsSniffSubrating) + .WillByDefault(Return(true)); } void TearDown() override { BtifCoreTest::TearDown(); } @@ -761,12 +762,13 @@ class BtifCoreWithVendorSupportTest : public BtifCoreWithControllerTest { protected: void SetUp() override { BtifCoreWithControllerTest::SetUp(); - bluetooth::hci::testing::mock_hci_layer_ = &hci_; + bluetooth::hci::testing::mock_hci_layer_ = + std::make_unique<bluetooth::hci::testing::MockHciLayer>(); test::mock::osi_properties::osi_property_get.body = get_properties; std::promise<void> configuration_promise; auto configuration_done = configuration_promise.get_future(); - EXPECT_CALL(hci_, + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, EnqueueCommand(_, Matcher<ContextualOnceCallback<void(CommandCompleteView)>>(_))) .WillOnce( // Replace with real PDL for 0xfc17 @@ -784,7 +786,7 @@ protected: configuration_promise.set_value(); }) .RetiresOnSaturation(); - EXPECT_CALL(hci_, + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, EnqueueCommand(_, Matcher<ContextualOnceCallback<void(CommandCompleteView)>>(_))) .WillOnce([](std::unique_ptr<CommandBuilder> cmd, ContextualOnceCallback<void(CommandCompleteView)> callback) { @@ -798,7 +800,8 @@ protected: callback(response); }) .RetiresOnSaturation(); - EXPECT_CALL(hci_, RegisterVendorSpecificEventHandler(VseSubeventCode::BQR_EVENT, _)) + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, + RegisterVendorSpecificEventHandler(VseSubeventCode::BQR_EVENT, _)) .WillOnce(SaveArg<1>(&this->vse_callback_)); do_in_main_thread(BindOnce([]() { bluetooth::bqr::EnableBtQualityReport(get_main()); })); ASSERT_EQ(std::future_status::ready, configuration_done.wait_for(std::chrono::seconds(1))); @@ -808,18 +811,18 @@ protected: std::promise<void> disable_promise; auto disable_future = disable_promise.get_future(); auto set_promise = [&disable_promise]() { disable_promise.set_value(); }; - EXPECT_CALL(hci_, UnregisterVendorSpecificEventHandler(VseSubeventCode::BQR_EVENT)); - EXPECT_CALL(hci_, + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, + UnregisterVendorSpecificEventHandler(VseSubeventCode::BQR_EVENT)); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, EnqueueCommand(_, Matcher<ContextualOnceCallback<void(CommandCompleteView)>>(_))) .WillOnce(Invoke(set_promise)) .RetiresOnSaturation(); do_in_main_thread(BindOnce([]() { bluetooth::bqr::DisableBtQualityReport(); })); ASSERT_EQ(std::future_status::ready, disable_future.wait_for(std::chrono::seconds(1))); - bluetooth::hci::testing::mock_hci_layer_ = nullptr; + bluetooth::hci::testing::mock_hci_layer_.reset(); BtifCoreWithControllerTest::TearDown(); } - bluetooth::hci::testing::MockHciLayer hci_; ContextualCallback<void(VendorSpecificEventView)> vse_callback_; }; diff --git a/system/common/BUILD.gn b/system/common/BUILD.gn index a1884973dd..50ec9c1dd0 100644 --- a/system/common/BUILD.gn +++ b/system/common/BUILD.gn @@ -30,7 +30,6 @@ static_library("common") { include_dirs = [ "//bt/system/", "//bt/system/stack/include", - "//bt/system/linux_include", ] deps = [ diff --git a/system/common/metrics.cc b/system/common/metrics.cc index a3b3eea450..c6c0977115 100644 --- a/system/common/metrics.cc +++ b/system/common/metrics.cc @@ -18,87 +18,19 @@ #include "common/metrics.h" -#include <base/base64.h> #include <bluetooth/log.h> #include <frameworks/proto_logging/stats/enums/bluetooth/le/enums.pb.h> -#include <include/hardware/bt_av.h> #include <statslog_bt.h> -#include <unistd.h> -#include <algorithm> -#include <cerrno> #include <cstdint> -#include <cstring> -#include <memory> -#include <mutex> // NOLINT -#include <utility> +#include <vector> -#include "common/address_obfuscator.h" -#include "common/leaky_bonded_queue.h" -#include "common/time_util.h" -#include "hci/address.h" #include "main/shim/metric_id_api.h" -#include "osi/include/osi.h" #include "types/raw_address.h" -namespace std { -template <> -struct formatter<android::bluetooth::DirectionEnum> - : enum_formatter<android::bluetooth::DirectionEnum> {}; -template <> -struct formatter<android::bluetooth::SocketConnectionstateEnum> - : enum_formatter<android::bluetooth::SocketConnectionstateEnum> {}; -template <> -struct formatter<android::bluetooth::SocketRoleEnum> - : enum_formatter<android::bluetooth::SocketRoleEnum> {}; -template <> -struct formatter<android::bluetooth::AddressTypeEnum> - : enum_formatter<android::bluetooth::AddressTypeEnum> {}; -template <> -struct formatter<android::bluetooth::DeviceInfoSrcEnum> - : enum_formatter<android::bluetooth::DeviceInfoSrcEnum> {}; -template <> -struct formatter<android::bluetooth::SocketErrorEnum> - : enum_formatter<android::bluetooth::SocketErrorEnum> {}; -} // namespace std - namespace bluetooth { namespace common { -using bluetooth::hci::Address; - -void LogLinkLayerConnectionEvent(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, uint16_t link_type, - uint32_t hci_cmd, uint16_t hci_event, uint16_t hci_ble_event, - uint16_t cmd_status, uint16_t reason_code) { - std::string obfuscated_id; - int metric_id = 0; - if (address != nullptr) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(*address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(*address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address != nullptr ? obfuscated_id.c_str() : nullptr, - address != nullptr ? obfuscated_id.size() : 0); - int ret = stats_write(BLUETOOTH_LINK_LAYER_CONNECTION_EVENT, bytes_field, connection_handle, - direction, link_type, hci_cmd, hci_event, hci_ble_event, cmd_status, - reason_code, metric_id); - if (ret < 0) { - log::warn( - "failed to log status 0x{:x}, reason 0x{:x} from cmd 0x{:x}, event " - "0x{:x}, ble_event 0x{:x} for {}, handle {}, type 0x{:x}, error {}", - cmd_status, reason_code, hci_cmd, hci_event, hci_ble_event, *address, connection_handle, - link_type, ret); - } -} - -void LogHciTimeoutEvent(uint32_t hci_cmd) { - int ret = stats_write(BLUETOOTH_HCI_TIMEOUT_REPORTED, static_cast<int64_t>(hci_cmd)); - if (ret < 0) { - log::warn("failed for opcode 0x{:x}, error {}", hci_cmd, ret); - } -} - 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, @@ -111,276 +43,6 @@ void LogRemoteVersionInfo(uint16_t handle, uint8_t status, uint8_t version, } } -void LogA2dpAudioUnderrunEvent(const RawAddress& address, uint64_t encoding_interval_millis, - int num_missing_pcm_bytes) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int64_t encoding_interval_nanos = encoding_interval_millis * 1000000; - int ret = stats_write(BLUETOOTH_A2DP_AUDIO_UNDERRUN_REPORTED, bytes_field, - encoding_interval_nanos, num_missing_pcm_bytes, metric_id); - if (ret < 0) { - log::warn( - "failed for {}, encoding_interval_nanos {}, num_missing_pcm_bytes {}, " - "error {}", - address, encoding_interval_nanos, num_missing_pcm_bytes, ret); - } -} - -void LogA2dpAudioOverrunEvent(const RawAddress& address, uint64_t encoding_interval_millis, - int num_dropped_buffers, int num_dropped_encoded_frames, - int num_dropped_encoded_bytes) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int64_t encoding_interval_nanos = encoding_interval_millis * 1000000; - int ret = stats_write(BLUETOOTH_A2DP_AUDIO_OVERRUN_REPORTED, bytes_field, encoding_interval_nanos, - num_dropped_buffers, num_dropped_encoded_frames, num_dropped_encoded_bytes, - metric_id); - if (ret < 0) { - log::warn( - "failed to log for {}, encoding_interval_nanos {}, num_dropped_buffers " - "{}, num_dropped_encoded_frames {}, num_dropped_encoded_bytes {}, " - "error {}", - address, encoding_interval_nanos, num_dropped_buffers, num_dropped_encoded_frames, - num_dropped_encoded_bytes, ret); - } -} - -void LogA2dpPlaybackEvent(const RawAddress& address, int playback_state, int audio_coding_mode) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_A2DP_PLAYBACK_STATE_CHANGED, bytes_field, playback_state, - audio_coding_mode, metric_id); - if (ret < 0) { - log::warn( - "failed to log for {}, playback_state {}, audio_coding_mode {}, error " - "{}", - address, playback_state, audio_coding_mode, ret); - } -} - -void LogReadRssiResult(const RawAddress& address, uint16_t handle, uint32_t cmd_status, - int8_t rssi) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_DEVICE_RSSI_REPORTED, bytes_field, handle, cmd_status, rssi, - metric_id); - if (ret < 0) { - log::warn("failed for {}, handle {}, status 0x{:x}, rssi {} dBm, error {}", address, handle, - cmd_status, rssi, ret); - } -} - -void LogReadFailedContactCounterResult(const RawAddress& address, uint16_t handle, - uint32_t cmd_status, int32_t failed_contact_counter) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_DEVICE_FAILED_CONTACT_COUNTER_REPORTED, bytes_field, handle, - cmd_status, failed_contact_counter, metric_id); - if (ret < 0) { - log::warn( - "failed for {}, handle {}, status 0x{:x}, failed_contact_counter {} " - "packets, error {}", - address, handle, cmd_status, failed_contact_counter, ret); - } -} - -void LogReadTxPowerLevelResult(const RawAddress& address, uint16_t handle, uint32_t cmd_status, - int32_t transmit_power_level) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField bytes_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_DEVICE_TX_POWER_LEVEL_REPORTED, bytes_field, handle, cmd_status, - transmit_power_level, metric_id); - if (ret < 0) { - log::warn( - "failed for {}, handle {}, status 0x{:x}, transmit_power_level {} " - "packets, error {}", - address, handle, cmd_status, transmit_power_level, ret); - } -} - -void LogSmpPairingEvent(const RawAddress& address, uint8_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField obfuscated_id_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_SMP_PAIRING_EVENT_REPORTED, obfuscated_id_field, smp_cmd, - direction, smp_fail_reason, metric_id); - if (ret < 0) { - log::warn( - "failed for {}, smp_cmd 0x{:x}, direction {}, smp_fail_reason 0x{:x}, " - "error {}", - address, smp_cmd, direction, smp_fail_reason, ret); - } -} - -void LogClassicPairingEvent(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, - uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, - int64_t event_value) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField obfuscated_id_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_CLASSIC_PAIRING_EVENT_REPORTED, obfuscated_id_field, handle, - hci_cmd, hci_event, cmd_status, reason_code, event_value, metric_id); - if (ret < 0) { - log::warn( - "failed for {}, handle {}, hci_cmd 0x{:x}, hci_event 0x{:x}, " - "cmd_status 0x{:x}, reason 0x{:x}, event_value {}, error {}", - address, handle, hci_cmd, hci_event, cmd_status, reason_code, event_value, ret); - } -} - -void LogSdpAttribute(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField obfuscated_id_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - BytesField attribute_field(attribute_value, attribute_size); - int ret = stats_write(BLUETOOTH_SDP_ATTRIBUTE_REPORTED, obfuscated_id_field, protocol_uuid, - attribute_id, attribute_field, metric_id); - if (ret < 0) { - log::warn("failed for {}, protocol_uuid 0x{:x}, attribute_id 0x{:x}, error {}", address, - protocol_uuid, attribute_id, ret); - } -} - -void LogSocketConnectionState(const RawAddress& address, int port, int type, - android::bluetooth::SocketConnectionstateEnum connection_state, - int64_t tx_bytes, int64_t rx_bytes, int uid, int server_port, - android::bluetooth::SocketRoleEnum socket_role, - uint64_t connection_duration_ms, - android::bluetooth::SocketErrorEnum error_code, - bool is_hardware_offload) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField obfuscated_id_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_SOCKET_CONNECTION_STATE_CHANGED, obfuscated_id_field, port, type, - connection_state, tx_bytes, rx_bytes, uid, server_port, socket_role, - metric_id, static_cast<int64_t>(connection_duration_ms), error_code, - is_hardware_offload); - if (ret < 0) { - log::warn( - "failed for {}, port {}, type {}, state {}, tx_bytes {}, rx_bytes {}, " - "uid {}, server_port {}, socket_role {}, error {}, connection_duration_ms {}, " - "socket_error_code {}, " - "is_hardware_offload {}", - address, port, type, connection_state, tx_bytes, rx_bytes, uid, server_port, - socket_role, ret, connection_duration_ms, error_code, is_hardware_offload); - } -} - -void LogManufacturerInfo(const RawAddress& address, - android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version) { - std::string obfuscated_id; - int metric_id = 0; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - metric_id = bluetooth::shim::AllocateIdFromMetricIdAllocator(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField obfuscated_id_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_DEVICE_INFO_REPORTED, obfuscated_id_field, source_type, - source_name.c_str(), manufacturer.c_str(), model.c_str(), - hardware_version.c_str(), software_version.c_str(), metric_id, address_type, - address.address[5], address.address[4], address.address[3]); - if (ret < 0) { - log::warn( - "failed for {}, source_type {}, source_name {}, manufacturer {}, model " - "{}, hardware_version {}, software_version {} MAC address type {} MAC " - "address prefix {} {} {}, error {}", - address, source_type, source_name, manufacturer, model, hardware_version, - software_version, address_type, address.address[5], address.address[4], - address.address[3], ret); - } -} - -void LogBluetoothHalCrashReason(const RawAddress& address, uint32_t error_code, - uint32_t vendor_error_code) { - std::string obfuscated_id; - if (!address.IsEmpty()) { - obfuscated_id = AddressObfuscator::GetInstance()->Obfuscate(address); - } - // nullptr and size 0 represent missing value for obfuscated_id - BytesField obfuscated_id_field(address.IsEmpty() ? nullptr : obfuscated_id.c_str(), - address.IsEmpty() ? 0 : obfuscated_id.size()); - int ret = stats_write(BLUETOOTH_HAL_CRASH_REASON_REPORTED, 0, obfuscated_id_field, error_code, - vendor_error_code); - if (ret < 0) { - log::warn("failed for {}, error_code 0x{:x}, vendor_error_code 0x{:x}, error {}", address, - error_code, vendor_error_code, 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, @@ -429,5 +91,4 @@ void LogLeAudioBroadcastSessionReported(int64_t duration_nanos) { } } // namespace common - } // namespace bluetooth diff --git a/system/common/metrics.h b/system/common/metrics.h index 4ccb47f64c..4d5a234ce5 100644 --- a/system/common/metrics.h +++ b/system/common/metrics.h @@ -18,22 +18,12 @@ #pragma once -#include <bta/include/bta_api.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/le/enums.pb.h> -#include <stdint.h> - -#include <memory> -#include <string> +#include <cstdint> #include <vector> -#include "hci/address.h" -#include "os/metrics.h" #include "types/raw_address.h" namespace bluetooth { - namespace common { /** @@ -42,34 +32,6 @@ namespace common { static const uint32_t kUnknownConnectionHandle = 0xFFFF; /** - * Log link layer connection event - * - * @param address Stack wide consistent Bluetooth address of this event, - * nullptr if unknown - * @param connection_handle connection handle of this event, - * {@link kUnknownConnectionHandle} if unknown - * @param direction direction of this connection - * @param link_type type of the link - * @param hci_cmd HCI command opecode associated with this event, if any - * @param hci_event HCI event code associated with this event, if any - * @param hci_ble_event HCI BLE event code associated with this event, if any - * @param cmd_status Command status associated with this event, if any - * @param reason_code Reason code associated with this event, if any - */ -void LogLinkLayerConnectionEvent(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, uint16_t link_type, - uint32_t hci_cmd, uint16_t hci_event, uint16_t hci_ble_event, - uint16_t cmd_status, uint16_t reason_code); - -/** - * Logs when Bluetooth controller failed to reply with command status within - * a timeout period after receiving an HCI command from the host - * - * @param hci_cmd opcode of HCI command that caused this timeout - */ -void LogHciTimeoutEvent(uint32_t hci_cmd); - -/** * Logs when we receive Bluetooth Read Remote Version Information Complete * Event from the remote device, as documented by the Bluetooth Core HCI * specification @@ -86,173 +48,6 @@ void LogHciTimeoutEvent(uint32_t hci_cmd); void LogRemoteVersionInfo(uint16_t handle, uint8_t status, uint8_t version, uint16_t manufacturer_name, uint16_t subversion); -/** - * Log A2DP audio buffer underrun event - * - * @param address A2DP device associated with this event - * @param encoding_interval_millis encoding interval in milliseconds - * @param num_missing_pcm_bytes number of PCM bytes that cannot be read from - * the source - */ -void LogA2dpAudioUnderrunEvent(const RawAddress& address, uint64_t encoding_interval_millis, - int num_missing_pcm_bytes); - -/** - * Log A2DP audio buffer overrun event - * - * @param address A2DP device associated with this event - * @param encoding_interval_millis encoding interval in milliseconds - * @param num_dropped_buffers number of encoded buffers dropped from Tx queue - * @param num_dropped_encoded_frames number of encoded frames dropped from Tx - * queue - * @param num_dropped_encoded_bytes number of encoded bytes dropped from Tx - * queue - */ -void LogA2dpAudioOverrunEvent(const RawAddress& address, uint64_t encoding_interval_millis, - int num_dropped_buffers, int num_dropped_encoded_frames, - int num_dropped_encoded_bytes); - -/** - * Log A2DP playback state changed event - * - * @param address A2DP device associated with this event - * @param playback_state audio playback state - * @param audio_coding_mode audio codec encoding mode - */ -void LogA2dpPlaybackEvent(const RawAddress& address, int playback_state, int audio_coding_mode); - -/** - * Log read RSSI result - * - * @param address device associated with this event - * @param handle connection handle of this event, - * {@link kUnknownConnectionHandle} if unknown - * @param cmd_status command status from read RSSI command - * @param rssi rssi value in dBm - */ -void LogReadRssiResult(const RawAddress& address, uint16_t handle, uint32_t cmd_status, - int8_t rssi); - -/** - * Log failed contact counter report - * - * @param address device associated with this event - * @param handle connection handle of this event, - * {@link kUnknownConnectionHandle} if unknown - * @param cmd_status command status from read failed contact counter command - * @param failed_contact_counter Number of consecutive failed contacts for a - * connection corresponding to the Handle - */ -void LogReadFailedContactCounterResult(const RawAddress& address, uint16_t handle, - uint32_t cmd_status, int32_t failed_contact_counter); - -/** - * Log transmit power level for a particular device after read - * - * @param address device associated with this event - * @param handle connection handle of this event, - * {@link kUnknownConnectionHandle} if unknown - * @param cmd_status command status from read failed contact counter command - * @param transmit_power_level transmit power level for connection to this - * device - */ -void LogReadTxPowerLevelResult(const RawAddress& address, uint16_t handle, uint32_t cmd_status, - int32_t transmit_power_level); - -/** - * Logs when there is an event related to Bluetooth Security Manager Protocol - * - * @param address address of associated device - * @param smp_cmd SMP command code associated with this event - * @param direction direction of this SMP command - * @param smp_fail_reason SMP pairing failure reason code from SMP spec - */ -void LogSmpPairingEvent(const RawAddress& address, uint8_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason); - -/** - * Logs there is an event related Bluetooth classic pairing - * - * @param address address of associated device - * @param handle connection handle of this event, - * {@link kUnknownConnectionHandle} if unknown - * @param hci_cmd HCI command associated with this event - * @param hci_event HCI event associated with this event - * @param cmd_status Command status associated with this event - * @param reason_code Reason code associated with this event - * @param event_value A status value related to this specific event - */ -void LogClassicPairingEvent(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, - uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, - int64_t event_value); - -/** - * Logs when certain Bluetooth SDP attributes are discovered - * - * @param address address of associated device - * @param protocol_uuid 16 bit protocol UUID from Bluetooth Assigned Numbers - * @param attribute_id 16 bit attribute ID from Bluetooth Assigned Numbers - * @param attribute_size size of this attribute - * @param attribute_value pointer to the attribute data, must be larger than - * attribute_size - */ -void LogSdpAttribute(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value); - -/** - * Logs when there is a change in Bluetooth socket connection state - * - * @param address address of associated device, empty if this is a server port - * @param port port of this socket connection - * @param type type of socket - * @param connection_state socket connection state - * @param tx_bytes number of bytes transmitted - * @param rx_bytes number of bytes received - * @param server_port server port of this socket, if any. When both - * |server_port| and |port| fields are populated, |port| must be spawned - * by |server_port| - * @param socket_role role of this socket, server or connection - * @param uid socket owner's uid - * @param connection_duration_ms duration of socket connection in milliseconds - * @param error_code error code of socket failures - * @param is_hardware_offload whether this is a offload socket - */ -void LogSocketConnectionState(const RawAddress& address, int port, int type, - android::bluetooth::SocketConnectionstateEnum connection_state, - int64_t tx_bytes, int64_t rx_bytes, int uid, int server_port, - android::bluetooth::SocketRoleEnum socket_role, - uint64_t connection_duration_ms, - android::bluetooth::SocketErrorEnum error_code, - bool is_hardware_offload); - -/** - * Logs when a Bluetooth device's manufacturer information is learnt - * - * @param address address of associated device - * @param source_type where is this device info obtained from - * @param source_name name of the data source, internal or external - * @param manufacturer name of the manufacturer of this device - * @param model model of this device - * @param hardware_version hardware version of this device - * @param software_version software version of this device - */ -void LogManufacturerInfo(const RawAddress& address, - android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version); - -/** - * Logs when received Bluetooth HAL crash reason report. - * - * @param address current connected address. - * @param error_code the crash reason from bluetooth hal - * @param vendor_error_code the vendor crash reason from bluetooth Firmware - */ -void LogBluetoothHalCrashReason(const RawAddress& address, uint32_t error_code, - uint32_t vendor_error_code); - 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, @@ -268,5 +63,4 @@ void LogLeAudioConnectionSessionReported( void LogLeAudioBroadcastSessionReported(int64_t duration_nanos); } // namespace common - } // namespace bluetooth diff --git a/system/common/metrics_linux.cc b/system/common/metrics_linux.cc index 3283619415..e886c05bf3 100644 --- a/system/common/metrics_linux.cc +++ b/system/common/metrics_linux.cc @@ -16,81 +16,18 @@ * ******************************************************************************/ -#include <bluetooth/log.h> +#include <cstdint> +#include <vector> #include "common/metrics.h" #include "types/raw_address.h" namespace bluetooth { - namespace common { -void LogClassicPairingEvent(const RawAddress& /* address */, uint16_t /* handle */, - uint32_t /* hci_cmd */, uint16_t /* hci_event */, - uint16_t /* cmd_status */, uint16_t /* reason_code */, - int64_t /* event_value */) {} - -void LogSocketConnectionState(const RawAddress& /* address */, int /* port */, int /* type */, - android::bluetooth::SocketConnectionstateEnum /* connection_state */, - int64_t /* tx_bytes */, int64_t /* rx_bytes */, int /* uid */, - int /* server_port */, - android::bluetooth::SocketRoleEnum /* socket_role */, - uint64_t /* connection_duration_ms */, - android::bluetooth::SocketErrorEnum /* error_code */, - bool /* is_hardware_offload */) {} - -void LogHciTimeoutEvent(uint32_t /* hci_cmd */) {} - -void LogA2dpAudioUnderrunEvent(const RawAddress& /* address */, - uint64_t /* encoding_interval_millis */, - int /* num_missing_pcm_bytes */) {} - -void LogA2dpAudioOverrunEvent(const RawAddress& /* address */, - uint64_t /* encoding_interval_millis */, - int /* num_dropped_buffers */, int /* num_dropped_encoded_frames */, - int /* num_dropped_encoded_bytes */) {} - -void LogA2dpPlaybackEvent(const RawAddress& /* address */, int /* playback_state */, - int /* audio_coding_mode */) {} - -void LogBluetoothHalCrashReason(const RawAddress& /* address */, uint32_t /* error_code */, - uint32_t /* vendor_error_code */) {} - -void LogReadRssiResult(const RawAddress& /* address */, uint16_t /* handle */, - uint32_t /* cmd_status */, int8_t /* rssi */) {} - -void LogReadFailedContactCounterResult(const RawAddress& /* address */, uint16_t /* handle */, - uint32_t /* cmd_status */, - int32_t /* failed_contact_counter */) {} - -void LogReadTxPowerLevelResult(const RawAddress& /* address */, uint16_t /* handle */, - uint32_t /* cmd_status */, int32_t /* transmit_power_level */) {} - void LogRemoteVersionInfo(uint16_t /* handle */, uint8_t /* status */, uint8_t /* version */, uint16_t /* manufacturer_name */, uint16_t /* subversion */) {} -void LogLinkLayerConnectionEvent(const RawAddress* /* address */, uint32_t /* connection_handle */, - android::bluetooth::DirectionEnum /* direction */, - uint16_t /* link_type */, uint32_t /* hci_cmd */, - uint16_t /* hci_event */, uint16_t /* hci_ble_event */, - uint16_t /* cmd_status */, uint16_t /* reason_code */) {} - -void LogManufacturerInfo(const RawAddress& /* address */, - android::bluetooth::AddressTypeEnum /* address_type */, - android::bluetooth::DeviceInfoSrcEnum /* source_type */, - const std::string& /* source_name */, - const std::string& /* manufacturer */, const std::string& /* model */, - const std::string& /* hardware_version */, - const std::string& /* software_version */) {} - -void LogSdpAttribute(const RawAddress& /* address */, uint16_t /* protocol_uuid */, - uint16_t /* attribute_id */, size_t /* attribute_size */, - const char* /* attribute_value */) {} - -void LogSmpPairingEvent(const RawAddress& /* address */, uint8_t /* smp_cmd */, - android::bluetooth::DirectionEnum /* direction */, - uint8_t /* smp_fail_reason */) {} - void LogLeAudioConnectionSessionReported( int32_t /* group_size */, int32_t /* group_metric_id */, int64_t /* connection_duration_nanos */, @@ -107,5 +44,4 @@ void LogLeAudioConnectionSessionReported( void LogLeAudioBroadcastSessionReported(int64_t /* duration_nanos */) {} } // namespace common - } // namespace bluetooth diff --git a/system/gd/Android.bp b/system/gd/Android.bp index 26f31f0b8e..9a6bd02551 100644 --- a/system/gd/Android.bp +++ b/system/gd/Android.bp @@ -357,8 +357,8 @@ cc_test { srcs: [ ":BluetoothPacketTestSources", ":TestCommonMockFunctions", + ":TestMockMainShim", ":TestMockMainShimEntry", - ":TestMockStackMetrics", "common/bidi_queue_unittest.cc", "common/blocking_queue_unittest.cc", "common/byte_array_test.cc", @@ -414,6 +414,9 @@ cc_test { "storage/mutation_test.cc", "storage/storage_module_test.cc", ], + cflags: [ + "-DUSE_FAKE_TIMERS", + ], static_libs: [ "bluetooth_flags_c_lib_for_test", "libbase", @@ -581,11 +584,12 @@ cc_fuzz { defaults: ["gd_fuzz_defaults"], srcs: [ ":TestCommonMockFunctions", - ":TestMockStackMetrics", + ":TestMockMainShim", "hci/fuzz/acl_manager_fuzz_test.cc", ], include_dirs: [ "packages/modules/Bluetooth/system", + "packages/modules/Bluetooth/system/include", ], static_libs: [ "libbt-platform-protos-lite", 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 73f455f168..280cc237b4 100644 --- a/system/gd/hci/acl_manager.cc +++ b/system/gd/hci/acl_manager.cc @@ -260,21 +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::IsOnBackgroundList(AddressWithType address_with_type, std::promise<bool> promise) { - CallOn(pimpl_->le_impl_, &le_impl::is_on_background_connection_list, address_with_type, - std::move(promise)); -} - -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 b6932eee52..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, @@ -108,7 +102,6 @@ public: // connected virtual void CancelConnect(Address address); virtual void RemoveFromBackgroundList(AddressWithType address_with_type); - virtual void IsOnBackgroundList(AddressWithType address_with_type, std::promise<bool> promise); virtual void CancelLeConnect(AddressWithType address_with_type); diff --git a/system/gd/hci/acl_manager/le_impl.h b/system/gd/hci/acl_manager/le_impl.h index a6761e38b7..ef5bfb0d7d 100644 --- a/system/gd/hci/acl_manager/le_impl.h +++ b/system/gd/hci/acl_manager/le_impl.h @@ -39,11 +39,11 @@ #include "hci/hci_packets.h" #include "hci/le_address_manager.h" #include "macros.h" +#include "main/shim/metrics_api.h" #include "os/alarm.h" #include "os/handler.h" #include "os/system_properties.h" #include "stack/include/btm_ble_api_types.h" -#include "stack/include/stack_metrics_logging.h" namespace bluetooth { namespace hci { @@ -426,7 +426,7 @@ public: return; } - log_le_connection_status(address, true /* is_connect */, status); + bluetooth::shim::LogMetricLeConnectionStatus(address, true /* is_connect */, status); const bool in_filter_accept_list = is_device_in_accept_list(remote_address); @@ -586,7 +586,8 @@ public: arm_on_resume_ = true; add_device_to_accept_list(remote_address); } - log_le_connection_status(remote_address.GetAddress(), false /* is_connect */, reason); + bluetooth::shim::LogMetricLeConnectionStatus(remote_address.GetAddress(), + false /* is_connect */, reason); } void on_le_connection_update_complete(LeMetaEventView view) { @@ -724,7 +725,8 @@ public: } void add_device_to_accept_list(AddressWithType address_with_type) { - log_le_device_in_accept_list(address_with_type.GetAddress(), true /* is_add */); + bluetooth::shim::LogMetricLeDeviceInAcceptList(address_with_type.GetAddress(), + true /* is_add */); if (connections.alreadyConnected(address_with_type)) { log::info("Device already connected, return"); return; @@ -747,7 +749,8 @@ public: } void remove_device_from_accept_list(AddressWithType address_with_type) { - log_le_device_in_accept_list(address_with_type.GetAddress(), false /* is_add */); + bluetooth::shim::LogMetricLeDeviceInAcceptList(address_with_type.GetAddress(), + false /* is_add */); if (accept_list.find(address_with_type) == accept_list.end()) { log::warn("Device not in acceptlist and cannot be removed: {}", address_with_type); return; @@ -1132,8 +1135,9 @@ public: remove_device_from_accept_list(address_with_type); } // Temporary mapping the error code to PAGE_TIMEOUT - log_le_connection_completion(address_with_type.GetAddress(), ErrorCode::PAGE_TIMEOUT, - true /* is locally initiated */); + bluetooth::shim::LogMetricLeConnectionCompletion(address_with_type.GetAddress(), + ErrorCode::PAGE_TIMEOUT, + true /* is locally initiated */); le_client_handler_->Post(common::BindOnce( &LeConnectionCallbacks::OnLeConnectFail, common::Unretained(le_client_callbacks_), address_with_type, ErrorCode::CONNECTION_ACCEPT_TIMEOUT)); @@ -1145,25 +1149,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, @@ -1249,12 +1234,6 @@ public: background_connections_.erase(address_with_type); } - void is_on_background_connection_list(AddressWithType address_with_type, - std::promise<bool> promise) { - promise.set_value(background_connections_.find(address_with_type) != - background_connections_.end()); - } - void OnPause() override { // bluetooth::hci::LeAddressManagerCallback if (!address_manager_registered) { log::warn("Unregistered!"); diff --git a/system/gd/hci/acl_manager/le_impl_test.cc b/system/gd/hci/acl_manager/le_impl_test.cc index 66bc16bbd8..274b6827c9 100644 --- a/system/gd/hci/acl_manager/le_impl_test.cc +++ b/system/gd/hci/acl_manager/le_impl_test.cc @@ -1475,29 +1475,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 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/controller.cc b/system/gd/hci/controller.cc index b14e434781..919643f14d 100644 --- a/system/gd/hci/controller.cc +++ b/system/gd/hci/controller.cc @@ -61,9 +61,7 @@ struct Controller::impl { handler->BindOn(this, &Controller::impl::NumberOfCompletedPackets)); set_event_mask(kDefaultEventMask); - if (com::android::bluetooth::flags::encryption_change_v2()) { - set_event_mask_page_2(kDefaultEventMaskPage2); - } + set_event_mask_page_2(kDefaultEventMaskPage2); write_le_host_support(Enable::ENABLED, Enable::DISABLED); hci_->EnqueueCommand( diff --git a/system/gd/hci/distance_measurement_manager.cc b/system/gd/hci/distance_measurement_manager.cc index 6bdfde00b9..6e57f2c9d5 100644 --- a/system/gd/hci/distance_measurement_manager.cc +++ b/system/gd/hci/distance_measurement_manager.cc @@ -441,6 +441,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { it->second.measurement_ongoing = true; it->second.waiting_for_start_callback = true; it->second.local_hci_role = local_hci_role; + it->second.retry_counter_for_create_config = 0; + it->second.retry_counter_for_cs_enable = 0; return true; } @@ -845,6 +847,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { static void reset_tracker_on_stopped(CsTracker& cs_tracker) { cs_tracker.measurement_ongoing = false; cs_tracker.state = CsTrackerState::STOPPED; + cs_tracker.procedure_data_list.clear(); } void handle_cs_setup_failure(uint16_t connection_handle, DistanceMeasurementErrorCode errorCode) { @@ -906,7 +909,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { if (cs_requester_trackers_.find(connection_handle) != cs_requester_trackers_.end()) { reset_tracker_on_stopped(cs_requester_trackers_[connection_handle]); } - } else if (status_view.GetStatus() != ErrorCode::SUCCESS) { + } else if (enable == Enable::ENABLED && status_view.GetStatus() != ErrorCode::SUCCESS) { if (cs_requester_trackers_.count(connection_handle) == 0) { log::error("Error code {} for connection_handle {}. No request tracker found.", ErrorCodeText(status), connection_handle); @@ -919,6 +922,14 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { if (cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable++ >= kMaxRetryCounterForCsEnable) { handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + } else { + cs_requester_trackers_[connection_handle].procedure_schedule_guard_alarm->Cancel(); + log::info("schedule next procedure enable after {} ms", + cs_requester_trackers_[connection_handle].interval_ms); + cs_requester_trackers_[connection_handle].procedure_schedule_guard_alarm->Schedule( + common::Bind(&impl::send_le_cs_procedure_enable, common::Unretained(this), + connection_handle, Enable::ENABLED), + std::chrono::milliseconds(cs_requester_trackers_[connection_handle].interval_ms)); } } } @@ -1237,25 +1248,8 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { void on_cs_procedure_enable_complete(LeCsProcedureEnableCompleteView event_view) { log::assert_that(event_view.IsValid(), "assert failed: event_view.IsValid()"); uint16_t connection_handle = event_view.GetConnectionHandle(); - log::debug("on cs procedure enabled complete"); - if (event_view.GetStatus() != ErrorCode::SUCCESS) { - std::string error_code = ErrorCodeText(event_view.GetStatus()); - if (cs_requester_trackers_.count(connection_handle) == 0) { - log::warn( - "Received LeCsProcedureEnableCompleteView with error code {}, No request tracker " - "found", - error_code); - handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); - return; - } - log::warn("Received LeCsProcedureEnableCompleteView with error code {}. Retry counter {}", - error_code, cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable); - if (cs_requester_trackers_[connection_handle].retry_counter_for_cs_enable++ >= - kMaxRetryCounterForCsEnable) { - handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); - } - return; - } + log::debug("Procedure enabled, {}", event_view.ToString()); + uint8_t config_id = event_view.GetConfigId(); CsTracker* live_tracker = nullptr; @@ -1272,7 +1266,7 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { live_tracker = get_live_tracker(connection_handle, config_id, valid_requester_states, valid_responder_states); if (live_tracker == nullptr) { - log::error("no tracker is available for {}", connection_handle); + log::error("enable - no tracker is available for {}", connection_handle); return; } if (live_tracker->used_config_id != config_id) { @@ -1280,7 +1274,22 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { live_tracker->used_config_id); return; } - log::debug("Procedure enabled, {}", event_view.ToString()); + + if (live_tracker->local_start && event_view.GetStatus() != ErrorCode::SUCCESS) { + log::warn("Received LeCsProcedureEnableCompleteView with error code {}. Retry counter {}", + ErrorCodeText(event_view.GetStatus()), live_tracker->retry_counter_for_cs_enable); + if (live_tracker->retry_counter_for_cs_enable++ >= kMaxRetryCounterForCsEnable) { + handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + } else { + live_tracker->procedure_schedule_guard_alarm->Cancel(); + log::info("schedule next procedure enable after {} ms", live_tracker->interval_ms); + live_tracker->procedure_schedule_guard_alarm->Schedule( + common::Bind(&impl::send_le_cs_procedure_enable, common::Unretained(this), + connection_handle, Enable::ENABLED), + std::chrono::milliseconds(live_tracker->interval_ms)); + } + return; + } live_tracker->state = CsTrackerState::STARTED; live_tracker->selected_tx_power = event_view.GetSelectedTxPower(); live_tracker->n_procedure_count = event_view.GetProcedureCount(); @@ -1297,38 +1306,45 @@ struct DistanceMeasurementManager::impl : bluetooth::hal::RangingHalCallback { if (live_tracker->n_procedure_count >= 1) { live_tracker->procedure_schedule_guard_alarm->Cancel(); log::info("schedule next procedure enable after {} ms", schedule_interval); - cs_requester_trackers_[connection_handle].procedure_schedule_guard_alarm->Schedule( + live_tracker->procedure_schedule_guard_alarm->Schedule( common::Bind(&impl::send_le_cs_procedure_enable, common::Unretained(this), connection_handle, Enable::ENABLED), std::chrono::milliseconds(schedule_interval)); } - } - if (live_tracker->local_start && live_tracker->waiting_for_start_callback) { - live_tracker->waiting_for_start_callback = false; - distance_measurement_callbacks_->OnDistanceMeasurementStarted(live_tracker->address, - METHOD_CS); - } - if (live_tracker->local_start && is_hal_v2()) { - // reset the procedure sequence - live_tracker->procedure_sequence_after_enable = -1; - ranging_hal_->UpdateProcedureEnableConfig(connection_handle, event_view); + if (live_tracker->waiting_for_start_callback) { + live_tracker->waiting_for_start_callback = false; + distance_measurement_callbacks_->OnDistanceMeasurementStarted(live_tracker->address, + METHOD_CS); + } + if (is_hal_v2()) { + // reset the procedure sequence + live_tracker->procedure_sequence_after_enable = -1; + ranging_hal_->UpdateProcedureEnableConfig(connection_handle, event_view); + } } } else if (event_view.GetState() == Enable::DISABLED) { - uint8_t valid_requester_states = static_cast<uint8_t>(CsTrackerState::STARTED); - uint8_t valid_responder_states = static_cast<uint8_t>(CsTrackerState::STARTED); - live_tracker = get_live_tracker(connection_handle, config_id, valid_requester_states, - valid_responder_states); - if (live_tracker == nullptr) { - log::error("no tracker is available for {}", connection_handle); - return; + if (event_view.GetStatus() == ErrorCode::SUCCESS) { + // local or remote host requested it. + uint8_t valid_requester_states = static_cast<uint8_t>(CsTrackerState::STARTED); + uint8_t valid_responder_states = static_cast<uint8_t>(CsTrackerState::STARTED); + live_tracker = get_live_tracker(connection_handle, config_id, valid_requester_states, + valid_responder_states); + if (live_tracker == nullptr) { + log::error("disable - no tracker is available for {}", connection_handle); + return; + } + reset_tracker_on_stopped(*live_tracker); + } else { + // work around, controller may send 'DISABLE' complete with error for 'ENABLE' command + auto req_it = cs_requester_trackers_.find(connection_handle); + if (req_it != cs_requester_trackers_.end() && + req_it->second.state == CsTrackerState::WAIT_FOR_PROCEDURE_ENABLED && + config_id == req_it->second.used_config_id) { + log::warn("expect ENABLE complete, bug got DISABLE complete."); + handle_cs_setup_failure(connection_handle, REASON_INTERNAL_ERROR); + } } - reset_tracker_on_stopped(*live_tracker); - } - // reset the procedure data list. - std::vector<CsProcedureData>& data_list = live_tracker->procedure_data_list; - while (!data_list.empty()) { - data_list.erase(data_list.begin()); } } diff --git a/system/gd/hci/distance_measurement_manager_test.cc b/system/gd/hci/distance_measurement_manager_test.cc index 8679ec27e3..35a629c53a 100644 --- a/system/gd/hci/distance_measurement_manager_test.cc +++ b/system/gd/hci/distance_measurement_manager_test.cc @@ -21,6 +21,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> +#include "common/bind.h" #include "common/strings.h" #include "hal/ranging_hal.h" #include "hal/ranging_hal_mock.h" @@ -32,9 +33,12 @@ #include "hci/hci_layer.h" #include "hci/hci_layer_fake.h" #include "module.h" +#include "os/fake_timer/fake_timerfd.h" #include "packet/packet_view.h" #include "ras/ras_packets.h" +using bluetooth::os::fake_timer::fake_timerfd_advance; +using bluetooth::os::fake_timer::fake_timerfd_reset; using testing::_; using testing::AtLeast; using testing::Return; @@ -42,6 +46,7 @@ using testing::Return; namespace { static constexpr auto kTimeout = std::chrono::seconds(1); static constexpr uint8_t kMaxRetryCounterForCreateConfig = 0x03; +static constexpr uint8_t kMaxRetryCounterForCsEnable = 0x03; } namespace bluetooth { @@ -140,6 +145,20 @@ struct CsConfigCompleteEvent { } }; +struct CsProcedureEnableCompleteEvent { + ErrorCode status = ErrorCode::SUCCESS; + uint8_t config_id = 0; + uint8_t tone_antenna_config_selection = 0; + uint8_t selected_tx_power = 0; // -127 to 20 dBm + uint32_t subevent_len = 2500; // 1250us to 4s + uint8_t subevents_per_event = 1; // 0x01 to 0x20 + uint16_t subevent_interval = 1; // N x 0.625ms + uint16_t event_interval = 0; // number of acl conn interval + uint16_t procedure_interval = 2; // number of acl conn interval + uint16_t procedure_count = 5; // 0x0001 to 0xFFFF + uint16_t max_procedure_len = 10; // N x 0.625 ms +}; + struct StartMeasurementParameters { Address remote_address = Address::FromString("12:34:56:78:9a:bc").value(); uint16_t connection_handle = 64; @@ -165,6 +184,7 @@ protected: EXPECT_CALL(*mock_controller_, SupportsBleChannelSounding()).WillOnce(Return(true)); EXPECT_CALL(*mock_ranging_hal_, IsBound()).Times(AtLeast(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(*mock_ranging_hal_, GetRangingHalVersion).WillRepeatedly(Return(hal::V_2)); handler_ = fake_registry_.GetTestHandler(); dm_manager_ = fake_registry_.Start<DistanceMeasurementManager>(&thread_, handler_); @@ -184,6 +204,19 @@ protected: return dm_session_promise_->get_future(); } + std::future<void> fake_timer_advance(uint64_t ms) { + std::promise<void> promise; + auto future = promise.get_future(); + handler_->Post(common::BindOnce( + [](std::promise<void> promise, uint64_t ms) { + fake_timerfd_advance(ms); + promise.set_value(); + }, + common::Passed(std::move(promise)), ms)); + + return future; + } + void sync_client_handler() { log::assert_that(thread_.GetReactor()->WaitForIdle(kTimeout), "assert failed: thread_.GetReactor()->WaitForIdle(kTimeout)"); @@ -248,6 +281,18 @@ protected: complete_event.t_fcs_time, complete_event.t_pm_time); } + static std::unique_ptr<LeCsProcedureEnableCompleteBuilder> GetProcedureEnableCompleteEvent( + uint16_t connection_handle, Enable enable, + CsProcedureEnableCompleteEvent complete_event) { + return LeCsProcedureEnableCompleteBuilder::Create( + complete_event.status, connection_handle, complete_event.config_id, enable, + complete_event.tone_antenna_config_selection, complete_event.selected_tx_power, + complete_event.subevent_len, complete_event.subevents_per_event, + complete_event.subevent_interval, complete_event.event_interval, + complete_event.procedure_interval, complete_event.procedure_count, + complete_event.max_procedure_len); + } + void StartMeasurement(const StartMeasurementParameters& params) { dm_manager_->StartDistanceMeasurement(params.remote_address, params.connection_handle, params.local_hci_role, params.interval, params.method); @@ -281,8 +326,48 @@ protected: test_hci_layer_->GetCommand(OpCode::LE_CS_READ_REMOTE_SUPPORTED_CAPABILITIES); CsReadCapabilitiesCompleteEvent read_cs_complete_event; + test_hci_layer_->IncomingEvent(LeCsReadRemoteSupportedCapabilitiesStatusBuilder::Create( + /*status=*/ErrorCode::SUCCESS, + /*num_hci_command_packets=*/0xFF)); test_hci_layer_->IncomingLeMetaEvent(GetRemoteSupportedCapabilitiesCompleteEvent( params.connection_handle, read_cs_complete_event)); + + test_hci_layer_->GetCommand(OpCode::LE_CS_SET_DEFAULT_SETTINGS); + test_hci_layer_->IncomingEvent(LeCsSetDefaultSettingsCompleteBuilder::Create( + /*num_hci_command_packets=*/static_cast<uint8_t>(0xEE), ErrorCode::SUCCESS, + params.connection_handle)); + } + + void StartMeasurementTillCreateConfig(const StartMeasurementParameters& params) { + StartMeasurementTillReadRemoteCaps(params); + + CsConfigCompleteEvent cs_config_complete_event; + test_hci_layer_->GetCommand(OpCode::LE_CS_CREATE_CONFIG); + test_hci_layer_->IncomingEvent(LeCsCreateConfigStatusBuilder::Create( + /*status=*/ErrorCode::SUCCESS, + /*num_hci_command_packets=*/0xFF)); + test_hci_layer_->IncomingLeMetaEvent( + GetConfigCompleteEvent(params.connection_handle, cs_config_complete_event)); + } + + void StartMeasurementTillSecurityEnable(const StartMeasurementParameters& params) { + StartMeasurementTillCreateConfig(params); + + test_hci_layer_->GetCommand(OpCode::LE_CS_SECURITY_ENABLE); + test_hci_layer_->IncomingEvent(LeCsSecurityEnableStatusBuilder::Create( + /*status=*/ErrorCode::SUCCESS, + /*num_hci_command_packets=*/0xFF)); + test_hci_layer_->IncomingLeMetaEvent(LeCsSecurityEnableCompleteBuilder::Create( + ErrorCode::SUCCESS, params.connection_handle)); + } + + void StartMeasurementTillSetProcedureParameters(const StartMeasurementParameters& params) { + StartMeasurementTillSecurityEnable(params); + + test_hci_layer_->GetCommand(OpCode::LE_CS_SET_PROCEDURE_PARAMETERS); + test_hci_layer_->IncomingEvent(LeCsSetProcedureParametersCompleteBuilder::Create( + /*num_hci_command_packets=*/static_cast<uint8_t>(0xEE), ErrorCode::SUCCESS, + params.connection_handle)); } protected: @@ -373,6 +458,7 @@ TEST_F(DistanceMeasurementManagerTest, error_read_remote_cs_caps_command) { test_hci_layer_->IncomingEvent(LeCsReadRemoteSupportedCapabilitiesStatusBuilder::Create( /*status=*/ErrorCode::COMMAND_DISALLOWED, /*num_hci_command_packets=*/0xff)); + sync_client_handler(); } TEST_F(DistanceMeasurementManagerTest, fail_read_remote_cs_caps_complete) { @@ -396,6 +482,7 @@ TEST_F(DistanceMeasurementManagerTest, fail_read_remote_cs_caps_complete) { read_cs_complete_event.error_code = ErrorCode::COMMAND_DISALLOWED; test_hci_layer_->IncomingLeMetaEvent(GetRemoteSupportedCapabilitiesCompleteEvent( params.connection_handle, read_cs_complete_event)); + sync_client_handler(); } TEST_F(DistanceMeasurementManagerTest, error_create_config_command) { @@ -418,6 +505,7 @@ TEST_F(DistanceMeasurementManagerTest, error_create_config_command) { test_hci_layer_->IncomingEvent(LeCsCreateConfigStatusBuilder::Create( /*status=*/ErrorCode::COMMAND_DISALLOWED, /*num_hci_command_packets=*/0xff)); + sync_client_handler(); } TEST_F(DistanceMeasurementManagerTest, fail_create_config_complete) { @@ -443,6 +531,97 @@ TEST_F(DistanceMeasurementManagerTest, fail_create_config_complete) { test_hci_layer_->IncomingLeMetaEvent( GetConfigCompleteEvent(params.connection_handle, cs_config_complete_event)); } + sync_client_handler(); +} + +TEST_F(DistanceMeasurementManagerTest, retry_fail_procedure_enable_command) { + auto dm_session_future = GetDmSessionFuture(); + StartMeasurementParameters params; + StartMeasurementTillSetProcedureParameters(params); + + EXPECT_CALL(mock_dm_callbacks_, + OnDistanceMeasurementStopped(params.remote_address, + DistanceMeasurementErrorCode::REASON_INTERNAL_ERROR, + DistanceMeasurementMethod::METHOD_CS)) + .WillOnce([this](const Address& /*address*/, DistanceMeasurementErrorCode /*error_code*/, + DistanceMeasurementMethod /*method*/) { + ASSERT_NE(dm_session_promise_, nullptr); + dm_session_promise_->set_value(); + dm_session_promise_.reset(); + }); + + for (int i = 0; i <= kMaxRetryCounterForCsEnable; i++) { + test_hci_layer_->GetCommand(OpCode::LE_CS_PROCEDURE_ENABLE); + test_hci_layer_->IncomingEvent(LeCsProcedureEnableStatusBuilder::Create( + /*status=*/ErrorCode::COMMAND_DISALLOWED, + /*num_hci_command_packets=*/0xff)); + auto future = fake_timer_advance(params.interval + 10); + future.wait_for(kTimeout); + sync_client_handler(); + } + fake_timerfd_reset(); + sync_client_handler(); +} + +TEST_F(DistanceMeasurementManagerTest, retry_fail_procedure_enable_complete) { + auto dm_session_future = GetDmSessionFuture(); + StartMeasurementParameters params; + StartMeasurementTillSetProcedureParameters(params); + + EXPECT_CALL(mock_dm_callbacks_, + OnDistanceMeasurementStopped(params.remote_address, + DistanceMeasurementErrorCode::REASON_INTERNAL_ERROR, + DistanceMeasurementMethod::METHOD_CS)) + .WillOnce([this](const Address& /*address*/, DistanceMeasurementErrorCode /*error_code*/, + DistanceMeasurementMethod /*method*/) { + ASSERT_NE(dm_session_promise_, nullptr); + dm_session_promise_->set_value(); + dm_session_promise_.reset(); + }); + + CsProcedureEnableCompleteEvent complete_event; + complete_event.status = ErrorCode::LINK_LAYER_COLLISION; + for (int i = 0; i <= kMaxRetryCounterForCsEnable; i++) { + test_hci_layer_->GetCommand(OpCode::LE_CS_PROCEDURE_ENABLE); + test_hci_layer_->IncomingEvent(LeCsProcedureEnableStatusBuilder::Create( + /*status=*/ErrorCode::SUCCESS, + /*num_hci_command_packets=*/0xff)); + test_hci_layer_->IncomingLeMetaEvent(GetProcedureEnableCompleteEvent( + params.connection_handle, Enable::ENABLED, complete_event)); + auto future = fake_timer_advance(params.interval + 10); + future.wait_for(kTimeout); + sync_client_handler(); + } + fake_timerfd_reset(); + sync_client_handler(); +} + +TEST_F(DistanceMeasurementManagerTest, unexpected_procedure_enable_complete_as_disable) { + auto dm_session_future = GetDmSessionFuture(); + StartMeasurementParameters params; + StartMeasurementTillSetProcedureParameters(params); + + EXPECT_CALL(mock_dm_callbacks_, + OnDistanceMeasurementStopped(params.remote_address, + DistanceMeasurementErrorCode::REASON_INTERNAL_ERROR, + DistanceMeasurementMethod::METHOD_CS)) + .WillOnce([this](const Address& /*address*/, DistanceMeasurementErrorCode /*error_code*/, + DistanceMeasurementMethod /*method*/) { + ASSERT_NE(dm_session_promise_, nullptr); + dm_session_promise_->set_value(); + dm_session_promise_.reset(); + }); + + test_hci_layer_->GetCommand(OpCode::LE_CS_PROCEDURE_ENABLE); + test_hci_layer_->IncomingEvent(LeCsProcedureEnableStatusBuilder::Create( + /*status=*/ErrorCode::SUCCESS, + /*num_hci_command_packets=*/0xff)); + CsProcedureEnableCompleteEvent complete_event; + complete_event.status = ErrorCode::LINK_LAYER_COLLISION; + test_hci_layer_->IncomingLeMetaEvent(GetProcedureEnableCompleteEvent( + params.connection_handle, Enable::DISABLED, complete_event)); + + sync_client_handler(); } } // namespace 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/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/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/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/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/include/hardware/bluetooth_headset_interface.h b/system/include/hardware/bluetooth_headset_interface.h index b2b98ffe1f..396c2b2880 100644 --- a/system/include/hardware/bluetooth_headset_interface.h +++ b/system/include/hardware/bluetooth_headset_interface.h @@ -97,9 +97,10 @@ public: /** * Start voice recognition * @param bd_addr remote device address + * @param sendResult whether a BVRA response should be sent * @return BT_STATUS_SUCCESS on success */ - virtual bt_status_t StartVoiceRecognition(RawAddress* bd_addr) = 0; + virtual bt_status_t StartVoiceRecognition(RawAddress* bd_addr, bool sendResult) = 0; /** * Stop voice recognition diff --git a/system/linux_include/log/log.h b/system/linux_include/log/log.h deleted file mode 100644 index 037649312c..0000000000 --- a/system/linux_include/log/log.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************** - * - * Copyright 2018 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 - -#ifndef __ANDROID__ - -#include <cstdint> - -#endif 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 bb8994aee0..b7b93d3690 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>(); @@ -169,11 +176,11 @@ void Stack::Stop() { log::assert_that(is_running_, "Gd stack not running"); is_running_ = false; - if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { - // Clear the handler only if the flag is not defined, otherwise it will be cleared by the - // registry - stack_handler_->Clear(); + stack_handler_->Clear(); + if(com::android::bluetooth::flags::same_handler_for_all_modules()) { + stack_handler_->WaitUntilStopped(bluetooth::kHandlerStopTimeout); } + WakelockManager::Get().Acquire(); std::promise<void> promise; @@ -195,14 +202,7 @@ void Stack::Stop() { delete management_handler_; delete management_thread_; - if (!com::android::bluetooth::flags::same_handler_for_all_modules()) { - // delete the handler only if the flag is not defined, otherwise it will be deleted by the - // registry - delete stack_handler_; - } - - // stack_handler_ is already deleted by the registry in handle_shut_down, just set it to nullptr - // to avoid any potential use-after-free + delete stack_handler_; stack_handler_ = nullptr; stack_thread_->Stop(); @@ -227,19 +227,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() { @@ -275,9 +275,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/main/test/main_shim_test.cc b/system/main/test/main_shim_test.cc index 2f4056a381..c66024cabb 100644 --- a/system/main/test/main_shim_test.cc +++ b/system/main/test/main_shim_test.cc @@ -332,8 +332,10 @@ protected: thread_ = new os::Thread("acl_thread", os::Thread::Priority::NORMAL); handler_ = new os::Handler(thread_); - /* extern */ test::mock_controller_ = new bluetooth::hci::testing::MockControllerInterface(); - /* extern */ test::mock_acl_manager_ = new bluetooth::hci::testing::MockAclManager(); + /* extern */ test::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + /* extern */ test::mock_acl_manager_ = + std::make_unique<bluetooth::hci::testing::MockAclManager>(); /* extern */ test::mock_le_scanning_manager_ = new bluetooth::hci::testing::MockLeScanningManager(); /* extern */ test::mock_le_advertising_manager_ = @@ -342,10 +344,8 @@ protected: new bluetooth::hci::testing::MockDistanceMeasurementManager(); } void TearDown() override { - delete test::mock_controller_; - test::mock_controller_ = nullptr; - delete test::mock_acl_manager_; - test::mock_acl_manager_ = nullptr; + test::mock_controller_.reset(); + test::mock_acl_manager_.release(); delete test::mock_le_advertising_manager_; test::mock_le_advertising_manager_ = nullptr; delete test::mock_le_scanning_manager_; @@ -567,7 +567,6 @@ TEST_F(MainShimTest, DISABLED_BleScannerInterfaceImpl_OnScanResult) { bluetooth::shim::get_ble_scanner_instance()); EXPECT_CALL(*hci::testing::mock_le_scanning_manager_, RegisterScanningCallback(_)).Times(1); - ; bluetooth::shim::init_scanning_manager(); TestScanningCallbacks cb; diff --git a/system/osi/BUILD.gn b/system/osi/BUILD.gn index 5bdf04c1e9..fa47062f52 100644 --- a/system/osi/BUILD.gn +++ b/system/osi/BUILD.gn @@ -43,7 +43,6 @@ static_library("osi") { include_dirs = [ "//bt/system/", - "//bt/system/linux_include", "//bt/system/osi/include_internal", "//bt/system/stack/include", ] diff --git a/system/stack/Android.bp b/system/stack/Android.bp index ebab1ff5f4..78af467a76 100644 --- a/system/stack/Android.bp +++ b/system/stack/Android.bp @@ -48,7 +48,6 @@ cc_library_static { "hid", "include", "l2cap", - "metrics", "pan", "sdp", "smp", @@ -183,7 +182,6 @@ cc_library_static { "l2cap/l2c_main.cc", "l2cap/l2c_utils.cc", "l2cap/l2cap_api.cc", - "metrics/stack_metrics_logging.cc", "pan/pan_api.cc", "pan/pan_main.cc", "pan/pan_utils.cc", @@ -374,9 +372,9 @@ cc_fuzz { ":TestFakeOsi", ":TestMockBtif", ":TestMockDevice", + ":TestMockMainShim", ":TestMockStackBtm", ":TestMockStackL2cap", - ":TestMockStackMetrics", "fuzzers/sdp_fuzzer.cc", ], static_libs: [ @@ -414,6 +412,7 @@ cc_fuzz { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockSrvcDis", ":TestMockStackAcl", @@ -421,7 +420,6 @@ cc_fuzz { ":TestMockStackBtm", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSdp", "fuzzers/rfcomm_fuzzer.cc", "rfcomm/*.cc", @@ -465,6 +463,7 @@ cc_fuzz { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockSrvcDis", ":TestMockStackAcl", @@ -473,7 +472,6 @@ cc_fuzz { ":TestMockStackConnMgr", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSdp", "ais/*.cc", "eatt/*.cc", @@ -517,12 +515,12 @@ cc_fuzz { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", "fuzzers/smp_fuzzer.cc", "smp/*.cc", // add other sources files (p256 related) under smp into this test ], @@ -563,12 +561,12 @@ cc_fuzz { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", "bnep/*.cc", "fuzzers/bnep_fuzzer.cc", ], @@ -601,12 +599,12 @@ cc_fuzz { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSdp", "avct/*.cc", "avrc/*.cc", @@ -648,11 +646,11 @@ cc_fuzz { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackHcic", - ":TestMockStackMetrics", "fuzzers/l2cap_fuzzer.cc", "l2cap/*.cc", ], @@ -809,10 +807,10 @@ cc_test { ":TestCommonMockFunctions", ":TestMockHci", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackBtm", ":TestMockStackL2capInterface", - ":TestMockStackMetrics", ":TestMockStackRfcommMetrics", "rfcomm/port_api.cc", "rfcomm/port_rfc.cc", @@ -903,12 +901,12 @@ cc_test { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", "smp/p_256_curvepara.cc", "smp/p_256_ecc_pp.cc", "smp/p_256_multprecision.cc", @@ -1006,9 +1004,9 @@ cc_test { ":TestCommonMainHandler", ":TestCommonMockFunctions", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackBtm", - ":TestMockStackMetrics", "connection_manager/connection_manager.cc", "test/connection_manager_test.cc", ], @@ -1419,10 +1417,10 @@ cc_test { ":TestMockBtif", ":TestMockDevice", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockStackBtm", ":TestMockStackConnMgr", ":TestMockStackL2cap", - ":TestMockStackMetrics", "gatt/gatt_db.cc", "gatt/gatt_sr_hash.cc", "gatt/gatt_utils.cc", @@ -1675,7 +1673,6 @@ cc_test { "btm/hfp_msbc_decoder.cc", "btm/hfp_msbc_encoder.cc", "btm/security_event_parser.cc", - "metrics/stack_metrics_logging.cc", "test/btm/peer_packet_types_test.cc", "test/btm/sco_hci_test.cc", "test/btm/sco_pkt_status_test.cc", @@ -1813,9 +1810,9 @@ cc_test { ], srcs: [ ":TestCommonMockFunctions", + ":TestMockMainShim", ":TestMockStackBtm", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSdp", "hid/hidd_api.cc", "hid/hidd_conn.cc", @@ -1887,12 +1884,12 @@ cc_test { ":TestMockBtif", ":TestMockHci", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSmp", "btu/btu_hcif.cc", "btu/main_thread.cc", @@ -1964,12 +1961,12 @@ cc_test { ":TestMockBtif", ":TestMockHci", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockSrvcDis", ":TestMockStackAcl", ":TestMockStackBtm", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSdp", ":TestMockStackSmp", "ais/ais_ble.cc", @@ -2165,6 +2162,7 @@ cc_test { ":TestMockHci", ":TestMockLegacyHciInterface", ":TestMockMainShim", + ":TestMockMainShim", ":TestMockMainShimEntry", ":TestMockStackBtm", ":TestMockStackBtu", @@ -2172,7 +2170,6 @@ cc_test { ":TestMockStackGatt", ":TestMockStackHcic", ":TestMockStackL2cap", - ":TestMockStackMetrics", ":TestMockStackSdp", ":TestMockStackSmp", "acl/*.cc", @@ -2349,9 +2346,9 @@ cc_test { ":TestFakeOsi", ":TestMockBta", ":TestMockBtif", + ":TestMockMainShim", ":TestMockStackBtm", ":TestMockStackL2cap", - ":TestMockStackMetrics", "test/sdp/stack_sdp_api_test.cc", "test/sdp/stack_sdp_db_test.cc", "test/sdp/stack_sdp_parse_test.cc", diff --git a/system/stack/BUILD.gn b/system/stack/BUILD.gn index d078d1bc14..f06a098bd5 100644 --- a/system/stack/BUILD.gn +++ b/system/stack/BUILD.gn @@ -149,7 +149,6 @@ source_set("stack") { "l2cap/l2c_main.cc", "l2cap/l2c_utils.cc", "l2cap/l2cap_api.cc", - "metrics/stack_metrics_logging.cc", "pan/pan_api.cc", "pan/pan_main.cc", "pan/pan_utils.cc", @@ -210,7 +209,6 @@ source_set("stack") { "//bt/system/ctrlr/include", "//bt/system/gd", "//bt/system/hcis", - "//bt/system/linux_include", "//bt/system/rpc/include", "//bt/system/udrv/include", "//bt/system/vnd/ble", @@ -367,7 +365,6 @@ if (defined(use.android) && use.android) { include_dirs = [ "//bt/system/", - "//bt/system/linux_include", "//bt/system/bta/include", "//bt/system/bta/sys", "//bt/system/embdrv/sbc/encoder/include", diff --git a/system/stack/btm/btm_ble_sec.cc b/system/stack/btm/btm_ble_sec.cc index cef553c79e..b8e2002c60 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; } } diff --git a/system/stack/btm/btm_sco.cc b/system/stack/btm/btm_sco.cc index c48b321448..82182135eb 100644 --- a/system/stack/btm/btm_sco.cc +++ b/system/stack/btm/btm_sco.cc @@ -44,6 +44,7 @@ #include "internal_include/bt_target.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" +#include "main/shim/metrics_api.h" #include "osi/include/properties.h" #include "osi/include/stack_power_telemetry.h" #include "stack/btm/btm_int_types.h" @@ -60,7 +61,6 @@ #include "stack/include/main_thread.h" #include "stack/include/sco_hci_link_interface.h" #include "stack/include/sdpdefs.h" -#include "stack/include/stack_metrics_logging.h" #include "types/raw_address.h" extern tBTM_CB btm_cb; @@ -1298,7 +1298,8 @@ static void btm_sco_on_disconnected(uint16_t hci_handle, tHCI_REASON reason) { if (fill_plc_stats(&num_decoded_frames, &packet_loss_ratio)) { const int16_t codec_id = sco_codec_type_to_id(codec_type); const std::string codec = sco_codec_type_text(codec_type); - log_hfp_audio_packet_loss_stats(bd_addr, num_decoded_frames, packet_loss_ratio, codec_id); + bluetooth::shim::LogMetricHfpPacketLossStats(bd_addr, num_decoded_frames, packet_loss_ratio, + codec_id); log::debug( "Stopped SCO codec:{}, num_decoded_frames:{}, " "packet_loss_ratio:{:f}", diff --git a/system/stack/btm/btm_sec.cc b/system/stack/btm/btm_sec.cc index b00b779eee..6244b951c6 100644 --- a/system/stack/btm/btm_sec.cc +++ b/system/stack/btm/btm_sec.cc @@ -47,6 +47,7 @@ #include "main/shim/acl_api.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" +#include "main/shim/metrics_api.h" #include "metrics/bluetooth_event.h" #include "osi/include/allocator.h" #include "osi/include/properties.h" @@ -75,7 +76,6 @@ #include "stack/include/main_thread.h" #include "stack/include/rnr_interface.h" #include "stack/include/smp_api.h" -#include "stack/include/stack_metrics_logging.h" #include "types/bt_transport.h" #include "types/raw_address.h" @@ -1923,7 +1923,7 @@ static void btm_sec_bond_cancel_complete(void) { 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); - log_link_layer_connection_event( + bluetooth::shim::LogMetricLinkLayerConnectionEvent( &bd_addr, bluetooth::common::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, android::bluetooth::hci::CMD_CREATE_CONNECTION_CANCEL, @@ -2440,8 +2440,7 @@ void btm_io_capabilities_req(RawAddress p) { /* If device is bonded, and encrypted it's upgrading security and it's ok. * If it's bonded and not encrypted, it's remote missing keys scenario */ - if (!p_dev_rec->sec_rec.is_device_encrypted() && - com::android::bluetooth::flags::key_missing_classic_device()) { + if (!p_dev_rec->sec_rec.is_device_encrypted()) { log::warn("Incoming bond request, but {} is already bonded (notifying user)", p); bta_dm_remote_key_missing(p); btm_sec_disconnect(p_dev_rec->hci_handle, HCI_ERR_AUTH_FAILURE, @@ -2632,8 +2631,7 @@ void btm_io_capabilities_rsp(const tBTM_SP_IO_RSP evt_data) { /* If device is bonded, and encrypted it's upgrading security and it's ok. * If it's bonded and not encrypted, it's remote missing keys scenario */ - if (btm_sec_is_a_bonded_dev(evt_data.bd_addr) && !p_dev_rec->sec_rec.is_device_encrypted() && - com::android::bluetooth::flags::key_missing_classic_device()) { + if (btm_sec_is_a_bonded_dev(evt_data.bd_addr) && !p_dev_rec->sec_rec.is_device_encrypted()) { log::warn("Incoming bond request, but {} is already bonded (notifying user)", evt_data.bd_addr); bta_dm_remote_key_missing(evt_data.bd_addr); btm_sec_disconnect(p_dev_rec->hci_handle, HCI_ERR_AUTH_FAILURE, @@ -3031,8 +3029,7 @@ void btm_sec_auth_complete(uint16_t handle, tHCI_STATUS status) { p_dev_rec->sec_rec.classic_link, p_dev_rec->bd_addr, reinterpret_cast<char const*>(p_dev_rec->sec_bd_name)); - if (status == HCI_ERR_KEY_MISSING && - com::android::bluetooth::flags::key_missing_classic_device()) { + if (status == HCI_ERR_KEY_MISSING) { log::warn("auth_complete KEY_MISSING {} is already bonded (notifying user)", p_dev_rec->bd_addr); bta_dm_remote_key_missing(p_dev_rec->bd_addr); @@ -3133,12 +3130,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)); @@ -3163,6 +3160,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; @@ -3321,10 +3323,8 @@ void btm_sec_encrypt_change(uint16_t handle, tHCI_STATUS status, uint8_t encr_en if (status == HCI_ERR_KEY_MISSING) { log::info("Remote key missing - will report"); bta_dm_remote_key_missing(p_dev_rec->ble.pseudo_addr); - if (com::android::bluetooth::flags::sec_disconnect_on_le_key_missing()) { - btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_HOST_REJECT_SECURITY, - p_dev_rec->ble_hci_handle, "encryption_change:key_missing"); - } + btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_HOST_REJECT_SECURITY, + p_dev_rec->ble_hci_handle, "encryption_change:key_missing"); return; } diff --git a/system/stack/btm/security_event_parser.cc b/system/stack/btm/security_event_parser.cc index 84d7005d16..deba84b67f 100644 --- a/system/stack/btm/security_event_parser.cc +++ b/system/stack/btm/security_event_parser.cc @@ -25,10 +25,10 @@ #include "common/metrics.h" #include "hci/hci_packets.h" #include "main/shim/helpers.h" +#include "main/shim/metrics_api.h" #include "stack/include/btm_sec_api_types.h" #include "stack/include/hci_error_code.h" #include "stack/include/sec_hci_link_interface.h" -#include "stack/include/stack_metrics_logging.h" #include "types/raw_address.h" using namespace bluetooth; @@ -46,8 +46,8 @@ static void log_address_and_status(const Address& bda, EventCode event_code, uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN; uint16_t handle = bluetooth::common::kUnknownConnectionHandle; int64_t value = 0; - log_classic_pairing_event(ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code), - status, reason, value); + bluetooth::shim::LogMetricClassicPairingEvent( + ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code), status, reason, value); } static void log_address(const Address& bda, EventCode event_code) { uint32_t cmd = android::bluetooth::hci::CMD_UNKNOWN; @@ -55,8 +55,8 @@ static void log_address(const Address& bda, EventCode event_code) { uint16_t reason = android::bluetooth::hci::STATUS_UNKNOWN; uint16_t handle = bluetooth::common::kUnknownConnectionHandle; int64_t value = 0; - log_classic_pairing_event(ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code), - status, reason, value); + bluetooth::shim::LogMetricClassicPairingEvent( + ToRawAddress(bda), handle, cmd, static_cast<uint16_t>(event_code), status, reason, value); } static void parse_encryption_change(const EventView event) { auto change_opt = EncryptionChangeView::CreateOptional(event); @@ -69,7 +69,7 @@ static void parse_encryption_change(const EventView event) { btm_sec_encryption_change_evt(handle, static_cast<tHCI_STATUS>(status), static_cast<uint8_t>(encr_enable), 0); - log_classic_pairing_event( + bluetooth::shim::LogMetricClassicPairingEvent( ToRawAddress(Address::kEmpty), handle, android::bluetooth::hci::CMD_UNKNOWN, static_cast<uint32_t>(change.GetEventCode()), static_cast<uint16_t>(status), android::bluetooth::hci::STATUS_UNKNOWN, 0); @@ -86,7 +86,7 @@ static void parse_encryption_change_v2(const EventView event) { btm_sec_encryption_change_evt(handle, static_cast<tHCI_STATUS>(status), static_cast<uint8_t>(encr_enable), key_size); - log_classic_pairing_event( + bluetooth::shim::LogMetricClassicPairingEvent( ToRawAddress(Address::kEmpty), handle, android::bluetooth::hci::CMD_UNKNOWN, static_cast<uint32_t>(change.GetEventCode()), static_cast<uint16_t>(status), android::bluetooth::hci::STATUS_UNKNOWN, 0); @@ -96,7 +96,7 @@ static void parse_change_connection_link_key_complete(const EventView event) { log::assert_that(complete_opt.has_value(), "assert failed: complete_opt.has_value()"); auto complete = complete_opt.value(); - log_classic_pairing_event( + bluetooth::shim::LogMetricClassicPairingEvent( ToRawAddress(Address::kEmpty), complete.GetConnectionHandle(), android::bluetooth::hci::CMD_UNKNOWN, static_cast<uint32_t>(complete.GetEventCode()), static_cast<uint16_t>(complete.GetStatus()), android::bluetooth::hci::STATUS_UNKNOWN, 0); diff --git a/system/stack/btu/btu_hcif.cc b/system/stack/btu/btu_hcif.cc index 7a4b6d1256..ee3c9757a5 100644 --- a/system/stack/btu/btu_hcif.cc +++ b/system/stack/btu/btu_hcif.cc @@ -40,6 +40,7 @@ #include "common/metrics.h" #include "internal_include/bt_target.h" #include "main/shim/hci_layer.h" +#include "main/shim/metrics_api.h" #include "osi/include/allocator.h" #include "stack/include/acl_api.h" #include "stack/include/acl_hci_link_interface.h" @@ -59,7 +60,6 @@ #include "stack/include/sco_hci_link_interface.h" #include "stack/include/sec_hci_link_interface.h" #include "stack/include/smp_api.h" -#include "stack/include/stack_metrics_logging.h" #include "types/hci_role.h" #include "types/raw_address.h" @@ -125,25 +125,29 @@ static void btu_hcif_log_event_metrics(uint8_t evt_code, const uint8_t* p_event) case HCI_KEYPRESS_NOTIFY_EVT: case HCI_REMOTE_OOB_DATA_REQUEST_EVT: STREAM_TO_BDADDR(bda, p_event); - log_classic_pairing_event(bda, handle, cmd, evt_code, status, reason, value); + bluetooth::shim::LogMetricClassicPairingEvent(bda, handle, cmd, evt_code, status, reason, + value); break; case HCI_SIMPLE_PAIRING_COMPLETE_EVT: STREAM_TO_UINT8(status, p_event); STREAM_TO_BDADDR(bda, p_event); - log_classic_pairing_event(bda, handle, cmd, evt_code, status, reason, value); + bluetooth::shim::LogMetricClassicPairingEvent(bda, handle, cmd, evt_code, status, reason, + value); break; case HCI_AUTHENTICATION_COMP_EVT: STREAM_TO_UINT8(status, p_event); STREAM_TO_UINT16(handle, p_event); handle = HCID_GET_HANDLE(handle); - log_classic_pairing_event(bda, handle, cmd, evt_code, status, reason, value); + bluetooth::shim::LogMetricClassicPairingEvent(bda, handle, cmd, evt_code, status, reason, + value); break; case HCI_ENCRYPTION_CHANGE_EVT: { uint8_t encryption_enabled; STREAM_TO_UINT8(status, p_event); STREAM_TO_UINT16(handle, p_event); STREAM_TO_UINT8(encryption_enabled, p_event); - log_classic_pairing_event(bda, handle, cmd, evt_code, status, reason, encryption_enabled); + bluetooth::shim::LogMetricClassicPairingEvent(bda, handle, cmd, evt_code, status, reason, + encryption_enabled); break; } case HCI_ENCRYPTION_CHANGE_EVT_V2: { @@ -153,7 +157,8 @@ static void btu_hcif_log_event_metrics(uint8_t evt_code, const uint8_t* p_event) STREAM_TO_UINT16(handle, p_event); STREAM_TO_UINT8(encryption_enabled, p_event); STREAM_TO_UINT8(key_size, p_event); - log_classic_pairing_event(bda, handle, cmd, evt_code, status, reason, encryption_enabled); + bluetooth::shim::LogMetricClassicPairingEvent(bda, handle, cmd, evt_code, status, reason, + encryption_enabled); break; } case HCI_ESCO_CONNECTION_COMP_EVT: { @@ -163,18 +168,19 @@ static void btu_hcif_log_event_metrics(uint8_t evt_code, const uint8_t* p_event) STREAM_TO_BDADDR(bda, p_event); STREAM_TO_UINT8(link_type, p_event); handle = HCID_GET_HANDLE(handle); - log_link_layer_connection_event(&bda, handle, android::bluetooth::DIRECTION_UNKNOWN, - link_type, cmd, evt_code, - android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason); + bluetooth::shim::LogMetricLinkLayerConnectionEvent( + &bda, handle, android::bluetooth::DIRECTION_UNKNOWN, link_type, cmd, evt_code, + android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason); break; } case HCI_ESCO_CONNECTION_CHANGED_EVT: { STREAM_TO_UINT8(status, p_event); STREAM_TO_UINT16(handle, p_event); handle = HCID_GET_HANDLE(handle); - log_link_layer_connection_event(nullptr, handle, android::bluetooth::DIRECTION_UNKNOWN, - android::bluetooth::LINK_TYPE_UNKNOWN, cmd, evt_code, - android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason); + bluetooth::shim::LogMetricLinkLayerConnectionEvent( + nullptr, handle, android::bluetooth::DIRECTION_UNKNOWN, + android::bluetooth::LINK_TYPE_UNKNOWN, cmd, evt_code, + android::bluetooth::hci::BLE_EVT_UNKNOWN, status, reason); break; } // Ignore these events @@ -368,7 +374,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_CREATE_CONNECTION: case HCI_CREATE_CONNECTION_CANCEL: STREAM_TO_BDADDR(bd_addr, p_cmd); - log_link_layer_connection_event( + bluetooth::shim::LogMetricLinkLayerConnectionEvent( &bd_addr, bluetooth::common::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); @@ -376,23 +382,24 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_DISCONNECT: STREAM_TO_UINT16(handle, p_cmd); STREAM_TO_UINT8(reason, p_cmd); - log_link_layer_connection_event(nullptr, handle, android::bluetooth::DIRECTION_UNKNOWN, - android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, - kUnknownBleEvt, cmd_status, reason); + bluetooth::shim::LogMetricLinkLayerConnectionEvent( + nullptr, handle, android::bluetooth::DIRECTION_UNKNOWN, + android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, kUnknownBleEvt, cmd_status, + reason); break; case HCI_SETUP_ESCO_CONNECTION: case HCI_ENH_SETUP_ESCO_CONNECTION: STREAM_TO_UINT16(handle, p_cmd); - log_link_layer_connection_event(nullptr, handle, android::bluetooth::DIRECTION_OUTGOING, - android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, - kUnknownBleEvt, cmd_status, - android::bluetooth::hci::STATUS_UNKNOWN); + bluetooth::shim::LogMetricLinkLayerConnectionEvent( + nullptr, handle, android::bluetooth::DIRECTION_OUTGOING, + android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, kUnknownBleEvt, cmd_status, + android::bluetooth::hci::STATUS_UNKNOWN); break; case HCI_ACCEPT_CONNECTION_REQUEST: case HCI_ACCEPT_ESCO_CONNECTION: case HCI_ENH_ACCEPT_ESCO_CONNECTION: STREAM_TO_BDADDR(bd_addr, p_cmd); - log_link_layer_connection_event( + bluetooth::shim::LogMetricLinkLayerConnectionEvent( &bd_addr, bluetooth::common::kUnknownConnectionHandle, android::bluetooth::DIRECTION_INCOMING, android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); @@ -401,10 +408,10 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_REJECT_ESCO_CONNECTION: STREAM_TO_BDADDR(bd_addr, p_cmd); STREAM_TO_UINT8(reason, p_cmd); - log_link_layer_connection_event(&bd_addr, bluetooth::common::kUnknownConnectionHandle, - android::bluetooth::DIRECTION_INCOMING, - android::bluetooth::LINK_TYPE_UNKNOWN, opcode, hci_event, - kUnknownBleEvt, cmd_status, reason); + bluetooth::shim::LogMetricLinkLayerConnectionEvent( + &bd_addr, bluetooth::common::kUnknownConnectionHandle, + android::bluetooth::DIRECTION_INCOMING, android::bluetooth::LINK_TYPE_UNKNOWN, opcode, + hci_event, kUnknownBleEvt, cmd_status, reason); break; // BLE Commands @@ -431,7 +438,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, // Selectively log to avoid log spam due to acceptlist connections: // - When doing non-acceptlist connection // - When there is an error in command status - log_link_layer_connection_event( + bluetooth::shim::LogMetricLinkLayerConnectionEvent( bd_addr_p, bluetooth::common::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); @@ -456,7 +463,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, // Selectively log to avoid log spam due to acceptlist connections: // - When doing non-acceptlist connection // - When there is an error in command status - log_link_layer_connection_event( + bluetooth::shim::LogMetricLinkLayerConnectionEvent( bd_addr_p, bluetooth::common::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); @@ -466,7 +473,7 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_BLE_CREATE_CONN_CANCEL: if (cmd_status != HCI_SUCCESS && !is_cmd_status) { // Only log errors to prevent log spam due to acceptlist connections - log_link_layer_connection_event( + bluetooth::shim::LogMetricLinkLayerConnectionEvent( nullptr, bluetooth::common::kUnknownConnectionHandle, android::bluetooth::DIRECTION_OUTGOING, android::bluetooth::LINK_TYPE_ACL, opcode, hci_event, kUnknownBleEvt, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN); @@ -474,46 +481,48 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, break; case HCI_READ_LOCAL_OOB_DATA: case HCI_READ_LOCAL_OOB_EXTENDED_DATA: - log_classic_pairing_event(RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, cmd_status, - android::bluetooth::hci::STATUS_UNKNOWN, 0); + bluetooth::shim::LogMetricClassicPairingEvent( + RawAddress::kEmpty, bluetooth::common::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); - log_classic_pairing_event(RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, cmd_status, - android::bluetooth::hci::STATUS_UNKNOWN, simple_pairing_mode); + bluetooth::shim::LogMetricClassicPairingEvent( + RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, + cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, simple_pairing_mode); break; } case HCI_WRITE_SECURE_CONNS_SUPPORT: { uint8_t secure_conn_host_support; STREAM_TO_UINT8(secure_conn_host_support, p_cmd); - log_classic_pairing_event(RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, cmd_status, - android::bluetooth::hci::STATUS_UNKNOWN, secure_conn_host_support); + bluetooth::shim::LogMetricClassicPairingEvent( + RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, + cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, secure_conn_host_support); break; } case HCI_AUTHENTICATION_REQUESTED: STREAM_TO_UINT16(handle, p_cmd); - log_classic_pairing_event(RawAddress::kEmpty, handle, opcode, hci_event, cmd_status, - android::bluetooth::hci::STATUS_UNKNOWN, 0); + bluetooth::shim::LogMetricClassicPairingEvent(RawAddress::kEmpty, handle, opcode, hci_event, + cmd_status, + android::bluetooth::hci::STATUS_UNKNOWN, 0); break; case HCI_SET_CONN_ENCRYPTION: { STREAM_TO_UINT16(handle, p_cmd); uint8_t encryption_enable; STREAM_TO_UINT8(encryption_enable, p_cmd); - log_classic_pairing_event(RawAddress::kEmpty, handle, opcode, hci_event, cmd_status, - android::bluetooth::hci::STATUS_UNKNOWN, encryption_enable); + bluetooth::shim::LogMetricClassicPairingEvent( + RawAddress::kEmpty, handle, opcode, hci_event, cmd_status, + android::bluetooth::hci::STATUS_UNKNOWN, encryption_enable); break; } case HCI_DELETE_STORED_LINK_KEY: { uint8_t delete_all_flag; STREAM_TO_BDADDR(bd_addr, p_cmd); STREAM_TO_UINT8(delete_all_flag, p_cmd); - log_classic_pairing_event(bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, - hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, - delete_all_flag); + bluetooth::shim::LogMetricClassicPairingEvent( + bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, hci_event, cmd_status, + android::bluetooth::hci::STATUS_UNKNOWN, delete_all_flag); break; } case HCI_RMT_NAME_REQUEST: @@ -528,14 +537,16 @@ static void btu_hcif_log_command_metrics(uint16_t opcode, const uint8_t* p_cmd, case HCI_REM_OOB_DATA_REQ_REPLY: case HCI_REM_OOB_DATA_REQ_NEG_REPLY: STREAM_TO_BDADDR(bd_addr, p_cmd); - log_classic_pairing_event(bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, - hci_event, cmd_status, android::bluetooth::hci::STATUS_UNKNOWN, 0); + bluetooth::shim::LogMetricClassicPairingEvent( + bd_addr, bluetooth::common::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); - log_classic_pairing_event(bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, - hci_event, cmd_status, reason, 0); + bluetooth::shim::LogMetricClassicPairingEvent(bd_addr, + bluetooth::common::kUnknownConnectionHandle, + opcode, hci_event, cmd_status, reason, 0); break; } } @@ -599,8 +610,9 @@ static void btu_hcif_log_command_complete_metrics(uint16_t opcode, const uint8_t case HCI_WRITE_SIMPLE_PAIRING_MODE: case HCI_WRITE_SECURE_CONNS_SUPPORT: STREAM_TO_UINT8(status, p_return_params); - log_classic_pairing_event(RawAddress::kEmpty, bluetooth::common::kUnknownConnectionHandle, - opcode, hci_event, status, reason, 0); + bluetooth::shim::LogMetricClassicPairingEvent(RawAddress::kEmpty, + bluetooth::common::kUnknownConnectionHandle, + opcode, hci_event, status, reason, 0); break; case HCI_READ_ENCR_KEY_SIZE: { uint16_t handle; @@ -608,8 +620,8 @@ static void btu_hcif_log_command_complete_metrics(uint16_t opcode, const uint8_t STREAM_TO_UINT8(status, p_return_params); STREAM_TO_UINT16(handle, p_return_params); STREAM_TO_UINT8(key_size, p_return_params); - log_classic_pairing_event(RawAddress::kEmpty, handle, opcode, hci_event, status, reason, - key_size); + bluetooth::shim::LogMetricClassicPairingEvent(RawAddress::kEmpty, handle, opcode, hci_event, + status, reason, key_size); break; } case HCI_LINK_KEY_REQUEST_REPLY: @@ -624,8 +636,9 @@ static void btu_hcif_log_command_complete_metrics(uint16_t opcode, const uint8_t case HCI_REM_OOB_DATA_REQ_NEG_REPLY: STREAM_TO_UINT8(status, p_return_params); STREAM_TO_BDADDR(bd_addr, p_return_params); - log_classic_pairing_event(bd_addr, bluetooth::common::kUnknownConnectionHandle, opcode, - hci_event, status, reason, 0); + bluetooth::shim::LogMetricClassicPairingEvent(bd_addr, + bluetooth::common::kUnknownConnectionHandle, + opcode, hci_event, status, reason, 0); break; } } diff --git a/system/stack/fuzzers/l2cap_fuzzer.cc b/system/stack/fuzzers/l2cap_fuzzer.cc index 6cb4d5170f..69243a67c4 100644 --- a/system/stack/fuzzers/l2cap_fuzzer.cc +++ b/system/stack/fuzzers/l2cap_fuzzer.cc @@ -121,26 +121,30 @@ public: GetInterfaceToProfiles()->profileSpecific_HACK->GetHearingAidDeviceCount = []() { return 1; }; - ON_CALL(controller_, GetLeSuggestedDefaultDataLength).WillByDefault(Return(512)); + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetLeSuggestedDefaultDataLength) + .WillByDefault(Return(512)); bluetooth::hci::LeBufferSize iso_size; iso_size.le_data_packet_length_ = 512; iso_size.total_num_le_packets_ = 6; - ON_CALL(controller_, GetControllerIsoBufferSize).WillByDefault(Return(iso_size)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetControllerIsoBufferSize) + .WillByDefault(Return(iso_size)); bluetooth::hci::LeBufferSize le_size; le_size.le_data_packet_length_ = 512; le_size.total_num_le_packets_ = 6; - ON_CALL(controller_, GetLeBufferSize).WillByDefault(Return(le_size)); - ON_CALL(controller_, SupportsBle).WillByDefault(Return(true)); - ON_CALL(controller_, GetAclPacketLength).WillByDefault(Return(512)); - bluetooth::hci::testing::mock_controller_ = &controller_; + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetLeBufferSize) + .WillByDefault(Return(le_size)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, SupportsBle).WillByDefault(Return(true)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetAclPacketLength) + .WillByDefault(Return(512)); } ~FakeBtStack() { test::mock::stack_acl::acl_send_data_packet_br_edr = {}; test::mock::stack_acl::acl_send_data_packet_ble = {}; - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } - bluetooth::hci::testing::MockControllerInterface controller_; }; class Fakes { diff --git a/system/stack/gatt/gatt_api.cc b/system/stack/gatt/gatt_api.cc index 1b60132b00..7b1a425f5e 100644 --- a/system/stack/gatt/gatt_api.cc +++ b/system/stack/gatt/gatt_api.cc @@ -34,6 +34,7 @@ #include "internal_include/bt_target.h" #include "internal_include/stack_config.h" #include "main/shim/helpers.h" +#include "main/shim/metrics_api.h" #include "os/system_properties.h" #include "osi/include/allocator.h" #include "stack/arbiter/acl_arbiter.h" @@ -48,7 +49,6 @@ #include "stack/include/l2cap_interface.h" #include "stack/include/l2cdefs.h" #include "stack/include/sdp_api.h" -#include "stack/include/stack_metrics_logging.h" #include "types/bluetooth/uuid.h" #include "types/bt_transport.h" #include "types/raw_address.h" @@ -1476,7 +1476,8 @@ bool GATT_Connect(tGATT_IF gatt_if, const RawAddress& bd_addr, tBLE_ADDR_TYPE ad return true; } - log_le_connection_lifecycle(ToGdAddress(bd_addr), true /* is_connect */, is_direct); + bluetooth::shim::LogMetricLeConnectionLifecycle(ToGdAddress(bd_addr), true /* is_connect */, + is_direct); bool ret = false; if (is_direct) { @@ -1618,8 +1619,8 @@ tGATT_STATUS GATT_Disconnect(tCONN_ID conn_id) { return GATT_ILLEGAL_PARAMETER; } - log_le_connection_lifecycle(ToGdAddress(p_tcb->peer_bda), true /* is_connect */, - false /* is_direct */); + bluetooth::shim::LogMetricLeConnectionLifecycle(ToGdAddress(p_tcb->peer_bda), + true /* is_connect */, false /* is_direct */); tGATT_IF gatt_if = gatt_get_gatt_if(conn_id); gatt_update_app_use_link_flag(gatt_if, p_tcb, false, true); diff --git a/system/stack/hid/hidd_api.cc b/system/stack/hid/hidd_api.cc index 169868bbfc..58d9bdc07a 100644 --- a/system/stack/hid/hidd_api.cc +++ b/system/stack/hid/hidd_api.cc @@ -38,13 +38,13 @@ #include "hidd_int.h" #include "hiddefs.h" #include "internal_include/bt_target.h" +#include "main/shim/metrics_api.h" #include "osi/include/allocator.h" #include "stack/include/bt_psm_types.h" #include "stack/include/bt_types.h" #include "stack/include/bt_uuid16.h" #include "stack/include/sdp_api.h" #include "stack/include/sdpdefs.h" -#include "stack/include/stack_metrics_logging.h" #include "stack/sdp/sdp_discovery_db.h" #include "types/raw_address.h" @@ -83,12 +83,14 @@ tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback) { log::verbose(""); if (hd_cb.reg_flag) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_REGISTERED, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_REGISTERED, 1); return HID_ERR_ALREADY_REGISTERED; } if (host_cback == NULL) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_HOST_CALLBACK_NULL, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_HOST_CALLBACK_NULL, 1); return HID_ERR_INVALID_PARAM; } @@ -122,7 +124,7 @@ tHID_STATUS HID_DevDeregister(void) { log::verbose(""); if (!hd_cb.reg_flag) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_DEREGISTER, 1); return HID_ERR_NOT_REGISTERED; } @@ -263,9 +265,10 @@ tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, char* p_description, if (desc_len > HIDD_APP_DESCRIPTOR_LEN) { log::error("descriptor length = {}, larger than max {}", desc_len, HIDD_APP_DESCRIPTOR_LEN); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum:: - HIDD_ERR_NOT_REGISTERED_DUE_TO_DESCRIPTOR_LENGTH, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum:: + HIDD_ERR_NOT_REGISTERED_DUE_TO_DESCRIPTOR_LENGTH, + 1); return HID_ERR_NOT_REGISTERED; }; @@ -273,9 +276,10 @@ tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, char* p_description, if (p_buf == NULL) { log::error("Buffer allocation failure for size = {}", buf_len); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum:: - HIDD_ERR_NOT_REGISTERED_DUE_TO_BUFFER_ALLOCATION, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum:: + HIDD_ERR_NOT_REGISTERED_DUE_TO_BUFFER_ALLOCATION, + 1); return HID_ERR_NOT_REGISTERED; } @@ -346,8 +350,8 @@ tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, char* p_description, if (!result) { log::error("failed to complete SDP record"); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_SDP, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_SDP, 1); return HID_ERR_NOT_REGISTERED; } @@ -376,7 +380,7 @@ tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, uint16_ return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA, HID_PAR_REP_TYPE_INPUT, id, len, p_data); } - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_INVALID_PARAM_SEND_REPORT, 1); return HID_ERR_INVALID_PARAM; } @@ -444,19 +448,20 @@ tHID_STATUS HID_DevUnplugDevice(const RawAddress& addr) { ******************************************************************************/ tHID_STATUS HID_DevConnect(void) { if (!hd_cb.reg_flag) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_CONNECT, 1); return HID_ERR_NOT_REGISTERED; } if (!hd_cb.device.in_use) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DEVICE_NOT_IN_USE_AT_CONNECT, 1); return HID_ERR_INVALID_PARAM; } if (hd_cb.device.state != HIDD_DEV_NO_CONN) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_CONN, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_ALREADY_CONN, 1); return HID_ERR_ALREADY_CONN; } @@ -474,13 +479,13 @@ tHID_STATUS HID_DevConnect(void) { ******************************************************************************/ tHID_STATUS HID_DevDisconnect(void) { if (!hd_cb.reg_flag) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_DISCONNECT, 1); return HID_ERR_NOT_REGISTERED; } if (!hd_cb.device.in_use) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DEVICE_NOT_IN_USE_AT_DISCONNECT, 1); return HID_ERR_INVALID_PARAM; @@ -492,10 +497,11 @@ tHID_STATUS HID_DevDisconnect(void) { tHID_STATUS ret = hidd_conn_disconnect(); hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED; hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_DISCONNECTING, NULL); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DISCONNECTING, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_DISCONNECTING, 1); return ret; } - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_CONNECTION_AT_DISCONNECT, 1); return HID_ERR_NO_CONNECTION; } @@ -565,7 +571,7 @@ tHID_STATUS HID_DevGetDevice(RawAddress* addr) { if (hd_cb.device.in_use) { *addr = hd_cb.device.addr; } else { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_GET_DEVICE, 1); return HID_ERR_NOT_REGISTERED; } diff --git a/system/stack/hid/hidd_conn.cc b/system/stack/hid/hidd_conn.cc index 7276af8acd..5f94c1c870 100644 --- a/system/stack/hid/hidd_conn.cc +++ b/system/stack/hid/hidd_conn.cc @@ -37,13 +37,13 @@ #include "hiddefs.h" #include "internal_include/bt_target.h" #include "l2cap_types.h" +#include "main/shim/metrics_api.h" #include "osi/include/allocator.h" #include "stack/hid/hidd_int.h" #include "stack/include/bt_hdr.h" #include "stack/include/bt_psm_types.h" #include "stack/include/l2cap_interface.h" #include "stack/include/l2cdefs.h" -#include "stack/include/stack_metrics_logging.h" #include "types/bt_transport.h" #include "types/raw_address.h" @@ -300,7 +300,7 @@ static void hidd_l2cif_config_cfm(uint16_t cid, uint16_t /* initiator */, tL2CAP log::warn("could not start L2CAP connection for INTR"); hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_NOT_STARTED_INCOMING, 1); return; } else { @@ -577,8 +577,8 @@ tHID_STATUS hidd_conn_reg(void) { HID_PSM_CONTROL, dev_reg_info, false /* enable_snoop */, nullptr, HID_DEV_MTU_SIZE, 0, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) { log::error("HID Control (device) registration failed"); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_CONTROL, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_CONTROL, 1); return HID_ERR_L2CAP_FAILED; } @@ -587,8 +587,8 @@ tHID_STATUS hidd_conn_reg(void) { 0, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) { stack::l2cap::get_interface().L2CA_Deregister(HID_PSM_CONTROL); log::error("HID Interrupt (device) registration failed"); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_INTERRUPT, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_INTERRUPT, 1); return HID_ERR_L2CAP_FAILED; } @@ -627,14 +627,15 @@ tHID_STATUS hidd_conn_initiate(void) { if (!p_dev->in_use) { log::warn("no virtual cable established"); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NOT_REGISTERED_AT_INITIATE, 1); return HID_ERR_NOT_REGISTERED; } if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) { log::warn("connection already in progress"); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_CONN_IN_PROCESS, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_CONN_IN_PROCESS, 1); return HID_ERR_CONN_IN_PROCESS; } @@ -649,8 +650,8 @@ tHID_STATUS hidd_conn_initiate(void) { HID_PSM_CONTROL, p_dev->addr, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) == 0) { log::warn("could not start L2CAP connection"); hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_INITIATE, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_L2CAP_FAILED_INITIATE, 1); } else { p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; } @@ -723,7 +724,7 @@ tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param tHID_CONN* p_hcon = &hd_cb.device.conn; if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_CONGESTED_AT_FLAG_CHECK, 1); return HID_ERR_CONGESTED; } @@ -744,13 +745,15 @@ tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param } break; default: - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_INVALID_PARAM, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_INVALID_PARAM, 1); return HID_ERR_INVALID_PARAM; } p_buf = (BT_HDR*)osi_malloc(buf_size); if (p_buf == NULL) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_RESOURCES, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_RESOURCES, 1); return HID_ERR_NO_RESOURCES; } @@ -792,7 +795,7 @@ tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param return HID_SUCCESS; } - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_NO_CONNECTION_AT_SEND_DATA, 1); return HID_ERR_NO_CONNECTION; } @@ -800,7 +803,7 @@ tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param log::verbose("report sent"); if (stack::l2cap::get_interface().L2CA_DataWrite(cid, p_buf) == tL2CAP_DW_RESULT::FAILED) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDD_ERR_CONGESTED_AT_DATA_WRITE, 1); return HID_ERR_CONGESTED; } diff --git a/system/stack/hid/hidh_api.cc b/system/stack/hid/hidh_api.cc index 97d811ed5b..1ab5ba2a6a 100644 --- a/system/stack/hid/hidh_api.cc +++ b/system/stack/hid/hidh_api.cc @@ -37,6 +37,7 @@ #include "hiddefs.h" #include "hidh_int.h" #include "internal_include/bt_target.h" +#include "main/shim/metrics_api.h" #include "osi/include/alarm.h" #include "osi/include/allocator.h" #include "sdp_api.h" @@ -44,7 +45,6 @@ #include "stack/include/bt_hdr.h" #include "stack/include/bt_uuid16.h" #include "stack/include/sdpdefs.h" -#include "stack/include/stack_metrics_logging.h" #include "stack/sdp/sdp_discovery_db.h" #include "types/bluetooth/uuid.h" #include "types/raw_address.h" @@ -69,7 +69,8 @@ static void hidh_search_callback(const RawAddress& bd_addr, tSDP_RESULT sdp_resu tHID_STATUS HID_HostGetSDPRecord(const RawAddress& addr, tSDP_DISCOVERY_DB* p_db, uint32_t db_len, tHID_HOST_SDP_CALLBACK* sdp_cback) { if (hh_cb.sdp_busy) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_SDP_BUSY, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_SDP_BUSY, 1); return HID_ERR_SDP_BUSY; } @@ -87,7 +88,8 @@ tHID_STATUS HID_HostGetSDPRecord(const RawAddress& addr, tSDP_DISCOVERY_DB* p_db return HID_SUCCESS; } else { log::warn("Unable to start SDP service search request peer:{}", addr); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_RESOURCES_SDP, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_RESOURCES_SDP, 1); return HID_ERR_NO_RESOURCES; } } @@ -289,12 +291,13 @@ tHID_STATUS HID_HostRegister(tHID_HOST_DEV_CALLBACK* dev_cback) { tHID_STATUS st; if (hh_cb.reg_flag) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_ALREADY_REGISTERED, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_ALREADY_REGISTERED, 1); return HID_ERR_ALREADY_REGISTERED; } if (dev_cback == NULL) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_REGISTER, 1); return HID_ERR_INVALID_PARAM; } @@ -392,7 +395,7 @@ tHID_STATUS HID_HostAddDev(const RawAddress& addr, uint16_t attr_mask, uint8_t* } if (i == HID_HOST_MAX_DEVICES) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_RESOURCES_ADD_DEVICE, 1); return HID_ERR_NO_RESOURCES; } @@ -427,7 +430,7 @@ tHID_STATUS HID_HostRemoveDev(uint8_t dev_handle) { } if ((dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use)) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_REMOVE_DEV, 1); return HID_ERR_INVALID_PARAM; @@ -457,13 +460,14 @@ tHID_STATUS HID_HostOpenDev(uint8_t dev_handle) { } if ((dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use)) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_OPEN_DEV, 1); return HID_ERR_INVALID_PARAM; } if (hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_ALREADY_CONN, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_ALREADY_CONN, 1); return HID_ERR_ALREADY_CONN; } @@ -494,13 +498,13 @@ tHID_STATUS HID_HostWriteDev(uint8_t dev_handle, uint8_t t_type, uint8_t param, if ((dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use)) { log::error("HID_ERR_INVALID_PARAM"); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_WRITE_DEV, 1); status = HID_ERR_INVALID_PARAM; } else if (hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED) { log::error("HID_ERR_NO_CONNECTION dev_handle {}", dev_handle); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_HOST_WRITE_DEV, 1); status = HID_ERR_NO_CONNECTION; @@ -530,14 +534,14 @@ tHID_STATUS HID_HostCloseDev(uint8_t dev_handle) { } if ((dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use)) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_HOST_CLOSE_DEV, 1); return HID_ERR_INVALID_PARAM; } if (hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_HOST_CLOSE_DEV, 1); return HID_ERR_NO_CONNECTION; diff --git a/system/stack/hid/hidh_conn.cc b/system/stack/hid/hidh_conn.cc index cdc7906d16..71785478a1 100644 --- a/system/stack/hid/hidh_conn.cc +++ b/system/stack/hid/hidh_conn.cc @@ -39,6 +39,7 @@ #include "internal_include/bt_target.h" #include "l2cap_types.h" #include "l2cdefs.h" +#include "main/shim/metrics_api.h" #include "osi/include/alarm.h" #include "osi/include/allocator.h" #include "osi/include/osi.h" @@ -48,7 +49,6 @@ #include "stack/include/btm_client_interface.h" #include "stack/include/btm_log_history.h" #include "stack/include/l2cap_interface.h" -#include "stack/include/stack_metrics_logging.h" #include "types/bt_transport.h" #include "types/raw_address.h" @@ -119,7 +119,7 @@ tHID_STATUS hidh_conn_reg(void) { HID_PSM_CONTROL, hst_reg_info, false /* enable_snoop */, nullptr, HID_HOST_MTU, 0, BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) { log::error("HID-Host Control Registration failed"); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_L2CAP_FAILED_AT_REGISTER_CONTROL, 1); return HID_ERR_L2CAP_FAILED; @@ -129,7 +129,7 @@ tHID_STATUS hidh_conn_reg(void) { BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)) { stack::l2cap::get_interface().L2CA_Deregister(HID_PSM_CONTROL); log::error("HID-Host Interrupt Registration failed"); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_L2CAP_FAILED_AT_REGISTER_INTERRUPT, 1); return HID_ERR_L2CAP_FAILED; @@ -549,7 +549,8 @@ static void hidh_l2cif_disconnect_ind(uint16_t l2cap_cid, bool ack_needed) { (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || (disc_res == HCI_ERR_REPEATED_ATTEMPTS)) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_AUTH_FAILED, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_AUTH_FAILED, 1); hid_close_evt_reason = HID_ERR_AUTH_FAILED; } @@ -771,14 +772,14 @@ tHID_STATUS hidh_conn_snd_data(uint8_t dhandle, uint8_t trans_type, uint8_t para if (!get_btm_client_interface().peer.BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr, BT_TRANSPORT_BR_EDR)) { osi_free(buf); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_NO_CONNECTION_AT_SEND_DATA, 1); return HID_ERR_NO_CONNECTION; } if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { osi_free(buf); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONGESTED_AT_FLAG_CHECK, 1); return HID_ERR_CONGESTED; } @@ -799,7 +800,7 @@ tHID_STATUS hidh_conn_snd_data(uint8_t dhandle, uint8_t trans_type, uint8_t para buf_size = HID_INTERRUPT_BUF_SIZE; break; default: - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_INVALID_PARAM_AT_SEND_DATA, 1); return HID_ERR_INVALID_PARAM; } @@ -860,7 +861,7 @@ tHID_STATUS hidh_conn_snd_data(uint8_t dhandle, uint8_t trans_type, uint8_t para /* Send the buffer through L2CAP */ if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) || (stack::l2cap::get_interface().L2CA_DataWrite(cid, p_buf) == tL2CAP_DW_RESULT::FAILED)) { - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONGESTED_AT_SEND_DATA, 1); return HID_ERR_CONGESTED; } @@ -889,7 +890,8 @@ tHID_STATUS hidh_conn_initiate(uint8_t dhandle) { tHID_HOST_DEV_CTB* p_dev = &hh_cb.devices[dhandle]; if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) { - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONN_IN_PROCESS, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_CONN_IN_PROCESS, 1); return HID_ERR_CONN_IN_PROCESS; } @@ -908,7 +910,7 @@ tHID_STATUS hidh_conn_initiate(uint8_t dhandle) { log::warn("HID-Host Originate failed"); hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::HIDH_ERR_L2CAP_FAILED_AT_INITIATE, 1); } else { /* Transition to the next appropriate state, waiting for connection confirm 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/include/stack_metrics_logging.h b/system/stack/include/stack_metrics_logging.h deleted file mode 100644 index 9a0a80baa4..0000000000 --- a/system/stack/include/stack_metrics_logging.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> - -#include <cstdint> - -#include "hci/address.h" -#include "hci/hci_packets.h" -#include "types/raw_address.h" - -void log_classic_pairing_event(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, - uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, - int64_t event_value); - -void log_link_layer_connection_event(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, - uint16_t link_type, uint32_t hci_cmd, uint16_t hci_event, - uint16_t hci_ble_event, uint16_t cmd_status, - uint16_t reason_code); - -void log_smp_pairing_event(const RawAddress& address, uint16_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason); - -void log_sdp_attribute(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value); - -void log_manufacturer_info(const RawAddress& address, - android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version); - -void log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum key, int64_t value); - -void log_hfp_audio_packet_loss_stats(const RawAddress& address, int num_decoded_frames, - double packet_loss_ratio, uint16_t codec_type); - -void log_mmc_transcode_rtt_stats(int maximum_rtt, double mean_rtt, int num_requests, - int codec_type); - -void log_le_pairing_fail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing); - -void log_le_connection_status(bluetooth::hci::Address address, bool is_connect, - bluetooth::hci::ErrorCode reason); - -void log_le_device_in_accept_list(bluetooth::hci::Address address, bool is_add); - -void log_le_connection_lifecycle(bluetooth::hci::Address address, bool is_connect, bool is_direct); - -void log_le_connection_completion(bluetooth::hci::Address address, bluetooth::hci::ErrorCode reason, - bool is_locally_initiated); 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.cc b/system/stack/l2cap/l2c_ble.cc index 49dca969dc..d964271002 100644 --- a/system/stack/l2cap/l2c_ble.cc +++ b/system/stack/l2cap/l2c_ble.cc @@ -1443,7 +1443,7 @@ tL2CAP_LE_RESULT_CODE l2ble_sec_access_req(const RawAddress& bd_addr, uint16_t p * constraints. For example, when there is at least one Hearing Aid device * bonded, the minimum interval is raised. On return, min_interval and * max_interval are updated. */ -void L2CA_AdjustConnectionIntervals(uint16_t* min_interval, uint16_t* max_interval, +void L2CA_AdjustConnectionIntervals(uint16_t* /* min_interval */, uint16_t* max_interval, uint16_t floor_interval) { // Allow for customization by systemprops for mainline uint16_t phone_min_interval = floor_interval; @@ -1462,13 +1462,6 @@ void L2CA_AdjustConnectionIntervals(uint16_t* min_interval, uint16_t* max_interv log::verbose("Have Hearing Aids. Min. interval is set to {}", phone_min_interval); } - if (!com::android::bluetooth::flags::l2cap_le_do_not_adjust_min_interval() && - *min_interval < phone_min_interval) { - log::verbose("requested min_interval={} too small. Set to {}", *min_interval, - phone_min_interval); - *min_interval = phone_min_interval; - } - // While this could result in connection parameters that fall // outside fo the range requested, this will allow the connection // to remain established. 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/metrics/stack_metrics_logging.cc b/system/stack/metrics/stack_metrics_logging.cc deleted file mode 100644 index 398a72ce33..0000000000 --- a/system/stack/metrics/stack_metrics_logging.cc +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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. - */ - -#include "stack/include/stack_metrics_logging.h" - -#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> - -#include "common/metrics.h" -#include "main/shim/metrics_api.h" -#include "main/shim/shim.h" -#include "types/raw_address.h" - -void log_classic_pairing_event(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, - uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, - int64_t event_value) { - bluetooth::shim::LogMetricClassicPairingEvent(address, handle, hci_cmd, hci_event, cmd_status, - reason_code, event_value); -} - -void log_link_layer_connection_event(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, - uint16_t link_type, uint32_t hci_cmd, uint16_t hci_event, - uint16_t hci_ble_event, uint16_t cmd_status, - uint16_t reason_code) { - bluetooth::shim::LogMetricLinkLayerConnectionEvent(address, connection_handle, direction, - link_type, hci_cmd, hci_event, hci_ble_event, - cmd_status, reason_code); -} - -void log_smp_pairing_event(const RawAddress& address, uint16_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) { - bluetooth::shim::LogMetricSmpPairingEvent(address, smp_cmd, direction, smp_fail_reason); -} - -void log_sdp_attribute(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value) { - bluetooth::shim::LogMetricSdpAttribute(address, protocol_uuid, attribute_id, attribute_size, - attribute_value); -} - -void log_manufacturer_info(const RawAddress& address, - android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version) { - bluetooth::shim::LogMetricManufacturerInfo(address, address_type, source_type, source_name, - manufacturer, model, hardware_version, - software_version); -} - -void log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum key, int64_t value) { - bluetooth::shim::CountCounterMetrics(key, value); -} - -void log_hfp_audio_packet_loss_stats(const RawAddress& address, int num_decoded_frames, - double packet_loss_ratio, uint16_t codec_type) { - bluetooth::shim::LogMetricHfpPacketLossStats(address, num_decoded_frames, packet_loss_ratio, - codec_type); -} - -void log_mmc_transcode_rtt_stats(int maximum_rtt, double mean_rtt, int num_requests, - int codec_type) { - bluetooth::shim::LogMetricMmcTranscodeRttStats(maximum_rtt, mean_rtt, num_requests, codec_type); -} - -void log_le_pairing_fail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing) { - bluetooth::shim::LogMetricLePairingFail(raw_address, failure_reason, is_outgoing); -} - -void log_le_connection_status(bluetooth::hci::Address address, bool is_connect, - bluetooth::hci::ErrorCode reason) { - bluetooth::shim::LogMetricLeConnectionStatus(address, is_connect, reason); -} - -void log_le_device_in_accept_list(bluetooth::hci::Address address, bool is_add) { - bluetooth::shim::LogMetricLeDeviceInAcceptList(address, is_add); -} - -void log_le_connection_lifecycle(bluetooth::hci::Address address, bool is_connect, bool is_direct) { - bluetooth::shim::LogMetricLeConnectionLifecycle(address, is_connect, is_direct); -} - -void log_le_connection_completion(bluetooth::hci::Address address, bluetooth::hci::ErrorCode reason, - bool is_locally_initiated) { - bluetooth::shim::LogMetricLeConnectionCompletion(address, reason, is_locally_initiated); -} diff --git a/system/stack/mmc/metrics/mmc_rtt_logger.cc b/system/stack/mmc/metrics/mmc_rtt_logger.cc index 761d825b44..c95566f0cb 100644 --- a/system/stack/mmc/metrics/mmc_rtt_logger.cc +++ b/system/stack/mmc/metrics/mmc_rtt_logger.cc @@ -20,7 +20,7 @@ #include <cmath> #include <string> -#include "stack/include/stack_metrics_logging.h" +#include "main/shim/metrics_api.h" namespace mmc { @@ -43,7 +43,8 @@ void MmcRttLogger::UploadTranscodeRttStatics() { if (num_requests_ == 0) { return; } - log_mmc_transcode_rtt_stats(maximum_rtt_, rtt_sum_ / num_requests_, num_requests_, codec_type_); + bluetooth::shim::LogMetricMmcTranscodeRttStats(maximum_rtt_, rtt_sum_ / num_requests_, + num_requests_, codec_type_); num_requests_ = 0; rtt_sum_ = 0; maximum_rtt_ = 0; diff --git a/system/stack/rfcomm/port_rfc.cc b/system/stack/rfcomm/port_rfc.cc index 860fdf0450..c1518665d9 100644 --- a/system/stack/rfcomm/port_rfc.cc +++ b/system/stack/rfcomm/port_rfc.cc @@ -36,12 +36,12 @@ #include "internal_include/bt_target.h" #include "internal_include/bt_trace.h" #include "main/shim/entry.h" +#include "main/shim/metrics_api.h" #include "osi/include/allocator.h" #include "osi/include/mutex.h" #include "stack/include/bt_hdr.h" #include "stack/include/bt_uuid16.h" #include "stack/include/rfc_metrics.h" -#include "stack/include/stack_metrics_logging.h" #include "stack/l2cap/l2c_int.h" #include "stack/rfcomm/port_int.h" #include "stack/rfcomm/rfc_int.h" @@ -186,7 +186,8 @@ void port_start_close(tPORT* p_port) { * clear tPort */ if (p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_CLOSED, p_port->handle); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_START_CLOSE, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_START_CLOSE, 1); } port_release_port(p_port); @@ -230,7 +231,7 @@ void PORT_StartCnf(tRFC_MCB* p_mcb, uint16_t result) { if (p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_START_FAILED, p_port->handle); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_START_CNF_FAILED, 1); } port_release_port(p_port); @@ -457,14 +458,14 @@ void PORT_DlcEstablishInd(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu) { if (p_port->rfc_cfg_info.data_path != BTSOCK_DATA_PATH_HARDWARE_OFFLOAD && p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_IND, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_IND, 1); } } else { if (p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_IND, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_IND, 1); } } @@ -493,7 +494,8 @@ void PORT_DlcEstablishCnf(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu, uint16_t if (result != RFCOMM_SUCCESS) { log::warn("Unable to establish configuration dlci:{} result:{}", dlci, result); port_rfc_closed(p_port, PORT_START_FAILED); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_START_FAILED, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_START_FAILED, 1); return; } @@ -513,14 +515,14 @@ void PORT_DlcEstablishCnf(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu, uint16_t if (p_port->rfc_cfg_info.data_path != BTSOCK_DATA_PATH_HARDWARE_OFFLOAD && p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_CNF, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_CNF, 1); } } else { if (p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_CNF, - 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_CNF, 1); } } @@ -590,7 +592,8 @@ void PORT_PortNegCnf(tRFC_MCB* p_mcb, uint8_t dlci, PortSettings* /* p_settings RFCOMM_DlcReleaseReq(p_mcb, p_port->dlci); port_rfc_closed(p_port, PORT_PORT_NEG_FAILED); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_NEG_FAILED, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_NEG_FAILED, 1); return; } @@ -661,7 +664,7 @@ void PORT_ControlInd(tRFC_MCB* p_mcb, uint8_t dlci, tPORT_CTRL* p_pars) { if (p_port->rfc_cfg_info.data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { if (p_port->port_ctrl == PORT_CTRL_SETUP_COMPLETED && p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_IND, 1); } } @@ -708,7 +711,7 @@ void PORT_ControlCnf(tRFC_MCB* p_mcb, uint8_t dlci, tPORT_CTRL* /* p_pars */) { if (p_port->rfc_cfg_info.data_path == BTSOCK_DATA_PATH_HARDWARE_OFFLOAD) { if (p_port->port_ctrl == PORT_CTRL_SETUP_COMPLETED && p_port->p_mgmt_callback) { p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::RFCOMM_CONNECTION_SUCCESS_CNF, 1); } } @@ -767,7 +770,8 @@ void PORT_DlcReleaseInd(tRFC_MCB* p_mcb, uint8_t dlci) { return; } port_rfc_closed(p_port, PORT_CLOSED); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_CLOSED, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_CLOSED, 1); } /******************************************************************************* @@ -788,7 +792,7 @@ void PORT_CloseInd(tRFC_MCB* p_mcb) { for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) { if (p_port->rfc.p_mcb == p_mcb) { port_rfc_closed(p_port, PORT_PEER_CONNECTION_FAILED); - log_counter_metrics( + bluetooth::shim::CountCounterMetrics( android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_PEER_CONNECTION_FAILED, 1); } } @@ -813,7 +817,8 @@ void PORT_TimeOutCloseMux(tRFC_MCB* p_mcb) { for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) { if (p_port->rfc.p_mcb == p_mcb) { port_rfc_closed(p_port, PORT_PEER_TIMEOUT); - log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_PEER_TIMEOUT, 1); + bluetooth::shim::CountCounterMetrics( + android::bluetooth::CodePathCounterKeyEnum::RFCOMM_PORT_PEER_TIMEOUT, 1); } } } diff --git a/system/stack/sdp/sdp_utils.cc b/system/stack/sdp/sdp_utils.cc index 2fb4f4622b..324f4dd06e 100644 --- a/system/stack/sdp/sdp_utils.cc +++ b/system/stack/sdp/sdp_utils.cc @@ -39,6 +39,7 @@ #include "device/include/interop.h" #include "internal_include/bt_target.h" #include "internal_include/bt_trace.h" +#include "main/shim/metrics_api.h" #include "osi/include/allocator.h" #include "osi/include/properties.h" #include "stack/include/avrc_api.h" @@ -50,7 +51,6 @@ #include "stack/include/btm_sec_api_types.h" #include "stack/include/l2cap_interface.h" #include "stack/include/sdpdefs.h" -#include "stack/include/stack_metrics_logging.h" #include "stack/sdp/internal/sdp_api.h" #include "stack/sdp/sdpint.h" #include "storage/config_keys.h" @@ -184,15 +184,15 @@ void sdpu_log_attribute_metrics(const RawAddress& bda, tSDP_DISCOVERY_DB* p_db) } // Log the existence of a profile role // This can be different from Bluetooth Profile Descriptor List - log_sdp_attribute(bda, service_uuid, 0, 0, nullptr); + bluetooth::shim::LogMetricSdpAttribute(bda, service_uuid, 0, 0, nullptr); // Log profile version from Bluetooth Profile Descriptor List auto uuid_version_array = sdpu_find_profile_version(p_rec); for (const auto& uuid_version_pair : uuid_version_array) { uint16_t profile_uuid = uuid_version_pair.first; uint16_t version = uuid_version_pair.second; auto version_array = to_little_endian_array(version); - log_sdp_attribute(bda, profile_uuid, ATTR_ID_BT_PROFILE_DESC_LIST, version_array.size(), - version_array.data()); + bluetooth::shim::LogMetricSdpAttribute(bda, profile_uuid, ATTR_ID_BT_PROFILE_DESC_LIST, + version_array.size(), version_array.data()); } // Log protocol version from Protocol Descriptor List uint16_t protocol_uuid = 0; @@ -217,8 +217,8 @@ void sdpu_log_attribute_metrics(const RawAddress& bda, tSDP_DISCOVERY_DB* p_db) if (protocol_elements.num_params >= 1) { uint16_t version = protocol_elements.params[0]; auto version_array = to_little_endian_array(version); - log_sdp_attribute(bda, protocol_uuid, ATTR_ID_PROTOCOL_DESC_LIST, version_array.size(), - version_array.data()); + bluetooth::shim::LogMetricSdpAttribute(bda, protocol_uuid, ATTR_ID_PROTOCOL_DESC_LIST, + version_array.size(), version_array.data()); } } } @@ -238,8 +238,8 @@ void sdpu_log_attribute_metrics(const RawAddress& bda, tSDP_DISCOVERY_DB* p_db) } uint16_t supported_features = p_attr->attr_value.v.u16; auto version_array = to_little_endian_array(supported_features); - log_sdp_attribute(bda, service_uuid, ATTR_ID_SUPPORTED_FEATURES, version_array.size(), - version_array.data()); + bluetooth::shim::LogMetricSdpAttribute(bda, service_uuid, ATTR_ID_SUPPORTED_FEATURES, + version_array.size(), version_array.data()); break; } case UUID_SERVCLASS_MESSAGE_NOTIFICATION: @@ -251,8 +251,8 @@ void sdpu_log_attribute_metrics(const RawAddress& bda, tSDP_DISCOVERY_DB* p_db) } uint32_t map_supported_features = p_attr->attr_value.v.u32; auto features_array = to_little_endian_array(map_supported_features); - log_sdp_attribute(bda, service_uuid, ATTR_ID_MAP_SUPPORTED_FEATURES, features_array.size(), - features_array.data()); + bluetooth::shim::LogMetricSdpAttribute(bda, service_uuid, ATTR_ID_MAP_SUPPORTED_FEATURES, + features_array.size(), features_array.data()); break; } case UUID_SERVCLASS_PBAP_PCE: @@ -264,8 +264,8 @@ void sdpu_log_attribute_metrics(const RawAddress& bda, tSDP_DISCOVERY_DB* p_db) } uint32_t pbap_supported_features = p_attr->attr_value.v.u32; auto features_array = to_little_endian_array(pbap_supported_features); - log_sdp_attribute(bda, service_uuid, ATTR_ID_PBAP_SUPPORTED_FEATURES, features_array.size(), - features_array.data()); + bluetooth::shim::LogMetricSdpAttribute(bda, service_uuid, ATTR_ID_PBAP_SUPPORTED_FEATURES, + features_array.size(), features_array.data()); break; } } @@ -278,15 +278,17 @@ void sdpu_log_attribute_metrics(const RawAddress& bda, tSDP_DISCOVERY_DB* p_db) tSDP_DI_GET_RECORD di_record = {}; if (SDP_GetDiRecord(1, &di_record, p_db) == tSDP_STATUS::SDP_SUCCESS) { auto version_array = to_little_endian_array(di_record.spec_id); - log_sdp_attribute(bda, UUID_SERVCLASS_PNP_INFORMATION, ATTR_ID_SPECIFICATION_ID, - version_array.size(), version_array.data()); + bluetooth::shim::LogMetricSdpAttribute(bda, UUID_SERVCLASS_PNP_INFORMATION, + ATTR_ID_SPECIFICATION_ID, version_array.size(), + version_array.data()); std::stringstream ss; // [N - native]::SDP::[DIP - Device ID Profile] ss << "N:SDP::DIP::" << loghex(di_record.rec.vendor_id_source); - log_manufacturer_info(bda, android::bluetooth::AddressTypeEnum::ADDRESS_TYPE_PUBLIC, - android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL, ss.str(), - loghex(di_record.rec.vendor), loghex(di_record.rec.product), - loghex(di_record.rec.version), ""); + bluetooth::shim::LogMetricManufacturerInfo( + bda, android::bluetooth::AddressTypeEnum::ADDRESS_TYPE_PUBLIC, + android::bluetooth::DeviceInfoSrcEnum::DEVICE_INFO_INTERNAL, ss.str(), + loghex(di_record.rec.vendor), loghex(di_record.rec.product), + loghex(di_record.rec.version), ""); std::string bda_string = bda.ToString(); // write manufacturer, model, HW version to config diff --git a/system/stack/smp/smp_l2c.cc b/system/stack/smp/smp_l2c.cc index 219cb723f7..69591b10e5 100644 --- a/system/stack/smp/smp_l2c.cc +++ b/system/stack/smp/smp_l2c.cc @@ -263,13 +263,6 @@ static void smp_br_connect_callback(uint16_t /* channel */, const RawAddress& bd log::info("BDA:{} pairing_bda:{}, connected:{}", bd_addr, p_cb->pairing_bda, connected); if (bd_addr != p_cb->pairing_bda) { - if (!com::android::bluetooth::flags::smp_state_machine_stuck_after_disconnection_fix()) { - log::info( - "If your pairing failed, get a build with " - "smp_state_machine_stuck_after_disconnection_fix and try again :)"); - return; - } - tBTM_SEC_DEV_REC* p_dev_rec = btm_find_dev(bd_addr); /* When pairing was initiated to RPA, and connection was on LE transport first using RPA, then * we must check record pseudo address, it might be same device */ diff --git a/system/stack/smp/smp_utils.cc b/system/stack/smp/smp_utils.cc index e5aefe3c6a..9be3de7db1 100644 --- a/system/stack/smp/smp_utils.cc +++ b/system/stack/smp/smp_utils.cc @@ -35,6 +35,7 @@ #include "internal_include/stack_config.h" #include "main/shim/entry.h" #include "main/shim/helpers.h" +#include "main/shim/metrics_api.h" #include "metrics/bluetooth_event.h" #include "osi/include/allocator.h" #include "p_256_ecc_pp.h" @@ -51,7 +52,6 @@ #include "stack/include/l2cap_interface.h" #include "stack/include/l2cdefs.h" #include "stack/include/smp_status.h" -#include "stack/include/stack_metrics_logging.h" #include "types/raw_address.h" #define SMP_PAIRING_REQ_SIZE 7 @@ -320,7 +320,7 @@ void smp_log_metrics(const RawAddress& bd_addr, bool is_outgoing, const uint8_t* uint8_t failure_reason = 0; if (raw_cmd == SMP_OPCODE_PAIRING_FAILED && buf_len >= 1) { STREAM_TO_UINT8(failure_reason, p_buf); - log_le_pairing_fail(bd_addr, failure_reason, is_outgoing); + bluetooth::shim::LogMetricLePairingFail(bd_addr, failure_reason, is_outgoing); } if (smp_cb.is_pair_cancel) { failure_reason = SMP_USER_CANCELLED; // Tracking pairing cancellations @@ -330,7 +330,8 @@ void smp_log_metrics(const RawAddress& bd_addr, bool is_outgoing, const uint8_t* android::bluetooth::DirectionEnum direction = is_outgoing ? android::bluetooth::DirectionEnum::DIRECTION_OUTGOING : android::bluetooth::DirectionEnum::DIRECTION_INCOMING; - log_smp_pairing_event(bd_addr, metric_cmd, direction, static_cast<uint16_t>(failure_reason)); + bluetooth::shim::LogMetricSmpPairingEvent(bd_addr, metric_cmd, direction, + static_cast<uint16_t>(failure_reason)); } /******************************************************************************* @@ -1011,7 +1012,8 @@ void smp_proc_pairing_cmpl(tSMP_CB* p_cb) { if (metric_status > SMP_MAX_FAIL_RSN_PER_SPEC) { metric_status |= SMP_METRIC_STATUS_INTERNAL_FLAG; } - log_smp_pairing_event(p_cb->pairing_bda, metric_cmd, direction, metric_status); + bluetooth::shim::LogMetricSmpPairingEvent(p_cb->pairing_bda, metric_cmd, direction, + metric_status); } if (p_cb->status == SMP_SUCCESS && p_cb->smp_over_br) { diff --git a/system/stack/test/btm/stack_btm_power_mode_test.cc b/system/stack/test/btm/stack_btm_power_mode_test.cc index a3376d8649..ad7b27a71a 100644 --- a/system/stack/test/btm/stack_btm_power_mode_test.cc +++ b/system/stack/test/btm/stack_btm_power_mode_test.cc @@ -49,8 +49,10 @@ std::deque<power_mode_callback> power_mode_callback_queue; class StackBtmPowerMode : public testing::Test { protected: void SetUp() override { - ON_CALL(controller_, SupportsSniffMode).WillByDefault(Return(true)); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + ON_CALL(*bluetooth::hci::testing::mock_controller_, SupportsSniffMode) + .WillByDefault(Return(true)); power_mode_callback_queue.clear(); reset_mock_function_count_map(); ASSERT_EQ(tBTM_STATUS::BTM_SUCCESS, @@ -71,10 +73,9 @@ protected: BTM_PmRegister(BTM_PM_DEREG, &pm_id_, [](const RawAddress& /* p_bda */, tBTM_PM_STATUS /* status */, uint16_t /* value */, tHCI_STATUS /* hci_status */) {})); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } - bluetooth::hci::testing::MockControllerInterface controller_; uint8_t pm_id_{0}; }; diff --git a/system/stack/test/btm/stack_btm_sec_test.cc b/system/stack/test/btm/stack_btm_sec_test.cc index 58317afc29..62226e2750 100644 --- a/system/stack/test/btm/stack_btm_sec_test.cc +++ b/system/stack/test/btm/stack_btm_sec_test.cc @@ -69,7 +69,8 @@ protected: down_thread_ = new bluetooth::os::Thread("down_thread", bluetooth::os::Thread::Priority::NORMAL); down_handler_ = new bluetooth::os::Handler(down_thread_); - bluetooth::hci::testing::mock_hci_layer_ = &mock_hci_; + bluetooth::hci::testing::mock_hci_layer_ = + std::make_unique<bluetooth::hci::testing::MockHciLayer>(); bluetooth::hci::testing::mock_gd_shim_handler_ = up_handler_; } void TearDown() override { @@ -79,10 +80,10 @@ protected: down_handler_->Clear(); delete down_handler_; delete down_thread_; + bluetooth::hci::testing::mock_hci_layer_.reset(); StackBtmSecTest::TearDown(); } bluetooth::common::BidiQueue<bluetooth::hci::ScoView, bluetooth::hci::ScoBuilder> sco_queue_{10}; - bluetooth::hci::testing::MockHciLayer mock_hci_; bluetooth::os::Thread* up_thread_; bluetooth::os::Handler* up_handler_; bluetooth::os::Thread* down_thread_; diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc index 7471d4faca..2341b21677 100644 --- a/system/stack/test/btm/stack_btm_test.cc +++ b/system/stack/test/btm/stack_btm_test.cc @@ -63,13 +63,13 @@ public: protected: void SetUp() override { BtmWithMocksTest::SetUp(); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); } void TearDown() override { - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); BtmWithMocksTest::TearDown(); } - bluetooth::hci::testing::MockControllerInterface controller_; }; class StackBtmWithQueuesTest : public StackBtmTest { @@ -82,11 +82,12 @@ protected: down_thread_ = new bluetooth::os::Thread("down_thread", bluetooth::os::Thread::Priority::NORMAL); down_handler_ = new bluetooth::os::Handler(down_thread_); - bluetooth::hci::testing::mock_hci_layer_ = &mock_hci_; + bluetooth::hci::testing::mock_hci_layer_ = + std::make_unique<bluetooth::hci::testing::MockHciLayer>(); bluetooth::hci::testing::mock_gd_shim_handler_ = up_handler_; bluetooth::legacy::hci::testing::SetMock(legacy_hci_mock_); - EXPECT_CALL(mock_hci_, RegisterForScoConnectionRequests(_)); - EXPECT_CALL(mock_hci_, RegisterForDisconnects(_)); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, RegisterForScoConnectionRequests(_)); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, RegisterForDisconnects(_)); } void TearDown() override { up_handler_->Clear(); @@ -95,10 +96,10 @@ protected: down_handler_->Clear(); delete down_handler_; delete down_thread_; + bluetooth::hci::testing::mock_hci_layer_.release(); StackBtmTest::TearDown(); } bluetooth::common::BidiQueue<bluetooth::hci::ScoView, bluetooth::hci::ScoBuilder> sco_queue_{10}; - bluetooth::hci::testing::MockHciLayer mock_hci_; bluetooth::legacy::hci::testing::MockInterface legacy_hci_mock_; bluetooth::os::Thread* up_thread_; bluetooth::os::Handler* up_handler_; @@ -111,7 +112,8 @@ public: protected: void SetUp() override { StackBtmWithQueuesTest::SetUp(); - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); btm_cb.Init(); btm_sec_cb.Init(BTM_SEC_MODE_SC); } @@ -123,7 +125,8 @@ protected: }; TEST_F(StackBtmWithQueuesTest, GlobalLifecycle) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); get_btm_client_interface().lifecycle.btm_init(); get_btm_client_interface().lifecycle.btm_free(); } @@ -134,20 +137,23 @@ TEST_F(StackBtmTest, DynamicLifecycle) { } TEST_F(StackBtmWithQueuesTest, InitFree) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); btm_cb.Init(); btm_cb.Free(); } TEST_F(StackBtmWithQueuesTest, tSCO_CB) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); tSCO_CB* p_sco = &btm_cb.sco_cb; p_sco->Init(); p_sco->Free(); } TEST_F(StackBtmWithQueuesTest, InformClientOnConnectionSuccess) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); get_btm_client_interface().lifecycle.btm_init(); RawAddress bda({0x11, 0x22, 0x33, 0x44, 0x55, 0x66}); @@ -159,7 +165,8 @@ TEST_F(StackBtmWithQueuesTest, InformClientOnConnectionSuccess) { } TEST_F(StackBtmWithQueuesTest, NoInformClientOnConnectionFail) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); get_btm_client_interface().lifecycle.btm_init(); RawAddress bda({0x11, 0x22, 0x33, 0x44, 0x55, 0x66}); @@ -171,7 +178,8 @@ TEST_F(StackBtmWithQueuesTest, NoInformClientOnConnectionFail) { } TEST_F(StackBtmWithQueuesTest, default_packet_type) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); get_btm_client_interface().lifecycle.btm_init(); btm_cb.acl_cb_.SetDefaultPacketTypeMask(0x4321); @@ -181,7 +189,8 @@ TEST_F(StackBtmWithQueuesTest, default_packet_type) { } TEST_F(StackBtmWithQueuesTest, change_packet_type) { - EXPECT_CALL(mock_hci_, GetScoQueueEnd()).WillOnce(Return(sco_queue_.GetUpEnd())); + EXPECT_CALL(*bluetooth::hci::testing::mock_hci_layer_, GetScoQueueEnd()) + .WillOnce(Return(sco_queue_.GetUpEnd())); get_btm_client_interface().lifecycle.btm_init(); uint16_t handle = 0x123; diff --git a/system/stack/test/btm_iso_test.cc b/system/stack/test/btm_iso_test.cc index 40453f703d..b44f6fe094 100644 --- a/system/stack/test/btm_iso_test.cc +++ b/system/stack/test/btm_iso_test.cc @@ -134,7 +134,8 @@ protected: bluetooth::shim::SetMockIsoInterface(&iso_interface_); hcic::SetMockHcicInterface(&hcic_interface_); bluetooth::shim::testing::hci_layer_set_interface(&bluetooth::shim::interface); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); big_callbacks_.reset(new MockBigCallbacks()); cig_callbacks_.reset(new MockCigCallbacks()); @@ -142,7 +143,8 @@ protected: iso_sizes_.total_num_le_packets_ = 6; iso_sizes_.le_data_packet_length_ = 1024; - ON_CALL(controller_, GetControllerIsoBufferSize()).WillByDefault(Return(iso_sizes_)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetControllerIsoBufferSize()) + .WillByDefault(Return(iso_sizes_)); InitIsoManager(); } @@ -156,7 +158,7 @@ protected: bluetooth::shim::SetMockIsoInterface(nullptr); hcic::SetMockHcicInterface(nullptr); bluetooth::shim::testing::hci_layer_set_interface(nullptr); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } virtual void InitIsoManager() { @@ -312,7 +314,6 @@ protected: IsoManager* manager_instance_; bluetooth::shim::MockIsoInterface iso_interface_; hcic::MockHcicInterface hcic_interface_; - bluetooth::hci::testing::MockControllerInterface controller_; bluetooth::hci::LeBufferSize iso_sizes_; std::unique_ptr<MockBigCallbacks> big_callbacks_; @@ -2207,7 +2208,8 @@ TEST_F(IsoManagerTest, SendIsoDataBigValid) { } TEST_F(IsoManagerTest, SendIsoDataNoCredits) { - uint8_t num_buffers = controller_.GetControllerIsoBufferSize().total_num_le_packets_; + uint8_t num_buffers = bluetooth::hci::testing::mock_controller_->GetControllerIsoBufferSize() + .total_num_le_packets_; std::vector<uint8_t> data_vec(108, 0); // Check on CIG @@ -2254,7 +2256,8 @@ TEST_F(IsoManagerTest, SendIsoDataNoCredits) { } TEST_F(IsoManagerTest, SendIsoDataCreditsReturned) { - uint8_t num_buffers = controller_.GetControllerIsoBufferSize().total_num_le_packets_; + uint8_t num_buffers = bluetooth::hci::testing::mock_controller_->GetControllerIsoBufferSize() + .total_num_le_packets_; std::vector<uint8_t> data_vec(108, 0); // Check on CIG @@ -2323,7 +2326,8 @@ TEST_F(IsoManagerTest, SendIsoDataCreditsReturned) { } TEST_F(IsoManagerTest, SendIsoDataCreditsReturnedByDisconnection) { - uint8_t num_buffers = controller_.GetControllerIsoBufferSize().total_num_le_packets_; + uint8_t num_buffers = bluetooth::hci::testing::mock_controller_->GetControllerIsoBufferSize() + .total_num_le_packets_; std::vector<uint8_t> data_vec(108, 0); // Check on CIG @@ -2542,7 +2546,8 @@ TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleDisconnect) { * is already stopped. */ TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleNumComplDataPkts) { - uint8_t num_buffers = controller_.GetControllerIsoBufferSize().total_num_le_packets_; + uint8_t num_buffers = bluetooth::hci::testing::mock_controller_->GetControllerIsoBufferSize() + .total_num_le_packets_; IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); diff --git a/system/stack/test/connection_manager_test.cc b/system/stack/test/connection_manager_test.cc index 1a06158ca0..db2908c76b 100644 --- a/system/stack/test/connection_manager_test.cc +++ b/system/stack/test/connection_manager_test.cc @@ -70,9 +70,10 @@ namespace connection_manager { class BleConnectionManager : public testing::Test { void SetUp() override { localConnTimeoutMock = std::make_unique<MockConnTimeout>(); - /* extern */ test::mock_acl_manager_ = new bluetooth::hci::testing::MockAclManager(); + /* extern */ test::mock_acl_manager_ = + std::make_unique<bluetooth::hci::testing::MockAclManager>(); /* extern */ test::mock_controller_ = - new testing::NiceMock<bluetooth::hci::testing::MockControllerInterface>(); + std::make_unique<testing::NiceMock<bluetooth::hci::testing::MockControllerInterface>>(); ON_CALL(*test::mock_controller_, GetLeFilterAcceptListSize()).WillByDefault(Return(16)); auto alarm_mock = AlarmMock::Get(); @@ -92,8 +93,8 @@ class BleConnectionManager : public testing::Test { void TearDown() override { connection_manager::reset(true); AlarmMock::Reset(); - delete test::mock_controller_; - delete test::mock_acl_manager_; + test::mock_controller_.reset(); + test::mock_acl_manager_.reset(); localConnTimeoutMock.reset(); } }; @@ -106,7 +107,7 @@ TEST_F(BleConnectionManager, test_background_connection_add_remove) { EXPECT_TRUE(background_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); std::set<tAPP_ID> apps = get_apps_connecting_to(address1); EXPECT_EQ(apps.size(), 1UL); @@ -119,7 +120,7 @@ TEST_F(BleConnectionManager, test_background_connection_add_remove) { EXPECT_EQ(get_apps_connecting_to(address1).size(), 0UL); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } /** Verify that multiple clients adding same device multiple times, result in @@ -136,7 +137,7 @@ TEST_F(BleConnectionManager, test_background_connection_multiple_clients) { EXPECT_EQ(get_apps_connecting_to(address1).size(), 3UL); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(_, _)).Times(0); @@ -153,7 +154,7 @@ TEST_F(BleConnectionManager, test_background_connection_multiple_clients) { EXPECT_EQ(get_apps_connecting_to(address1).size(), 0UL); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } /** Verify adding/removing device to direct connection. */ @@ -173,7 +174,7 @@ TEST_F(BleConnectionManager, test_direct_connection_client) { // Client that don't do direct connection should fail attempt to stop it EXPECT_FALSE(direct_connect_remove(CLIENT2, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(_)).Times(1); EXPECT_CALL(*AlarmMock::Get(), AlarmFree(_)).Times(1); @@ -183,7 +184,7 @@ TEST_F(BleConnectionManager, test_direct_connection_client) { // acceptlist is in use, i.e. next connection attempt EXPECT_TRUE(direct_connect_remove(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } /** Verify direct connection timeout does remove device from acceptlist, and @@ -201,7 +202,7 @@ TEST_F(BleConnectionManager, test_direct_connect_timeout) { // Start direct connect attempt... EXPECT_TRUE(direct_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(_)).Times(1); EXPECT_CALL(*localConnTimeoutMock, OnConnectionTimedOut(CLIENT1, address1)).Times(1); @@ -210,7 +211,7 @@ TEST_F(BleConnectionManager, test_direct_connect_timeout) { // simulate timeout seconds passed, alarm executing alarm_callback(alarm_data); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } /** Verify that we properly handle successfull direct connection */ @@ -222,7 +223,7 @@ TEST_F(BleConnectionManager, test_direct_connection_success) { // Start direct connect attempt... EXPECT_TRUE(direct_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(address1_hci)).Times(1); EXPECT_CALL(*AlarmMock::Get(), AlarmFree(_)).Times(1); @@ -230,7 +231,7 @@ TEST_F(BleConnectionManager, test_direct_connection_success) { // successfully. on_connection_complete(address1); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } /** Verify that we properly handle application unregistration */ @@ -244,23 +245,23 @@ TEST_F(BleConnectionManager, test_app_unregister) { EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(address1_hci, true)).Times(1); EXPECT_TRUE(direct_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(address2_hci, false)).Times(1); EXPECT_TRUE(background_connect_add(CLIENT1, address2)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(address2_hci, true)).Times(1); EXPECT_TRUE(direct_connect_add(CLIENT2, address2)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(address1_hci)).Times(1); on_app_deregistered(CLIENT1); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(address2_hci)).Times(1); on_app_deregistered(CLIENT2); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } /** Verify adding device to both direct connection and background connection. */ @@ -273,7 +274,7 @@ TEST_F(BleConnectionManager, test_direct_and_background_connect) { EXPECT_TRUE(direct_connect_add(CLIENT1, address1)); EXPECT_TRUE(background_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*AlarmMock::Get(), AlarmFree(_)).Times(1); // not removing from acceptlist yet, as the background connection is still @@ -284,7 +285,7 @@ TEST_F(BleConnectionManager, test_direct_and_background_connect) { EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(_)).Times(1); EXPECT_TRUE(background_connect_remove(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } TEST_F(BleConnectionManager, test_target_announement_connect) { @@ -302,7 +303,7 @@ TEST_F(BleConnectionManager, test_add_targeted_announement_when_allow_list_used) EXPECT_TRUE(background_connect_add(CLIENT1, address1)); EXPECT_TRUE(background_connect_targeted_announcement_add(CLIENT2, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } TEST_F(BleConnectionManager, test_add_background_connect_when_targeted_announcement_are_enabled) { @@ -315,7 +316,7 @@ TEST_F(BleConnectionManager, test_add_background_connect_when_targeted_announcem EXPECT_TRUE(background_connect_targeted_announcement_add(CLIENT2, address1)); EXPECT_TRUE(background_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } TEST_F(BleConnectionManager, test_re_add_background_connect_to_allow_list) { @@ -325,7 +326,7 @@ TEST_F(BleConnectionManager, test_re_add_background_connect_to_allow_list) { EXPECT_TRUE(background_connect_targeted_announcement_add(CLIENT2, address1)); EXPECT_TRUE(background_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); /* Now remove app using targeted announcement and expect device * to be added to white list @@ -335,11 +336,11 @@ TEST_F(BleConnectionManager, test_re_add_background_connect_to_allow_list) { EXPECT_CALL(*test::mock_acl_manager_, CreateLeConnection(address1_hci, false)).Times(1); EXPECT_TRUE(background_connect_remove(CLIENT2, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*test::mock_acl_manager_, CancelLeConnect(_)).Times(1); EXPECT_TRUE(background_connect_remove(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } TEST_F(BleConnectionManager, test_re_add_to_allow_list_after_timeout_with_multiple_clients) { @@ -352,7 +353,7 @@ TEST_F(BleConnectionManager, test_re_add_to_allow_list_after_timeout_with_multip EXPECT_TRUE(background_connect_add(CLIENT1, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); EXPECT_CALL(*AlarmMock::Get(), AlarmSetOnMloop(_, _, _, _)) .Times(1) @@ -360,7 +361,7 @@ TEST_F(BleConnectionManager, test_re_add_to_allow_list_after_timeout_with_multip // Start direct connect attempt... EXPECT_TRUE(direct_connect_add(CLIENT2, address1)); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); // simulate timeout seconds passed, alarm executing EXPECT_CALL(*localConnTimeoutMock, OnConnectionTimedOut(CLIENT2, address1)).Times(1); @@ -369,7 +370,7 @@ TEST_F(BleConnectionManager, test_re_add_to_allow_list_after_timeout_with_multip EXPECT_CALL(*AlarmMock::Get(), AlarmFree(_)).Times(1); alarm_callback(alarm_data); - Mock::VerifyAndClearExpectations(test::mock_acl_manager_); + Mock::VerifyAndClearExpectations(test::mock_acl_manager_.get()); } } // namespace connection_manager diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc index 130893a0a5..5d7a2eeea8 100644 --- a/system/stack/test/eatt/eatt_test.cc +++ b/system/stack/test/eatt/eatt_test.cc @@ -219,11 +219,13 @@ protected: le_buffer_size_.le_data_packet_length_ = 128; le_buffer_size_.total_num_le_packets_ = 24; - EXPECT_CALL(controller_, GetLeBufferSize).WillRepeatedly(Return(le_buffer_size_)); + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + EXPECT_CALL(*bluetooth::hci::testing::mock_controller_, GetLeBufferSize) + .WillRepeatedly(Return(le_buffer_size_)); bluetooth::l2cap::SetMockInterface(&l2cap_interface_); bluetooth::manager::SetMockBtmApiInterface(&btm_api_interface_); bluetooth::gatt::SetMockGattInterface(&gatt_interface_); - bluetooth::hci::testing::mock_controller_ = &controller_; // Clear the static memory for each test case memset(&test_tcb, 0, sizeof(test_tcb)); @@ -258,7 +260,7 @@ protected: bluetooth::l2cap::SetMockInterface(nullptr); bluetooth::testing::stack::l2cap::reset_interface(); bluetooth::manager::SetMockBtmApiInterface(nullptr); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); Test::TearDown(); } @@ -269,7 +271,6 @@ protected: bluetooth::l2cap::MockL2capInterface l2cap_interface_; bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_; bluetooth::gatt::MockGattInterface gatt_interface_; - bluetooth::hci::testing::MockControllerInterface controller_; bluetooth::hci::LeBufferSize le_buffer_size_; tL2CAP_APPL_INFO l2cap_app_info_; diff --git a/system/stack/test/stack_acl_test.cc b/system/stack/test/stack_acl_test.cc index b08a45cb80..bd0b153b9f 100644 --- a/system/stack/test/stack_acl_test.cc +++ b/system/stack/test/stack_acl_test.cc @@ -48,12 +48,12 @@ class StackAclTest : public testing::Test { protected: void SetUp() override { reset_mock_function_count_map(); - bluetooth::hci::testing::mock_controller_ = &controller_; + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); } - void TearDown() override { bluetooth::hci::testing::mock_controller_ = nullptr; } + void TearDown() override { bluetooth::hci::testing::mock_controller_.reset(); } tBTM_SEC_DEV_REC device_record_; - bluetooth::hci::testing::MockControllerInterface controller_; }; TEST_F(StackAclTest, nop) {} diff --git a/system/stack/test/stack_l2cap_test.cc b/system/stack/test/stack_l2cap_test.cc index ffe78055bc..204900b462 100644 --- a/system/stack/test/stack_l2cap_test.cc +++ b/system/stack/test/stack_l2cap_test.cc @@ -49,23 +49,23 @@ constexpr uint16_t kAclBufferSizeBle = 45; class StackL2capTest : public ::testing::Test { protected: void SetUp() override { - bluetooth::hci::testing::mock_controller_ = &controller_interface_; - ON_CALL(controller_interface_, GetNumAclPacketBuffers) + bluetooth::hci::testing::mock_controller_ = + std::make_unique<bluetooth::hci::testing::MockControllerInterface>(); + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetNumAclPacketBuffers) .WillByDefault(Return(kAclBufferCountClassic)); bluetooth::hci::LeBufferSize le_sizes; le_sizes.total_num_le_packets_ = kAclBufferCountBle; le_sizes.le_data_packet_length_ = kAclBufferSizeBle; - ON_CALL(controller_interface_, GetLeBufferSize).WillByDefault(Return(le_sizes)); - ON_CALL(controller_interface_, SupportsBle).WillByDefault(Return(true)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, GetLeBufferSize) + .WillByDefault(Return(le_sizes)); + ON_CALL(*bluetooth::hci::testing::mock_controller_, SupportsBle).WillByDefault(Return(true)); l2c_init(); } void TearDown() override { l2c_free(); - bluetooth::hci::testing::mock_controller_ = nullptr; + bluetooth::hci::testing::mock_controller_.reset(); } - - bluetooth::hci::testing::MockControllerInterface controller_interface_; }; TEST_F(StackL2capTest, l2cble_process_data_length_change_event) { diff --git a/system/test/Android.bp b/system/test/Android.bp index c35931ea17..106dedb7bd 100644 --- a/system/test/Android.bp +++ b/system/test/Android.bp @@ -450,13 +450,6 @@ filegroup { } filegroup { - name: "TestMockStackMetrics", - srcs: [ - "mock/mock_stack_metrics*.cc", - ], -} - -filegroup { name: "TestMockStackGap", srcs: [ "mock/mock_stack_gap*.cc", 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_main_shim_entry.cc b/system/test/mock/mock_main_shim_entry.cc index 5d68f11b9d..3cdc714eaf 100644 --- a/system/test/mock/mock_main_shim_entry.cc +++ b/system/test/mock/mock_main_shim_entry.cc @@ -38,9 +38,9 @@ namespace bluetooth { namespace hci { namespace testing { -MockAclManager* mock_acl_manager_{nullptr}; -MockControllerInterface* mock_controller_{nullptr}; -HciInterface* mock_hci_layer_{nullptr}; +std::unique_ptr<MockAclManager> mock_acl_manager_; +std::unique_ptr<MockControllerInterface> mock_controller_; +std::unique_ptr<MockHciLayer> mock_hci_layer_; os::Handler* mock_gd_shim_handler_{nullptr}; MockLeAdvertisingManager* mock_le_advertising_manager_{nullptr}; MockLeScanningManager* mock_le_scanning_manager_{nullptr}; @@ -58,9 +58,9 @@ class Dumpsys; namespace shim { -hci::AclManager* GetAclManager() { return hci::testing::mock_acl_manager_; } -hci::ControllerInterface* GetController() { return hci::testing::mock_controller_; } -hci::HciInterface* GetHciLayer() { return hci::testing::mock_hci_layer_; } +hci::AclManager* GetAclManager() { return hci::testing::mock_acl_manager_.get(); } +hci::ControllerInterface* GetController() { return hci::testing::mock_controller_.get(); } +hci::HciInterface* GetHciLayer() { return hci::testing::mock_hci_layer_.get(); } hci::LeAdvertisingManager* GetAdvertising() { return hci::testing::mock_le_advertising_manager_; } hci::LeScanningManager* GetScanning() { return hci::testing::mock_le_scanning_manager_; } hci::DistanceMeasurementManager* GetDistanceMeasurementManager() { diff --git a/system/test/mock/mock_main_shim_entry.h b/system/test/mock/mock_main_shim_entry.h index ca84a27193..5d427fd963 100644 --- a/system/test/mock/mock_main_shim_entry.h +++ b/system/test/mock/mock_main_shim_entry.h @@ -15,11 +15,12 @@ */ #include <functional> +#include <memory> #include "hci/acl_manager_mock.h" #include "hci/controller_interface_mock.h" #include "hci/distance_measurement_manager_mock.h" -#include "hci/hci_interface.h" +#include "hci/hci_layer_mock.h" #include "hci/le_advertising_manager_mock.h" #include "hci/le_scanning_manager_mock.h" #include "storage/storage_module.h" @@ -28,9 +29,9 @@ namespace bluetooth { namespace hci { namespace testing { -extern MockAclManager* mock_acl_manager_; -extern MockControllerInterface* mock_controller_; -extern HciInterface* mock_hci_layer_; +extern std::unique_ptr<MockAclManager> mock_acl_manager_; +extern std::unique_ptr<MockControllerInterface> mock_controller_; +extern std::unique_ptr<MockHciLayer> mock_hci_layer_; extern os::Handler* mock_gd_shim_handler_; extern MockLeAdvertisingManager* mock_le_advertising_manager_; extern MockLeScanningManager* mock_le_scanning_manager_; 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/mock/mock_stack_metrics_logging.cc b/system/test/mock/mock_stack_metrics_logging.cc deleted file mode 100644 index ff2cee4472..0000000000 --- a/system/test/mock/mock_stack_metrics_logging.cc +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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:5 - * - * mockcify.pl ver 0.2 - */ -// Mock include file to share data between tests and mock -#include "test/mock/mock_stack_metrics_logging.h" - -#include <string> - -// Original included files, if any -#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> - -#include "stack/include/stack_metrics_logging.h" -#include "test/common/mock_functions.h" -#include "types/raw_address.h" - -// Mocked compile conditionals, if any -// Mocked internal structures, if any - -namespace test { -namespace mock { -namespace stack_metrics_logging { - -// Function state capture and return values, if needed -struct log_classic_pairing_event log_classic_pairing_event; -struct log_link_layer_connection_event log_link_layer_connection_event; -struct log_smp_pairing_event log_smp_pairing_event; -struct log_le_pairing_fail log_le_pairing_fail; -struct log_sdp_attribute log_sdp_attribute; -struct log_manufacturer_info log_manufacturer_info; -struct log_counter_metrics log_counter_metrics; -struct log_hfp_audio_packet_loss_stats log_hfp_audio_packet_loss_stats; -struct log_mmc_transcode_rtt_stats log_mmc_transcode_rtt_stats; -struct log_le_connection_status log_le_connection_status; -struct log_le_device_in_accept_list log_le_device_in_accept_list; -struct log_le_connection_lifecycle log_le_connection_lifecycle; -struct log_le_connection_completion log_le_connection_completion; - -} // namespace stack_metrics_logging -} // namespace mock -} // namespace test - -// Mocked functions, if any -void log_classic_pairing_event(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, - uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, - int64_t event_value) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_classic_pairing_event( - address, handle, hci_cmd, hci_event, cmd_status, reason_code, event_value); -} -void log_link_layer_connection_event(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, - uint16_t link_type, uint32_t hci_cmd, uint16_t hci_event, - uint16_t hci_ble_event, uint16_t cmd_status, - uint16_t reason_code) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_link_layer_connection_event( - address, connection_handle, direction, link_type, hci_cmd, hci_event, hci_ble_event, - cmd_status, reason_code); -} -void log_smp_pairing_event(const RawAddress& address, uint16_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_smp_pairing_event(address, smp_cmd, direction, - smp_fail_reason); -} - -void log_le_pairing_fail(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_le_pairing_fail(raw_address, failure_reason, is_outgoing); -} - -void log_sdp_attribute(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_sdp_attribute(address, protocol_uuid, attribute_id, - attribute_size, attribute_value); -} -void log_manufacturer_info(const RawAddress& address, - android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_manufacturer_info(address, address_type, source_type, - source_name, manufacturer, model, - hardware_version, software_version); -} - -void log_counter_metrics(android::bluetooth::CodePathCounterKeyEnum key, int64_t value) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_counter_metrics(key, value); -} - -void log_hfp_audio_packet_loss_stats(const RawAddress& address, int num_decoded_frames, - double packet_loss_ratio, uint16_t codec_type) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_hfp_audio_packet_loss_stats(address, num_decoded_frames, - packet_loss_ratio, codec_type); -} - -void log_mmc_transcode_rtt_stats(int maximum_rtt, double mean_rtt, int num_requests, - int codec_type) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_mmc_transcode_rtt_stats(maximum_rtt, mean_rtt, - num_requests, codec_type); -} - -void log_le_connection_status(bluetooth::hci::Address address, bool is_connect, - bluetooth::hci::ErrorCode reason) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_le_connection_status(address, is_connect, reason); -} - -void log_le_device_in_accept_list(bluetooth::hci::Address address, bool is_add) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_le_device_in_accept_list(address, is_add); -} - -void log_le_connection_lifecycle(bluetooth::hci::Address address, bool is_connect, bool is_direct) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_le_connection_lifecycle(address, is_connect, is_direct); -} - -void log_le_connection_completion(bluetooth::hci::Address address, bluetooth::hci::ErrorCode reason, - bool is_locally_initiated) { - inc_func_call_count(__func__); - test::mock::stack_metrics_logging::log_le_connection_completion(address, reason, - is_locally_initiated); -} -// END mockcify generation diff --git a/system/test/mock/mock_stack_metrics_logging.h b/system/test/mock/mock_stack_metrics_logging.h deleted file mode 100644 index 40212e60ea..0000000000 --- a/system/test/mock/mock_stack_metrics_logging.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * 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:5 - * - * mockcify.pl ver 0.2 - */ - -#include <string> - -// Original included files, if any -#include <frameworks/proto_logging/stats/enums/bluetooth/enums.pb.h> -#include <frameworks/proto_logging/stats/enums/bluetooth/hci/enums.pb.h> - -#include "hci/address.h" -#include "hci/hci_packets.h" -#include "types/raw_address.h" - -// Mocked compile conditionals, if any - -namespace test { -namespace mock { -namespace stack_metrics_logging { - -// Shared state between mocked functions and tests -// Name: log_classic_pairing_event -// Params: const RawAddress& address, uint16_t handle, uint32_t hci_cmd, -// uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, int64_t -// event_value Returns: void -struct log_classic_pairing_event { - std::function<void(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, - uint16_t hci_event, uint16_t cmd_status, uint16_t reason_code, - int64_t event_value)> - body{[](const RawAddress& /* address */, uint16_t /* handle */, uint32_t /* hci_cmd */, - uint16_t /* hci_event */, uint16_t /* cmd_status */, uint16_t /* reason_code */, - int64_t /* event_value */) {}}; - void operator()(const RawAddress& address, uint16_t handle, uint32_t hci_cmd, uint16_t hci_event, - uint16_t cmd_status, uint16_t reason_code, int64_t event_value) { - body(address, handle, hci_cmd, hci_event, cmd_status, reason_code, event_value); - } -}; -extern struct log_classic_pairing_event log_classic_pairing_event; -// Name: log_link_layer_connection_event -// Params: const RawAddress* address, uint32_t connection_handle, -// android::bluetooth::DirectionEnum direction, uint16_t link_type, uint32_t -// hci_cmd, uint16_t hci_event, uint16_t hci_ble_event, uint16_t cmd_status, -// uint16_t reason_code Returns: void -struct log_link_layer_connection_event { - std::function<void(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, uint16_t link_type, - uint32_t hci_cmd, uint16_t hci_event, uint16_t hci_ble_event, - uint16_t cmd_status, uint16_t reason_code)> - body{[](const RawAddress* /* address */, uint32_t /* connection_handle */, - android::bluetooth::DirectionEnum /* direction */, uint16_t /* link_type */, - uint32_t /* hci_cmd */, uint16_t /* hci_event */, uint16_t /* hci_ble_event */, - uint16_t /* cmd_status */, uint16_t /* reason_code */) {}}; - void operator()(const RawAddress* address, uint32_t connection_handle, - android::bluetooth::DirectionEnum direction, uint16_t link_type, uint32_t hci_cmd, - uint16_t hci_event, uint16_t hci_ble_event, uint16_t cmd_status, - uint16_t reason_code) { - body(address, connection_handle, direction, link_type, hci_cmd, hci_event, hci_ble_event, - cmd_status, reason_code); - } -}; -extern struct log_link_layer_connection_event log_link_layer_connection_event; -// Name: log_smp_pairing_event -// Params: const RawAddress& address, uint16_t smp_cmd, -// android::bluetooth::DirectionEnum direction, uint8_t smp_fail_reason Returns: -// void -struct log_smp_pairing_event { - std::function<void(const RawAddress& address, uint16_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason)> - body{[](const RawAddress& /* address */, uint16_t /* smp_cmd */, - android::bluetooth::DirectionEnum /* direction */, - uint16_t /* smp_fail_reason */) {}}; - void operator()(const RawAddress& address, uint16_t smp_cmd, - android::bluetooth::DirectionEnum direction, uint16_t smp_fail_reason) { - body(address, smp_cmd, direction, smp_fail_reason); - } -}; -extern struct log_smp_pairing_event log_smp_pairing_event; - -// Name: log_le_pairing_fail -// Params: const RawAddress& raw_address, uint8_t failure_reason, bool -// is_outgoing Returns: -// void -// Name: log_sdp_attribute -// Params: const RawAddress& address, uint16_t protocol_uuid, uint16_t -// attribute_id, size_t attribute_size, const char* attribute_value Returns: -// void -struct log_le_pairing_fail { - std::function<void(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing)> body{ - [](const RawAddress& /* address */, uint8_t /* failure reason */, - bool /* is_outgoing */) {}}; - void operator()(const RawAddress& raw_address, uint8_t failure_reason, bool is_outgoing) { - body(raw_address, failure_reason, is_outgoing); - } -}; -extern struct log_le_pairing_fail log_le_pairing_fail; - -struct log_sdp_attribute { - std::function<void(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value)> - body{[](const RawAddress& /* address */, uint16_t /* protocol_uuid */, - uint16_t /* attribute_id */, size_t /* attribute_size */, - const char* /* attribute_value */) {}}; - void operator()(const RawAddress& address, uint16_t protocol_uuid, uint16_t attribute_id, - size_t attribute_size, const char* attribute_value) { - body(address, protocol_uuid, attribute_id, attribute_size, attribute_value); - } -}; -extern struct log_sdp_attribute log_sdp_attribute; -// Name: log_manufacturer_info -// Params: const RawAddress& address, android::bluetooth::DeviceInfoSrcEnum -// source_type, const std::string& source_name, const std::string& manufacturer, -// const std::string& model, const std::string& hardware_version, const -// std::string& software_version Returns: void -struct log_manufacturer_info { - std::function<void(const RawAddress& address, android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version)> - body2{[](const RawAddress& /* address */, - android::bluetooth::AddressTypeEnum /* address_type */, - android::bluetooth::DeviceInfoSrcEnum /* source_type */, - const std::string& /* source_name */, const std::string& /* manufacturer */, - const std::string& /* model */, const std::string& /* hardware_version */, - const std::string& /* software_version */) {}}; - void operator()(const RawAddress& address, android::bluetooth::AddressTypeEnum address_type, - android::bluetooth::DeviceInfoSrcEnum source_type, const std::string& source_name, - const std::string& manufacturer, const std::string& model, - const std::string& hardware_version, const std::string& software_version) { - body2(address, address_type, source_type, source_name, manufacturer, model, hardware_version, - software_version); - } - std::function<void(const RawAddress& address, android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version)> - body{[](const RawAddress& /* address */, - android::bluetooth::DeviceInfoSrcEnum /* source_type */, - const std::string& /* source_name */, const std::string& /* manufacturer */, - const std::string& /* model */, const std::string& /* hardware_version */, - const std::string& /* software_version */) {}}; - void operator()(const RawAddress& address, android::bluetooth::DeviceInfoSrcEnum source_type, - const std::string& source_name, const std::string& manufacturer, - const std::string& model, const std::string& hardware_version, - const std::string& software_version) { - body(address, source_type, source_name, manufacturer, model, hardware_version, - software_version); - } -}; -extern struct log_manufacturer_info log_manufacturer_info; - -// Name: log_counter_metrics -struct log_counter_metrics { - std::function<void(android::bluetooth::CodePathCounterKeyEnum key, int64_t value)> body{ - [](android::bluetooth::CodePathCounterKeyEnum /* key */, int64_t /* value */) {}}; - void operator()(android::bluetooth::CodePathCounterKeyEnum key, int64_t value) { - body(key, value); - } -}; -extern struct log_counter_metrics log_counter_metrics; - -// Name: log_hfp_audio_packet_loss_stats -struct log_hfp_audio_packet_loss_stats { - std::function<void(const RawAddress& address, int num_decoded_frames, double packet_loss_ratio, - uint16_t codec_type)> - body{[](const RawAddress& /* address */, int /* num_decoded_frames */, - double /* packet_loss_ratio */, uint16_t /* codec_type */) {}}; - void operator()(const RawAddress& address, int num_decoded_frames, double packet_loss_ratio, - uint16_t codec_type) { - body(address, num_decoded_frames, packet_loss_ratio, codec_type); - } -}; -extern struct log_hfp_audio_packet_loss_stats log_hfp_audio_packet_loss_stats; - -// Name: log_mmc_transcode_rtt_stats -struct log_mmc_transcode_rtt_stats { - std::function<void(int maximum_rtt, double mean_rtt, int num_requests, int codec_type)> body{ - [](int /* maximum_rtt */, double /* mean_rtt */, int /* num_requests */, - int /* codec_type */) {}}; - void operator()(int maximum_rtt, double mean_rtt, int num_requests, int codec_type) { - body(maximum_rtt, mean_rtt, num_requests, codec_type); - } -}; -extern struct log_mmc_transcode_rtt_stats log_mmc_transcode_rtt_stats; - -// Name: log_le_connection_status -struct log_le_connection_status { - std::function<void(bluetooth::hci::Address address, bool is_connect, - bluetooth::hci::ErrorCode reason)> - body{[](bluetooth::hci::Address /* address */, bool /* is_connect */, - bluetooth::hci::ErrorCode /* reason */) {}}; - void operator()(bluetooth::hci::Address address, bool is_connect, - bluetooth::hci::ErrorCode reason) { - body(address, is_connect, reason); - } -}; -extern struct log_le_connection_status log_le_connection_status; - -// Name: log_le_device_in_accept_list -struct log_le_device_in_accept_list { - std::function<void(bluetooth::hci::Address address, bool is_add)> body{ - [](bluetooth::hci::Address /* address */, bool /* is_add */) {}}; - void operator()(bluetooth::hci::Address address, bool is_add) { body(address, is_add); } -}; -extern struct log_le_device_in_accept_list log_le_device_in_accept_list; - -// Name: log_le_connection_lifecycle -struct log_le_connection_lifecycle { - std::function<void(bluetooth::hci::Address address, bool is_connect, bool is_direct)> body{ - [](bluetooth::hci::Address /* address */, bool /* is_connect */, bool /* is_direct */) { - }}; - void operator()(bluetooth::hci::Address address, bool is_connect, bool is_direct) { - body(address, is_connect, is_direct); - } -}; -extern struct log_le_device_in_accept_list log_le_device_in_accept_list; - -// Name: log_le_connection_completion -struct log_le_connection_completion { - std::function<void(bluetooth::hci::Address address, bluetooth::hci::ErrorCode reason, - bool is_locally_initiated)> - body{[](bluetooth::hci::Address /* address */, bluetooth::hci::ErrorCode /* reason */, - bool /* is locally initiated */) {}}; - void operator()(bluetooth::hci::Address address, bluetooth::hci::ErrorCode reason, - bool is_locally_initiated) { - body(address, reason, is_locally_initiated); - } -}; -extern struct log_le_connection_completion log_le_connection_completion; - -} // namespace stack_metrics_logging -} // namespace mock -} // namespace test - -// END mockcify generation |