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

[RFR-772] Ignore sync errors while in background #288

Merged
merged 2 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import kotlinx.coroutines.runBlocking
*
* @author Klemens Muthmann
* @author Armin Schnabel
* @version 5.1.0
* @version 5.1.1
* @since 2.0.0
*/
class CyfaceAuthenticator(private val context: Context) :
Expand Down Expand Up @@ -112,7 +112,8 @@ class CyfaceAuthenticator(private val context: Context) :
ErrorHandler.sendErrorIntent(
context,
ErrorHandler.ErrorCode.UNKNOWN.code,
e.message
e.message,
false // login currently only happens while the user is active
)
throw NetworkErrorException(e)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2022 Cyface GmbH
* Copyright 2018-2023 Cyface GmbH
*
* This file is part of the Cyface SDK for Android.
*
Expand Down Expand Up @@ -41,13 +41,14 @@
* support time for all involved.
*
* @author Armin Schnabel
* @version 2.1.0
* @version 2.2.0
* @since 2.2.0
*/
public class ErrorHandler extends BroadcastReceiver {

public final static String ERROR_INTENT = "de.cyface.error";
public final static String ERROR_CODE_EXTRA = "de.cyface.error.error_code";
public final static String ERROR_BACKGROUND_EXTRA = "de.cyface.error.from_background";
public final static String HTTP_CODE_EXTRA = "de.cyface.error.http_code";
private final Collection<ErrorListener> errorListeners;

Expand Down Expand Up @@ -95,11 +96,15 @@ public static void sendErrorIntent(final Context context, final int errorCode, f
*
* @param context the {@link Context}
* @param errorCode the Cyface error code
* @param message A message which can be shown to the user, e.g. as toast.
* @param fromBackground `true` if the error was caused without user interaction, e.g. to avoid
* disturbing the user while he is not using the app.
*/
public static void sendErrorIntent(final Context context, final int errorCode, @Nullable final String message) {
public static void sendErrorIntent(final Context context, final int errorCode, @Nullable final String message, final boolean fromBackground) {

final Intent intent = new Intent(ERROR_INTENT);
intent.putExtra(ERROR_CODE_EXTRA, errorCode);
intent.putExtra(ERROR_BACKGROUND_EXTRA, fromBackground);
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
if (message != null) {
Log.d(TAG, message);
Expand All @@ -111,6 +116,7 @@ public void onReceive(final Context context, final Intent intent) {

Validate.notNull(intent.getExtras());
final int errorCodeInt = intent.getExtras().getInt(ERROR_CODE_EXTRA);
final var fromBackground = intent.getExtras().getBoolean(ERROR_BACKGROUND_EXTRA);
final ErrorCode errorCode = ErrorCode.getValueForCode(errorCodeInt);
Validate.notNull(errorCode);
String errorMessage;
Expand Down Expand Up @@ -220,7 +226,7 @@ public void onReceive(final Context context, final Intent intent) {
}

for (final ErrorListener errorListener : errorListeners) {
errorListener.onErrorReceive(errorCode, errorMessage);
errorListener.onErrorReceive(errorCode, errorMessage, fromBackground);
}
}

Expand Down Expand Up @@ -274,10 +280,15 @@ public static ErrorCode getValueForCode(final int code) {
* @since 1.0.0
*/
public interface ErrorListener {
// These enhanced error details will be (re)implemented with #CY-3709
// @ param causeId Optional id of the cause object for the error, e.g. measurementId
// @ param causeExtra Optional, additional information such as the date of a broken measurement
void onErrorReceive(final ErrorCode errorCode, final String errorMessage);
/**
* Handler called upon new errors.
*
* @param errorCode the Cyface error code
* @param errorMessage A message which can be shown to the user, e.g. as toast.
* @param fromBackground `true` if the error was caused without user interaction, e.g. to avoid
* disturbing the user while he is not using the app.
*/
void onErrorReceive(final ErrorCode errorCode, final String errorMessage, final boolean fromBackground);
}

// The following error handling will be (re)implemented with #CY-3709
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.accounts.NetworkErrorException
import android.content.AbstractThreadedSyncAdapter
import android.content.ContentProviderClient
import android.content.ContentResolver
import android.content.ContentResolver.SYNC_EXTRAS_MANUAL
import android.content.Context
import android.content.SyncResult
import android.content.pm.PackageManager
Expand Down Expand Up @@ -56,7 +57,7 @@ import java.io.File
*
* @author Armin Schnabel
* @author Klemens Muthmann
* @version 4.1.0
* @version 4.1.1
* @since 2.0.0
* @property authenticator The authenticator to use for synchronization.
* @property uploader The uploader to use for synchronization.
Expand Down Expand Up @@ -122,7 +123,10 @@ class SyncAdapter private constructor(
context,
DefaultPersistenceBehaviour()
)
val syncPerformer = SyncPerformer(context)
// Ensure sync errors are shown to the user when triggering sync manually
val fromBackground = !extras.getBoolean(SYNC_EXTRAS_MANUAL)
val syncPerformer = SyncPerformer(context, fromBackground)


// Ensure user is authorized before starting synchronization
authenticator.performActionWithFreshTokens { _, _, ex ->
Expand All @@ -132,7 +136,8 @@ class SyncAdapter private constructor(
ErrorHandler.sendErrorIntent(
context,
ErrorCode.AUTHENTICATION_ERROR.code,
ex.message
ex.message,
fromBackground
)
} else {
try {
Expand Down Expand Up @@ -211,7 +216,8 @@ class SyncAdapter private constructor(
ErrorHandler.sendErrorIntent(
context,
ErrorCode.AUTHENTICATION_ERROR.code,
e.message
e.message,
fromBackground
)
} else {
val result = syncPerformer.sendData(
Expand Down Expand Up @@ -272,22 +278,29 @@ class SyncAdapter private constructor(
} catch (e: CursorIsNullException) {
Log.w(TAG, e.javaClass.simpleName + ": " + e.message)
syncResult.databaseError = true
ErrorHandler.sendErrorIntent(context, ErrorCode.DATABASE_ERROR.code, e.message)
ErrorHandler.sendErrorIntent(
context,
ErrorCode.DATABASE_ERROR.code,
e.message,
fromBackground
)
} catch (e: AuthenticatorException) {
Log.w(TAG, e.javaClass.simpleName + ": " + e.message)
syncResult.stats.numAuthExceptions++
ErrorHandler.sendErrorIntent(
context,
ErrorCode.AUTHENTICATION_ERROR.code,
e.message
e.message,
fromBackground
)
} catch (e: SynchronizationInterruptedException) {
Log.w(TAG, e.javaClass.simpleName + ": " + e.message)
syncResult.stats.numIoExceptions++
ErrorHandler.sendErrorIntent(
context,
ErrorCode.SYNCHRONIZATION_INTERRUPTED.code,
e.message
e.message,
fromBackground
)
} catch (e: NetworkErrorException) {
Log.w(TAG, e.javaClass.simpleName + ": " + e.message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ import java.net.MalformedURLException
*
* @author Klemens Muthmann
* @author Armin Schnabel
* @version 7.0.0
* @version 8.0.0
* @since 2.0.0
* @property context The Android `Context` to use for setting the correct server certification information.
* @property fromBackground `true` if the error was caused without user interaction, e.g. to avoid
* disturbing the user while he is not using the app.
*/
internal class SyncPerformer(private val context: Context) {
internal class SyncPerformer(private val context: Context, private val fromBackground: Boolean) {
/**
* Triggers the data transmission to a Cyface server API. The `measurementIdentifier` and
* `deviceIdentifier` need to be globally unique. If they are not the server will probably reject the
Expand Down Expand Up @@ -135,7 +137,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.SERVER_UNAVAILABLE.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -145,7 +148,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.FORBIDDEN.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -155,7 +159,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.MALFORMED_URL.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -165,7 +170,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.SYNCHRONIZATION_ERROR.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -175,7 +181,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.UNAUTHORIZED.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -185,7 +192,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.INTERNAL_SERVER_ERROR.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -195,7 +203,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.ENTITY_NOT_PARSABLE.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -205,7 +214,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.BAD_REQUEST.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -215,7 +225,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.NETWORK_UNAVAILABLE.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -225,7 +236,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.SYNCHRONIZATION_INTERRUPTED.code,
e.message
e.message,
fromBackground
)
e.printStackTrace()
Result.UPLOAD_FAILED
Expand All @@ -236,7 +248,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.TOO_MANY_REQUESTS.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -246,7 +259,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.UPLOAD_SESSION_EXPIRED.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -256,7 +270,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.UNEXPECTED_RESPONSE_CODE.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand All @@ -266,7 +281,8 @@ internal class SyncPerformer(private val context: Context) {
ErrorHandler.sendErrorIntent(
context,
ErrorCode.ACCOUNT_NOT_ACTIVATED.code,
e.message
e.message,
fromBackground
)
Result.UPLOAD_FAILED
}
Expand Down