diff options
author | 2025-03-11 17:10:23 -0700 | |
---|---|---|
committer | 2025-03-11 17:46:12 -0700 | |
commit | fb610bad48bab1bbfea676a93fa5ea9ab30070a4 (patch) | |
tree | b07cc67f3230ef21a86b77216c3cae3f2433a86e | |
parent | fdbf19f4c09baffc00d00cbf3cbcdec8264c751c (diff) |
SystemServer: Improve test checks
Tests are now using:
* inOrder to validate the order of event
* Intent matcher to better validate what has been broadcasted
Bug: 402332571
Flag: TEST_ONLY
Test: atest ServiceBluetoothTests
Change-Id: I91750faa6dd8159ba7afa5ab92e83100578a45e6
3 files changed, 126 insertions, 93 deletions
diff --git a/service/src/com/android/server/bluetooth/BluetoothManagerService.java b/service/src/com/android/server/bluetooth/BluetoothManagerService.java index 08a949d81a..3f1017f88d 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; @@ -1228,10 +1226,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); } @@ -2254,33 +2252,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 +2294,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..c6105ec731 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; @@ -41,13 +44,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 +61,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 +68,25 @@ 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.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import androidx.test.platform.app.InstrumentationRegistry; +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; @@ -103,24 +112,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; - - @Spy - private final Context mContext = - new ContextWrapper(InstrumentationRegistry.getInstrumentation().getTargetContext()); + private final Context mTargetContext = + 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 +157,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 +175,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(); @@ -285,6 +310,7 @@ public class BluetoothManagerServiceTest { mManagerService.enableBle("enable_bindFailure_removesTimeout", mBinder); 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 @@ -312,7 +338,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 +350,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 +362,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 +373,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 +404,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); @@ -393,7 +417,7 @@ public class BluetoothManagerServiceTest { 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 @@ -403,7 +427,7 @@ public class BluetoothManagerServiceTest { btCallback.onBluetoothStateChange(STATE_BLE_TURNING_ON, STATE_BLE_ON); syncHandler(MESSAGE_BLUETOOTH_STATE_CHANGE); - verify(mAdapterBinder).bleOnToOn(any()); + mInOrder.verify(mAdapterBinder).bleOnToOn(any()); } @Test @@ -417,13 +441,13 @@ public class BluetoothManagerServiceTest { 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 @@ -433,12 +457,8 @@ public class BluetoothManagerServiceTest { 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); } @@ -460,8 +480,8 @@ public class BluetoothManagerServiceTest { 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,9 +504,7 @@ public class BluetoothManagerServiceTest { @Test public void disableAirplane_whenNothing_startBluetooth() throws Exception { - doReturn(BluetoothManagerService.BLUETOOTH_ON_BLUETOOTH) - .when(mBluetoothServerProxy) - .getBluetoothPersistedState(any(), anyInt()); + mPersistedState = BluetoothManagerService.BLUETOOTH_ON_BLUETOOTH; mManagerService.enable("disableAirplane_whenNothing_startBluetooth"); discardMessage(MESSAGE_ENABLE); @@ -496,16 +514,7 @@ public class BluetoothManagerServiceTest { @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()); - - mManagerService.enable("test_offToOn"); + mManagerService.enable("disableAirplane_whenFactoryReset_doesNotStartBluetooth"); syncHandler(MESSAGE_ENABLE); IBluetoothCallback btCallback = transition_offToOn(); assertThat(mManagerService.getState()).isEqualTo(STATE_ON); @@ -522,4 +531,25 @@ public class BluetoothManagerServiceTest { mManagerService.onAirplaneModeChanged(false); assertThat(mLooper.nextMessage()).isNull(); // Must not create a MESSAGE_ENABLE } + + @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)); + } } |