From 0d4fc55861ed4393aa82f124f2865695ef564641 Mon Sep 17 00:00:00 2001 From: Marc Blank Date: Tue, 8 Feb 2011 17:50:30 -0800 Subject: [PATCH] Email split, part one: EmailService * Create emailcommon package * Move EmailService classes to emailcommon package * Change references to aidl's to emailcommon package * Add getApiLevel() command to EmailService Bug: 3442973 Change-Id: Ic7d2115363cdff6ebb86c46650b0a5b2109b1c72 --- Android.mk | 6 +- src/com/android/email/Controller.java | 14 +- src/com/android/email/ExchangeUtils.java | 12 +- .../email/activity/MessageViewFragment.java | 2 +- .../setup/AccountCheckSettingsFragment.java | 2 +- .../email/mail/store/ExchangeStore.java | 4 +- .../android/email/mail/store/ImapStore.java | 2 +- .../android/email/mail/store/Pop3Store.java | 2 +- .../service/AttachmentDownloadService.java | 7 +- .../email/service/EmailServiceProxy.java | 354 ------------------ src/com/android/emailcommon/Api.java | 24 ++ .../service/EmailServiceConstants.java | 2 +- .../service/EmailServiceProxy.java | 264 +++++++++++++ .../service/EmailServiceStatus.java | 2 +- .../service/IEmailService.aidl | 29 +- .../service/IEmailServiceCallback.aidl | 2 +- .../emailcommon/service/ServiceProxy.java | 196 ++++++++++ .../android/exchange/EasOutboxService.java | 2 +- src/com/android/exchange/EasSyncService.java | 6 +- src/com/android/exchange/ExchangeService.java | 20 +- .../AttachmentDownloadServiceTests.java | 1 + 21 files changed, 555 insertions(+), 398 deletions(-) delete mode 100644 src/com/android/email/service/EmailServiceProxy.java create mode 100644 src/com/android/emailcommon/Api.java rename src/com/android/{email => emailcommon}/service/EmailServiceConstants.java (95%) create mode 100644 src/com/android/emailcommon/service/EmailServiceProxy.java rename src/com/android/{email => emailcommon}/service/EmailServiceStatus.java (97%) rename src/com/android/{email => emailcommon}/service/IEmailService.aidl (58%) rename src/com/android/{email => emailcommon}/service/IEmailServiceCallback.aidl (98%) create mode 100644 src/com/android/emailcommon/service/ServiceProxy.java diff --git a/Android.mk b/Android.mk index 104fa3e57..960ed4315 100644 --- a/Android.mk +++ b/Android.mk @@ -18,11 +18,9 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) -# EXCHANGE-REMOVE-SECTION-START LOCAL_SRC_FILES += \ - src/com/android/email/service/IEmailService.aidl \ - src/com/android/email/service/IEmailServiceCallback.aidl -# EXCHANGE-REMOVE-SECTION-END + src/com/android/emailcommon/service/IEmailService.aidl \ + src/com/android/emailcommon/service/IEmailServiceCallback.aidl LOCAL_STATIC_JAVA_LIBRARIES := android-common # Revive this when the app is unbundled. diff --git a/src/com/android/email/Controller.java b/src/com/android/email/Controller.java index 47667b073..b99c48e72 100644 --- a/src/com/android/email/Controller.java +++ b/src/com/android/email/Controller.java @@ -17,9 +17,9 @@ package com.android.email; import com.android.email.mail.AuthenticationFailedException; -import com.android.email.mail.Folder.MessageRetrievalListener; import com.android.email.mail.MessagingException; import com.android.email.mail.Store; +import com.android.email.mail.Folder.MessageRetrievalListener; import com.android.email.mail.store.Pop3Store.Pop3Message; import com.android.email.provider.AttachmentProvider; import com.android.email.provider.EmailContent; @@ -30,9 +30,10 @@ 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.provider.EmailContent.MessageColumns; -import com.android.email.service.EmailServiceStatus; -import com.android.email.service.IEmailService; -import com.android.email.service.IEmailServiceCallback; +import com.android.emailcommon.Api; +import com.android.emailcommon.service.EmailServiceStatus; +import com.android.emailcommon.service.IEmailService; +import com.android.emailcommon.service.IEmailServiceCallback; import android.app.Service; import android.content.ContentResolver; @@ -1686,6 +1687,11 @@ public class Controller { public void deleteAccountPIMData(long accountId) throws RemoteException { } + + @Override + public int getApiLevel() throws RemoteException { + return Api.LEVEL; + } }; @Override diff --git a/src/com/android/email/ExchangeUtils.java b/src/com/android/email/ExchangeUtils.java index 0be3debaf..61cef6a30 100644 --- a/src/com/android/email/ExchangeUtils.java +++ b/src/com/android/email/ExchangeUtils.java @@ -16,9 +16,10 @@ package com.android.email; -import com.android.email.service.EmailServiceProxy; -import com.android.email.service.IEmailService; -import com.android.email.service.IEmailServiceCallback; +import com.android.emailcommon.Api; +import com.android.emailcommon.service.EmailServiceProxy; +import com.android.emailcommon.service.IEmailService; +import com.android.emailcommon.service.IEmailServiceCallback; import com.android.exchange.CalendarSyncEnabler; import com.android.exchange.ExchangeService; @@ -142,6 +143,11 @@ public class ExchangeUtils { return null; } + @Override + public int getApiLevel() throws RemoteException { + return Api.LEVEL; + } + @Override public IBinder onBind(Intent intent) { return null; diff --git a/src/com/android/email/activity/MessageViewFragment.java b/src/com/android/email/activity/MessageViewFragment.java index f14b77789..1dff7b38a 100644 --- a/src/com/android/email/activity/MessageViewFragment.java +++ b/src/com/android/email/activity/MessageViewFragment.java @@ -23,7 +23,7 @@ import com.android.email.mail.MeetingInfo; import com.android.email.mail.PackedString; import com.android.email.provider.EmailContent.Mailbox; import com.android.email.provider.EmailContent.Message; -import com.android.email.service.EmailServiceConstants; +import com.android.emailcommon.service.EmailServiceConstants; import android.app.Activity; import android.content.res.Resources; diff --git a/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java b/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java index b3984fe75..6f5a47c36 100644 --- a/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java +++ b/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java @@ -25,7 +25,7 @@ import com.android.email.mail.Sender; import com.android.email.mail.Store; import com.android.email.provider.EmailContent.Account; import com.android.email.provider.EmailContent.HostAuth; -import com.android.email.service.EmailServiceProxy; +import com.android.emailcommon.service.EmailServiceProxy; import android.app.Activity; import android.app.AlertDialog; diff --git a/src/com/android/email/mail/store/ExchangeStore.java b/src/com/android/email/mail/store/ExchangeStore.java index 0d06e7693..ace052bba 100644 --- a/src/com/android/email/mail/store/ExchangeStore.java +++ b/src/com/android/email/mail/store/ExchangeStore.java @@ -21,8 +21,8 @@ import com.android.email.mail.Folder; import com.android.email.mail.MessagingException; import com.android.email.mail.Store; import com.android.email.mail.StoreSynchronizer; -import com.android.email.service.EmailServiceProxy; -import com.android.email.service.IEmailService; +import com.android.emailcommon.service.EmailServiceProxy; +import com.android.emailcommon.service.IEmailService; import android.content.Context; import android.os.Bundle; diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java index 4b43d3711..ebe1d3ec0 100644 --- a/src/com/android/email/mail/store/ImapStore.java +++ b/src/com/android/email/mail/store/ImapStore.java @@ -47,7 +47,7 @@ import com.android.email.mail.transport.CountingOutputStream; import com.android.email.mail.transport.DiscourseLogger; import com.android.email.mail.transport.EOLConvertingOutputStream; import com.android.email.mail.transport.MailTransport; -import com.android.email.service.EmailServiceProxy; +import com.android.emailcommon.service.EmailServiceProxy; import com.beetstra.jutf7.CharsetProvider; import android.content.Context; diff --git a/src/com/android/email/mail/store/Pop3Store.java b/src/com/android/email/mail/store/Pop3Store.java index 6f0ccd27e..3bd7145f5 100644 --- a/src/com/android/email/mail/store/Pop3Store.java +++ b/src/com/android/email/mail/store/Pop3Store.java @@ -30,7 +30,7 @@ import com.android.email.mail.Folder.OpenMode; import com.android.email.mail.internet.MimeMessage; import com.android.email.mail.transport.LoggingInputStream; import com.android.email.mail.transport.MailTransport; -import com.android.email.service.EmailServiceProxy; +import com.android.emailcommon.service.EmailServiceProxy; import android.content.Context; import android.os.Bundle; diff --git a/src/com/android/email/service/AttachmentDownloadService.java b/src/com/android/email/service/AttachmentDownloadService.java index 4fae126eb..2f155f04f 100644 --- a/src/com/android/email/service/AttachmentDownloadService.java +++ b/src/com/android/email/service/AttachmentDownloadService.java @@ -17,17 +17,20 @@ package com.android.email.service; import com.android.email.AttachmentInfo; -import com.android.email.Controller.ControllerService; import com.android.email.Email; import com.android.email.EmailConnectivityManager; -import com.android.email.ExchangeUtils.NullEmailService; import com.android.email.NotificationController; import com.android.email.Utility; +import com.android.email.Controller.ControllerService; +import com.android.email.ExchangeUtils.NullEmailService; import com.android.email.provider.AttachmentProvider; import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent.Account; import com.android.email.provider.EmailContent.Attachment; import com.android.email.provider.EmailContent.Message; +import com.android.emailcommon.service.EmailServiceProxy; +import com.android.emailcommon.service.EmailServiceStatus; +import com.android.emailcommon.service.IEmailServiceCallback; import com.android.exchange.ExchangeService; import android.accounts.AccountManager; diff --git a/src/com/android/email/service/EmailServiceProxy.java b/src/com/android/email/service/EmailServiceProxy.java deleted file mode 100644 index 959843201..000000000 --- a/src/com/android/email/service/EmailServiceProxy.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2009 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 com.android.email.mail.MessagingException; - -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; -import android.util.Log; - -/** - * The EmailServiceProxy class provides a simple interface for the UI to call into the various - * EmailService classes (e.g. ExchangeService for EAS). It wraps the service connect/disconnect - * process so that the caller need not be concerned with it. - * - * Use the class like this: - * new EmailServiceClass(context, class).loadAttachment(attachmentId, callback) - * - * Methods without a return value return immediately (i.e. are asynchronous); methods with a - * return value wait for a result from the Service (i.e. they should not be called from the UI - * thread) with a default timeout of 30 seconds (settable) - * - * An EmailServiceProxy object cannot be reused (trying to do so generates a RemoteException) - */ - -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"; - - public static final String VALIDATE_BUNDLE_RESULT_CODE = "validate_result_code"; - public static final String VALIDATE_BUNDLE_POLICY_SET = "validate_policy_set"; - public static final String VALIDATE_BUNDLE_ERROR_MESSAGE = "validate_error_message"; - - private final Context mContext; - private final Class mClass; - private final IEmailServiceCallback mCallback; - private Runnable mRunnable; - private final ServiceConnection mExchangeServiceConnection = new EmailServiceConnection (); - private IEmailService mService = null; - private Object mReturn = null; - // Service call timeout (in seconds) - private int mTimeout = 45; - private boolean mDead = false; - - public EmailServiceProxy(Context _context, Class _class) { - this(_context, _class, null); - } - - public EmailServiceProxy(Context _context, Class _class, IEmailServiceCallback _callback) { - mContext = _context; - mClass = _class; - mCallback = _callback; - // Proxy calls have a timeout, and this can cause failures while debugging due to the - // far slower execution speed. In particular, validate calls fail regularly with ssl - // connections at the default timeout (30 seconds) - if (Debug.isDebuggerConnected()) { - mTimeout <<= 2; - } - } - - class EmailServiceConnection implements ServiceConnection { - public void onServiceConnected(ComponentName name, IBinder binder) { - mService = IEmailService.Stub.asInterface(binder); - if (DEBUG_PROXY) { - Log.v(TAG, "Service " + mClass.getSimpleName() + " connected"); - } - // Run our task on a new thread - new Thread(new Runnable() { - public void run() { - runTask(); - }}).start(); - } - - public void onServiceDisconnected(ComponentName name) { - if (DEBUG_PROXY) { - Log.v(TAG, "Service " + mClass.getSimpleName() + " disconnected"); - } - } - } - - public EmailServiceProxy setTimeout(int secs) { - mTimeout = secs; - return this; - } - - private void runTask() { - Thread thread = new Thread(mRunnable); - thread.start(); - try { - thread.join(); - } catch (InterruptedException e) { - } - - try { - mContext.unbindService(mExchangeServiceConnection); - } catch (IllegalArgumentException e) { - // This can happen if the user ended the activity that was using the service - // This is harmless, but we've got to catch it - } - - mDead = true; - synchronized(mExchangeServiceConnection) { - if (DEBUG_PROXY) { - Log.v(TAG, "Service task completed; disconnecting"); - } - mExchangeServiceConnection.notify(); - } - } - - private void setTask(Runnable runnable) throws RemoteException { - if (mDead) { - throw new RemoteException(); - } - mRunnable = runnable; - if (DEBUG_PROXY) { - Log.v(TAG, "Service " + mClass.getSimpleName() + " bind requested"); - } - mContext.bindService(new Intent(mContext, mClass), mExchangeServiceConnection, - Context.BIND_AUTO_CREATE); - } - - public void waitForCompletion() { - synchronized (mExchangeServiceConnection) { - long time = System.currentTimeMillis(); - try { - if (DEBUG_PROXY) { - Log.v(TAG, "Waiting for task to complete..."); - } - mExchangeServiceConnection.wait(mTimeout * 1000L); - } catch (InterruptedException e) { - // Can be ignored safely - } - if (DEBUG_PROXY) { - Log.v(TAG, "Wait finished in " + (System.currentTimeMillis() - time) + "ms"); - } - } - } - - public void loadAttachment(final long attachmentId, final String destinationFile, - final String contentUriString, final boolean background) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mService.loadAttachment( - attachmentId, destinationFile, contentUriString, background); - } catch (RemoteException e) { - try { - // Try to send a callback (if set) - if (mCallback != null) { - mCallback.loadAttachmentStatus(-1, attachmentId, - EmailServiceStatus.REMOTE_EXCEPTION, 0); - } - } catch (RemoteException e1) { - } - } - } - }); - } - - public void startSync(final long mailboxId, final boolean userRequest) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mService.startSync(mailboxId, userRequest); - } catch (RemoteException e) { - } - } - }); - } - - public void stopSync(final long mailboxId) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mService.stopSync(mailboxId); - } catch (RemoteException e) { - } - } - }); - } - - public Bundle validate(final String protocol, final String host, final String userName, - final String password, final int port, final boolean ssl, - final boolean trustCertificates) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mReturn = mService.validate(protocol, host, userName, password, port, ssl, - trustCertificates); - } catch (RemoteException e) { - } - } - }); - waitForCompletion(); - if (mReturn == null) { - Bundle bundle = new Bundle(); - bundle.putInt(VALIDATE_BUNDLE_RESULT_CODE, MessagingException.UNSPECIFIED_EXCEPTION); - return bundle; - } else { - Bundle bundle = (Bundle) mReturn; - Log.v(TAG, "validate returns " + bundle.getInt(VALIDATE_BUNDLE_RESULT_CODE)); - return bundle; - } - } - - 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() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mService.updateFolderList(accountId); - } catch (RemoteException e) { - } - } - }); - } - - public void setLogging(final int on) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mService.setLogging(on); - } catch (RemoteException e) { - } - } - }); - } - - public void setCallback(final IEmailServiceCallback cb) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - mService.setCallback(cb); - } catch (RemoteException e) { - } - } - }); - } - - public void hostChanged(final long accountId) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - mService.hostChanged(accountId); - } catch (RemoteException e) { - } - } - }); - } - - public void sendMeetingResponse(final long messageId, final int response) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - if (mCallback != null) mService.setCallback(mCallback); - mService.sendMeetingResponse(messageId, response); - } catch (RemoteException e) { - } - } - }); - } - - public void loadMore(long messageId) throws RemoteException { - // TODO Auto-generated method stub - } - - public boolean createFolder(long accountId, String name) throws RemoteException { - return false; - } - - public boolean deleteFolder(long accountId, String name) throws RemoteException { - return false; - } - - public boolean renameFolder(long accountId, String oldName, String newName) - throws RemoteException { - return false; - } - - public void moveMessage(final long messageId, final long mailboxId) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - mService.moveMessage(messageId, mailboxId); - } catch (RemoteException e) { - } - } - }); - } - - public void deleteAccountPIMData(final long accountId) throws RemoteException { - setTask(new Runnable () { - public void run() { - try { - mService.deleteAccountPIMData(accountId); - } catch (RemoteException e) { - } - } - }); - } - - public IBinder asBinder() { - return null; - } -} diff --git a/src/com/android/emailcommon/Api.java b/src/com/android/emailcommon/Api.java new file mode 100644 index 000000000..ed284d7fb --- /dev/null +++ b/src/com/android/emailcommon/Api.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 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.emailcommon; + +/** + * This class will be used for API-related definitions; for now, just the api "level" + */ +public class Api { + public static final int LEVEL = 1; +} diff --git a/src/com/android/email/service/EmailServiceConstants.java b/src/com/android/emailcommon/service/EmailServiceConstants.java similarity index 95% rename from src/com/android/email/service/EmailServiceConstants.java rename to src/com/android/emailcommon/service/EmailServiceConstants.java index 8a5238f28..b229ffc64 100644 --- a/src/com/android/email/service/EmailServiceConstants.java +++ b/src/com/android/emailcommon/service/EmailServiceConstants.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.email.service; +package com.android.emailcommon.service; public class EmailServiceConstants { public static final int MEETING_REQUEST_ACCEPTED = 1; diff --git a/src/com/android/emailcommon/service/EmailServiceProxy.java b/src/com/android/emailcommon/service/EmailServiceProxy.java new file mode 100644 index 000000000..af2ceff81 --- /dev/null +++ b/src/com/android/emailcommon/service/EmailServiceProxy.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2009 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.emailcommon.service; + +import com.android.email.mail.MessagingException; +import com.android.email.provider.EmailContent.HostAuth; +import com.android.emailcommon.Api; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +/** + * The EmailServiceProxy class provides a simple interface for the UI to call into the various + * EmailService classes (e.g. ExchangeService for EAS). It wraps the service connect/disconnect + * process so that the caller need not be concerned with it. + * + * Use the class like this: + * new EmailServiceClass(context, class).loadAttachment(attachmentId, callback) + * + * Methods without a return value return immediately (i.e. are asynchronous); methods with a + * return value wait for a result from the Service (i.e. they should not be called from the UI + * thread) with a default timeout of 30 seconds (settable) + * + * An EmailServiceProxy object cannot be reused (trying to do so generates a RemoteException) + */ + +public class EmailServiceProxy extends ServiceProxy implements IEmailService { + private static final String TAG = "EmailServiceProxy"; + + // Private intent that will be used to connect to an independent Exchange service + public static final String EXCHANGE_INTENT = "com.android.email.EXCHANGE_INTENT"; + + public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code"; + public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth"; + + public static final String VALIDATE_BUNDLE_RESULT_CODE = "validate_result_code"; + public static final String VALIDATE_BUNDLE_POLICY_SET = "validate_policy_set"; + public static final String VALIDATE_BUNDLE_ERROR_MESSAGE = "validate_error_message"; + + private final IEmailServiceCallback mCallback; + private Object mReturn = null; + private IEmailService mService; + + // Standard debugging + public static final int DEBUG_BIT = 1; + // Verbose (parser) logging + public static final int DEBUG_VERBOSE_BIT = 2; + // File (SD card) logging + public static final int DEBUG_FILE_BIT = 4; + + // The first two constructors are used with local services that can be referenced by class + public EmailServiceProxy(Context _context, Class _class) { + this(_context, _class, null); + } + + public EmailServiceProxy(Context _context, Class _class, IEmailServiceCallback _callback) { + super(_context, new Intent(_context, _class)); + mCallback = _callback; + } + + // The following two constructors are used with remote services that must be referenced by + // a known action or by a prebuilt intent + public EmailServiceProxy(Context _context, Intent _intent, IEmailServiceCallback _callback) { + super(_context, _intent); + mCallback = _callback; + } + + public EmailServiceProxy(Context _context, String _action, IEmailServiceCallback _callback) { + super(_context, new Intent(_action)); + mCallback = _callback; + } + + @Override + public void onConnected(IBinder binder) { + mService = IEmailService.Stub.asInterface(binder); + } + + @Override + public int getApiLevel() { + return Api.LEVEL; + } + + public void loadAttachment(final long attachmentId, final String destinationFile, + final String contentUriString, final boolean background) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + try { + if (mCallback != null) mService.setCallback(mCallback); + mService.loadAttachment( + attachmentId, destinationFile, contentUriString, background); + } catch (RemoteException e) { + try { + // Try to send a callback (if set) + if (mCallback != null) { + mCallback.loadAttachmentStatus(-1, attachmentId, + EmailServiceStatus.REMOTE_EXCEPTION, 0); + } + } catch (RemoteException e1) { + } + } + } + }, "loadAttachment"); + } + + public void startSync(final long mailboxId, final boolean userRequest) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + if (mCallback != null) mService.setCallback(mCallback); + mService.startSync(mailboxId, userRequest); + } + }, "startSync"); + } + + public void stopSync(final long mailboxId) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + if (mCallback != null) mService.setCallback(mCallback); + mService.stopSync(mailboxId); + } + }, "stopSync"); + } + + public Bundle validate(final String protocol, final String host, final String userName, + final String password, final int port, final boolean ssl, + final boolean trustCertificates) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException{ + if (mCallback != null) mService.setCallback(mCallback); + mReturn = mService.validate(protocol, host, userName, password, port, ssl, + trustCertificates); + } + }, "validate"); + waitForCompletion(); + if (mReturn == null) { + Bundle bundle = new Bundle(); + bundle.putInt(VALIDATE_BUNDLE_RESULT_CODE, MessagingException.UNSPECIFIED_EXCEPTION); + return bundle; + } else { + Bundle bundle = (Bundle) mReturn; + // STOPSHIP The following line will be necessary when Email and Exchange are split + //bundle.setClassLoader(PolicySet.class.getClassLoader()); + Log.v(TAG, "validate returns " + bundle.getInt(VALIDATE_BUNDLE_RESULT_CODE)); + return bundle; + } + } + + public Bundle autoDiscover(final String userName, final String password) + throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException{ + if (mCallback != null) mService.setCallback(mCallback); + mReturn = mService.autoDiscover(userName, password); + } + }, "autoDiscover"); + waitForCompletion(); + if (mReturn == null) { + return null; + } else { + Bundle bundle = (Bundle) mReturn; + bundle.setClassLoader(HostAuth.class.getClassLoader()); + Log.v(TAG, "autoDiscover returns " + bundle.getInt(AUTO_DISCOVER_BUNDLE_ERROR_CODE)); + return bundle; + } + } + + public void updateFolderList(final long accountId) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + if (mCallback != null) mService.setCallback(mCallback); + mService.updateFolderList(accountId); + } + }, "updateFolderList"); + } + + public void setLogging(final int on) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + if (mCallback != null) mService.setCallback(mCallback); + mService.setLogging(on); + } + }, "setLogging"); + } + + public void setCallback(final IEmailServiceCallback cb) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + mService.setCallback(cb); + } + }, "setCallback"); + } + + public void hostChanged(final long accountId) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + mService.hostChanged(accountId); + } + }, "hostChanged"); + } + + public void sendMeetingResponse(final long messageId, final int response) + throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + if (mCallback != null) mService.setCallback(mCallback); + mService.sendMeetingResponse(messageId, response); + } + }, "sendMeetingResponse"); + } + + public void loadMore(long messageId) throws RemoteException { + // TODO Auto-generated method stub + } + + public boolean createFolder(long accountId, String name) throws RemoteException { + return false; + } + + public boolean deleteFolder(long accountId, String name) throws RemoteException { + return false; + } + + public boolean renameFolder(long accountId, String oldName, String newName) + throws RemoteException { + return false; + } + + public void moveMessage(final long messageId, final long mailboxId) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + mService.moveMessage(messageId, mailboxId); + } + }, "moveMessage"); + } + + public void deleteAccountPIMData(final long accountId) throws RemoteException { + setTask(new ProxyTask() { + public void run() throws RemoteException { + mService.deleteAccountPIMData(accountId); + } + }, "deleteAccountPIMData"); + } + + public IBinder asBinder() { + return null; + } +} diff --git a/src/com/android/email/service/EmailServiceStatus.java b/src/com/android/emailcommon/service/EmailServiceStatus.java similarity index 97% rename from src/com/android/email/service/EmailServiceStatus.java rename to src/com/android/emailcommon/service/EmailServiceStatus.java index 135797637..055f79a33 100644 --- a/src/com/android/email/service/EmailServiceStatus.java +++ b/src/com/android/emailcommon/service/EmailServiceStatus.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.android.email.service; +package com.android.emailcommon.service; /** * Definitions of service status codes returned to IEmailServiceCallback's status method diff --git a/src/com/android/email/service/IEmailService.aidl b/src/com/android/emailcommon/service/IEmailService.aidl similarity index 58% rename from src/com/android/email/service/IEmailService.aidl rename to src/com/android/emailcommon/service/IEmailService.aidl index 187320895..c91706ace 100644 --- a/src/com/android/email/service/IEmailService.aidl +++ b/src/com/android/emailcommon/service/IEmailService.aidl @@ -14,39 +14,46 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.email.service; -import com.android.email.service.IEmailServiceCallback; +package com.android.emailcommon.service; + +import com.android.emailcommon.service.IEmailServiceCallback; import android.os.Bundle; interface IEmailService { Bundle validate(in String protocol, in String host, in String userName, in String password, int port, boolean ssl, boolean trustCertificates) ; - void startSync(long mailboxId, boolean userRequest); - void stopSync(long mailboxId); + oneway void startSync(long mailboxId, boolean userRequest); + oneway void stopSync(long mailboxId); - void loadMore(long messageId); - void loadAttachment(long attachmentId, String destinationFile, String contentUriString, + oneway void loadMore(long messageId); + oneway void loadAttachment(long attachmentId, String destinationFile, String contentUriString, boolean background); - void updateFolderList(long accountId); + oneway void updateFolderList(long accountId); boolean createFolder(long accountId, String name); boolean deleteFolder(long accountId, String name); boolean renameFolder(long accountId, String oldName, String newName); + // Must not be oneway; unless an exception is thrown, the caller is guaranteed that the callback + // has been registered void setCallback(IEmailServiceCallback cb); - void setLogging(int on); + oneway void setLogging(int on); - void hostChanged(long accountId); + oneway void hostChanged(long accountId); Bundle autoDiscover(String userName, String password); - void sendMeetingResponse(long messageId, int response); + oneway void sendMeetingResponse(long messageId, int response); - void moveMessage(long messageId, long mailboxId); + oneway void moveMessage(long messageId, long mailboxId); + // Must not be oneway; unless an exception is thrown, the caller is guaranteed that the action + // has been completed void deleteAccountPIMData(long accountId); + + int getApiLevel(); } \ No newline at end of file diff --git a/src/com/android/email/service/IEmailServiceCallback.aidl b/src/com/android/emailcommon/service/IEmailServiceCallback.aidl similarity index 98% rename from src/com/android/email/service/IEmailServiceCallback.aidl rename to src/com/android/emailcommon/service/IEmailServiceCallback.aidl index ac2cb419e..e4c6093fc 100644 --- a/src/com/android/email/service/IEmailServiceCallback.aidl +++ b/src/com/android/emailcommon/service/IEmailServiceCallback.aidl @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.android.email.service; +package com.android.emailcommon.service; oneway interface IEmailServiceCallback { /* diff --git a/src/com/android/emailcommon/service/ServiceProxy.java b/src/com/android/emailcommon/service/ServiceProxy.java new file mode 100644 index 000000000..215fb726f --- /dev/null +++ b/src/com/android/emailcommon/service/ServiceProxy.java @@ -0,0 +1,196 @@ +/* + /* + * Copyright (C) 2011 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.emailcommon.service; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Debug; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +/** + * The EmailServiceProxy class provides a simple interface for the UI to call into the various + * EmailService classes (e.g. ExchangeService for EAS). It wraps the service connect/disconnect + * process so that the caller need not be concerned with it. + * + * Use the class like this: + * new EmailServiceClass(context, class).loadAttachment(attachmentId, callback) + * + * Methods without a return value return immediately (i.e. are asynchronous); methods with a + * return value wait for a result from the Service (i.e. they should not be called from the UI + * thread) with a default timeout of 30 seconds (settable) + * + * An EmailServiceProxy object cannot be reused (trying to do so generates a RemoteException) + */ + +public abstract class ServiceProxy { + private static final boolean DEBUG_PROXY = true; // STOPSHIP DO NOT CHECK THIS IN SET TO TRUE + private static final String TAG = "ServiceProxy"; + + private final Context mContext; + protected final Intent mIntent; + private Runnable mRunnable = new ProxyRunnable(); + private ProxyTask mTask; + private String mName = " unnamed"; + private final ServiceConnection mConnection = new ProxyConnection(); + // Service call timeout (in seconds) + private int mTimeout = 45; + private boolean mDead = false; + + public abstract void onConnected(IBinder binder); + + public ServiceProxy(Context _context, Intent _intent) { + mContext = _context; + mIntent = _intent; + if (Debug.isDebuggerConnected()) { + mTimeout <<= 2; + } + } + + private class ProxyConnection implements ServiceConnection { + public void onServiceConnected(ComponentName name, IBinder binder) { + onConnected(binder); + if (DEBUG_PROXY) { + Log.v(TAG, "Connected: " + name.getShortClassName()); + } + // Run our task on a new thread + new Thread(new Runnable() { + public void run() { + runTask(); + }}).start(); + } + + public void onServiceDisconnected(ComponentName name) { + if (DEBUG_PROXY) { + Log.v(TAG, "Disconnected: " + name.getShortClassName()); + } + } + } + + public interface ProxyTask { + public void run() throws RemoteException; + } + + private class ProxyRunnable implements Runnable { + @Override + public void run() { + try { + mTask.run(); + } catch (RemoteException e) { + } + } + } + + public ServiceProxy setTimeout(int secs) { + mTimeout = secs; + return this; + } + + public int getTimeout() { + return mTimeout; + } + + public void endTask() { + try { + mContext.unbindService(mConnection); + } catch (IllegalArgumentException e) { + // This can happen if the user ended the activity that was using the service + // This is harmless, but we've got to catch it + } + + mDead = true; + synchronized(mConnection) { + if (DEBUG_PROXY) { + Log.v(TAG, "Task " + mName + " completed; disconnecting"); + } + mConnection.notify(); + } + } + + private void runTask() { + Thread thread = new Thread(mRunnable); + thread.start(); + try { + thread.join(); + } catch (InterruptedException e) { + } + endTask(); + } + + public boolean setTask(ProxyTask task, String name) { + mName = name; + return setTask(task); + } + + public boolean setTask(ProxyTask task) throws IllegalStateException { + if (mDead) { + throw new IllegalStateException(); + } + mTask = task; + if (DEBUG_PROXY) { + Log.v(TAG, "Bind requested for task " + mName); + } + return mContext.bindService(mIntent, mConnection, Context.BIND_AUTO_CREATE); + } + + public void waitForCompletion() { + synchronized (mConnection) { + long time = System.currentTimeMillis(); + try { + if (DEBUG_PROXY) { + Log.v(TAG, "Waiting for task " + mName + " to complete..."); + } + mConnection.wait(mTimeout * 1000L); + } catch (InterruptedException e) { + // Can be ignored safely + } + if (DEBUG_PROXY) { + Log.v(TAG, "Wait finished in " + (System.currentTimeMillis() - time) + "ms"); + } + } + } + + public void close() throws RemoteException { + if (mDead) { + throw new RemoteException(); + } + endTask(); + } + + /** + * Connection test; return indicates whether the remote service can be connected to + * @return the result of trying to connect to the remote service + */ + public boolean test() { + try { + return setTask(new ProxyTask() { + public void run() throws RemoteException { + if (DEBUG_PROXY) { + Log.v(TAG, "Connection test succeeded"); + } + } + }, "test"); + } catch (Exception e) { + // For any failure, return false. + return false; + } + } +} diff --git a/src/com/android/exchange/EasOutboxService.java b/src/com/android/exchange/EasOutboxService.java index a4b043cbb..d0392b492 100644 --- a/src/com/android/exchange/EasOutboxService.java +++ b/src/com/android/exchange/EasOutboxService.java @@ -28,7 +28,7 @@ import com.android.email.provider.EmailContent.MailboxColumns; import com.android.email.provider.EmailContent.Message; import com.android.email.provider.EmailContent.MessageColumns; import com.android.email.provider.EmailContent.SyncColumns; -import com.android.email.service.EmailServiceStatus; +import com.android.emailcommon.service.EmailServiceStatus; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; diff --git a/src/com/android/exchange/EasSyncService.java b/src/com/android/exchange/EasSyncService.java index 98a34d8f2..32441b094 100644 --- a/src/com/android/exchange/EasSyncService.java +++ b/src/com/android/exchange/EasSyncService.java @@ -34,9 +34,9 @@ import com.android.email.provider.EmailContent.MailboxColumns; import com.android.email.provider.EmailContent.Message; import com.android.email.provider.EmailContent.MessageColumns; import com.android.email.provider.EmailContent.SyncColumns; -import com.android.email.service.EmailServiceConstants; -import com.android.email.service.EmailServiceProxy; -import com.android.email.service.EmailServiceStatus; +import com.android.emailcommon.service.EmailServiceConstants; +import com.android.emailcommon.service.EmailServiceProxy; +import com.android.emailcommon.service.EmailServiceStatus; import com.android.exchange.adapter.AbstractSyncAdapter; import com.android.exchange.adapter.AccountSyncAdapter; import com.android.exchange.adapter.CalendarSyncAdapter; diff --git a/src/com/android/exchange/ExchangeService.java b/src/com/android/exchange/ExchangeService.java index 0f0df3693..19bc2264a 100644 --- a/src/com/android/exchange/ExchangeService.java +++ b/src/com/android/exchange/ExchangeService.java @@ -23,6 +23,7 @@ import com.android.email.NotificationController; import com.android.email.Utility; import com.android.email.mail.transport.SSLUtils; import com.android.email.provider.EmailContent; +import com.android.email.provider.EmailProvider; import com.android.email.provider.EmailContent.Account; import com.android.email.provider.EmailContent.Attachment; import com.android.email.provider.EmailContent.HostAuth; @@ -31,11 +32,11 @@ 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.provider.EmailContent.SyncColumns; -import com.android.email.provider.EmailProvider; -import com.android.email.service.EmailServiceStatus; -import com.android.email.service.IEmailService; -import com.android.email.service.IEmailServiceCallback; import com.android.email.service.MailService; +import com.android.emailcommon.Api; +import com.android.emailcommon.service.EmailServiceStatus; +import com.android.emailcommon.service.IEmailService; +import com.android.emailcommon.service.IEmailServiceCallback; import com.android.exchange.adapter.CalendarSyncAdapter; import com.android.exchange.adapter.ContactsSyncAdapter; import com.android.exchange.utility.FileLogger; @@ -67,21 +68,21 @@ import android.database.ContentObserver; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.NetworkInfo.State; import android.net.Uri; +import android.net.NetworkInfo.State; import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; import android.os.PowerManager; -import android.os.PowerManager.WakeLock; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.PowerManager.WakeLock; import android.provider.Calendar; +import android.provider.ContactsContract; import android.provider.Calendar.Calendars; import android.provider.Calendar.Events; -import android.provider.ContactsContract; import android.util.Log; import java.io.BufferedReader; @@ -462,6 +463,11 @@ public class ExchangeService extends Service implements Runnable { // Delete the data ExchangeService.deleteAccountPIMData(accountId); } + + @Override + public int getApiLevel() throws RemoteException { + return Api.LEVEL; + } }; private static AccountList collectEasAccounts(Context context, AccountList accounts) { diff --git a/tests/src/com/android/email/service/AttachmentDownloadServiceTests.java b/tests/src/com/android/email/service/AttachmentDownloadServiceTests.java index 004bf4e57..8bf9f882a 100644 --- a/tests/src/com/android/email/service/AttachmentDownloadServiceTests.java +++ b/tests/src/com/android/email/service/AttachmentDownloadServiceTests.java @@ -26,6 +26,7 @@ import com.android.email.provider.EmailContent.Message; import com.android.email.provider.ProviderTestUtils; import com.android.email.service.AttachmentDownloadService.DownloadRequest; import com.android.email.service.AttachmentDownloadService.DownloadSet; +import com.android.emailcommon.service.EmailServiceStatus; import android.content.Context;