Handle ALERT response to login command

* Allow AccountService loginFailed API to take a reason string
* Present the reason string in the dialog shown from the login
  failure notification
* Handle ALERTs in IMAP login responses (for example, some servers
  will occasionally require web login and we need to inform them,
  rather than simply saying the password is wrong)
* This fixes a longstanding bug in our Imap1 implementation

Change-Id: I8b270cd5d4746559b6c8a78bce02f0e7c525bdea
This commit is contained in:
Marc Blank 2012-07-27 13:36:41 -07:00
parent ae57810e1e
commit dba0b20d95
9 changed files with 55 additions and 17 deletions

View File

@ -45,11 +45,11 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
}
@Override
public void notifyLoginFailed(final long accountId) {
public void notifyLoginFailed(final long accountId, final String reason) {
setTask(new ProxyTask() {
@Override
public void run() throws RemoteException {
mService.notifyLoginFailed(accountId);
mService.notifyLoginFailed(accountId, reason);
}
}, "notifyLoginFailed");
}

View File

@ -19,7 +19,7 @@ package com.android.emailcommon.service;
import android.os.Bundle;
interface IAccountService {
oneway void notifyLoginFailed(long accountId);
oneway void notifyLoginFailed(long accountId, String reason);
oneway void notifyLoginSucceeded(long accountId);
void reconcileAccounts(String protocol, String accountManagerType);

View File

@ -59,6 +59,7 @@ public abstract class AbstractSyncService implements Runnable {
public Mailbox mMailbox;
protected long mMailboxId;
protected int mExitStatus = EXIT_EXCEPTION;
protected String mExitReason;
protected String mMailboxName;
public Account mAccount;
public Context mContext;

View File

@ -2209,7 +2209,7 @@ public abstract class SyncManager extends Service implements Runnable {
break;
// These errors are not retried automatically
case AbstractSyncService.EXIT_LOGIN_FAILURE:
new AccountServiceProxy(ssm).notifyLoginFailed(m.mAccountKey);
new AccountServiceProxy(ssm).notifyLoginFailed(m.mAccountKey, svc.mExitReason);
lastResult = EmailContent.LAST_SYNC_RESULT_AUTH_ERROR;
break;
case AbstractSyncService.EXIT_SECURITY_FAILURE:

View File

@ -101,6 +101,7 @@ public class Imap2SyncService extends AbstractSyncService {
private static final SimpleDateFormat GMAIL_INTERNALDATE_FORMAT =
new SimpleDateFormat("EEE, dd MMM yy HH:mm:ss z");
private static final String IMAP_ERR = "ERR";
private static final String IMAP_NO = "NO";
private static final SimpleDateFormat IMAP_DATE_FORMAT =
new SimpleDateFormat("dd-MMM-yyyy");
@ -1573,6 +1574,7 @@ public class Imap2SyncService extends AbstractSyncService {
public static class Connection {
Socket socket;
int status;
String reason;
ImapInputStream reader;
BufferedWriter writer;
@ -1649,7 +1651,13 @@ public class Imap2SyncService extends AbstractSyncService {
tag = writeCommand(writer,
"login " + hostAuth.mLogin + ' ' + hostAuth.mPassword);
if (!readResponse(reader, tag).equals(IMAP_OK)) {
if (!IMAP_OK.equals(readResponse(reader, tag))) {
if (IMAP_NO.equals(mImapResult)) {
int alertPos = mImapErrorLine.indexOf("[ALERT]");
if (alertPos > 0) {
conn.reason = mImapErrorLine.substring(alertPos + 7);
}
}
conn.status = EXIT_LOGIN_FAILURE;
} else {
conn.socket = socket;
@ -1960,6 +1968,7 @@ public class Imap2SyncService extends AbstractSyncService {
Connection conn = connectAndLogin(hostAuth, "main");
if (conn.status != EXIT_DONE) {
mExitStatus = conn.status;
mExitReason = conn.reason;
return;
}
setConnection(conn);

View File

@ -911,11 +911,16 @@ as <xliff:g id="filename">%s</xliff:g>.</string>
<!-- On AccountSettingsXL, dialog title if you were brought here due to a login failure.
[CHAR_LIMIT=40] -->
<string name="account_settings_login_dialog_title">Couldn\'t sign in</string>
<!-- On AccountSettingsXL, dialog content if you were brought here due to a login failure.
<!-- Dialog content if you were brought here due to a login failure.
[CHAR_LIMIT=none] -->
<string name="account_settings_login_dialog_content_fmt">
The username or password for <xliff:g id="account">%s</xliff:g> is incorrect.
Do you want to update them now?</string>
<!-- Dialog content if you were brought here due to a login failure, and the server provided a reason
[CHAR_LIMIT=none] -->
<string name="account_settings_login_dialog_reason_fmt">
Your login to <xliff:g id="account">%s</xliff:g> failed; the server said: <xliff:g id="reason">%s</xliff:g>
Do you want to update your username and/or password?</string>
<!-- On Settings screen, setting option name -->
<string name="account_settings_default_label">Default account</string>

View File

@ -747,6 +747,10 @@ public class NotificationController {
* NOTE: DO NOT CALL THIS METHOD FROM THE UI THREAD (DATABASE ACCESS)
*/
public void showLoginFailedNotification(long accountId) {
showLoginFailedNotification(accountId, null);
}
public void showLoginFailedNotification(long accountId, String reason) {
final Account account = Account.restoreAccountWithId(mContext, accountId);
if (account == null) return;
final Mailbox mailbox = Mailbox.restoreMailboxOfType(mContext, account.mId,
@ -757,7 +761,7 @@ public class NotificationController {
mContext.getString(R.string.login_failed_title),
account.getDisplayName(),
AccountSettings.createAccountSettingsIntent(mContext, accountId,
account.mDisplayName),
account.mDisplayName, reason),
getLoginFailedNotificationId(accountId));
}
@ -841,7 +845,8 @@ public class NotificationController {
* account settings screen where he can view the list of enforced policies
*/
public void showSecurityChangedNotification(Account account) {
Intent intent = AccountSettings.createAccountSettingsIntent(mContext, account.mId, null);
Intent intent =
AccountSettings.createAccountSettingsIntent(mContext, account.mId, null, null);
String accountName = account.getDisplayName();
String ticker =
mContext.getString(R.string.security_changed_ticker_fmt, accountName);
@ -855,7 +860,8 @@ public class NotificationController {
* account settings screen where he can view the list of unsupported policies
*/
public void showSecurityUnsupportedNotification(Account account) {
Intent intent = AccountSettings.createAccountSettingsIntent(mContext, account.mId, null);
Intent intent =
AccountSettings.createAccountSettingsIntent(mContext, account.mId, null, null);
String accountName = account.getDisplayName();
String ticker =
mContext.getString(R.string.security_unsupported_ticker_fmt, accountName);

View File

@ -76,6 +76,8 @@ public class AccountSettings extends PreferenceActivity {
// Intent extras for our internal activity launch
private static final String EXTRA_ENABLE_DEBUG = "AccountSettings.enable_debug";
private static final String EXTRA_LOGIN_WARNING_FOR_ACCOUNT = "AccountSettings.for_account";
private static final String EXTRA_LOGIN_WARNING_REASON_FOR_ACCOUNT =
"AccountSettings.for_account_reason";
private static final String EXTRA_TITLE = "AccountSettings.title";
public static final String EXTRA_NO_ACCOUNTS = "AccountSettings.no_account";
@ -127,7 +129,8 @@ public class AccountSettings extends PreferenceActivity {
* Display (and edit) settings for a specific account, or -1 for any/all accounts
*/
public static void actionSettings(Activity fromActivity, long accountId) {
fromActivity.startActivity(createAccountSettingsIntent(fromActivity, accountId, null));
fromActivity.startActivity(
createAccountSettingsIntent(fromActivity, accountId, null, null));
}
/**
@ -136,13 +139,16 @@ public class AccountSettings extends PreferenceActivity {
* displayed as well.
*/
public static Intent createAccountSettingsIntent(Context context, long accountId,
String loginWarningAccountName) {
String loginWarningAccountName, String loginWarningReason) {
final Uri.Builder b = IntentUtilities.createActivityIntentUrlBuilder("settings");
IntentUtilities.setAccountId(b, accountId);
Intent i = new Intent(Intent.ACTION_EDIT, b.build());
if (loginWarningAccountName != null) {
i.putExtra(EXTRA_LOGIN_WARNING_FOR_ACCOUNT, loginWarningAccountName);
}
if (loginWarningReason != null) {
i.putExtra(EXTRA_LOGIN_WARNING_REASON_FOR_ACCOUNT, loginWarningReason);
}
return i;
}
@ -182,9 +188,12 @@ public class AccountSettings extends PreferenceActivity {
// Otherwise, we're called from within the Email app and look for our extras
mRequestedAccountId = IntentUtilities.getAccountIdFromIntent(i);
String loginWarningAccount = i.getStringExtra(EXTRA_LOGIN_WARNING_FOR_ACCOUNT);
String loginWarningReason =
i.getStringExtra(EXTRA_LOGIN_WARNING_REASON_FOR_ACCOUNT);
if (loginWarningAccount != null) {
// Show dialog (first time only - don't re-show on a rotation)
LoginWarningDialog dialog = LoginWarningDialog.newInstance(loginWarningAccount);
LoginWarningDialog dialog =
LoginWarningDialog.newInstance(loginWarningAccount, loginWarningReason);
dialog.show(getFragmentManager(), "loginwarning");
}
}
@ -809,15 +818,17 @@ public class AccountSettings extends PreferenceActivity {
public static class LoginWarningDialog extends DialogFragment
implements DialogInterface.OnClickListener {
private static final String BUNDLE_KEY_ACCOUNT_NAME = "account_name";
private String mReason;
/**
* Create a new dialog.
*/
public static LoginWarningDialog newInstance(String accountName) {
public static LoginWarningDialog newInstance(String accountName, String reason) {
final LoginWarningDialog dialog = new LoginWarningDialog();
Bundle b = new Bundle();
b.putString(BUNDLE_KEY_ACCOUNT_NAME, accountName);
dialog.setArguments(b);
dialog.mReason = reason;
return dialog;
}
@ -830,8 +841,13 @@ public class AccountSettings extends PreferenceActivity {
final AlertDialog.Builder b = new AlertDialog.Builder(context);
b.setTitle(R.string.account_settings_login_dialog_title);
b.setIconAttribute(android.R.attr.alertDialogIcon);
b.setMessage(res.getString(R.string.account_settings_login_dialog_content_fmt,
accountName));
if (mReason != null) {
b.setMessage(res.getString(R.string.account_settings_login_dialog_reason_fmt,
accountName, mReason));
} else {
b.setMessage(res.getString(R.string.account_settings_login_dialog_content_fmt,
accountName));
}
b.setPositiveButton(R.string.okay_action, this);
b.setNegativeButton(R.string.cancel_action, this);
return b.create();

View File

@ -47,8 +47,9 @@ public class AccountService extends Service {
private final IAccountService.Stub mBinder = new IAccountService.Stub() {
@Override
public void notifyLoginFailed(long accountId) {
NotificationController.getInstance(mContext).showLoginFailedNotification(accountId);
public void notifyLoginFailed(long accountId, String reason) {
NotificationController nc = NotificationController.getInstance(mContext);
nc.showLoginFailedNotification(accountId, reason);
}
@Override