android: tile/control activity - no bind leaks, no callbacks after destroy

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
DenozordecandClaude Opus 5.5 committed 2026-10-10 09:09:40 +07:00
1 parent f2e4ffe860
commit 01c398f2f7
2 files changed
+24 -7

No files matched your search

@@ -79,6 +79,7 @@ public class VpnProfileControlActivity extends AppCompatActivity implements VpnD
private Bundle mProfileInfo;
private boolean mWaitingForResult;
private VpnStateService mService;
private boolean mBound;
/* profile lookups are SQLite queries and must not block the main thread */
private final ExecutorService mProfileExecutor = Executors.newSingleThreadExecutor();
private final ServiceConnection mServiceConnection = new ServiceConnection()
@@ -140,8 +141,8 @@ public class VpnProfileControlActivity extends AppCompatActivity implements VpnD
{
mWaitingForResult = savedInstanceState.getBoolean(WAITING_FOR_RESULT, false);
}
this.bindService(new Intent(this, VpnStateService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
mBound = this.bindService(new Intent(this, VpnStateService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
VpnDialogBus.INSTANCE.setListener(this);
VpnDialogsKt.installVpnDialogHost(this);
}
@@ -159,10 +160,12 @@ public class VpnProfileControlActivity extends AppCompatActivity implements VpnD
super.onDestroy();
VpnDialogBus.INSTANCE.setListener(null);
mProfileExecutor.shutdown();
if (mService != null)
if (mBound)
{
this.unbindService(mServiceConnection);
mBound = false;
}
mService = null;
}
/**
@@ -429,6 +432,10 @@ public class VpnProfileControlActivity extends AppCompatActivity implements VpnD
mProfileExecutor.execute(() -> {
VpnProfile profile = loadProfile(uuid);
runOnUiThread(() -> {
if (isFinishing() || isDestroyed())
{ /* rotated or finished while the query ran */
return;
}
if (profile != null)
{
startVpnProfile(profile);
@@ -479,7 +486,12 @@ public class VpnProfileControlActivity extends AppCompatActivity implements VpnD
final Intent startIntent = intent;
mProfileExecutor.execute(() -> {
VpnProfile profile = loadProfile(uuid);
runOnUiThread(() -> handleDisconnect(startIntent, profile));
runOnUiThread(() -> {
if (!isFinishing() && !isDestroyed())
{
handleDisconnect(startIntent, profile);
}
});
});
}
@@ -45,6 +45,7 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
private boolean mListening;
private VpnProfileDataSource mDataSource;
private VpnStateService mService;
private boolean mBound;
private final ServiceConnection mServiceConnection = new ServiceConnection()
{
@Override
@@ -71,8 +72,8 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
super.onCreate();
Context context = getApplicationContext();
context.bindService(new Intent(context, VpnStateService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
mBound = context.bindService(new Intent(context, VpnStateService.class),
mServiceConnection, Service.BIND_AUTO_CREATE);
mDataSource = new VpnProfileSource(this);
mDataSource.open();
@@ -83,10 +84,14 @@ public class VpnTileService extends TileService implements VpnStateService.VpnSt
{
super.onDestroy();
if (mService != null)
/* the binding lives on the application context: unbind even if the
* service never connected, or it leaks for the life of the process */
if (mBound)
{
getApplicationContext().unbindService(mServiceConnection);
mBound = false;
}
mService = null;
mDataSource.close();
mDataSource = null;
}