Email split, part trois: AccountService

* Create AccountService.aidl and AccountServiceProxy in emailcommon
* Implement AccountService in email
* Use AccountServiceProxy in Exchange for account reconciliation,
  notifications, etc.
* Move sync window constants into emailcommon
* Split attachment provider utilities and constants into emailcommon

Bug: 3442973
Change-Id: I89dce28b799b193243c07774dab65d830ae62775
diff --git a/Android.mk b/Android.mk
index 451c39b..3dd43e7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,7 +21,8 @@
 LOCAL_SRC_FILES += \
     src/com/android/emailcommon/service/IEmailService.aidl \
     src/com/android/emailcommon/service/IEmailServiceCallback.aidl \
-    src/com/android/emailcommon/service/IPolicyService.aidl
+    src/com/android/emailcommon/service/IPolicyService.aidl \
+    src/com/android/emailcommon/service/IAccountService.aidl
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-common
 # Revive this when the app is unbundled.
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f8b4b56..3a949df 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -370,6 +370,17 @@
             </intent-filter>
         </service>
 
+        <service
+            android:name=".service.AccountService"
+            android:enabled="true"
+            android:permission="com.android.email.permission.ACCESS_PROVIDER"
+            >
+            <intent-filter>
+                <action
+                    android:name="com.android.email.ACCOUNT_INTENT" />
+            </intent-filter>
+        </service>
+
         <!--EXCHANGE-REMOVE-SECTION-START-->
         <!--Required stanza to register the EAS EmailSyncAdapterService with SyncManager -->
         <service
diff --git a/src/com/android/exchange/ExchangeService.java b/src/com/android/exchange/ExchangeService.java
index 19bc226..6afcbb8 100644
--- a/src/com/android/exchange/ExchangeService.java
+++ b/src/com/android/exchange/ExchangeService.java
@@ -17,9 +17,7 @@
 
 package com.android.exchange;
 
-import com.android.email.AccountBackupRestore;
 import com.android.email.Email;
-import com.android.email.NotificationController;
 import com.android.email.Utility;
 import com.android.email.mail.transport.SSLUtils;
 import com.android.email.provider.EmailContent;
@@ -32,11 +30,12 @@
 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.service.MailService;
 import com.android.emailcommon.Api;
+import com.android.emailcommon.service.AccountServiceProxy;
 import com.android.emailcommon.service.EmailServiceStatus;
 import com.android.emailcommon.service.IEmailService;
 import com.android.emailcommon.service.IEmailServiceCallback;
+import com.android.emailcommon.utility.AccountReconciler;
 import com.android.exchange.adapter.CalendarSyncAdapter;
 import com.android.exchange.adapter.ContactsSyncAdapter;
 import com.android.exchange.utility.FileLogger;
@@ -1046,8 +1045,15 @@
         // list, which would cause the deletion of all of our accounts
         AccountList accountList = collectEasAccounts(context, new AccountList());
         alwaysLog("Reconciling accounts...");
-        MailService.reconcileAccountsWithAccountManager(context, accountList, accountMgrList,
-                false, context.getContentResolver());
+        boolean accountsDeleted = AccountReconciler.reconcileAccounts(context, accountList,
+                accountMgrList, context.getContentResolver());
+        if (accountsDeleted) {
+            try {
+                new AccountServiceProxy(context).accountDeleted();
+            } catch (RemoteException e) {
+                // ?
+            }
+        }
     }
 
     public static void log(String str) {
@@ -1749,13 +1755,22 @@
             @Override
             public void run() {
                 synchronized (sSyncLock) {
+                    // ExchangeService cannot start unless we can connect to AccountService
+                    if (!new AccountServiceProxy(ExchangeService.this).test()) {
+                        log("!!! Email application not found; stopping self");
+                        stopSelf();
+                    }
                     // Restore accounts, if it has not happened already
-                    AccountBackupRestore.restoreAccountsIfNeeded(ExchangeService.this);
+                    try {
+                        new AccountServiceProxy(ExchangeService.this).restoreAccountsIfNeeded();
+                    } catch (RemoteException e) {
+                        // If we can't restore accounts, don't run
+                        return;
+                    }
                     // Run the reconciler and clean up any mismatched accounts - if we weren't
                     // running when accounts were deleted, it won't have been called.
                     runAccountReconcilerSync(ExchangeService.this);
                     // Update other services depending on final account configuration
-                    Email.setServicesEnabledSync(ExchangeService.this);
                     maybeStartExchangeServiceThread();
                     if (sServiceThread == null) {
                         log("!!! EAS ExchangeService, stopping self");
@@ -2385,8 +2400,12 @@
                 if (account == null) return;
                 if (exchangeService.releaseSyncHolds(exchangeService,
                         AbstractSyncService.EXIT_LOGIN_FAILURE, account)) {
-                    NotificationController.getInstance(exchangeService)
-                        .cancelLoginFailedNotification(accountId);
+                    try {
+                        new AccountServiceProxy(exchangeService).notifyLoginSucceeded(
+                                accountId);
+                    } catch (RemoteException e) {
+                        // No harm if the notification fails
+                    }
                 }
             }
 
@@ -2413,8 +2432,11 @@
                     break;
                 // These errors are not retried automatically
                 case AbstractSyncService.EXIT_LOGIN_FAILURE:
-                    NotificationController.getInstance(exchangeService)
-                        .showLoginFailedNotification(m.mAccountKey);
+                    try {
+                        new AccountServiceProxy(exchangeService).notifyLoginFailed(m.mAccountKey);
+                    } catch (RemoteException e) {
+                        // ? Anything to do?
+                    }
                     // Fall through
                 case AbstractSyncService.EXIT_SECURITY_FAILURE:
                 case AbstractSyncService.EXIT_EXCEPTION:
diff --git a/src/com/android/exchange/PolicyServiceDelegate.java b/src/com/android/exchange/PolicyServiceDelegate.java
new file mode 100644
index 0000000..d712c55
--- /dev/null
+++ b/src/com/android/exchange/PolicyServiceDelegate.java
@@ -0,0 +1,91 @@
+/*
+ * 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.exchange;
+
+import com.android.email.provider.EmailContent.Account;
+import com.android.emailcommon.service.PolicyServiceProxy;
+import com.android.emailcommon.service.PolicySet;
+
+import android.content.Context;
+import android.os.RemoteException;
+
+public class PolicyServiceDelegate {
+
+    public static boolean isActive(Context context, PolicySet policies) {
+        try {
+            return new PolicyServiceProxy(context).isActive(policies);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    public static void policiesRequired(Context context, long accountId) {
+        try {
+            new PolicyServiceProxy(context).policiesRequired(accountId);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("PolicyService transaction failed");
+        }
+    }
+
+    public static void updatePolicies(Context context, long accountId) {
+        try {
+            new PolicyServiceProxy(context).updatePolicies(accountId);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("PolicyService transaction failed");
+        }
+    }
+
+    public static void setAccountHoldFlag(Context context, Account account, boolean newState) {
+        try {
+            new PolicyServiceProxy(context).setAccountHoldFlag(account.mId, newState);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("PolicyService transaction failed");
+        }
+    }
+
+    public static boolean isActiveAdmin(Context context) {
+        try {
+            return new PolicyServiceProxy(context).isActiveAdmin();
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    public static void remoteWipe(Context context) {
+        try {
+            new PolicyServiceProxy(context).remoteWipe();
+        } catch (RemoteException e) {
+            throw new IllegalStateException("PolicyService transaction failed");
+        }
+    }
+
+    public static boolean isSupported(Context context, PolicySet policies) {
+        try {
+            return new PolicyServiceProxy(context).isSupported(policies);
+        } catch (RemoteException e) {
+        }
+        return false;
+     }
+
+    public static PolicySet clearUnsupportedPolicies(Context context, PolicySet policies) {
+        try {
+            return new PolicyServiceProxy(context).clearUnsupportedPolicies(policies);
+        } catch (RemoteException e) {
+        }
+        throw new IllegalStateException("PolicyService transaction failed");
+    }
+}
diff --git a/src/com/android/exchange/adapter/EmailSyncAdapter.java b/src/com/android/exchange/adapter/EmailSyncAdapter.java
index d4a27ff..b9666c1 100644
--- a/src/com/android/exchange/adapter/EmailSyncAdapter.java
+++ b/src/com/android/exchange/adapter/EmailSyncAdapter.java
@@ -17,7 +17,6 @@
 
 package com.android.exchange.adapter;
 
-import com.android.email.LegacyConversions;
 import com.android.email.Utility;
 import com.android.email.mail.Address;
 import com.android.email.mail.MeetingInfo;
@@ -26,8 +25,8 @@
 import com.android.email.mail.Part;
 import com.android.email.mail.internet.MimeMessage;
 import com.android.email.mail.internet.MimeUtility;
-import com.android.email.provider.AttachmentProvider;
 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.AccountColumns;
 import com.android.email.provider.EmailContent.Attachment;
@@ -36,8 +35,10 @@
 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.provider.EmailProvider;
-import com.android.email.service.MailService;
+import com.android.emailcommon.service.AccountServiceProxy;
+import com.android.emailcommon.service.SyncWindow;
+import com.android.emailcommon.utility.AttachmentUtilities;
+import com.android.emailcommon.utility.ConversionUtilities;
 import com.android.exchange.Eas;
 import com.android.exchange.EasSyncService;
 import com.android.exchange.MessageMoveRequest;
@@ -117,22 +118,23 @@
         mService.clearRequests();
         mFetchRequestList.clear();
         // Delete attachments...
-        AttachmentProvider.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId, mMailbox.mId);
+        AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId,
+                mMailbox.mId);
     }
 
     private String getEmailFilter() {
         switch (mAccount.mSyncLookback) {
-            case com.android.email.Account.SYNC_WINDOW_1_DAY:
+            case SyncWindow.SYNC_WINDOW_1_DAY:
                 return Eas.FILTER_1_DAY;
-            case com.android.email.Account.SYNC_WINDOW_3_DAYS:
+            case SyncWindow.SYNC_WINDOW_3_DAYS:
                 return Eas.FILTER_3_DAYS;
-            case com.android.email.Account.SYNC_WINDOW_1_WEEK:
+            case SyncWindow.SYNC_WINDOW_1_WEEK:
                 return Eas.FILTER_1_WEEK;
-            case com.android.email.Account.SYNC_WINDOW_2_WEEKS:
+            case SyncWindow.SYNC_WINDOW_2_WEEKS:
                 return Eas.FILTER_2_WEEKS;
-            case com.android.email.Account.SYNC_WINDOW_1_MONTH:
+            case SyncWindow.SYNC_WINDOW_1_MONTH:
                 return Eas.FILTER_1_MONTH;
-            case com.android.email.Account.SYNC_WINDOW_ALL:
+            case SyncWindow.SYNC_WINDOW_ALL:
                 return Eas.FILTER_ALL;
             default:
                 return Eas.FILTER_1_WEEK;
@@ -496,7 +498,7 @@
                 MimeUtility.collectParts(mimeMessage, viewables, attachments);
                 Body tempBody = new Body();
                 // updateBodyFields fills in the content fields of the Body
-                LegacyConversions.updateBodyFields(tempBody, msg, viewables);
+                ConversionUtilities.updateBodyFields(tempBody, msg, viewables);
                 // But we need them in the message itself for handling during commit()
                 msg.mHtml = tempBody.mHtmlContent;
                 msg.mText = tempBody.mTextContent;
@@ -770,7 +772,7 @@
             for (Long id : deletedEmails) {
                 ops.add(ContentProviderOperation.newDelete(
                         ContentUris.withAppendedId(Message.CONTENT_URI, id)).build());
-                AttachmentProvider.deleteAllAttachmentFiles(mContext, mAccount.mId, id);
+                AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id);
             }
 
             if (!changedEmails.isEmpty()) {
@@ -822,7 +824,11 @@
                 cv.put(EmailContent.ADD_COLUMN_NAME, notifyCount);
                 Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, mAccount.mId);
                 mContentResolver.update(uri, cv, null, null);
-                MailService.actionNotifyNewMessages(mContext, mAccount.mId);
+                try {
+                    new AccountServiceProxy(mService.mContext).notifyNewMessages(mAccount.mId);
+                } catch (RemoteException e) {
+                    // ? Anything to do here?
+                }
             }
         }
     }
diff --git a/src/com/android/exchange/adapter/FolderSyncParser.java b/src/com/android/exchange/adapter/FolderSyncParser.java
index 7d6229b..5013337 100644
--- a/src/com/android/exchange/adapter/FolderSyncParser.java
+++ b/src/com/android/exchange/adapter/FolderSyncParser.java
@@ -18,13 +18,13 @@
 package com.android.exchange.adapter;
 
 import com.android.email.Utility;
-import com.android.email.provider.AttachmentProvider;
 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.AccountColumns;
 import com.android.email.provider.EmailContent.Mailbox;
 import com.android.email.provider.EmailContent.MailboxColumns;
-import com.android.email.provider.EmailProvider;
+import com.android.emailcommon.utility.AttachmentUtilities;
 import com.android.exchange.Eas;
 import com.android.exchange.ExchangeService;
 import com.android.exchange.MockParserStream;
@@ -187,7 +187,7 @@
                             ops.add(ContentProviderOperation.newDelete(
                                     ContentUris.withAppendedId(Mailbox.CONTENT_URI,
                                             c.getLong(0))).build());
-                            AttachmentProvider.deleteAllMailboxAttachmentFiles(mContext,
+                            AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext,
                                     mAccountId, mMailbox.mId);
                         }
                     } finally {
diff --git a/src/com/android/exchange/utility/CalendarUtilities.java b/src/com/android/exchange/utility/CalendarUtilities.java
index e1879f6..27452eb 100644
--- a/src/com/android/exchange/utility/CalendarUtilities.java
+++ b/src/com/android/exchange/utility/CalendarUtilities.java
@@ -18,7 +18,6 @@
 
 import com.android.email.Email;
 import com.android.email.R;
-import com.android.email.ResourceHelper;
 import com.android.email.Utility;
 import com.android.email.mail.Address;
 import com.android.email.provider.EmailContent;
@@ -26,6 +25,7 @@
 import com.android.email.provider.EmailContent.Attachment;
 import com.android.email.provider.EmailContent.Mailbox;
 import com.android.email.provider.EmailContent.Message;
+import com.android.emailcommon.service.AccountServiceProxy;
 import com.android.exchange.Eas;
 import com.android.exchange.EasSyncService;
 import com.android.exchange.ExchangeService;
@@ -1215,8 +1215,12 @@
         cv.put(Calendars.ORGANIZER_CAN_RESPOND, 0);
 
         // TODO Coordinate account colors w/ Calendar, if possible
-        // Make Email account color opaque
-        int color = ResourceHelper.getInstance(service.mContext).getAccountColor(account.mId);
+        int color = AccountServiceProxy.DEFAULT_ACCOUNT_COLOR;
+        try {
+            color = new AccountServiceProxy(service.mContext).getAccountColor(account.mId);
+        } catch (RemoteException e) {
+            // Use the default
+        }
         cv.put(Calendars.COLOR, color);
         cv.put(Calendars.TIMEZONE, Time.getCurrentTimezone());
         cv.put(Calendars.ACCESS_LEVEL, Calendars.OWNER_ACCESS);