From a6abcbe2a620f11d726c2408522035dd727704ed Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 14 Oct 2026 12:53:35 +0200 Subject: [PATCH 1/3] handle restore pending login flow and cancel button Signed-off-by: alperozturk96 --- .../authentication/AuthenticatorActivity.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index dd720ccc985f..b4bfc995e859 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -63,6 +63,7 @@ import com.nextcloud.client.onboarding.OnboardingService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.PlainClient; +import com.nextcloud.model.HTTPStatusCodes; import com.nextcloud.operations.PostMethod; import com.nextcloud.utils.RawResourceReader; import com.nextcloud.utils.SnackbarUtil; @@ -168,6 +169,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private static final String KEY_SERVER_AUTH_METHOD = "SERVER_AUTH_METHOD"; private static final String KEY_WAITING_FOR_OP_ID = "WAITING_FOR_OP_ID"; private static final String KEY_ONLY_ADD = "onlyAdd"; + private static final String KEY_LOGIN_FLOW_AUTH_OBJECT = "LOGIN_FLOW_AUTH_OBJECT"; public static final byte ACTION_CREATE = 0; public static final byte ACTION_UPDATE_EXPIRED_TOKEN = 2; // detected by the app @@ -344,6 +346,7 @@ protected void onCreate(Bundle savedInstanceState) { } initServerPreFragment(savedInstanceState); + restorePendingLoginFlow(savedInstanceState); } ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); @@ -404,6 +407,7 @@ private void deleteCookies() { private final ScheduledExecutorService loginFlowExecutorService = Executors.newSingleThreadScheduledExecutor(); private boolean isLoginProcessCompleted = false; private boolean isRedirectedToTheDefaultBrowser = false; + private boolean isServerCheckPendingServiceBinding = false; private String baseUrl; private void poolLogin() { @@ -555,7 +559,7 @@ private void performLoginFlowV2() { Log_OC.d(TAG, "performLoginFlowV2 status: " + status); Log_OC.d(TAG, "performLoginFlowV2 response: " + response); - if (!response.isEmpty()) { + if (status == HTTPStatusCodes.SUCCESS.getCode() && !response.isEmpty()) { runOnUiThread(() -> completeLoginFlow(response, status)); } } @@ -583,11 +587,30 @@ private void completeLoginFlow(String response, int status) { showServerStatus(); } - checkOcServer(); + if (mOperationsServiceBinder == null) { + isServerCheckPendingServiceBinding = true; + } else { + checkOcServer(); + } loginFlowExecutorService.shutdown(); ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); } + private void restorePendingLoginFlow(Bundle savedInstanceState) { + if (savedInstanceState == null) { + return; + } + + authObject = gson.fromJson(savedInstanceState.getString(KEY_LOGIN_FLOW_AUTH_OBJECT), AuthObject.class); + if (authObject == null) { + return; + } + + accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); + setContentView(accountSetupWebviewBinding.getRoot()); + initLoginInfoView(); + } + private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { if (event == Lifecycle.Event.ON_START && authObject != null && !TextUtils.isEmpty(authObject.getPoll().getToken())) { Log_OC.d(TAG, "Start poolLogin"); @@ -837,6 +860,9 @@ protected void onSaveInstanceState(@NonNull Bundle outState) { /// authentication outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt); + if (authObject != null && !isLoginProcessCompleted) { + outState.putString(KEY_LOGIN_FLOW_AUTH_OBJECT, gson.toJson(authObject)); + } /// AsyncTask (User and password) if (mAsyncTask != null) { @@ -1171,6 +1197,7 @@ private void initLoginInfoView() { cancelButton.setOnClickListener(v -> { loginFlowExecutorService.shutdown(); ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); + authObject = null; recreate(); }); } @@ -1775,6 +1802,10 @@ public void onServiceConnected(ComponentName component, IBinder service) { } } else { doOnResumeAndBound(); + if (isServerCheckPendingServiceBinding) { + isServerCheckPendingServiceBinding = false; + checkOcServer(); + } } } } From 667bbeda96ae33cab4881ac1fef67e0e769677ec Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 14 Oct 2026 13:04:07 +0200 Subject: [PATCH 2/3] use kotlinx instead of gson Signed-off-by: alperozturk96 --- .../owncloud/android/authentication/AuthObject.kt | 15 ++++++++++++++- .../authentication/AuthenticatorActivity.java | 11 ++++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt b/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt index eddaecc42ac4..650dedd15d8b 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt +++ b/app/src/main/java/com/owncloud/android/authentication/AuthObject.kt @@ -7,6 +7,19 @@ package com.owncloud.android.authentication -data class AuthObject(val poll: Poll, val login: String) +import com.nextcloud.utils.serialization.AppJson +import kotlinx.serialization.Serializable +@Serializable +data class AuthObject(val poll: Poll, val login: String) { + companion object { + @JvmStatic + fun fromJson(value: String): AuthObject = AppJson.instance.decodeFromString(value) + + @JvmStatic + fun toJson(value: AuthObject): String = AppJson.instance.encodeToString(value) + } +} + +@Serializable data class Poll(val token: String, val endpoint: String) diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index b4bfc995e859..4bada893ab88 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -452,8 +452,8 @@ private void anonymouslyPostLoginRequest(String url) { private String extractLoginUrl(String response) { try { - authObject = gson.fromJson(response, AuthObject.class); - if (authObject != null && !TextUtils.isEmpty(authObject.getLogin())) { + authObject = AuthObject.fromJson(response); + if (!TextUtils.isEmpty(authObject.getLogin())) { return authObject.getLogin(); } else { Log_OC.e(TAG, "AuthObject parsing failed or login empty, trying JSONObject fallback"); @@ -601,11 +601,12 @@ private void restorePendingLoginFlow(Bundle savedInstanceState) { return; } - authObject = gson.fromJson(savedInstanceState.getString(KEY_LOGIN_FLOW_AUTH_OBJECT), AuthObject.class); - if (authObject == null) { + String authObjectJson = savedInstanceState.getString(KEY_LOGIN_FLOW_AUTH_OBJECT); + if (authObjectJson == null) { return; } + authObject = AuthObject.fromJson(authObjectJson); accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); initLoginInfoView(); @@ -861,7 +862,7 @@ protected void onSaveInstanceState(@NonNull Bundle outState) { /// authentication outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt); if (authObject != null && !isLoginProcessCompleted) { - outState.putString(KEY_LOGIN_FLOW_AUTH_OBJECT, gson.toJson(authObject)); + outState.putString(KEY_LOGIN_FLOW_AUTH_OBJECT, AuthObject.toJson(authObject)); } /// AsyncTask (User and password) From 0650bf63abe41ba02787013ab33d04e52001e23f Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 14 Oct 2026 13:09:58 +0200 Subject: [PATCH 3/3] adds more logs to clearly follow execution flow in the logs Signed-off-by: alperozturk96 --- .../authentication/AuthenticatorActivity.java | 111 +++++++++++------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 4bada893ab88..d1d39232eb9d 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -349,6 +349,8 @@ protected void onCreate(Bundle savedInstanceState) { restorePendingLoginFlow(savedInstanceState); } + Log_OC.d(TAG, "Started, action=" + (mAction == ACTION_CREATE ? "create account" : "update credentials") + + ", recreated=" + (savedInstanceState != null) + ", webViewLogin=" + webViewLoginMethod); ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); } @@ -399,7 +401,7 @@ private void deleteCookies() { try { CookieManager.getInstance().removeAllCookies(null); } catch (Exception e) { - Log_OC.e(TAG, e.getMessage()); + Log_OC.e(TAG, "Failed to delete web view cookies", e); } } @@ -430,14 +432,17 @@ private void poolLogin() { */ private void anonymouslyPostLoginRequest(String url) { if (TextUtils.isEmpty(url)) { + Log_OC.e(TAG, "Login flow v2: cannot start, login request URL is empty"); SnackbarUtil.show(this, R.string.authenticator_activity_empty_base_url); return; } baseUrl = url; + Log_OC.d(TAG, "Login flow v2: requesting login URL from " + url); singleThreadExecutor.execute(() -> { String response = getResponseOfAnonymouslyPostLoginRequest(); if (TextUtils.isEmpty(response)) { + Log_OC.e(TAG, "Login flow v2: login URL request returned an empty response"); SnackbarUtil.show(AuthenticatorActivity.this, R.string.authenticator_activity_empty_response_message); return; } @@ -456,10 +461,11 @@ private String extractLoginUrl(String response) { if (!TextUtils.isEmpty(authObject.getLogin())) { return authObject.getLogin(); } else { - Log_OC.e(TAG, "AuthObject parsing failed or login empty, trying JSONObject fallback"); + Log_OC.e(TAG, "Login flow v2: login URL missing in response, trying fallback parser"); } } catch (Exception e) { - Log_OC.e(TAG, "Error parsing AuthObject: " + e.getMessage(), e); + Log_OC.e(TAG, "Login flow v2: response parsing failed, trying fallback parser: " + + e.getClass().getSimpleName()); } try { @@ -467,13 +473,13 @@ private String extractLoginUrl(String response) { if (!TextUtils.isEmpty(fallbackUrl)) { return fallbackUrl; } else { - Log_OC.e(TAG, "Fallback JSONObject parsing failed or login empty"); + Log_OC.e(TAG, "Login flow v2: fallback parser found no login URL"); } } catch (Exception e) { - Log_OC.e(TAG, "Error parsing fallback JSONObject: " + e.getMessage(), e); + Log_OC.e(TAG, "Login flow v2: fallback parser failed: " + e.getClass().getSimpleName()); } - Log_OC.e(TAG, "Both AuthObject and fallback parsing failed, returning default login URL"); + Log_OC.e(TAG, "Login flow v2: no login URL in response, using default login URL"); SnackbarUtil.show(this, R.string.authenticator_activity_login_error); return getResources().getString(R.string.webview_login_url); } @@ -487,12 +493,14 @@ private String getLoginFromJsonObject(String response) { private String getResponseOfAnonymouslyPostLoginRequest() { PostMethod post = new PostMethod(baseUrl, false, new FormBody.Builder().build()); PlainClient client = clientFactory.createPlainClient(); - post.execute(client); + int status = post.execute(client); + Log_OC.d(TAG, "Login flow v2: login URL request finished with status " + status); return post.getResponseBodyAsString(); } private void launchDefaultWebBrowser(String url) { if (url == null || url.isBlank()) { + Log_OC.e(TAG, "Login flow v2: cannot open browser, login URL is blank"); SnackbarUtil.show(this, R.string.invalid_url); return; } @@ -504,9 +512,10 @@ private void launchDefaultWebBrowser(String url) { int toolbarColor = ContextCompat.getColor(this, R.color.primary); AuthTabIntent authTabIntent = new AuthTabIntent.Builder().setColorScheme(toolbarColor).build(); authTabIntent.launch(authTabResultLauncher, uri, loginScheme); + Log_OC.d(TAG, "Login flow v2: login page opened in Auth Tab"); return; } catch (Exception e) { - Log_OC.e(TAG, "Auth Tab login URL launch failed: " + e); + Log_OC.e(TAG, "Login flow v2: Auth Tab launch failed: " + e.getClass().getSimpleName()); } try { @@ -514,12 +523,14 @@ private void launchDefaultWebBrowser(String url) { PackageManager packageManager = getPackageManager(); if (intent.resolveActivity(packageManager) != null) { startActivity(intent); + Log_OC.d(TAG, "Login flow v2: login page opened in external browser"); return; } } catch (Exception e) { - Log_OC.e(TAG, "External browser launch failed: " + e); + Log_OC.e(TAG, "Login flow v2: external browser launch failed: " + e.getClass().getSimpleName()); } + Log_OC.e(TAG, "Login flow v2: no browser available to open login page"); SnackbarUtil.show(this, R.string.authenticator_activity_no_web_browser_found); } @@ -529,18 +540,12 @@ private Pair extractPollUrlAndToken() { String pollUrl = poll.getEndpoint(); String token = poll.getToken(); - if (TextUtils.isEmpty(pollUrl)) { - Log_OC.e(TAG, "auth object poll url is empty."); - } - if (TextUtils.isEmpty(token)) { - Log_OC.e(TAG, "auth object token is empty."); - } - if (!TextUtils.isEmpty(pollUrl) && !TextUtils.isEmpty(token)) { return new Pair<>(pollUrl, token); } } + Log_OC.w(TAG, "Login flow v2: poll endpoint or token missing, using fallback endpoint and token"); return new Pair<>(baseUrl + "/poll", fallbackToken); } @@ -556,9 +561,12 @@ private void performLoginFlowV2() { int status = post.execute(client); String response = post.getResponseBodyAsString(); - Log_OC.d(TAG, "performLoginFlowV2 status: " + status); - Log_OC.d(TAG, "performLoginFlowV2 response: " + response); + if (status == HTTPStatusCodes.NOT_FOUND.getCode()) { + Log_OC.d(TAG, "Login flow v2: access not granted in browser yet, polling continues"); + return; + } + Log_OC.d(TAG, "Login flow v2: poll finished with status " + status); if (status == HTTPStatusCodes.SUCCESS.getCode() && !response.isEmpty()) { runOnUiThread(() -> completeLoginFlow(response, status)); } @@ -568,10 +576,12 @@ private void completeLoginFlow(String response, int status) { try { LoginUrlInfo loginUrlInfo = gson.fromJson(response, LoginUrlInfo.class); if (loginUrlInfo == null) { - Log_OC.e(TAG, "cannot complete login flow loginUrl is null"); + Log_OC.e(TAG, "Login flow v2: cannot complete, poll response has no credentials"); return; } isLoginProcessCompleted = loginUrlInfo.isValid(status); + Log_OC.d(TAG, "Login flow v2: credentials received for " + loginUrlInfo.getServer() + + ", valid=" + isLoginProcessCompleted); if (accountSetupBinding != null) { accountSetupBinding.hostUrlInput.setText(""); @@ -581,19 +591,21 @@ private void completeLoginFlow(String response, int status) { webViewUser = loginUrlInfo.getLoginName(); webViewPassword = loginUrlInfo.getAppPassword(); } catch (Exception e) { - Log_OC.d(TAG, "Error completeLoginFlow: " + e); + Log_OC.e(TAG, "Login flow v2: poll response parsing failed: " + e.getClass().getSimpleName()); mServerStatusIcon = R.drawable.ic_alert; mServerStatusText = getString(R.string.qr_could_not_be_read); showServerStatus(); } if (mOperationsServiceBinder == null) { + Log_OC.d(TAG, "Login flow v2: operations service not bound yet, server check deferred"); isServerCheckPendingServiceBinding = true; } else { checkOcServer(); } loginFlowExecutorService.shutdown(); ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); + Log_OC.d(TAG, "Login flow v2: polling stopped"); } private void restorePendingLoginFlow(Bundle savedInstanceState) { @@ -607,6 +619,7 @@ private void restorePendingLoginFlow(Bundle savedInstanceState) { } authObject = AuthObject.fromJson(authObjectJson); + Log_OC.d(TAG, "Login flow v2: pending flow restored after activity recreation"); accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); initLoginInfoView(); @@ -614,7 +627,7 @@ private void restorePendingLoginFlow(Bundle savedInstanceState) { private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { if (event == Lifecycle.Event.ON_START && authObject != null && !TextUtils.isEmpty(authObject.getPoll().getToken())) { - Log_OC.d(TAG, "Start poolLogin"); + Log_OC.d(TAG, "Login flow v2: app is in foreground, polling started"); poolLogin(); } }); @@ -674,6 +687,7 @@ public void parseAndLoginFromWebView(String dataString) { LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString); if (!checkAllowedServers(loginUrlInfo.getServer())) { + Log_OC.w(TAG, "Login rejected, server is not allowed: " + loginUrlInfo.getServer()); mServerStatusIcon = R.drawable.ic_alert; mServerStatusText = getString(R.string.server_not_allowed); showServerStatus(); @@ -687,6 +701,7 @@ public void parseAndLoginFromWebView(String dataString) { webViewUser = loginUrlInfo.getLoginName(); webViewPassword = loginUrlInfo.getAppPassword(); } catch (Exception e) { + Log_OC.e(TAG, "Login data could not be parsed: " + e.getClass().getSimpleName()); mServerStatusIcon = R.drawable.ic_alert; mServerStatusText = getString(R.string.qr_could_not_be_read); showServerStatus(); @@ -846,7 +861,6 @@ private void initAuthorizationPreFragment(Bundle savedInstanceState) { */ @Override protected void onSaveInstanceState(@NonNull Bundle outState) { - //Log_OC.e(TAG, "onSaveInstanceState init" ); super.onSaveInstanceState(outState); /// global state @@ -900,8 +914,6 @@ public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - Log_OC.d(TAG, "onNewIntent()"); - if (intent.getBooleanExtra(FirstRunActivity.EXTRA_EXIT, false)) { super.finish(); } @@ -913,8 +925,10 @@ protected void onNewIntent(Intent intent) { Uri data = intent.getData(); if (data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme))) { + Log_OC.d(TAG, "Login data received via deep link"); if (!MDMConfig.INSTANCE.multiAccountSupport(this) && accountManager.getAccounts().length == 1) { + Log_OC.w(TAG, "Login rejected, multiple accounts are not allowed"); SnackbarUtil.show(this, R.string.no_mutliple_accounts_allowed); finish(); return; @@ -1006,7 +1020,6 @@ protected void onDestroy() { mOperationsServiceBinder = null; } - Log_OC.d(TAG, "AuthenticatorActivity onDestroy called"); singleThreadExecutor.shutdown(); super.onDestroy(); } @@ -1036,7 +1049,7 @@ private void checkOcServer() { uri = AuthenticatorUrlUtils.INSTANCE.normalizeScheme(uri); } catch (IllegalArgumentException ex) { // Let the Nextcloud library check the error of the malformed URI - Log_OC.e(TAG, "Invalid URL", ex); + Log_OC.e(TAG, "Server check: cannot normalize scheme of " + uri, ex); } // Handle internationalized domain names @@ -1044,7 +1057,7 @@ private void checkOcServer() { uri = LinkFormatter.toAsciiDomain(uri); } catch (IllegalArgumentException ex) { // Let the Nextcloud library check the error of the malformed URI - Log_OC.e(TAG, "Error converting internationalized domain name " + uri, ex); + Log_OC.e(TAG, "Server check: cannot convert internationalized domain name " + uri, ex); } if (accountSetupBinding != null) { @@ -1061,8 +1074,9 @@ private void checkOcServer() { if (mOperationsServiceBinder != null) { mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getServerInfoIntent); + Log_OC.d(TAG, "Server check: started for " + uri); } else { - Log_OC.e(TAG, "Server check tried with OperationService unbound!"); + Log_OC.e(TAG, "Server check: skipped, operations service is not bound"); } } @@ -1078,6 +1092,7 @@ private void checkBasicAuthorization(@Nullable String webViewUsername, @Nullable } private void accessRootFolder(OwnCloudCredentials credentials) { + Log_OC.d(TAG, "Credential check: started for " + mServerInfo.mBaseUrl); mAsyncTask = new AuthenticatorAsyncTask(this); Object[] params = {mServerInfo.mBaseUrl, credentials}; mAsyncTask.execute(params); @@ -1114,7 +1129,7 @@ private void onGetUserNameFinish(RemoteOperationResult result) { success = true; } catch (AccountNotFoundException e) { - Log_OC.e(TAG, "Account " + mAccount + " was removed!", e); + Log_OC.e(TAG, "Account: " + mAccount.name + " no longer exists", e); SnackbarUtil.show(accountSetupBinding.scroll, R.string.auth_account_does_not_exist); finish(); } @@ -1128,7 +1143,7 @@ private void onGetUserNameFinish(RemoteOperationResult result) { int statusText = result.getCode() == ResultCode.MAINTENANCE_MODE ? R.string.maintenance_mode : R.string.auth_fail_get_user_name; updateStatusIconFailUserName(statusText); showAuthStatus(); - Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage()); + Log_OC.e(TAG, "User info: request failed, code=" + result.getCode() + ", " + result.getLogMessage()); } } @@ -1148,6 +1163,7 @@ private void onGetServerInfoFinish(RemoteOperationResult result) { // 3. we got the server version // 4. we got the authentication method required by the server mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0)); + Log_OC.d(TAG, "Server check: succeeded for " + mServerInfo.mBaseUrl); if (webViewUser != null && !webViewUser.isEmpty() && webViewPassword != null && !webViewPassword.isEmpty()) { @@ -1166,6 +1182,7 @@ private void onGetServerInfoFinish(RemoteOperationResult result) { } } } else { + Log_OC.e(TAG, "Server check: failed, code=" + result.getCode() + ", " + result.getLogMessage()); updateServerStatusIconAndText(result); showServerStatus(); } @@ -1196,6 +1213,7 @@ private void initLoginInfoView() { ViewCompat.requestApplyInsets(loginFlowLayout); cancelButton.setOnClickListener(v -> { + Log_OC.d(TAG, "Login flow v2: cancelled by user"); loginFlowExecutorService.shutdown(); ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); authObject = null; @@ -1393,7 +1411,7 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) mAsyncTask = null; if (result.isSuccess()) { - Log_OC.d(TAG, "Successful access - time to save the account"); + Log_OC.d(TAG, "Credential check: succeeded, saving account"); boolean success = false; @@ -1405,7 +1423,7 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) success = true; } catch (AccountNotFoundException e) { - Log_OC.e(TAG, "Account " + mAccount + " was removed!", e); + Log_OC.e(TAG, "Account: " + mAccount.name + " no longer exists", e); SnackbarUtil.show(accountSetupBinding.scroll, R.string.auth_account_does_not_exist); finish(); } @@ -1430,6 +1448,8 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) } } else if (result.isServerFail() || result.isException()) { + Log_OC.e(TAG, "Credential check: server error, code=" + result.getCode() + ", " + + result.getLogMessage()); /// server errors or exceptions in authorization take to requiring a new check of the server mServerInfo = new GetServerInfoOperation.ServerInfo(); @@ -1445,7 +1465,9 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) } } else { // authorization fail due to client side - probably wrong credentials + Log_OC.e(TAG, "Credential check: rejected, code=" + result.getCode() + ", " + result.getLogMessage()); if (accountSetupWebviewBinding != null) { + Log_OC.d(TAG, "Login flow v2: restarting flow after rejected credentials"); anonymouslyPostLoginRequest(mServerInfo.mBaseUrl + WEB_LOGIN); } else { SnackbarUtil.show(this, R.string.auth_access_failed, result.getLogMessage(this)); @@ -1458,8 +1480,6 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) webViewPassword = null; webViewUser = null; deleteCookies(); - - Log_OC.d(TAG, "Access failed: " + result.getLogMessage()); } } @@ -1490,12 +1510,12 @@ private void getUserCapabilitiesAndFinish() { new GetCapabilitiesOperation(storageManager).execute(MainApp.getAppContext()); handler.post(this::endSuccess); } catch (Exception e) { - Log_OC.e(TAG, "Failed to fetch capabilities", e); + Log_OC.e(TAG, "Account: failed to fetch capabilities for " + mAccount.name, e); handler.post(this::endSuccess); } }); } else { - Log_OC.w(TAG, "User not present for fetching capabilities"); + Log_OC.w(TAG, "Account: " + mAccount.name + " not found, capabilities not fetched"); endSuccess(); } } @@ -1524,6 +1544,7 @@ private void updateAccountAuthentication() throws AccountNotFoundException { Intent intent = new Intent(); intent.putExtras(response); setResult(RESULT_OK, intent); + Log_OC.d(TAG, "Account: credentials updated for " + mAccount.name); } /** @@ -1558,13 +1579,13 @@ protected boolean createAccount(RemoteOperationResult authResult) { updateAuthStatusIconAndText(result); showAuthStatus(); - Log_OC.d(TAG, result.getLogMessage()); + Log_OC.w(TAG, "Account: " + accountName + " already exists"); return false; } else { UserInfo userInfo = authResult.getResultData(); if (userInfo == null) { - Log_OC.e(this, "Could not read user data!"); + Log_OC.e(TAG, "Account: cannot create " + accountName + ", user info is missing"); return false; } @@ -1605,6 +1626,7 @@ protected boolean createAccount(RemoteOperationResult authResult) { // notify Document Provider DocumentsStorageProvider.notifyRootsChanged(this); + Log_OC.d(TAG, "Account: created " + accountName); return true; } } @@ -1624,7 +1646,7 @@ private void startQRScanner() { private final ActivityResultLauncher authTabResultLauncher = AuthTabIntent.registerActivityResultLauncher( this, - result -> Log_OC.d(TAG, "Auth Tab result code: " + result.resultCode) + result -> Log_OC.d(TAG, "Login flow v2: Auth Tab closed with result code " + result.resultCode) ); private final ActivityResultLauncher qrScanResultLauncher = registerForActivityResult( @@ -1640,6 +1662,7 @@ private void startQRScanner() { String resultData = data.getStringExtra("com.blikoon.qrcodescanner.got_qr_scan_relult"); if (resultData == null || !resultData.startsWith(getString(R.string.login_data_own_scheme))) { + Log_OC.w(TAG, "QR login: scanned code does not contain login data"); mServerStatusIcon = R.drawable.ic_alert; mServerStatusText = "QR Code could not be read!"; showServerStatus(); @@ -1648,8 +1671,9 @@ private void startQRScanner() { if (!MDMConfig.INSTANCE.multiAccountSupport(this) && accountManager.getAccounts().length == 1) { + Log_OC.w(TAG, "Login rejected, multiple accounts are not allowed"); SnackbarUtil.show(this, R.string.no_mutliple_accounts_allowed); - + return; } @@ -1763,7 +1787,7 @@ private void dismissWaitingDialog() { try { dialog.dismiss(); } catch (IllegalStateException e) { - Log_OC.e(TAG, e.getMessage()); + Log_OC.e(TAG, "Waiting dialog dismiss failed, dismissing with state loss", e); dialog.dismissAllowingStateLoss(); } } @@ -1797,6 +1821,8 @@ public void onServiceConnected(ComponentName component, IBinder service) { login(loginUrlInfo); } catch (Exception e) { + Log_OC.e(TAG, "Login data from deep link could not be parsed: " + + e.getClass().getSimpleName()); mServerStatusIcon = R.drawable.ic_alert; mServerStatusText = getString(R.string.qr_could_not_be_read); showServerStatus(); @@ -1804,6 +1830,7 @@ public void onServiceConnected(ComponentName component, IBinder service) { } else { doOnResumeAndBound(); if (isServerCheckPendingServiceBinding) { + Log_OC.d(TAG, "Login flow v2: operations service bound, running deferred server check"); isServerCheckPendingServiceBinding = false; checkOcServer(); } @@ -1816,7 +1843,7 @@ public void onServiceDisconnected(ComponentName component) { if (component.equals( new ComponentName(AuthenticatorActivity.this, OperationsService.class) )) { - Log_OC.e(TAG, "Operations service crashed"); + Log_OC.e(TAG, "Operations service disconnected unexpectedly"); mOperationsServiceBinder = null; } }