DeviceCommunicationService - stress test connect/dispose (#5020)

only effective in debug builds:
Test if a DeviceSupport is ready for multiple connect triggers (manual, AutoConnectIntervalReceiver, BluetoothConnectReceiver, GBCompanionDeviceService)

Device specific debugging preferences:
1) optionally call DeviceSupport.dispose() twice
2) optionally call DeviceSupport.connect() multiple times in series or in parallel
This commit is contained in:
Thomas Kuehne
2025-06-29 16:31:53 +02:00
committed by José Rebelo
parent 8c91b5dd3f
commit 6728e87393
6 changed files with 150 additions and 4 deletions
@@ -645,4 +645,8 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_EVEN_REALITIES_SCREEN_HEIGHT = "pref_even_realities_g1_screen_height";
public static final String PREF_EVEN_REALITIES_SCREEN_DEPTH = "pref_even_realities_g1_screen_depth";
public static final String PREF_EVEN_REALITIES_SCREEN_ACTIVATION_ANGLE = "pref_even_realities_g1_screen_activation_angle";
public static final String PREF_DEVICE_STRESS_TEST_CONNECT_COUNT = "pref_device_stress_test_connect_count";
public static final String PREF_DEVICE_STRESS_TEST_CONNECT_PARALLEL = "pref_device_stress_test_connect_parallel";
public static final String PREF_DEVICE_STRESS_TEST_DISPOSE = "pref_device_stress_test_dispose";
}
@@ -69,6 +69,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractPreferenceFragment;
@@ -1528,6 +1529,12 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i
R.xml.devicesettings_gatt_synchronous_writes
);
}
if(BuildConfig.DEBUG) {
deviceSpecificSettings.addRootScreen(
DeviceSpecificSettingsScreen.DEVELOPER,
R.xml.devicesettings_stress_test
);
}
}
final DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer = coordinator.getDeviceSpecificSettingsCustomizer(device);
@@ -60,6 +60,7 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.BuildConfig;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.GBException;
import nodomain.freeyourgadget.gadgetbridge.R;
@@ -113,6 +114,9 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.language.LanguageUtils;
import nodomain.freeyourgadget.gadgetbridge.util.language.Transliterator;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_STRESS_TEST_CONNECT_COUNT;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_STRESS_TEST_CONNECT_PARALLEL;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DEVICE_STRESS_TEST_DISPOSE;
import static nodomain.freeyourgadget.gadgetbridge.model.DeviceService.*;
public class DeviceCommunicationService extends Service implements SharedPreferences.OnSharedPreferenceChangeListener {
@@ -695,7 +699,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
if (deviceSupport != null && !deviceSupport.canReconnect()) {
try {
LOG.debug("connectToDevice - {} dispose device support", deviceAddress);
deviceSupport.dispose();
if (BuildConfig.DEBUG) {
stressTestDispose(deviceSupport);
} else {
deviceSupport.dispose();
}
} catch (final Exception e) {
LOG.error("connectToDevice - {} failed to dispose device support",
deviceAddress, e);
@@ -717,7 +725,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
} else {
deviceSupport.setAutoReconnect(autoReconnect);
deviceSupport.setScanReconnect(reconnectViaScan);
connected = deviceSupport.connect();
if (BuildConfig.DEBUG) {
connected = stressTestConnect(deviceSupport);
} else {
connected = deviceSupport.connect();
}
}
LOG.debug("connectToDevice - {} connected:{} firstTime:{}", deviceAddress,
connected, firstTime);
@@ -1193,7 +1205,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
DeviceStruct struct = getDeviceStruct(device);
DeviceSupport support = struct.getDeviceSupport();
if(support != null){
support.dispose();
if (BuildConfig.DEBUG) {
stressTestDispose(support);
} else {
support.dispose();
}
}
struct.setDeviceSupport(null);
}
@@ -1607,4 +1623,76 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
return devices;
}
private static void stressTestDispose(DeviceSupport deviceSupport) {
SharedPreferences pref = GBApplication.getDeviceSpecificSharedPrefs(deviceSupport.getDevice().getAddress());
final boolean extra = pref.getBoolean(PREF_DEVICE_STRESS_TEST_DISPOSE, false);
LOG.debug("stress test - dispose(normal)");
deviceSupport.dispose();
if (extra) {
LOG.debug("stress test - dispose(extra)");
deviceSupport.dispose();
}
}
private static boolean stressTestConnect(DeviceSupport deviceSupport) {
SharedPreferences pref = GBApplication.getDeviceSpecificSharedPrefs(deviceSupport.getDevice().getAddress());
final int extras = pref.getInt(PREF_DEVICE_STRESS_TEST_CONNECT_COUNT, 0);
final boolean parallel = pref.getBoolean(PREF_DEVICE_STRESS_TEST_CONNECT_PARALLEL, false);
LOG.debug("stress test - connect() extras:{} parallel:{}", extras, parallel);
StressTestConnect[] testers = new StressTestConnect[extras+1];
for (int i = 0; i < testers.length; i++) {
testers[i] = new StressTestConnect(i, deviceSupport);
}
if (parallel) {
// parallel calls
for (int i = 0; i < testers.length; i++) {
testers[i].start();
}
for (int i = testers.length - 1; 0 <= i; i--) {
// reverse order to increase chances that something "interesting" happens
try {
testers[i].join(0L);
} catch (final Throwable t) {
LOG.debug("stress test - connect(#{}) join exception", i, t);
}
}
LOG.debug("stress test - connect() parallel => {}", testers[0].result);
return testers[0].result;
} else {
// serial calls
for (int i = 0; i < testers.length; i++) {
testers[i].run();
}
LOG.debug("stress test - connect() serial => {}", testers[0].result);
return testers[0].result;
}
}
private static class StressTestConnect extends Thread {
private final int i;
private final DeviceSupport support;
boolean result;
StressTestConnect(int index, DeviceSupport deviceSupport) {
i = index;
support = deviceSupport;
}
@Override
public void run() {
LOG.debug("stress test - connect(#{})", i);
try {
result = support.connect();
LOG.debug("stress test - connect(#{}) => {}", i, result);
} catch (Throwable t) {
LOG.error("stress test - connect(#{}) exception", i, t);
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M240,880L240,710Q201,693 171.5,664.5Q142,636 121.5,600Q101,564 90.5,523Q80,482 80,440Q80,282 192,181Q304,80 480,80Q656,80 768,181Q880,282 880,440Q880,482 869.5,523Q859,564 838.5,600Q818,636 788.5,664.5Q759,693 720,710L720,880L240,880ZM320,800L360,800L360,720L440,720L440,800L520,800L520,720L600,720L600,800L640,800L640,658Q678,649 707.5,628Q737,607 757.5,578Q778,549 789,514Q800,479 800,440Q800,315 711.5,237.5Q623,160 480,160Q337,160 248.5,237.5Q160,315 160,440Q160,479 171,514Q182,549 202.5,578Q223,607 253,628Q283,649 320,658L320,800ZM420,600L540,600L480,480L420,600ZM340,520Q373,520 396.5,496.5Q420,473 420,440Q420,407 396.5,383.5Q373,360 340,360Q307,360 283.5,383.5Q260,407 260,440Q260,473 283.5,496.5Q307,520 340,520ZM620,520Q653,520 676.5,496.5Q700,473 700,440Q700,407 676.5,383.5Q653,360 620,360Q587,360 563.5,383.5Q540,407 540,440Q540,473 563.5,496.5Q587,520 620,520ZM480,800L480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800Q480,800 480,800L480,800L480,800L480,800L480,800L480,800L480,800L480,800L480,800L480,800Z"/>
</vector>
+8 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation" tools:language="en">
<string name="application_name_generic">Gadgetbridge</string>
<string name="title_activity_controlcenter_generic">Gadgetbridge</string>
<string name="about_activity_title_generic">About Gadgetbridge</string>
@@ -628,6 +628,13 @@
<string name="pref_auto_fetch_limit_fetches">Minimum time between fetches</string>
<string name="pref_auto_fetch_limit_fetches_summary">Fetches every %d minutes</string>
<!-- developer/debug preferences-->
<string name="device_stress_test_category" translatable="false">DANGEROUS Stress Test</string>
<string name="device_stress_test_connect_count_summary" translatable="false">DeviceCommunicationService executes additional DeviceSupport.connect() calls</string>
<string name="device_stress_test_connect_count_title" translatable="false">additional connect()</string>
<string name="device_stress_test_connect_parallel_summary" translatable="false">DeviceCommunicationService executes the additional DeviceSupport.connect() calls in parallel</string>
<string name="device_stress_test_connect_parallel_title" translatable="false">parallel connect()</string>
<string name="device_stress_test_dispose_summary" translatable="false">DeviceCommunicationService calls DeviceSupport.dispose() a second time</string>
<string name="device_stress_test_dispose_title" translatable="false">repeat dispose()</string>
<string name="prefs_title_gatt_settings">Generic Attribute Profile</string>
<string name="gatt_synchronous_writes_title">Synchronous GATT writes</string>
<string name="gatt_synchronous_writes_summary">Prefer synchronous GATT writes over asynchronous writes if a characteristic supports both.</string>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="@string/device_stress_test_category" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_skull"
android:key="pref_device_stress_test_dispose"
android:layout="@layout/preference_checkbox"
android:summary="@string/device_stress_test_dispose_summary"
android:title="@string/device_stress_test_dispose_title" />
<SeekBarPreference
android:defaultValue="0"
android:icon="@drawable/ic_skull"
android:key="pref_device_stress_test_connect_count"
android:max="20"
android:summary="@string/device_stress_test_connect_count_summary"
android:title="@string/device_stress_test_connect_count_title"
app:showSeekBarValue="true" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_skull"
android:key="pref_device_stress_test_connect_parallel"
android:layout="@layout/preference_checkbox"
android:summary="@string/device_stress_test_connect_parallel_summary"
android:title="@string/device_stress_test_connect_parallel_title" />
</PreferenceScreen>