replicant-packages_apps_Email/src/com/android/email/service/EmailServiceUtils.java
Martin Hibdon 9195a12024 Create AuthenticationFragment
This is one fragment that holds all types of
authentication information, e.g. password,
OAuth info, and client certificates. What gets
displayed depends upon the type of account it is
dealing with.
So far this is only used in AccountSetupIncoming,
but later it can be added to other settings fragments.
There are still some issues with this, but I'd like
to check it in sooner than later to unblock other
work.

Change-Id: Iea675ad5c1727f32ca0baa270dfa793ab7109993
2014-01-29 11:20:53 -08:00

723 lines
32 KiB
Java

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.service;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
import android.provider.CalendarContract.SyncState;
import android.provider.ContactsContract;
import android.provider.ContactsContract.RawContacts;
import android.provider.SyncStateContract;
import com.android.email.R;
import com.android.emailcommon.Api;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.AccountColumns;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.service.EmailServiceProxy;
import com.android.emailcommon.service.IEmailService;
import com.android.emailcommon.service.IEmailServiceCallback;
import com.android.emailcommon.service.SearchParams;
import com.android.emailcommon.service.ServiceProxy;
import com.android.emailcommon.service.SyncWindow;
import com.android.mail.utils.LogUtils;
import com.google.common.collect.ImmutableMap;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
/**
* Utility functions for EmailService support.
*/
public class EmailServiceUtils {
/**
* Ask a service to kill its process. This is used when an account is deleted so that
* no background thread that happens to be running will continue, possibly hitting an
* NPE or other error when trying to operate on an account that no longer exists.
* TODO: This is kind of a hack, it's only needed because we fail so badly if an account
* is deleted out from under us while a sync or other operation is in progress. It would
* be a lot cleaner if our background services could handle this without crashing.
*/
public static void killService(Context context, String protocol) {
EmailServiceInfo info = getServiceInfo(context, protocol);
if (info != null && info.intentAction != null) {
final Intent serviceIntent = getServiceIntent(info);
serviceIntent.putExtra(ServiceProxy.EXTRA_FORCE_SHUTDOWN, true);
context.startService(serviceIntent);
}
}
/**
* Starts an EmailService by protocol
*/
public static void startService(Context context, String protocol) {
EmailServiceInfo info = getServiceInfo(context, protocol);
if (info != null && info.intentAction != null) {
final Intent serviceIntent = getServiceIntent(info);
context.startService(serviceIntent);
}
}
/**
* Starts all remote services
*/
public static void startRemoteServices(Context context) {
for (EmailServiceInfo info: getServiceInfoList(context)) {
if (info.intentAction != null) {
final Intent serviceIntent = getServiceIntent(info);
context.startService(serviceIntent);
}
}
}
/**
* Returns whether or not remote services are present on device
*/
public static boolean areRemoteServicesInstalled(Context context) {
for (EmailServiceInfo info: getServiceInfoList(context)) {
if (info.intentAction != null) {
return true;
}
}
return false;
}
/**
* Starts all remote services
*/
public static void setRemoteServicesLogging(Context context, int debugBits) {
for (EmailServiceInfo info: getServiceInfoList(context)) {
if (info.intentAction != null) {
EmailServiceProxy service =
EmailServiceUtils.getService(context, info.protocol);
if (service != null) {
try {
service.setLogging(debugBits);
} catch (RemoteException e) {
// Move along, nothing to see
}
}
}
}
}
/**
* Determine if the EmailService is available
*/
public static boolean isServiceAvailable(Context context, String protocol) {
EmailServiceInfo info = getServiceInfo(context, protocol);
if (info == null) return false;
if (info.klass != null) return true;
final Intent serviceIntent = getServiceIntent(info);
return new EmailServiceProxy(context, serviceIntent).test();
}
private static Intent getServiceIntent(EmailServiceInfo info) {
final Intent serviceIntent = new Intent(info.intentAction);
serviceIntent.setPackage(info.intentPackage);
return serviceIntent;
}
/**
* For a given account id, return a service proxy if applicable, or null.
*
* @param accountId the message of interest
* @return service proxy, or null if n/a
*/
public static EmailServiceProxy getServiceForAccount(Context context, long accountId) {
return getService(context, Account.getProtocol(context, accountId));
}
/**
* Holder of service information (currently just name and class/intent); if there is a class
* member, this is a (local, i.e. same process) service; otherwise, this is a remote service
*/
public static class EmailServiceInfo {
public String protocol;
public String name;
public String accountType;
Class<? extends Service> klass;
String intentAction;
String intentPackage;
public int port;
public int portSsl;
public boolean defaultSsl;
public boolean offerTls;
public boolean offerCerts;
public boolean offerOAuth;
public boolean usesSmtp;
public boolean offerLocalDeletes;
public int defaultLocalDeletes;
public boolean offerPrefix;
public boolean usesAutodiscover;
public boolean offerLookback;
public int defaultLookback;
public boolean syncChanges;
public boolean syncContacts;
public boolean syncCalendar;
public boolean offerAttachmentPreload;
public CharSequence[] syncIntervalStrings;
public CharSequence[] syncIntervals;
public int defaultSyncInterval;
public String inferPrefix;
public boolean offerLoadMore;
public boolean offerMoveTo;
public boolean requiresSetup;
public boolean hide;
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Protocol: ");
sb.append(protocol);
sb.append(", ");
sb.append(klass != null ? "Local" : "Remote");
sb.append(" , Account Type: ");
sb.append(accountType);
return sb.toString();
}
}
public static EmailServiceProxy getService(Context context, String protocol) {
EmailServiceInfo info = null;
// Handle the degenerate case here (account might have been deleted)
if (protocol != null) {
info = getServiceInfo(context, protocol);
}
if (info == null) {
LogUtils.w(Logging.LOG_TAG, "Returning NullService for " + protocol);
return new EmailServiceProxy(context, NullService.class);
} else {
return getServiceFromInfo(context, info);
}
}
public static EmailServiceProxy getServiceFromInfo(Context context, EmailServiceInfo info) {
if (info.klass != null) {
return new EmailServiceProxy(context, info.klass);
} else {
final Intent serviceIntent = getServiceIntent(info);
return new EmailServiceProxy(context, serviceIntent);
}
}
public static EmailServiceInfo getServiceInfoForAccount(Context context, long accountId) {
String protocol = Account.getProtocol(context, accountId);
return getServiceInfo(context, protocol);
}
public static EmailServiceInfo getServiceInfo(Context context, String protocol) {
return getServiceMap(context).get(protocol);
}
public static Collection<EmailServiceInfo> getServiceInfoList(Context context) {
return getServiceMap(context).values();
}
private static void finishAccountManagerBlocker(AccountManagerFuture<?> future) {
try {
// Note: All of the potential errors are simply logged
// here, as there is nothing to actually do about them.
future.getResult();
} catch (OperationCanceledException e) {
LogUtils.w(Logging.LOG_TAG, e.toString());
} catch (AuthenticatorException e) {
LogUtils.w(Logging.LOG_TAG, e.toString());
} catch (IOException e) {
LogUtils.w(Logging.LOG_TAG, e.toString());
}
}
/**
* Add an account to the AccountManager.
* @param context Our {@link Context}.
* @param account The {@link Account} we're adding.
* @param email Whether the user wants to sync email on this account.
* @param calendar Whether the user wants to sync calendar on this account.
* @param contacts Whether the user wants to sync contacts on this account.
* @param callback A callback for when the AccountManager is done.
* @return The result of {@link AccountManager#addAccount}.
*/
public static AccountManagerFuture<Bundle> setupAccountManagerAccount(final Context context,
final Account account, final boolean email, final boolean calendar,
final boolean contacts, final AccountManagerCallback<Bundle> callback) {
final Bundle options = new Bundle(5);
final HostAuth hostAuthRecv =
HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
if (hostAuthRecv == null) {
return null;
}
// Set up username/password
options.putString(EasAuthenticatorService.OPTIONS_USERNAME, account.mEmailAddress);
options.putString(EasAuthenticatorService.OPTIONS_PASSWORD, hostAuthRecv.mPassword);
options.putBoolean(EasAuthenticatorService.OPTIONS_CONTACTS_SYNC_ENABLED, contacts);
options.putBoolean(EasAuthenticatorService.OPTIONS_CALENDAR_SYNC_ENABLED, calendar);
options.putBoolean(EasAuthenticatorService.OPTIONS_EMAIL_SYNC_ENABLED, email);
final EmailServiceInfo info = getServiceInfo(context, hostAuthRecv.mProtocol);
return AccountManager.get(context).addAccount(info.accountType, null, null, options, null,
callback, null);
}
public static void updateAccountManagerType(Context context,
android.accounts.Account amAccount, final Map<String, String> protocolMap) {
final ContentResolver resolver = context.getContentResolver();
final Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION,
AccountColumns.EMAIL_ADDRESS + "=?", new String[] { amAccount.name }, null);
// That's odd, isn't it?
if (c == null) return;
try {
if (c.moveToNext()) {
// Get the EmailProvider Account/HostAuth
final Account account = new Account();
account.restore(c);
final HostAuth hostAuth =
HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv);
if (hostAuth == null) {
return;
}
final String newProtocol = protocolMap.get(hostAuth.mProtocol);
if (newProtocol == null) {
// This account doesn't need updating.
return;
}
LogUtils.w(Logging.LOG_TAG, "Converting " + amAccount.name + " to "
+ newProtocol);
final ContentValues accountValues = new ContentValues();
int oldFlags = account.mFlags;
// Mark the provider account incomplete so it can't get reconciled away
account.mFlags |= Account.FLAGS_INCOMPLETE;
accountValues.put(AccountColumns.FLAGS, account.mFlags);
final Uri accountUri = ContentUris.withAppendedId(Account.CONTENT_URI, account.mId);
resolver.update(accountUri, accountValues, null, null);
// Change the HostAuth to reference the new protocol; this has to be done before
// trying to create the AccountManager account (below)
final ContentValues hostValues = new ContentValues();
hostValues.put(HostAuth.PROTOCOL, newProtocol);
resolver.update(ContentUris.withAppendedId(HostAuth.CONTENT_URI, hostAuth.mId),
hostValues, null, null);
LogUtils.w(Logging.LOG_TAG, "Updated HostAuths");
try {
// Get current settings for the existing AccountManager account
boolean email = ContentResolver.getSyncAutomatically(amAccount,
EmailContent.AUTHORITY);
if (!email) {
// Try our old provider name
email = ContentResolver.getSyncAutomatically(amAccount,
"com.android.email.provider");
}
final boolean contacts = ContentResolver.getSyncAutomatically(amAccount,
ContactsContract.AUTHORITY);
final boolean calendar = ContentResolver.getSyncAutomatically(amAccount,
CalendarContract.AUTHORITY);
LogUtils.w(Logging.LOG_TAG, "Email: " + email + ", Contacts: " + contacts + ","
+ " Calendar: " + calendar);
// Get sync keys for calendar/contacts
final String amName = amAccount.name;
final String oldType = amAccount.type;
ContentProviderClient client = context.getContentResolver()
.acquireContentProviderClient(CalendarContract.CONTENT_URI);
byte[] calendarSyncKey = null;
try {
calendarSyncKey = SyncStateContract.Helpers.get(client,
asCalendarSyncAdapter(SyncState.CONTENT_URI, amName, oldType),
new android.accounts.Account(amName, oldType));
} catch (RemoteException e) {
LogUtils.w(Logging.LOG_TAG, "Get calendar key FAILED");
} finally {
client.release();
}
client = context.getContentResolver()
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
byte[] contactsSyncKey = null;
try {
contactsSyncKey = SyncStateContract.Helpers.get(client,
ContactsContract.SyncState.CONTENT_URI,
new android.accounts.Account(amName, oldType));
} catch (RemoteException e) {
LogUtils.w(Logging.LOG_TAG, "Get contacts key FAILED");
} finally {
client.release();
}
if (calendarSyncKey != null) {
LogUtils.w(Logging.LOG_TAG, "Got calendar key: "
+ new String(calendarSyncKey));
}
if (contactsSyncKey != null) {
LogUtils.w(Logging.LOG_TAG, "Got contacts key: "
+ new String(contactsSyncKey));
}
// Set up a new AccountManager account with new type and old settings
AccountManagerFuture<?> amFuture = setupAccountManagerAccount(context, account,
email, calendar, contacts, null);
finishAccountManagerBlocker(amFuture);
LogUtils.w(Logging.LOG_TAG, "Created new AccountManager account");
// TODO: Clean up how we determine the type.
final String accountType = protocolMap.get(hostAuth.mProtocol + "_type");
// Move calendar and contacts data from the old account to the new one.
// We must do this before deleting the old account or the data is lost.
moveCalendarData(context.getContentResolver(), amName, oldType, accountType);
moveContactsData(context.getContentResolver(), amName, oldType, accountType);
// Delete the AccountManager account
amFuture = AccountManager.get(context)
.removeAccount(amAccount, null, null);
finishAccountManagerBlocker(amFuture);
LogUtils.w(Logging.LOG_TAG, "Deleted old AccountManager account");
// Restore sync keys for contacts/calendar
if (accountType != null &&
calendarSyncKey != null && calendarSyncKey.length != 0) {
client = context.getContentResolver()
.acquireContentProviderClient(CalendarContract.CONTENT_URI);
try {
SyncStateContract.Helpers.set(client,
asCalendarSyncAdapter(SyncState.CONTENT_URI, amName,
accountType),
new android.accounts.Account(amName, accountType),
calendarSyncKey);
LogUtils.w(Logging.LOG_TAG, "Set calendar key...");
} catch (RemoteException e) {
LogUtils.w(Logging.LOG_TAG, "Set calendar key FAILED");
} finally {
client.release();
}
}
if (accountType != null &&
contactsSyncKey != null && contactsSyncKey.length != 0) {
client = context.getContentResolver()
.acquireContentProviderClient(ContactsContract.AUTHORITY_URI);
try {
SyncStateContract.Helpers.set(client,
ContactsContract.SyncState.CONTENT_URI,
new android.accounts.Account(amName, accountType),
contactsSyncKey);
LogUtils.w(Logging.LOG_TAG, "Set contacts key...");
} catch (RemoteException e) {
LogUtils.w(Logging.LOG_TAG, "Set contacts key FAILED");
}
}
// That's all folks!
LogUtils.w(Logging.LOG_TAG, "Account update completed.");
} finally {
// Clear the incomplete flag on the provider account
accountValues.put(AccountColumns.FLAGS, oldFlags);
resolver.update(accountUri, accountValues, null, null);
LogUtils.w(Logging.LOG_TAG, "[Incomplete flag cleared]");
}
}
} finally {
c.close();
}
}
private static void moveCalendarData(final ContentResolver resolver, final String name,
final String oldType, final String newType) {
final Uri oldCalendars = Calendars.CONTENT_URI.buildUpon()
.appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(Calendars.ACCOUNT_NAME, name)
.appendQueryParameter(Calendars.ACCOUNT_TYPE, oldType)
.build();
// Update this calendar to have the new account type.
final ContentValues values = new ContentValues();
values.put(CalendarContract.Calendars.ACCOUNT_TYPE, newType);
resolver.update(oldCalendars, values,
Calendars.ACCOUNT_NAME + "=? AND " + Calendars.ACCOUNT_TYPE + "=?",
new String[] {name, oldType});
}
private static void moveContactsData(final ContentResolver resolver, final String name,
final String oldType, final String newType) {
final Uri oldContacts = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(RawContacts.ACCOUNT_NAME, name)
.appendQueryParameter(RawContacts.ACCOUNT_TYPE, oldType)
.build();
// Update this calendar to have the new account type.
final ContentValues values = new ContentValues();
values.put(CalendarContract.Calendars.ACCOUNT_TYPE, newType);
resolver.update(oldContacts, values, null, null);
}
private static final Configuration sOldConfiguration = new Configuration();
private static Map<String, EmailServiceInfo> sServiceMap = null;
private static final Object sServiceMapLock = new Object();
/**
* Parse services.xml file to find our available email services
*/
private static Map<String, EmailServiceInfo> getServiceMap(final Context context) {
synchronized (sServiceMapLock) {
/**
* We cache localized strings here, so make sure to regenerate the service map if
* the locale changes
*/
if (sServiceMap == null) {
sOldConfiguration.setTo(context.getResources().getConfiguration());
}
final int delta =
sOldConfiguration.updateFrom(context.getResources().getConfiguration());
if (sServiceMap != null
&& !Configuration.needNewResources(delta, ActivityInfo.CONFIG_LOCALE)) {
return sServiceMap;
}
final ImmutableMap.Builder<String, EmailServiceInfo> builder = ImmutableMap.builder();
try {
final Resources res = context.getResources();
final XmlResourceParser xml = res.getXml(R.xml.services);
int xmlEventType;
// walk through senders.xml file.
while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
if (xmlEventType == XmlResourceParser.START_TAG &&
"emailservice".equals(xml.getName())) {
final EmailServiceInfo info = new EmailServiceInfo();
final TypedArray ta =
res.obtainAttributes(xml, R.styleable.EmailServiceInfo);
info.protocol = ta.getString(R.styleable.EmailServiceInfo_protocol);
info.accountType = ta.getString(R.styleable.EmailServiceInfo_accountType);
info.name = ta.getString(R.styleable.EmailServiceInfo_name);
info.hide = ta.getBoolean(R.styleable.EmailServiceInfo_hide, false);
final String klass =
ta.getString(R.styleable.EmailServiceInfo_serviceClass);
info.intentAction = ta.getString(R.styleable.EmailServiceInfo_intent);
info.intentPackage =
ta.getString(R.styleable.EmailServiceInfo_intentPackage);
info.defaultSsl =
ta.getBoolean(R.styleable.EmailServiceInfo_defaultSsl, false);
info.port = ta.getInteger(R.styleable.EmailServiceInfo_port, 0);
info.portSsl = ta.getInteger(R.styleable.EmailServiceInfo_portSsl, 0);
info.offerTls = ta.getBoolean(R.styleable.EmailServiceInfo_offerTls, false);
info.offerCerts =
ta.getBoolean(R.styleable.EmailServiceInfo_offerCerts, false);
info.offerOAuth =
ta.getBoolean(R.styleable.EmailServiceInfo_offerOAuth, false);
info.offerLocalDeletes =
ta.getBoolean(R.styleable.EmailServiceInfo_offerLocalDeletes, false);
info.defaultLocalDeletes =
ta.getInteger(R.styleable.EmailServiceInfo_defaultLocalDeletes,
Account.DELETE_POLICY_ON_DELETE);
info.offerPrefix =
ta.getBoolean(R.styleable.EmailServiceInfo_offerPrefix, false);
info.usesSmtp = ta.getBoolean(R.styleable.EmailServiceInfo_usesSmtp, false);
info.usesAutodiscover =
ta.getBoolean(R.styleable.EmailServiceInfo_usesAutodiscover, false);
info.offerLookback =
ta.getBoolean(R.styleable.EmailServiceInfo_offerLookback, false);
info.defaultLookback =
ta.getInteger(R.styleable.EmailServiceInfo_defaultLookback,
SyncWindow.SYNC_WINDOW_3_DAYS);
info.syncChanges =
ta.getBoolean(R.styleable.EmailServiceInfo_syncChanges, false);
info.syncContacts =
ta.getBoolean(R.styleable.EmailServiceInfo_syncContacts, false);
info.syncCalendar =
ta.getBoolean(R.styleable.EmailServiceInfo_syncCalendar, false);
info.offerAttachmentPreload =
ta.getBoolean(R.styleable.EmailServiceInfo_offerAttachmentPreload,
false);
info.syncIntervalStrings =
ta.getTextArray(R.styleable.EmailServiceInfo_syncIntervalStrings);
info.syncIntervals =
ta.getTextArray(R.styleable.EmailServiceInfo_syncIntervals);
info.defaultSyncInterval =
ta.getInteger(R.styleable.EmailServiceInfo_defaultSyncInterval, 15);
info.inferPrefix = ta.getString(R.styleable.EmailServiceInfo_inferPrefix);
info.offerLoadMore =
ta.getBoolean(R.styleable.EmailServiceInfo_offerLoadMore, false);
info.offerMoveTo =
ta.getBoolean(R.styleable.EmailServiceInfo_offerMoveTo, false);
info.requiresSetup =
ta.getBoolean(R.styleable.EmailServiceInfo_requiresSetup, false);
// Must have either "class" (local) or "intent" (remote)
if (klass != null) {
try {
// noinspection unchecked
info.klass = (Class<? extends Service>) Class.forName(klass);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(
"Class not found in service descriptor: " + klass);
}
}
if (info.klass == null && info.intentAction == null) {
throw new IllegalStateException(
"No class or intent action specified in service descriptor");
}
if (info.klass != null && info.intentAction != null) {
throw new IllegalStateException(
"Both class and intent action specified in service descriptor");
}
builder.put(info.protocol, info);
}
}
} catch (XmlPullParserException e) {
// ignore
} catch (IOException e) {
// ignore
}
sServiceMap = builder.build();
return sServiceMap;
}
}
private static Uri asCalendarSyncAdapter(Uri uri, String account, String accountType) {
return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
.appendQueryParameter(Calendars.ACCOUNT_NAME, account)
.appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
}
/**
* A no-op service that can be returned for non-existent/null protocols
*/
class NullService implements IEmailService {
@Override
public IBinder asBinder() {
return null;
}
@Override
public Bundle validate(HostAuth hostauth) throws RemoteException {
return null;
}
@Override
public void startSync(long mailboxId, boolean userRequest, int deltaMessageCount)
throws RemoteException {
}
@Override
public void stopSync(long mailboxId) throws RemoteException {
}
@Override
public void loadMore(long messageId) throws RemoteException {
}
@Override
public void loadAttachment(final IEmailServiceCallback cb, final long attachmentId,
final boolean background) throws RemoteException {
}
@Override
public void updateFolderList(long accountId) throws RemoteException {
}
@Override
public boolean createFolder(long accountId, String name) throws RemoteException {
return false;
}
@Override
public boolean deleteFolder(long accountId, String name) throws RemoteException {
return false;
}
@Override
public boolean renameFolder(long accountId, String oldName, String newName)
throws RemoteException {
return false;
}
@Override
public void setLogging(int on) throws RemoteException {
}
@Override
public void hostChanged(long accountId) throws RemoteException {
}
@Override
public Bundle autoDiscover(String userName, String password) throws RemoteException {
return null;
}
@Override
public void sendMeetingResponse(long messageId, int response) throws RemoteException {
}
@Override
public void deleteAccountPIMData(final String emailAddress) throws RemoteException {
}
@Override
public int getApiLevel() throws RemoteException {
return Api.LEVEL;
}
@Override
public int searchMessages(long accountId, SearchParams params, long destMailboxId)
throws RemoteException {
return 0;
}
@Override
public void sendMail(long accountId) throws RemoteException {
}
@Override
public void serviceUpdated(String emailAddress) throws RemoteException {
}
@Override
public int getCapabilities(Account acct) throws RemoteException {
return 0;
}
}
}