-
Notifications
You must be signed in to change notification settings - Fork 77
Expand file tree
/
Copy pathhandler.dart
More file actions
138 lines (130 loc) · 5.95 KB
/
Copy pathhandler.dart
File metadata and controls
138 lines (130 loc) · 5.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import 'package:bb_mobile/core/background_tasks/tasks.dart';
import 'package:bb_mobile/core/storage/sqlite_database.dart';
import 'package:bb_mobile/core/storage/data/datasources/key_value_storage/keychain_locked_exception.dart';
import 'package:bb_mobile/core/storage/database_encryption_key_store.dart';
import 'package:bb_mobile/core/swaps/domain/usecases/process_ongoing_swaps_usecase.dart';
import 'package:bb_mobile/core/utils/logger.dart' show log;
import 'package:bb_mobile/core/wallet/domain/usecases/get_wallets_usecase.dart';
import 'package:bb_mobile/core/wallet/domain/usecases/sync_wallet_usecase.dart';
import 'package:bb_mobile/locator.dart';
import 'package:bb_mobile/main.dart';
import 'package:get_it/get_it.dart';
import 'package:workmanager/workmanager.dart';
@pragma('vm:entry-point')
void backgroundTasksHandler() {
Workmanager().executeTask((task, inputData) async {
return await tasksHandler(task);
});
}
Future<bool> tasksHandler(String task) async {
final startTime = DateTime.now();
await Bull.initLogs(background: true);
// Note: `Report.init` is intentionally NOT called here. The BG isolate
// has its own Dart-side Sentry hub but the native plugin (Sentry
// Android / iOS) is a process-level singleton. Calling
// `SentryFlutter.init` again from the BG isolate would re-init the
// native SDK and stomp the main isolate's crash-handler config when
// both isolates are alive (foreground app + BG task firing). The
// trade-off: BG-task failures are captured to the on-disk TSV log
// only; they don't reach Sentry. If/when we need BG observability,
// switch to envelope-forwarding (write events to a queue here, ship
// them next time the main isolate boots).
await Bull.initFlutterRustBridgeDependencies();
try {
// `loadExisting` never creates a key: a background task can fire
// before the user has ever opened the app on this install, and
// minting a key here would either race the foreground boot or
// write a key that doesn't match an existing encrypted database.
final String? databaseKey;
try {
databaseKey = await DatabaseEncryptionKeyStore.loadExisting();
} on KeychainLockedException {
// iOS can fire a BGTask before the device has been unlocked since
// boot, at which point a `first_unlock_this_device` item is
// unreadable. That is "come back later", not "the key is gone" —
// return false so workmanager reschedules, and touch nothing.
log.warning('Background task skipped: keychain locked, will retry');
return false;
}
if (databaseKey == null) {
log.warning('Background task skipped before database key initialization');
return false;
}
final driftIsolate = await SqliteDatabase.createIsolateWithSpawn(
databaseKey,
);
final sqlite = SqliteDatabase(
await driftIsolate.connect(singleClientMode: true),
);
final locator = GetIt.asNewInstance();
// No payjoin recovery here: only the foreground composition root resumes
// sessions. A stale persisted schedule firing before the first foreground
// launch of this build would otherwise open payjoin.sqlite in this
// isolate, run the legacy migration and the full recovery sweep —
// concurrently with the foreground engine on the same database, inside a
// ~30s iOS background budget.
await AppLocator.setup(
locator,
sqlite,
databaseKey: databaseKey,
startPayjoinRecovery: false,
);
final syncWalletUsecase = locator<SyncWalletUsecase>();
final getWalletsUsecase = locator<GetWalletsUsecase>();
final processOngoingSwapsUsecase = locator<ProcessOngoingSwapsUsecase>();
final backgroundTask = BackgroundTask.fromName(task);
switch (backgroundTask) {
case BackgroundTask.bitcoinSync:
final wallets = await getWalletsUsecase.execute(onlyBitcoin: true);
for (final wallet in wallets) {
await syncWalletUsecase.execute(wallet);
log.fine('Bitcoin Wallet ${wallet.id} synced');
}
case BackgroundTask.liquidSync:
final wallets = await getWalletsUsecase.execute(onlyLiquid: true);
for (final wallet in wallets) {
await syncWalletUsecase.execute(wallet);
log.fine('Liquid Wallet ${wallet.id} synced');
}
case BackgroundTask.swapsSync:
final wallets = await getWalletsUsecase.execute();
if (wallets.isEmpty) {
log.warning('No wallets to sync');
} else {
// Poll + act to completion: the BG isolate dies right after this
// returns, so a websocket-based restart would never see an event.
// Bounded to respect the iOS background budget.
await processOngoingSwapsUsecase.execute().timeout(
const Duration(seconds: 25),
onTimeout: () =>
log.warning('Swaps background processing hit time budget'),
);
}
case BackgroundTask.logsPrune:
await log.prune();
}
final elapsedTime = DateTime.now().difference(startTime).inSeconds;
log.config('Background task $task completed in $elapsedTime seconds');
return Future.value(true);
} catch (e) {
log.severe(
message: 'Background task $task failed',
error: e,
trace: StackTrace.current,
);
return Future.value(false);
} finally {
// iOS tears down the BG `FlutterEngine` shortly after we return.
// Force the Dart-side IOSink buffer to disk now, otherwise any
// non-SEVERE writes (the success line above, every `log.fine`
// from the task body) are abandoned with the dying isolate and
// never appear in `bull_background_logs.tsv`. Foreground writes
// get flushed implicitly by `readLogs()`'s own `await flush()`;
// the BG sink has no equivalent trigger so it must flush itself.
//
// `log.flush()` swallows its own errors internally
// (see `_enqueue` in logger.dart), so it can't change the return
// value or throw past the `finally`.
await log.flush();
}
}