Skip to content
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

5128: Fix stuck uploads #5257

Conversation

RitikaPahwa4444
Copy link
Collaborator

Description (required)

Fixes #5128

What changes did you make and why?
Uploads often get stuck when users turn their screen off or simply navigate away from their screen, especially when the battery usage for the app is not set as "Unrestricted".

  • Changed compileSdkVersion to 33 as suggested in the documentation
  • Upgraded to the latest stable release of WorkManager
  • Passed FLAG_IMMUTABLE with PendingIntent for version code S and above. Android has made it mandatory for apps targeting S+ (API level 31 and above) to pass either FLAG_MUTABLE or FLAG_IMMUTABLE with PendingIntent (based on my logcat error).

The above changes apparently fixed stuck uploads. The uploads, however, still fail with a SocketTimeoutException.

  • Implemented retries in onResume() so that failed uploads resume as soon as the user returns to the app. The code related to this may be improved with correct usage of WorkManager, but I need to investigate its behaviour on various devices under different network and battery conditions.

Key improvements:
Uploads no longer remain stuck and resume automatically in case the user returns to the app within 2 minutes (the connection time associated with the SocketTimeoutException). After 2 minutes, a "Failed to upload" notification is shown for all the uploads but these failed uploads, too, start as soon as the user returns to the app because of the retry logic.

This may not be the best solution, so I'm adding a WIP/Draft status to it.

Further improvements I am looking forward to:
I'll be researching more about network conditions when the screen is turned off. Even if Android provides a small window to resume work under "Optimised" battery conditions, we can fix the SocketTimeout issue.

Tests performed (required)

Tested prodDebug on Moto g62 with API level 31.

@RitikaPahwa4444 RitikaPahwa4444 marked this pull request as draft July 7, 2023 13:22
@nicolas-raoul
Copy link
Member

Thanks!
I started testing it.
At first in mode "Unrestricted", as a baseline, it correctly uploaded a large set. :-)
Next I will try in optimized mode, but it might slip to after the weekend.

@nicolas-raoul
Copy link
Member

Trying in Restricted mode, I am getting failures, and retry does not seem to work:

screen-20230709-211850.mp4

Copy link
Member

@sivaraam sivaraam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just leaving some comments based on my initial observation.

@@ -540,7 +541,11 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
val intent = Intent(appContext,toClass)
return TaskStackBuilder.create(appContext).run {
addNextIntentWithParentStack(intent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
getPendingIntent(0, PendingIntent.FLAG_IMMUTABLE)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, I believe we should be keeping both the FLAG_IMMUTABLE and FLAG_UPDATE_CURRENT flags [ ref ]. Is there any reason behind not sending the FLAG_UPDATE_CURRENT here? Have you explored on what difference including / not including it makes ?

@@ -540,7 +541,11 @@ class UploadWorker(var appContext: Context, workerParams: WorkerParameters) :
val intent = Intent(appContext,toClass)
return TaskStackBuilder.create(appContext).run {
addNextIntentWithParentStack(intent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the mandate has been since Android S, can we not send the flag for VERSION_CODES.M and above since the IMMUTABLE flag is available since that version?

@RitikaPahwa4444
Copy link
Collaborator Author

Thank you for testing, I'll fix this.

I'd read about these possibilities related to PendingIntent on Stack Overflow. Since uploads were not getting stuck on older Android versions and FLAG_IMMUTABLE apparently fixed stuck uploads, I just went for the bare minimum. Will test it with the changes suggested above on my older device too. Thank you for the hints!

@RitikaPahwa4444
Copy link
Collaborator Author

I could not manage to get the retry button, it simply restarts the process on my Android 12 device. I have modified the flag and version code, though.

@@ -75,6 +76,9 @@
<string name="login_success">Login success!</string>
<string name="login_failed">Login failed!</string>
<string name="upload_failed">File not found. Please try another file.</string>
<string name="retry_limit_reached">Maximum retry limit reached! Please cancel the upload and try again</string>
<string name="unrestricted_battery_mode">Turn battery optimisations off</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a ? at the end to make it friendlier. Also, "optimization" (singular not plural) is less freightening and reflects the wording in the OS settings.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I'll use 'z' instead of 's' everywhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, I only meant about the singular. Either optimization or optimisation is fine for me 😊

@@ -75,6 +76,9 @@
<string name="login_success">Login success!</string>
<string name="login_failed">Login failed!</string>
<string name="upload_failed">File not found. Please try another file.</string>
<string name="retry_limit_reached">Maximum retry limit reached! Please cancel the upload and try again</string>
<string name="unrestricted_battery_mode">Turn battery optimisations off</string>
<string name="suggest_unrestricted_mode">Uploading more than 3 images works more reliably when the battery optimisations are turned off. Please turn battery optimisations off for the Commons app or fr.free.nrw.commons from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimisation off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimised\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimise\'.\n\nStep 5: Press \'Done\'.</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first "or fr.etc" is not needed I think

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

@nicolas-raoul nicolas-raoul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few minor suggestions

@@ -138,6 +148,9 @@ public void onCreate(Bundle savedInstanceState) {
setTitle(getString(R.string.navigation_item_explore));
setUpLoggedOutPager();
} else {
if (applicationKvStore.getBoolean("firstrun", true)) {
applicationKvStore.putBoolean("firstBigUploadSet", true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate the consistency with firstrun, but it is troubling to have firstBigUploadSet being true during small uploads.
How about renaming it to hasAlreadyLaunchedBigMultiupload and inverting true/false?
It would be easier to understand, I believe.
Thanks!

@@ -364,6 +376,24 @@ private void receiveSharedItems() {
.getQuantityString(R.plurals.upload_count_title, uploadableFiles.size(), uploadableFiles.size()));

fragments = new ArrayList<>();
// Suggest users to switch to Unrestricted battery usage mode
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Append when uploading more than a few files. That's because we have noticed that many-files uploads have a much higher probability of failing than uploads with less files.

getString(R.string.title_activity_settings),
getString(R.string.cancel),
() -> {
Intent batteryOptimisationSettingsIntent = new Intent(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment about the library you tried, explaining how it did not make the setting dialog appear on Pixel nor Xiaomi. Someone may want to revisit that in the future.

@@ -75,6 +76,9 @@
<string name="login_success">Login success!</string>
<string name="login_failed">Login failed!</string>
<string name="upload_failed">File not found. Please try another file.</string>
<string name="retry_limit_reached">Maximum retry limit reached! Please cancel the upload and try again</string>
<string name="unrestricted_battery_mode">Turn battery optimization off?</string>
<string name="suggest_unrestricted_mode">Uploading more than 3 images works more reliably when the battery optimization are turned off. Please turn battery optimization off for the Commons app from the settings for a smooth upload experience. \n\nPossible steps to turn battery optimization off:\n\nStep 1: Tap on the \'Settings\' button below.\n\nStep 2: Switch from \'Not optimized\' to \'All apps\'.\n\nStep 3: Scroll to find the Commons app or fr.free.nrw.commons.\n\nStep 4: Tap it and select \'Don\'t optimize\'.\n\nStep 5: Press \'Done\'.</string>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are turned off -> is turned off

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should have read the sentence again after removing the 's' :')

Copy link
Contributor

@kartikaykaushik14 kartikaykaushik14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work overall. Well commented and relevant TODOs added. Please check the comments.

will never be successful */
if(retries < MAX_RETRIES) {
contribution.setRetries(retries + 1);
Timber.d("Retried %d times", retries + 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be changed to "Retried upload %d times" so it's clear what we're trying to retry?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed the debug log line could say that, and also say what file, because often several files are being retried at the same time.

Copy link
Member

@sivaraam sivaraam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've left a couple of comments based on my initial look at the changes. Kindly take a look.

Also, have you checked the interaction of these changes with the "Limited Connection mode" @RitikaPahwa4444 ? Do they work as intended even after these changes ?

/**
* Number of times a contribution has been retried after a failure
*/
var retries: Int = 0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a schema change to the contribution table and requires a bump of the db version number [ref]. Kindly take care of the same.

Comment on lines 383 to 384
if (uploadableFiles.size() > 3
&& !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess we should show this dialog only for relevant Android versions. The intent itself is only available since API 23 (6+). Kindly check and make sure we only invoke this for the relevant Android versions.

@RitikaPahwa4444
Copy link
Collaborator Author

I often switch to limited connection mode, never found any of these changes interfering with its behaviour. Will test more since you feel these changes could affect its functioning👍🏻

Timber.d("Restarting for %s", contribution.toString());
if (contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) {
restartUpload(contribution);
} else if (contribution.getState() == STATE_FAILED) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was trying to test the change in my device. Blindly retrying all failed uploads does seem to be affecting the UX badly. The app seems to just retrying genuinely failed uploads again-and-again each time I open the app.

Is there really no other attribute that we have that could help with identifying if an upload failed for a genuine reason? (I'm presuming its because of beta cluster not supporting stashed uploads).

Also, I remember cancelling all uploads manually one time. The cancel didn't seem to have worked. Has anyone else observed this too?

@RitikaPahwa4444 RitikaPahwa4444 changed the title [WIP] 5128: Fix stuck uploads 5128: Fix stuck uploads Aug 27, 2023
@RitikaPahwa4444 RitikaPahwa4444 marked this pull request as ready for review August 27, 2023 07:49
Copy link
Member

@nicolas-raoul nicolas-raoul left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested on 100+ pictures, this pull request brings a great improvement in upload reliability.
The code also looks good to me.

@RitikaPahwa4444
Copy link
Collaborator Author

Closing this as these changes have already been merged with #5272.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upload often fails or gets stuck, especially when uploading 3 or more pictures
4 participants