Implement Autodiscover for Exchange servers
* Autodiscover allows complete configuration using only email address and password * Code handles the two standard autodiscover addresses and redirect * Autodiscover process starts when the user chooses "Exchange" as the account type. If the account is created via the AccountManager, autodiscover begins upon tapping "Next" for the first time * If autodiscover fails due to anything other than auth failure for autodiscover-capable servers, the user is placed into the standard manual configuration screen Bug: 2366019 Change-Id: I936712b924833d9a133e8da04e11c3ba45d92f92
This commit is contained in:
parent
a4d32a8fec
commit
17da1767e3
|
@ -139,6 +139,9 @@ public class AccountSetupAccountType extends Activity implements OnClickListener
|
|||
mAccount.setSyncInterval(Account.CHECK_INTERVAL_PUSH);
|
||||
mAccount.setSyncLookback(1);
|
||||
AccountSetupExchange.actionIncomingSettings(this, mAccount, mMakeDefault, easFlowMode);
|
||||
if (easFlowMode) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -73,6 +73,9 @@ public class AccountSetupBasics extends Activity
|
|||
private static final String ACTION_START_AT_MESSAGE_LIST =
|
||||
"com.android.email.AccountSetupBasics.messageList";
|
||||
|
||||
private final static String EXTRA_USERNAME = "com.android.email.AccountSetupBasics.username";
|
||||
private final static String EXTRA_PASSWORD = "com.android.email.AccountSetupBasics.password";
|
||||
|
||||
private final static int DIALOG_NOTE = 1;
|
||||
private final static int DIALOG_DUPLICATE_ACCOUNT = 2;
|
||||
|
||||
|
@ -99,6 +102,15 @@ public class AccountSetupBasics extends Activity
|
|||
fromActivity.startActivity(i);
|
||||
}
|
||||
|
||||
public static void actionNewAccountWithCredentials(Activity fromActivity,
|
||||
String username, String password, boolean easFlow) {
|
||||
Intent i = new Intent(fromActivity, AccountSetupBasics.class);
|
||||
i.putExtra(EXTRA_USERNAME, username);
|
||||
i.putExtra(EXTRA_PASSWORD, password);
|
||||
i.putExtra(EXTRA_EAS_FLOW, easFlow);
|
||||
fromActivity.startActivity(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* This creates an intent that can be used to start a self-contained account creation flow
|
||||
* for exchange accounts.
|
||||
|
@ -147,6 +159,7 @@ public class AccountSetupBasics extends Activity
|
|||
}
|
||||
|
||||
setContentView(R.layout.account_setup_basics);
|
||||
|
||||
mEmailView = (EditText)findViewById(R.id.account_email);
|
||||
mPasswordView = (EditText)findViewById(R.id.account_password);
|
||||
mDefaultView = (CheckBox)findViewById(R.id.account_default);
|
||||
|
@ -185,6 +198,13 @@ public class AccountSetupBasics extends Activity
|
|||
welcomeView.setText(R.string.accounts_welcome_exchange);
|
||||
}
|
||||
|
||||
if (intent.hasExtra(EXTRA_USERNAME)) {
|
||||
mEmailView.setText(intent.getStringExtra(EXTRA_USERNAME));
|
||||
}
|
||||
if (intent.hasExtra(EXTRA_PASSWORD)) {
|
||||
mPasswordView.setText(intent.getStringExtra(EXTRA_PASSWORD));
|
||||
}
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
|
||||
mAccount = (EmailContent.Account)savedInstanceState.getParcelable(EXTRA_ACCOUNT);
|
||||
}
|
||||
|
@ -267,34 +287,34 @@ public class AccountSetupBasics extends Activity
|
|||
if (id == DIALOG_NOTE) {
|
||||
if (mProvider != null && mProvider.note != null) {
|
||||
return new AlertDialog.Builder(this)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
.setMessage(mProvider.note)
|
||||
.setPositiveButton(
|
||||
getString(R.string.okay_action),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finishAutoSetup();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getString(R.string.cancel_action),
|
||||
null)
|
||||
.create();
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(android.R.string.dialog_alert_title)
|
||||
.setMessage(mProvider.note)
|
||||
.setPositiveButton(
|
||||
getString(R.string.okay_action),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finishAutoSetup();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(
|
||||
getString(R.string.cancel_action),
|
||||
null)
|
||||
.create();
|
||||
}
|
||||
} else if (id == DIALOG_DUPLICATE_ACCOUNT) {
|
||||
return new AlertDialog.Builder(this)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(R.string.account_duplicate_dlg_title)
|
||||
.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
|
||||
mDuplicateAccountName))
|
||||
.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dismissDialog(DIALOG_DUPLICATE_ACCOUNT);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setTitle(R.string.account_duplicate_dlg_title)
|
||||
.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
|
||||
mDuplicateAccountName))
|
||||
.setPositiveButton(R.string.okay_action,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dismissDialog(DIALOG_DUPLICATE_ACCOUNT);
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -384,7 +404,7 @@ public class AccountSetupBasics extends Activity
|
|||
mAccount.setDeletePolicy(EmailContent.Account.DELETE_POLICY_ON_DELETE);
|
||||
}
|
||||
mAccount.setSyncInterval(DEFAULT_ACCOUNT_CHECK_INTERVAL);
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, true);
|
||||
AccountSetupCheckSettings.actionValidateSettings(this, mAccount, true, true);
|
||||
}
|
||||
|
||||
private void onNext() {
|
||||
|
|
|
@ -23,6 +23,7 @@ import com.android.email.mail.MessagingException;
|
|||
import com.android.email.mail.Sender;
|
||||
import com.android.email.mail.Store;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.service.EmailServiceProxy;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
|
@ -56,6 +57,15 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
private static final String EXTRA_ACCOUNT = "account";
|
||||
private static final String EXTRA_CHECK_INCOMING = "checkIncoming";
|
||||
private static final String EXTRA_CHECK_OUTGOING = "checkOutgoing";
|
||||
private static final String EXTRA_AUTO_DISCOVER = "autoDiscover";
|
||||
private static final String EXTRA_AUTO_DISCOVER_USERNAME = "userName";
|
||||
private static final String EXTRA_AUTO_DISCOVER_PASSWORD = "password";
|
||||
|
||||
public static final int REQUEST_CODE_VALIDATE = 1;
|
||||
public static final int REQUEST_CODE_AUTO_DISCOVER = 2;
|
||||
|
||||
// We'll define a special result code for AutoDiscover auth failures
|
||||
public static final int RESULT_AUTO_DISCOVER_AUTH_FAILED = Activity.RESULT_FIRST_USER;
|
||||
|
||||
private Handler mHandler = new Handler();
|
||||
private ProgressBar mProgressBar;
|
||||
|
@ -64,16 +74,39 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
private EmailContent.Account mAccount;
|
||||
private boolean mCheckIncoming;
|
||||
private boolean mCheckOutgoing;
|
||||
private boolean mAutoDiscover;
|
||||
private boolean mCanceled;
|
||||
private boolean mDestroyed;
|
||||
|
||||
public static void actionCheckSettings(Activity fromActivity, EmailContent.Account account,
|
||||
public static void actionValidateSettings(Activity fromActivity, EmailContent.Account account,
|
||||
boolean checkIncoming, boolean checkOutgoing) {
|
||||
Intent i = new Intent(fromActivity, AccountSetupCheckSettings.class);
|
||||
i.putExtra(EXTRA_ACCOUNT, account);
|
||||
i.putExtra(EXTRA_CHECK_INCOMING, checkIncoming);
|
||||
i.putExtra(EXTRA_CHECK_OUTGOING, checkOutgoing);
|
||||
fromActivity.startActivityForResult(i, 1);
|
||||
fromActivity.startActivityForResult(i, REQUEST_CODE_VALIDATE);
|
||||
}
|
||||
|
||||
public static void actionAutoDiscover(Activity fromActivity, EmailContent.Account account,
|
||||
String userName, String password) {
|
||||
Intent i = new Intent(fromActivity, AccountSetupCheckSettings.class);
|
||||
i.putExtra(EXTRA_ACCOUNT, account);
|
||||
i.putExtra(EXTRA_AUTO_DISCOVER, true);
|
||||
i.putExtra(EXTRA_AUTO_DISCOVER_USERNAME, userName);
|
||||
i.putExtra(EXTRA_AUTO_DISCOVER_PASSWORD, password);
|
||||
fromActivity.startActivityForResult(i, REQUEST_CODE_AUTO_DISCOVER);
|
||||
}
|
||||
|
||||
/**
|
||||
* We create this simple class so that showErrorDialog can differentiate between a regular
|
||||
* auth error and an auth error during the autodiscover sequence and respond appropriately
|
||||
*/
|
||||
private class AutoDiscoverAuthenticationException extends AuthenticationFailedException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public AutoDiscoverAuthenticationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -94,15 +127,52 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
return;
|
||||
}
|
||||
|
||||
mAccount = (EmailContent.Account) getIntent().getParcelableExtra(EXTRA_ACCOUNT);
|
||||
mCheckIncoming = getIntent().getBooleanExtra(EXTRA_CHECK_INCOMING, false);
|
||||
mCheckOutgoing = getIntent().getBooleanExtra(EXTRA_CHECK_OUTGOING, false);
|
||||
final Intent intent = getIntent();
|
||||
mAccount = (EmailContent.Account)intent.getParcelableExtra(EXTRA_ACCOUNT);
|
||||
mCheckIncoming = intent.getBooleanExtra(EXTRA_CHECK_INCOMING, false);
|
||||
mCheckOutgoing = intent.getBooleanExtra(EXTRA_CHECK_OUTGOING, false);
|
||||
mAutoDiscover = intent.getBooleanExtra(EXTRA_AUTO_DISCOVER, false);
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
try {
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
if (mCanceled) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (mAutoDiscover) {
|
||||
String userName = intent.getStringExtra(EXTRA_AUTO_DISCOVER_USERNAME);
|
||||
String password = intent.getStringExtra(EXTRA_AUTO_DISCOVER_PASSWORD);
|
||||
Store store = Store.getInstance(
|
||||
mAccount.getStoreUri(AccountSetupCheckSettings.this),
|
||||
getApplication(), null);
|
||||
Bundle result = store.autoDiscover(AccountSetupCheckSettings.this,
|
||||
userName, password);
|
||||
// Result will be null if there was a remote exception
|
||||
// Otherwise, we can check the exception code and handle auth failed
|
||||
// Other errors will be ignored, and the user will be taken to manual
|
||||
// setup
|
||||
if (result != null) {
|
||||
int errorCode =
|
||||
result.getInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE);
|
||||
if (errorCode == MessagingException.AUTHENTICATION_FAILED) {
|
||||
throw new AutoDiscoverAuthenticationException(null);
|
||||
} else if (errorCode != MessagingException.NO_ERROR) {
|
||||
return;
|
||||
}
|
||||
// The success case is here
|
||||
Intent resultIntent = new Intent();
|
||||
resultIntent.putExtra("HostAuth", result.getParcelable(
|
||||
EmailServiceProxy.AUTO_DISCOVER_BUNDLE_HOST_AUTH));
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
|
@ -135,24 +205,23 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
if (mDestroyed) {
|
||||
return;
|
||||
}
|
||||
if (mCanceled) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
} catch (final AuthenticationFailedException afe) {
|
||||
// Could be two separate blocks (one for AutoDiscover) but this way we save
|
||||
// some code
|
||||
String message = afe.getMessage();
|
||||
int id = (message == null)
|
||||
int id = (message == null)
|
||||
? R.string.account_setup_failed_dlg_auth_message
|
||||
: R.string.account_setup_failed_dlg_auth_message_fmt;
|
||||
showErrorDialog(id, message);
|
||||
showErrorDialog(afe instanceof AutoDiscoverAuthenticationException,
|
||||
id, message);
|
||||
} catch (final CertificateValidationException cve) {
|
||||
String message = cve.getMessage();
|
||||
int id = (message == null)
|
||||
int id = (message == null)
|
||||
? R.string.account_setup_failed_dlg_certificate_message
|
||||
: R.string.account_setup_failed_dlg_certificate_message_fmt;
|
||||
showErrorDialog(id, message);
|
||||
showErrorDialog(false, id, message);
|
||||
} catch (final MessagingException me) {
|
||||
int id;
|
||||
String message = me.getMessage();
|
||||
|
@ -173,12 +242,12 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
id = R.string.account_setup_failed_security;
|
||||
break;
|
||||
default:
|
||||
id = (message == null)
|
||||
id = (message == null)
|
||||
? R.string.account_setup_failed_dlg_server_message
|
||||
: R.string.account_setup_failed_dlg_server_message_fmt;
|
||||
: R.string.account_setup_failed_dlg_server_message_fmt;
|
||||
break;
|
||||
}
|
||||
showErrorDialog(id, message);
|
||||
showErrorDialog(false, id, message);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
|
@ -202,7 +271,13 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
});
|
||||
}
|
||||
|
||||
private void showErrorDialog(final int msgResId, final Object... args) {
|
||||
/**
|
||||
* The first argument here indicates whether we return an OK result or a cancelled result
|
||||
* An OK result is used by Exchange to indicate a failed authentication via AutoDiscover
|
||||
* In that case, we'll end up returning to the AccountSetupBasic screen
|
||||
*/
|
||||
private void showErrorDialog(final boolean autoDiscoverAuthException, final int msgResId,
|
||||
final Object... args) {
|
||||
mHandler.post(new Runnable() {
|
||||
public void run() {
|
||||
if (mDestroyed) {
|
||||
|
@ -218,9 +293,9 @@ public class AccountSetupCheckSettings extends Activity implements OnClickListen
|
|||
getString(R.string.account_setup_failed_dlg_edit_details_action),
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// while debugging connection logic, force a true result
|
||||
// note, this will save possibly-bad settings
|
||||
if (DBG_FORCE_RESULT_OK) {
|
||||
if (autoDiscoverAuthException) {
|
||||
setResult(RESULT_AUTO_DISCOVER_AUTH_FAILED);
|
||||
} else if (DBG_FORCE_RESULT_OK) {
|
||||
setResult(RESULT_OK);
|
||||
}
|
||||
finish();
|
||||
|
|
|
@ -20,6 +20,7 @@ import com.android.email.R;
|
|||
import com.android.email.Utility;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.HostAuth;
|
||||
import com.android.email.service.EmailServiceProxy;
|
||||
import com.android.exchange.SyncManager;
|
||||
|
||||
|
@ -29,6 +30,7 @@ import android.app.Dialog;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
|
@ -147,10 +149,11 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
|||
mAccount = (EmailContent.Account) savedInstanceState.getParcelable(EXTRA_ACCOUNT);
|
||||
}
|
||||
|
||||
String username = null;
|
||||
String password = null;
|
||||
|
||||
try {
|
||||
URI uri = new URI(mAccount.getStoreUri(this));
|
||||
String username = null;
|
||||
String password = null;
|
||||
if (uri.getUserInfo() != null) {
|
||||
String[] userInfoParts = uri.getUserInfo().split(":", 2);
|
||||
username = userInfoParts[0];
|
||||
|
@ -195,6 +198,13 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
|||
}
|
||||
|
||||
validateFields();
|
||||
|
||||
// If we've got a username and password and we're NOT editing, try autodiscover
|
||||
if (username != null && password != null &&
|
||||
!Intent.ACTION_EDIT.equals(getIntent().getAction())) {
|
||||
AccountSetupCheckSettings
|
||||
.actionAutoDiscover(this, mAccount, mAccount.mEmailAddress, password);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -266,36 +276,63 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
|||
Utility.setCompoundDrawablesAlpha(mNextButton, enabled ? 255 : 128);
|
||||
}
|
||||
|
||||
private void doOptions() {
|
||||
boolean easFlowMode = getIntent().getBooleanExtra(EXTRA_EAS_FLOW, false);
|
||||
AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault, easFlowMode);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* We can get here two ways, either by validate returning or by autodiscover returning.
|
||||
*/
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
|
||||
if (mAccount.isSaved()) {
|
||||
// Account.update will NOT save the HostAuth's
|
||||
mAccount.update(this, mAccount.toContentValues());
|
||||
mAccount.mHostAuthRecv.update(this, mAccount.mHostAuthRecv.toContentValues());
|
||||
mAccount.mHostAuthSend.update(this, mAccount.mHostAuthSend.toContentValues());
|
||||
if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
|
||||
// For EAS, notify SyncManager that the password has changed
|
||||
try {
|
||||
new EmailServiceProxy(this, SyncManager.class)
|
||||
.hostChanged(mAccount.mId);
|
||||
} catch (RemoteException e) {
|
||||
// Nothing to be done if this fails
|
||||
if (requestCode == AccountSetupCheckSettings.REQUEST_CODE_VALIDATE) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
|
||||
if (mAccount.isSaved()) {
|
||||
// Account.update will NOT save the HostAuth's
|
||||
mAccount.update(this, mAccount.toContentValues());
|
||||
mAccount.mHostAuthRecv.update(this,
|
||||
mAccount.mHostAuthRecv.toContentValues());
|
||||
mAccount.mHostAuthSend.update(this,
|
||||
mAccount.mHostAuthSend.toContentValues());
|
||||
if (mAccount.mHostAuthRecv.mProtocol.equals("eas")) {
|
||||
// For EAS, notify SyncManager that the password has changed
|
||||
try {
|
||||
new EmailServiceProxy(this, SyncManager.class)
|
||||
.hostChanged(mAccount.mId);
|
||||
} catch (RemoteException e) {
|
||||
// Nothing to be done if this fails
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Account.save will save the HostAuth's
|
||||
mAccount.save(this);
|
||||
}
|
||||
finish();
|
||||
} else {
|
||||
// Account.save will save the HostAuth's
|
||||
mAccount.save(this);
|
||||
// Go directly to end - there is no 2nd screen for incoming settings
|
||||
doOptions();
|
||||
}
|
||||
finish();
|
||||
} else {
|
||||
// Go directly to end - there is no 2nd screen for incoming settings
|
||||
boolean easFlowMode = getIntent().getBooleanExtra(EXTRA_EAS_FLOW, false);
|
||||
AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault, easFlowMode);
|
||||
}
|
||||
} else if (requestCode == AccountSetupCheckSettings.REQUEST_CODE_AUTO_DISCOVER) {
|
||||
// The idea here is that it only matters if we've gotten a HostAuth back from the
|
||||
// autodiscover service call. In all other cases, we can ignore the result
|
||||
if (data != null) {
|
||||
Parcelable p = data.getParcelableExtra("HostAuth");
|
||||
if (p != null) {
|
||||
HostAuth hostAuth = (HostAuth)p;
|
||||
mAccount.mHostAuthSend = hostAuth;
|
||||
mAccount.mHostAuthRecv = hostAuth;
|
||||
doOptions();
|
||||
}
|
||||
// If we've got an auth failed, we need to go back to the basic screen
|
||||
// Otherwise, we just continue on with the Exchange setup screen
|
||||
} else if (resultCode == AccountSetupCheckSettings.RESULT_AUTO_DISCOVER_AUTH_FAILED) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -357,7 +394,7 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
|||
throw new Error(use);
|
||||
}
|
||||
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
|
||||
AccountSetupCheckSettings.actionValidateSettings(this, mAccount, true, false);
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
|
|
|
@ -412,7 +412,7 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
|
|||
}
|
||||
|
||||
mAccount.setDeletePolicy((Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value);
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
|
||||
AccountSetupCheckSettings.actionValidateSettings(this, mAccount, true, false);
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
|
|
|
@ -297,7 +297,7 @@ public class AccountSetupOutgoing extends Activity implements OnClickListener,
|
|||
*/
|
||||
throw new Error(use);
|
||||
}
|
||||
AccountSetupCheckSettings.actionCheckSettings(this, mAccount, false, true);
|
||||
AccountSetupCheckSettings.actionValidateSettings(this, mAccount, false, true);
|
||||
}
|
||||
|
||||
public void onClick(View v) {
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -284,4 +285,17 @@ public abstract class Store {
|
|||
*/
|
||||
public String getPersistentString(String key, String defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle discovery of account settings using only the user's email address and password
|
||||
* @param context the context of the caller
|
||||
* @param emailAddress the email address of the exchange user
|
||||
* @param password the password of the exchange user
|
||||
* @return a Bundle containing an error code and a HostAuth (if successful)
|
||||
* @throws MessagingException
|
||||
*/
|
||||
public Bundle autoDiscover(Context context, String emailAddress, String password)
|
||||
throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,5 +236,20 @@ public class ExchangeStore extends Store {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We handle AutoDiscover for Exchange 2007 (and later) here, wrapping the EmailService call.
|
||||
* The service call returns a HostAuth and we return null if there was a service issue
|
||||
*/
|
||||
@Override
|
||||
public Bundle autoDiscover(Context context, String username, String password)
|
||||
throws MessagingException {
|
||||
try {
|
||||
return new EmailServiceProxy(context, SyncManager.class)
|
||||
.autoDiscover(username, password);
|
||||
} catch (RemoteException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.exchange;
|
||||
package com.android.email.provider;
|
||||
|
||||
parcelable EmailContent.Attachment;
|
||||
parcelable EmailContent.HostAuth;
|
||||
|
|
@ -24,6 +24,7 @@ import android.content.ComponentName;
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.os.Bundle;
|
||||
import android.os.Debug;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
|
@ -48,6 +49,9 @@ public class EmailServiceProxy implements IEmailService {
|
|||
private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE
|
||||
private static final String TAG = "EmailServiceProxy";
|
||||
|
||||
public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code";
|
||||
public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth";
|
||||
|
||||
private Context mContext;
|
||||
private Class<?> mClass;
|
||||
private IEmailServiceCallback mCallback;
|
||||
|
@ -209,6 +213,27 @@ public class EmailServiceProxy implements IEmailService {
|
|||
}
|
||||
}
|
||||
|
||||
public Bundle autoDiscover(final String userName, final String password)
|
||||
throws RemoteException {
|
||||
setTask(new Runnable () {
|
||||
public void run() {
|
||||
try {
|
||||
if (mCallback != null) mService.setCallback(mCallback);
|
||||
mReturn = mService.autoDiscover(userName, password);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
});
|
||||
waitForCompletion();
|
||||
if (mReturn == null) {
|
||||
return null;
|
||||
} else {
|
||||
Bundle bundle = (Bundle) mReturn;
|
||||
Log.v(TAG, "autoDiscover returns " + bundle.getInt(AUTO_DISCOVER_BUNDLE_ERROR_CODE));
|
||||
return bundle;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateFolderList(final long accountId) throws RemoteException {
|
||||
setTask(new Runnable () {
|
||||
public void run() {
|
||||
|
|
|
@ -28,6 +28,7 @@ import com.android.email.provider.EmailContent.HostAuth;
|
|||
import com.android.email.provider.EmailContent.Mailbox;
|
||||
import com.android.email.provider.EmailContent.MailboxColumns;
|
||||
import com.android.email.provider.EmailContent.Message;
|
||||
import com.android.email.service.EmailServiceProxy;
|
||||
import com.android.exchange.adapter.AbstractSyncAdapter;
|
||||
import com.android.exchange.adapter.AccountSyncAdapter;
|
||||
import com.android.exchange.adapter.ContactsSyncAdapter;
|
||||
|
@ -42,25 +43,35 @@ import org.apache.http.Header;
|
|||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpOptions;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.apache.http.params.BasicHttpParams;
|
||||
import org.apache.http.params.HttpConnectionParams;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.Xml;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -94,6 +105,11 @@ public class EasSyncService extends AbstractSyncService {
|
|||
// Define our default protocol version as 2.5 (Exchange 2003)
|
||||
static private final String DEFAULT_PROTOCOL_VERSION = "2.5";
|
||||
|
||||
static private final String AUTO_DISCOVER_SCHEMA_PREFIX =
|
||||
"http://schemas.microsoft.com/exchange/autodiscover/mobilesync/";
|
||||
static private final String AUTO_DISCOVER_PAGE = "/autodiscover/autodiscover.xml";
|
||||
static private final int AUTO_DISCOVER_REDIRECT_CODE = 451;
|
||||
|
||||
/**
|
||||
* We start with an 8 minute timeout, and increase/decrease by 3 minutes at a time. There's
|
||||
* no point having a timeout shorter than 5 minutes, I think; at that point, we can just let
|
||||
|
@ -200,7 +216,7 @@ public class EasSyncService extends AbstractSyncService {
|
|||
* @return whether or not the code represents an authentication error
|
||||
*/
|
||||
protected boolean isAuthError(int code) {
|
||||
return ((code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN));
|
||||
return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -261,6 +277,300 @@ public class EasSyncService extends AbstractSyncService {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the redirect location from the HTTP headers and uses that to modify the HttpPost so that
|
||||
* it can be reused
|
||||
*
|
||||
* @param resp the HttpResponse that indicates a redirect (451)
|
||||
* @param post the HttpPost that was originally sent to the server
|
||||
* @return the HttpPost, updated with the redirect location
|
||||
*/
|
||||
private HttpPost getRedirect(HttpResponse resp, HttpPost post) {
|
||||
Header locHeader = resp.getFirstHeader("X-MS-Location");
|
||||
if (locHeader != null) {
|
||||
String loc = locHeader.getValue();
|
||||
// If we've gotten one and it shows signs of looking like an address, we try
|
||||
// sending our request there
|
||||
if (loc != null && loc.startsWith("http")) {
|
||||
post.setURI(URI.create(loc));
|
||||
return post;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the POST command to the autodiscover server, handling a redirect, if necessary, and
|
||||
* return the HttpResponse
|
||||
*
|
||||
* @param client the HttpClient to be used for the request
|
||||
* @param post the HttpPost we're going to send
|
||||
* @return an HttpResponse from the original or redirect server
|
||||
* @throws IOException on any IOException within the HttpClient code
|
||||
* @throws MessagingException
|
||||
*/
|
||||
private HttpResponse postAutodiscover(HttpClient client, HttpPost post)
|
||||
throws IOException, MessagingException {
|
||||
userLog("Posting autodiscover to: " + post.getURI());
|
||||
HttpResponse resp = client.execute(post);
|
||||
int code = resp.getStatusLine().getStatusCode();
|
||||
// On a redirect, try the new location
|
||||
if (code == AUTO_DISCOVER_REDIRECT_CODE) {
|
||||
post = getRedirect(resp, post);
|
||||
if (post != null) {
|
||||
userLog("Posting autodiscover to redirect: " + post.getURI());
|
||||
return client.execute(post);
|
||||
}
|
||||
} else if (isAuthError(code)) {
|
||||
throw new MessagingException(MessagingException.AUTHENTICATION_FAILED);
|
||||
} else if (code != HttpStatus.SC_OK) {
|
||||
// We'll try the next address if this doesn't work
|
||||
userLog("Code: " + code + ", throwing IOException");
|
||||
throw new IOException();
|
||||
}
|
||||
return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the Exchange 2007 AutoDiscover feature to try to retrieve server information using
|
||||
* only an email address and the password
|
||||
*
|
||||
* @param userName the user's email address
|
||||
* @param password the user's password
|
||||
* @return a HostAuth ready to be saved in an Account or null (failure)
|
||||
*/
|
||||
public Bundle tryAutodiscover(String userName, String password) throws RemoteException {
|
||||
XmlSerializer s = Xml.newSerializer();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
|
||||
HostAuth hostAuth = new HostAuth();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
|
||||
MessagingException.NO_ERROR);
|
||||
try {
|
||||
// Build the XML document that's sent to the autodiscover server(s)
|
||||
s.setOutput(os, "UTF-8");
|
||||
s.startDocument("UTF-8", false);
|
||||
s.startTag(null, "Autodiscover");
|
||||
s.attribute(null, "xmlns", AUTO_DISCOVER_SCHEMA_PREFIX + "requestschema/2006");
|
||||
s.startTag(null, "Request");
|
||||
s.startTag(null, "EMailAddress").text(userName).endTag(null, "EMailAddress");
|
||||
s.startTag(null, "AcceptableResponseSchema");
|
||||
s.text(AUTO_DISCOVER_SCHEMA_PREFIX + "responseschema/2006");
|
||||
s.endTag(null, "AcceptableResponseSchema");
|
||||
s.endTag(null, "Request");
|
||||
s.endTag(null, "Autodiscover");
|
||||
s.endDocument();
|
||||
String req = os.toString();
|
||||
|
||||
// Initialize the user name and password
|
||||
mUserName = userName;
|
||||
mPassword = password;
|
||||
// Make sure the authentication string is created (mAuthString)
|
||||
makeUriString("foo", null);
|
||||
|
||||
// Split out the domain name
|
||||
int amp = userName.indexOf('@');
|
||||
// The UI ensures that userName is a valid email address
|
||||
if (amp < 0) {
|
||||
throw new RemoteException();
|
||||
}
|
||||
String domain = userName.substring(amp + 1);
|
||||
|
||||
// There are up to four attempts here; the two URLs that we're supposed to try per the
|
||||
// specification, and up to one redirect for each (handled in postAutodiscover)
|
||||
|
||||
// Try the domain first and see if we can get a response
|
||||
HttpPost post = new HttpPost("https://" + domain + AUTO_DISCOVER_PAGE);
|
||||
setHeaders(post);
|
||||
post.setHeader("Content-Type", "text/xml");
|
||||
post.setEntity(new StringEntity(req));
|
||||
HttpClient client = getHttpClient(COMMAND_TIMEOUT);
|
||||
HttpResponse resp;
|
||||
try {
|
||||
resp = postAutodiscover(client, post);
|
||||
} catch (ClientProtocolException e1) {
|
||||
return null;
|
||||
} catch (IOException e1) {
|
||||
// We catch the IOException here because we have an alternate address to try
|
||||
post.setURI(URI.create("https://autodiscover." + domain + AUTO_DISCOVER_PAGE));
|
||||
// If we fail here, we're out of options, so we let the outer try catch the
|
||||
// IOException and return null
|
||||
resp = postAutodiscover(client, post);
|
||||
}
|
||||
|
||||
// Get the "final" code; if it's not 200, just return null
|
||||
int code = resp.getStatusLine().getStatusCode();
|
||||
userLog("Code: " + code);
|
||||
if (code != HttpStatus.SC_OK) return null;
|
||||
|
||||
// At this point, we have a 200 response (SC_OK)
|
||||
HttpEntity e = resp.getEntity();
|
||||
InputStream is = e.getContent();
|
||||
try {
|
||||
// The response to Autodiscover is regular XML (not WBXML)
|
||||
// If we ever get an error in this process, we'll just punt and return null
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
XmlPullParser parser = factory.newPullParser();
|
||||
parser.setInput(is, "UTF-8");
|
||||
int type = parser.getEventType();
|
||||
if (type == XmlPullParser.START_DOCUMENT) {
|
||||
type = parser.next();
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
if (name.equals("Autodiscover")) {
|
||||
hostAuth = new HostAuth();
|
||||
parseAutodiscover(parser, hostAuth);
|
||||
// On success, we'll have a server address and login
|
||||
if (hostAuth.mAddress != null && hostAuth.mLogin != null) {
|
||||
// Fill in the rest of the HostAuth
|
||||
hostAuth.mPassword = password;
|
||||
hostAuth.mPort = 443;
|
||||
hostAuth.mProtocol = "eas";
|
||||
hostAuth.mFlags =
|
||||
HostAuth.FLAG_SSL | HostAuth.FLAG_AUTHENTICATE;
|
||||
bundle.putParcelable(
|
||||
EmailServiceProxy.AUTO_DISCOVER_BUNDLE_HOST_AUTH, hostAuth);
|
||||
} else {
|
||||
bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
|
||||
MessagingException.UNSPECIFIED_EXCEPTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (XmlPullParserException e1) {
|
||||
// This would indicate an I/O error of some sort
|
||||
// We will simply return null and user can configure manually
|
||||
}
|
||||
// There's no reason at all for exceptions to be thrown, and it's ok if so.
|
||||
// We just won't do auto-discover; user can configure manually
|
||||
} catch (IllegalArgumentException e) {
|
||||
bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
|
||||
MessagingException.UNSPECIFIED_EXCEPTION);
|
||||
} catch (IllegalStateException e) {
|
||||
bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
|
||||
MessagingException.UNSPECIFIED_EXCEPTION);
|
||||
} catch (IOException e) {
|
||||
userLog("IOException in Autodiscover", e);
|
||||
bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
|
||||
MessagingException.IOERROR);
|
||||
} catch (MessagingException e) {
|
||||
bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE,
|
||||
MessagingException.AUTHENTICATION_FAILED);
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
void parseServer(XmlPullParser parser, HostAuth hostAuth)
|
||||
throws XmlPullParserException, IOException {
|
||||
boolean mobileSync = false;
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
if (type == XmlPullParser.END_TAG && parser.getName().equals("Server")) {
|
||||
break;
|
||||
} else if (type == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
if (name.equals("Type")) {
|
||||
if (parser.nextText().equals("MobileSync")) {
|
||||
mobileSync = true;
|
||||
}
|
||||
} else if (mobileSync && name.equals("Url")) {
|
||||
String url = parser.nextText().toLowerCase();
|
||||
// This will look like https://<server address>/Microsoft-Server-ActiveSync
|
||||
// We need to extract the <server address>
|
||||
if (url.startsWith("https://") &&
|
||||
url.endsWith("/microsoft-server-activesync")) {
|
||||
int lastSlash = url.lastIndexOf('/');
|
||||
hostAuth.mAddress = url.substring(8, lastSlash);
|
||||
userLog("Autodiscover, server: " + hostAuth.mAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseSettings(XmlPullParser parser, HostAuth hostAuth)
|
||||
throws XmlPullParserException, IOException {
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
if (type == XmlPullParser.END_TAG && parser.getName().equals("Settings")) {
|
||||
break;
|
||||
} else if (type == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
if (name.equals("Server")) {
|
||||
parseServer(parser, hostAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseAction(XmlPullParser parser, HostAuth hostAuth)
|
||||
throws XmlPullParserException, IOException {
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
if (type == XmlPullParser.END_TAG && parser.getName().equals("Action")) {
|
||||
break;
|
||||
} else if (type == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
if (name.equals("Error")) {
|
||||
// Should parse the error
|
||||
} else if (name.equals("Redirect")) {
|
||||
Log.d(TAG, "Redirect: " + parser.nextText());
|
||||
} else if (name.equals("Settings")) {
|
||||
parseSettings(parser, hostAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseUser(XmlPullParser parser, HostAuth hostAuth)
|
||||
throws XmlPullParserException, IOException {
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
if (type == XmlPullParser.END_TAG && parser.getName().equals("User")) {
|
||||
break;
|
||||
} else if (type == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
if (name.equals("EMailAddress")) {
|
||||
String addr = parser.nextText();
|
||||
hostAuth.mLogin = addr;
|
||||
userLog("Autodiscover, login: " + addr);
|
||||
} else if (name.equals("DisplayName")) {
|
||||
String dn = parser.nextText();
|
||||
userLog("Autodiscover, user: " + dn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseResponse(XmlPullParser parser, HostAuth hostAuth)
|
||||
throws XmlPullParserException, IOException {
|
||||
while (true) {
|
||||
int type = parser.next();
|
||||
if (type == XmlPullParser.END_TAG && parser.getName().equals("Response")) {
|
||||
break;
|
||||
} else if (type == XmlPullParser.START_TAG) {
|
||||
String name = parser.getName();
|
||||
if (name.equals("User")) {
|
||||
parseUser(parser, hostAuth);
|
||||
} else if (name.equals("Action")) {
|
||||
parseAction(parser, hostAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseAutodiscover(XmlPullParser parser, HostAuth hostAuth)
|
||||
throws XmlPullParserException, IOException {
|
||||
while (true) {
|
||||
int type = parser.nextTag();
|
||||
if (type == XmlPullParser.END_TAG && parser.getName().equals("Autodiscover")) {
|
||||
break;
|
||||
} else if (type == XmlPullParser.START_TAG && parser.getName().equals("Response")) {
|
||||
parseResponse(parser, hostAuth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void doStatusCallback(long messageId, long attachmentId, int status) {
|
||||
try {
|
||||
SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, status, 0);
|
||||
|
@ -372,7 +682,7 @@ public class EasSyncService extends AbstractSyncService {
|
|||
errorLog("totalRead is greater than attachment length?");
|
||||
break;
|
||||
}
|
||||
int pct = (totalRead * 100 / length);
|
||||
int pct = (totalRead * 100) / length;
|
||||
doProgressCallback(msg.mId, att.mId, pct);
|
||||
}
|
||||
}
|
||||
|
@ -721,7 +1031,6 @@ public class EasSyncService extends AbstractSyncService {
|
|||
int pingStatus = SyncManager.pingStatus(mailboxId);
|
||||
String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
|
||||
if (pingStatus == SyncManager.PING_STATUS_OK) {
|
||||
|
||||
String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
|
||||
if ((syncKey == null) || syncKey.equals("0")) {
|
||||
// We can't push until the initial sync is done
|
||||
|
@ -1156,7 +1465,7 @@ public class EasSyncService extends AbstractSyncService {
|
|||
}
|
||||
} catch (IOException e) {
|
||||
String message = e.getMessage();
|
||||
userLog("Caught IOException: ", ((message == null) ? "No message" : message));
|
||||
userLog("Caught IOException: ", (message == null) ? "No message" : message);
|
||||
mExitStatus = EXIT_IO_ERROR;
|
||||
} catch (Exception e) {
|
||||
userLog("Uncaught exception in EasSyncService", e);
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
package com.android.exchange;
|
||||
|
||||
import com.android.exchange.IEmailServiceCallback;
|
||||
import com.android.exchange.EmailContent;
|
||||
import com.android.email.provider.EmailContent;
|
||||
import android.os.Bundle;
|
||||
|
||||
interface IEmailService {
|
||||
int validate(in String protocol, in String host, in String userName, in String password,
|
||||
|
@ -40,4 +41,6 @@ interface IEmailService {
|
|||
void setLogging(int on);
|
||||
|
||||
void hostChanged(long accountId);
|
||||
|
||||
Bundle autoDiscover(String userName, String password);
|
||||
}
|
|
@ -277,6 +277,10 @@ public class SyncManager extends Service implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
public Bundle autoDiscover(String userName, String password) throws RemoteException {
|
||||
return new EasSyncService().tryAutodiscover(userName, password);
|
||||
}
|
||||
|
||||
public void startSync(long mailboxId) throws RemoteException {
|
||||
checkSyncManagerServiceRunning();
|
||||
Mailbox m = Mailbox.restoreMailboxWithId(INSTANCE, mailboxId);
|
||||
|
|
Loading…
Reference in New Issue