-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
[mob][photos] Add iOS upload grace window and bg handoff #9810
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: prtk/bg-patches
Are you sure you want to change the base?
Changes from 7 commits
c71eea4
6f163ca
80788ec
d7cd73a
4bec369
ec7ed51
177b70a
e45da7d
6c6787d
d60d117
1c46d45
e2682f4
ce0eb76
f7447d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| import "dart:async"; | ||
| import "dart:io"; | ||
|
|
||
| import "package:grace_window_ios/grace_window_ios.dart"; | ||
| import "package:logging/logging.dart"; | ||
| import "package:photos/db/upload_locks_db.dart"; | ||
| import "package:photos/service_locator.dart"; | ||
| import "package:photos/utils/bg_task_utils.dart"; | ||
| import "package:photos/utils/file_uploader.dart"; | ||
| import "package:shared_preferences/shared_preferences.dart"; | ||
|
|
||
| const _keyIOSUploadGraceActive = "ios_bg_upload_grace_active"; | ||
|
|
||
| final _logger = Logger("UploadBackgroundCoordinator"); | ||
|
|
||
| Future<void> onUploadAppBackground() async { | ||
| if (!Platform.isIOS || !flagService.enableIOSBackgroundHandoff) { | ||
| return; | ||
| } | ||
|
|
||
| if (FileUploader.instance.hasActiveUploads) { | ||
| if (!flagService.enableIOSBackgroundGraceWindow) { | ||
| return; | ||
| } | ||
|
|
||
| final prefs = await SharedPreferences.getInstance(); | ||
| if (prefs.getBool(_keyIOSUploadGraceActive) ?? false) { | ||
| return; | ||
| } | ||
|
|
||
| _logger.info("Starting iOS upload grace window"); | ||
| await GraceWindowIos.beginGraceWindow("ente-upload-grace-window"); | ||
| await prefs.setBool(_keyIOSUploadGraceActive, true); | ||
| unawaited(_waitForGraceWindowExpiration()); | ||
| return; | ||
| } | ||
|
|
||
| if (await BgTaskUtils.isIOSBackupEligible()) { | ||
| await BgTaskUtils.scheduleIOSBackgroundProcessingTask( | ||
| source: "appBackground:maintenance", | ||
| initialDelay: BgTaskUtils.maintenanceDelay(), | ||
| reason: BgTaskUtils.iOSBackgroundProcessingReasonMaintenance, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| Future<void> onUploadAppForeground() async { | ||
| if (!Platform.isIOS || !flagService.enableIOSBackgroundHandoff) { | ||
| return; | ||
| } | ||
|
|
||
| if (!flagService.enableIOSBackgroundGraceWindow) { | ||
| await BgTaskUtils.cancelIOSBackgroundProcessingTask( | ||
| source: "appForeground", | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| final prefs = await SharedPreferences.getInstance(); | ||
| final graceWasActive = prefs.getBool(_keyIOSUploadGraceActive) ?? false; | ||
|
|
||
| if (graceWasActive) { | ||
| final cutoffMicros = DateTime.now().microsecondsSinceEpoch; | ||
|
|
||
| _logger.info("Finishing iOS upload grace window and reconciling uploads"); | ||
| await GraceWindowIos.endGraceWindow(); | ||
| final didExpireGraceWindow = await GraceWindowIos.consumeExpiredState(); | ||
| await prefs.remove(_keyIOSUploadGraceActive); | ||
| if (didExpireGraceWindow) { | ||
| _logger.info( | ||
| "Grace window expiration was observed natively before foreground recovery", | ||
| ); | ||
| await UploadLocksDB.instance.releaseLocksAcquiredByOwnerBefore( | ||
| ProcessType.foreground.toString(), | ||
| cutoffMicros, | ||
| ); | ||
| } | ||
| await FileUploader.instance.reconcileAfterBackground(); | ||
| } | ||
|
|
||
| await BgTaskUtils.cancelIOSBackgroundProcessingTask( | ||
| source: "appForeground", | ||
| ); | ||
| } | ||
|
|
||
| /// Best-effort same-process expiration detection via a pending MethodChannel | ||
| /// call. Swift holds the result and completes it when the native expiration | ||
| /// handler fires. If this path misses (e.g. process suspended before | ||
| /// delivery), [onUploadAppForeground] catches it via [consumeExpiredState]. | ||
| Future<void> _waitForGraceWindowExpiration() async { | ||
| final expired = await GraceWindowIos.awaitExpiration(); | ||
| if (!expired) { | ||
|
Comment on lines
+104
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This flow relies on Useful? React with 👍 / 👎. |
||
| return; | ||
| } | ||
|
|
||
| final cutoffMicros = DateTime.now().microsecondsSinceEpoch; | ||
|
|
||
| _logger.info("iOS upload grace window expired (via pending call)"); | ||
|
|
||
| // Consume the durable marker *before* releasing locks so that | ||
| // onUploadAppForeground (which can run between any two awaits here) won't | ||
| // see the marker and do a second lock release against freshly | ||
| // reacquired locks. | ||
| await GraceWindowIos.consumeExpiredState(); | ||
prateekmedia marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| await BgTaskUtils.scheduleIOSBackgroundProcessingTask( | ||
| source: "graceWindowExpired", | ||
| initialDelay: BgTaskUtils.continuationDelay(), | ||
| reason: BgTaskUtils.iOSBackgroundProcessingReasonContinuation, | ||
| ); | ||
|
|
||
| await UploadLocksDB.instance.releaseLocksAcquiredByOwnerBefore( | ||
| ProcessType.foreground.toString(), | ||
| cutoffMicros, | ||
| ); | ||
|
|
||
| final prefs = await SharedPreferences.getInstance(); | ||
| await prefs.remove(_keyIOSUploadGraceActive); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.