Skip to content

Commit a146b40

Browse files
authored
imporvement fixes (#8546)
* Android improvments * startup changes and remove sentry * code review updates * code review updates
1 parent f37d138 commit a146b40

21 files changed

Lines changed: 350 additions & 292 deletions

android/app/src/main/kotlin/org/getlantern/lantern/service/LanternVpnService.kt

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import org.getlantern.lantern.BuildConfig
2020
import org.getlantern.lantern.MainActivity
2121
import org.getlantern.lantern.constant.VPNStatus
2222
import org.getlantern.lantern.notification.NotificationHelper
23+
import org.getlantern.lantern.service.LanternVpnService.Companion.ACTION_STOP_VPN
2324
import org.getlantern.lantern.utils.AppLogger
2425
import org.getlantern.lantern.utils.DeviceUtil
2526
import org.getlantern.lantern.utils.FlutterEventListener
@@ -146,8 +147,20 @@ class LanternVpnService :
146147
closeTunInterface()
147148
// Clean up synchronously — cannot use serviceScope here because
148149
// it is cancelled in the finally block below.
149-
runCatching { Mobile.stopVPN() }
150-
.onFailure { e -> AppLogger.e(TAG, "Mobile.stopVPN() failed during destroy", e) }
150+
// Only call stopVPN if Radiance IPC is actually running; calling it before
151+
// setup completes results in a misleading "IPC not running" error.
152+
if (Mobile.isRadianceConnected()) {
153+
runCatching { Mobile.stopVPN() }
154+
.onFailure { e ->
155+
AppLogger.e(
156+
TAG,
157+
"Mobile.stopVPN() failed during destroy",
158+
e
159+
)
160+
}
161+
} else {
162+
AppLogger.d(TAG, "Skipping stopVPN — Radiance IPC not running")
163+
}
151164
runCatching {
152165
runBlocking(Dispatchers.IO) { DefaultNetworkMonitor.stop() }
153166
}.onFailure { e ->
@@ -263,6 +276,14 @@ class LanternVpnService :
263276
// VPN service starts, replaced by connected notification on success.
264277
notificationHelper.showStartingVPNConnectedNotification(this@LanternVpnService)
265278
runCatching {
279+
// Radiance is pre-warmed via ACTION_START_RADIANCE, but as a background
280+
// service it may have been killed by the OS before setup completed.
281+
// Re-run setup here under the foreground notification so it is guaranteed
282+
// to finish before we attempt to start the VPN tunnel.
283+
if (!Mobile.isRadianceConnected()) {
284+
AppLogger.d(TAG, "Radiance not ready, setting up before VPN start")
285+
Mobile.setupRadiance(opts(), flutterEventListener)
286+
}
266287
DefaultNetworkMonitor.start()
267288
connect()
268289
VpnStatusManager.postVPNStatus(VPNStatus.Connected)
@@ -296,7 +317,13 @@ class LanternVpnService :
296317
private suspend fun stopVPNTunnel() {
297318
try {
298319
closeTunInterface()
299-
runCatching { Mobile.stopVPN() }
320+
runCatching {
321+
if (!Mobile.isVPNConnected()) {
322+
AppLogger.d(TAG, "VPN is not connected, skipping stopVPN")
323+
return@runCatching
324+
}
325+
Mobile.stopVPN()
326+
}
300327
.onFailure { e -> AppLogger.e(TAG, "Mobile.stopVPN() failed", e) }
301328

302329
runCatching { DefaultNetworkMonitor.stop() }

android/app/src/main/kotlin/org/getlantern/lantern/utils/LanternLogger.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,16 @@ object AppLogger {
5151
writeAsync("INFO", tag, message)
5252
}
5353

54-
fun w(tag: String, message: String, tr: Throwable? = null) {
55-
Log.w(tag, message, tr)
56-
writeAsync("WARN", tag, message)
54+
fun w(tag: String, message: String, throwable: Throwable? = null) {
55+
Log.w(tag, message, throwable)
56+
val errorMessage = buildString {
57+
append(message)
58+
if (throwable != null) {
59+
append("\n")
60+
append(throwable.stackTraceToString())
61+
}
62+
}
63+
writeAsync("WARN", tag, errorMessage)
5764
}
5865

5966
fun e(tag: String, message: String, throwable: Throwable? = null) {

ios/Podfile.lock

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,6 @@ PODS:
2626
- OrderedSet (6.0.3)
2727
- package_info_plus (0.4.5):
2828
- Flutter
29-
- Sentry/HybridSDK (8.56.2)
30-
- sentry_flutter (9.14.0):
31-
- Flutter
32-
- FlutterMacOS
33-
- Sentry/HybridSDK (= 8.56.2)
3429
- share_plus (0.0.1):
3530
- Flutter
3631
- shared_preferences_foundation (0.0.1):
@@ -112,7 +107,6 @@ DEPENDENCIES:
112107
- integration_test (from `.symlinks/plugins/integration_test/ios`)
113108
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
114109
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
115-
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
116110
- share_plus (from `.symlinks/plugins/share_plus/ios`)
117111
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
118112
- store_checker (from `.symlinks/plugins/store_checker/ios`)
@@ -122,7 +116,6 @@ DEPENDENCIES:
122116
SPEC REPOS:
123117
https://github.com/CocoaPods/Specs.git:
124118
- OrderedSet
125-
- Sentry
126119
- Stripe
127120
- StripeApplePay
128121
- StripeCore
@@ -154,8 +147,6 @@ EXTERNAL SOURCES:
154147
:path: ".symlinks/plugins/mobile_scanner/darwin"
155148
package_info_plus:
156149
:path: ".symlinks/plugins/package_info_plus/ios"
157-
sentry_flutter:
158-
:path: ".symlinks/plugins/sentry_flutter/ios"
159150
share_plus:
160151
:path: ".symlinks/plugins/share_plus/ios"
161152
shared_preferences_foundation:
@@ -179,8 +170,6 @@ SPEC CHECKSUMS:
179170
mobile_scanner: 77265f3dc8d580810e91849d4a0811a90467ed5e
180171
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
181172
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
182-
Sentry: b53951377b78e21a734f5dc8318e333dbfc682d7
183-
sentry_flutter: a9f60d84bfddcae9a0ebf834547702f8854766a1
184173
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
185174
shared_preferences_foundation: 5086985c1d43c5ba4d5e69a4e8083a389e2909e6
186175
store_checker: a7be624ac44a4ba88e7917e71afe8433f197161e

lib/core/common/app_secrets.dart

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import 'dart:io';
2-
31
import 'package:flutter_dotenv/flutter_dotenv.dart';
42

53
class AppSecrets {
@@ -18,13 +16,4 @@ class AppSecrets {
1816

1917
static String get lanternPackageName => "org.getlantern.lantern";
2018

21-
static String dnsConfig() {
22-
if (Platform.isAndroid) {
23-
return "https://4753d78f885f4b79a497435907ce4210@o75725.ingest.sentry.io/5850353";
24-
}
25-
if (Platform.isIOS) {
26-
return "https://c14296fdf5a6be272e1ecbdb7cb23f76@o75725.ingest.sentry.io/4506081382694912";
27-
}
28-
return "https://7397d9db6836eb599f41f2c496dee648@o75725.ingest.us.sentry.io/4507734480912384";
29-
}
3019
}

lib/core/models/feature_flags.dart

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
enum FeatureFlag {
2-
sentry('sentry'),
32
privateGcp('private.gcp'),
4-
autoUpdateEnabled('autoUpdateEnabled'),
53
metrics('otel.metrics'),
6-
traces('otel.traces');
4+
traces('otel.traces'),
5+
autoUpdateEnabled('autoUpdateEnabled');
76

87
final String key;
98

lib/core/services/injection_container.dart

Lines changed: 59 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,62 +17,76 @@ import 'logger_service.dart';
1717
final GetIt sl = GetIt.instance;
1818

1919
Future<void> injectServices() async {
20-
sl.registerLazySingleton<Updater>(() => Updater());
21-
20+
appLogger.debug('Initializing storage services...');
21+
final storage = LocalStorageService();
22+
final storeUtils = StoreUtils();
2223
try {
23-
appLogger.info("Initializing LocalStorageService");
24-
final storage = LocalStorageService();
25-
await storage.init();
26-
sl.registerSingleton<LocalStorageService>(storage);
24+
await Future.wait([storage.init(), storeUtils.init()]);
25+
appLogger.debug('Storage services initialized');
2726

28-
appLogger.info("Initializing StoreUtils");
29-
final storeUtils = StoreUtils();
30-
await storeUtils.init();
27+
sl.registerSingleton<LocalStorageService>(storage);
3128
sl.registerSingleton<StoreUtils>(storeUtils);
29+
} catch (e, st) {
30+
appLogger.error('Storage init failed', e, st);
31+
rethrow;
32+
}
3233

33-
sl.registerLazySingleton(() => AppRouter());
34-
sl.registerLazySingleton(() => AppPurchase());
35-
sl<AppPurchase>().init();
36-
sl.registerLazySingleton<DeepLinkCallbackManager>(
37-
() => DeepLinkCallbackManager());
38-
// We want to make sure the platform service and FFI service are
39-
// initialized as early as possible so we can communicate with
40-
// native code on different platforms.
41-
final ps = LanternPlatformService();
42-
await ps.init();
43-
sl.registerSingleton<LanternPlatformService>(ps);
34+
sl.registerLazySingleton<Updater>(() => Updater());
35+
sl.registerLazySingleton<AppRouter>(() => AppRouter());
36+
sl.registerLazySingleton<DeepLinkCallbackManager>(
37+
() => DeepLinkCallbackManager(),
38+
);
4439

45-
if (PlatformUtils.isFFISupported) {
46-
sl.registerLazySingleton(() => LanternFFIService());
47-
await sl<LanternFFIService>().init();
48-
} else {
49-
sl.registerLazySingleton<LanternFFIService>(
50-
() => MockLanternFFIService());
51-
}
52-
sl.registerLazySingleton<LanternService>(
53-
() => LanternService(
40+
appLogger.debug('Initializing AppPurchase...');
41+
final appPurchase = AppPurchase();
42+
appPurchase.init();
43+
sl.registerSingleton<AppPurchase>(appPurchase);
44+
appLogger.debug('AppPurchase initialized');
45+
46+
sl.registerSingleton<LanternPlatformService>(LanternPlatformService());
47+
sl.registerSingleton<LanternFFIService>(
48+
PlatformUtils.isFFISupported
49+
? LanternFFIService()
50+
: MockLanternFFIService(),
51+
);
52+
53+
sl.registerSingletonAsync<LanternService>(
54+
() async {
55+
final service = LanternService(
5456
ffiService: sl<LanternFFIService>(),
5557
platformService: sl<LanternPlatformService>(),
5658
appPurchase: sl<AppPurchase>(),
57-
),
58-
);
59+
);
60+
try {
61+
await service.init();
62+
appLogger.debug('LanternService initialized');
63+
} catch (e, st) {
64+
appLogger.error('LanternService init failed', e, st);
65+
}
66+
return service;
67+
},
68+
);
5969

70+
appLogger.debug('Initializing notification/Stripe services...');
71+
final notificationService = NotificationService();
72+
try {
6073
if (PlatformUtils.isAndroid) {
61-
sl.registerSingletonAsync<StripeService>(() async {
62-
appLogger.info("Initializing StripeService");
63-
final stripeService = StripeService();
64-
await stripeService.initialize();
65-
return stripeService;
66-
});
67-
}
68-
sl.registerSingletonAsync<NotificationService>(() async {
69-
appLogger.info("Initializing NotificationService");
70-
final notificationService = NotificationService();
74+
final stripeService = StripeService();
75+
await Future.wait([
76+
notificationService.init(),
77+
stripeService.initialize(),
78+
]);
79+
sl.registerSingleton<StripeService>(stripeService);
80+
appLogger.debug('StripeService initialized');
81+
} else {
7182
await notificationService.init();
72-
return notificationService;
73-
});
74-
appLogger.info("All services injected ✅");
83+
}
7584
} catch (e, st) {
76-
appLogger.error("Error during service injection", e, st);
85+
appLogger.error('Notification/Stripe init failed', e, st);
7786
}
87+
sl.registerSingleton<NotificationService>(notificationService);
88+
appLogger.debug('NotificationService initialized');
89+
90+
await sl.allReady();
91+
appLogger.info('All services injected ✅');
7892
}

lib/core/updater/updater.dart

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,67 @@
11
import 'dart:async';
2+
import 'dart:convert';
23
import 'dart:io';
34

45
import 'package:auto_updater/auto_updater.dart';
56
import 'package:flutter/foundation.dart';
67
import 'package:lantern/core/common/app_build_info.dart';
78
import 'package:lantern/core/common/common.dart';
89
import 'package:lantern/core/models/feature_flags.dart';
10+
import 'package:lantern/core/services/injection_container.dart';
11+
import 'package:lantern/lantern/lantern_service.dart';
912

1013
class Updater {
1114
bool _initialized = false;
1215

1316
bool get _isSupportedPlatform =>
1417
!kIsWeb && (Platform.isMacOS || Platform.isWindows);
1518

16-
Future<void> init({required Map<String, dynamic> flags}) async {
19+
Future<void> init() async {
1720
if (_initialized) return;
1821
_initialized = true;
1922

2023
if (kDebugMode) return;
2124
if (!_isSupportedPlatform) return;
2225

23-
final enabled = flags.getBool(FeatureFlag.autoUpdateEnabled);
24-
if (!enabled) return;
26+
final flagResult = await sl<LanternService>().featureFlag();
27+
final flags = flagResult.fold((_) => <String, dynamic>{}, (jsonStr) {
28+
try {
29+
return json.decode(jsonStr) as Map<String, dynamic>;
30+
} catch (_) {
31+
return <String, dynamic>{};
32+
}
33+
});
34+
35+
if (!flags.getBool(FeatureFlag.autoUpdateEnabled, defaultValue: true)) {
36+
appLogger.info('autoUpdater disabled by feature flag');
37+
return;
38+
}
2539

2640
final buildType = AppBuildInfo.buildType;
2741
final feedUrl = AppUrls.appcastFor(buildType);
2842

2943
try {
30-
final _updater = AutoUpdater.instance;
31-
await _updater.setFeedURL(feedUrl);
32-
await _updater.setScheduledCheckInterval(3600);
44+
final updater = AutoUpdater.instance;
45+
await updater.setFeedURL(feedUrl);
46+
await updater.setScheduledCheckInterval(3600);
3347

3448
// Background check after startup (avoid modal immediately on launch)
3549
const firstPromptDelay = Duration(seconds: 45);
36-
unawaited(Future<void>.delayed(firstPromptDelay, () async {
37-
try {
38-
await _updater.checkForUpdates(inBackground: true);
39-
} catch (e, st) {
40-
appLogger.error('Failed to check for auto-updates: $e', st);
41-
}
42-
}));
43-
44-
appLogger
45-
.info('autoUpdater configured. buildType=$buildType url=$feedUrl');
50+
unawaited(
51+
Future<void>.delayed(firstPromptDelay, () async {
52+
try {
53+
await updater.checkForUpdates(inBackground: true);
54+
} catch (e, st) {
55+
appLogger.error('Failed to check for auto-updates', e, st);
56+
}
57+
}),
58+
);
59+
60+
appLogger.info(
61+
'autoUpdater configured. buildType=$buildType url=$feedUrl',
62+
);
4663
} catch (e, st) {
47-
appLogger.error('Failed to configure autoUpdater: $e', st);
64+
appLogger.error('Failed to configure autoUpdater:', e, st);
4865
}
4966
}
5067

lib/lantern/lantern_core_service.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import '../core/services/app_purchase.dart';
1414

1515
/// LanternCoreService has all method that interact with lantern-core services
1616
abstract class LanternCoreService {
17+
Future<void> init();
18+
1719
///App Methods
1820
Future<Either<Failure, Unit>> updateLocal(String locale);
1921

lib/lantern/lantern_ffi_service.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class LanternFFIService implements LanternCoreService {
116116
return candidates.first;
117117
}
118118

119+
@override
119120
Future<void> init() async {
120121
// Set safe defaults up front so callers always have something to listen to.
121122
_status = _defaultStatusStream();
@@ -1694,4 +1695,7 @@ class FfiPlatformPaths {
16941695
}
16951696
}
16961697

1697-
class MockLanternFFIService extends LanternFFIService {}
1698+
class MockLanternFFIService extends LanternFFIService {
1699+
@override
1700+
Future<void> init() async {}
1701+
}

0 commit comments

Comments
 (0)