| /* |
| * 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.exchange.service; |
| |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.app.Service; |
| import android.content.AbstractThreadedSyncAdapter; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.ServiceConnection; |
| import android.content.SyncResult; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.IBinder; |
| import android.text.format.DateUtils; |
| |
| import com.android.emailcommon.provider.Account; |
| import com.android.emailcommon.provider.EmailContent; |
| import com.android.emailcommon.service.EmailServiceStatus; |
| import com.android.emailcommon.service.IEmailService; |
| import com.android.emailcommon.utility.IntentUtilities; |
| import com.android.exchange.R; |
| import com.android.mail.utils.LogUtils; |
| |
| /** |
| * Base class for services that handle sync requests from the system SyncManager. |
| * This class covers the boilerplate for using an {@link AbstractThreadedSyncAdapter}. Subclasses |
| * should just implement their sync adapter, and override {@link #getSyncAdapter}. |
| */ |
| public abstract class AbstractSyncAdapterService extends Service { |
| private static final String TAG = LogUtils.TAG; |
| |
| // The call to ServiceConnection.onServiceConnected is asynchronous to bindService. It's |
| // possible for that to be delayed if, in which case, a call to onPerformSync |
| // could occur before we have a connection to the service. |
| // In onPerformSync, if we don't yet have our EasService, we will wait for up to 10 |
| // seconds for it to appear. If it takes longer than that, we will fail the sync. |
| private static final long MAX_WAIT_FOR_SERVICE_MS = 10 * DateUtils.SECOND_IN_MILLIS; |
| |
| public AbstractSyncAdapterService() { |
| super(); |
| } |
| |
| protected IEmailService mEasService; |
| protected ServiceConnection mConnection; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| // Make sure EmailContent is initialized in Exchange app |
| EmailContent.init(this); |
| mConnection = new ServiceConnection() { |
| @Override |
| public void onServiceConnected(ComponentName name, IBinder binder) { |
| LogUtils.v(TAG, "onServiceConnected"); |
| synchronized (mConnection) { |
| mEasService = IEmailService.Stub.asInterface(binder); |
| mConnection.notify(); |
| } |
| } |
| |
| @Override |
| public void onServiceDisconnected(ComponentName name) { |
| mEasService = null; |
| } |
| }; |
| bindService(new Intent(this, EasService.class), mConnection, Context.BIND_AUTO_CREATE); |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| unbindService(mConnection); |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return getSyncAdapter().getSyncAdapterBinder(); |
| } |
| |
| /** |
| * Subclasses should override this to supply an instance of its sync adapter. Best practice is |
| * to create a singleton and return that. |
| * @return An instance of the sync adapter. |
| */ |
| protected abstract AbstractThreadedSyncAdapter getSyncAdapter(); |
| |
| /** |
| * Create and return an intent to display (and edit) settings for a specific account, or -1 |
| * for any/all accounts. If an account name string is provided, a warning dialog will be |
| * displayed as well. |
| */ |
| public static Intent createAccountSettingsIntent(long accountId, String accountName) { |
| final Uri.Builder builder = IntentUtilities.createActivityIntentUrlBuilder( |
| IntentUtilities.PATH_SETTINGS); |
| IntentUtilities.setAccountId(builder, accountId); |
| IntentUtilities.setAccountName(builder, accountName); |
| return new Intent(Intent.ACTION_EDIT, builder.build()); |
| } |
| |
| protected void showAuthNotification(long accountId, String accountName) { |
| final PendingIntent pendingIntent = PendingIntent.getActivity( |
| this, |
| 0, |
| createAccountSettingsIntent(accountId, accountName), |
| 0); |
| |
| final Notification notification = new Notification.Builder(this) |
| .setContentTitle(this.getString(R.string.auth_error_notification_title)) |
| .setContentText(this.getString( |
| R.string.auth_error_notification_text, accountName)) |
| .setSmallIcon(R.drawable.stat_notify_auth) |
| .setContentIntent(pendingIntent) |
| .setAutoCancel(true) |
| .getNotification(); |
| |
| final NotificationManager nm = (NotificationManager) |
| this.getSystemService(Context.NOTIFICATION_SERVICE); |
| nm.notify("AuthError", 0, notification); |
| } |
| |
| /** |
| * Interpret a result code from an {@link IEmailService.sync()} and, if it's an error, write |
| * it to the appropriate field in {@link android.content.SyncResult}. |
| * @param result |
| * @param syncResult |
| * @return Whether an error code was written to syncResult. |
| */ |
| public static boolean writeResultToSyncResult(final int result, final SyncResult syncResult) { |
| switch (result) { |
| case EmailServiceStatus.SUCCESS: |
| return false; |
| |
| case EmailServiceStatus.REMOTE_EXCEPTION: |
| case EmailServiceStatus.LOGIN_FAILED: |
| case EmailServiceStatus.SECURITY_FAILURE: |
| case EmailServiceStatus.CLIENT_CERTIFICATE_ERROR: |
| case EmailServiceStatus.ACCESS_DENIED: |
| syncResult.stats.numAuthExceptions = 1; |
| return true; |
| |
| case EmailServiceStatus.HARD_DATA_ERROR: |
| case EmailServiceStatus.INTERNAL_ERROR: |
| syncResult.databaseError = true; |
| return true; |
| |
| case EmailServiceStatus.CONNECTION_ERROR: |
| case EmailServiceStatus.IO_ERROR: |
| syncResult.stats.numIoExceptions = 1; |
| return true; |
| |
| case EmailServiceStatus.TOO_MANY_REDIRECTS: |
| syncResult.tooManyRetries = true; |
| return true; |
| |
| case EmailServiceStatus.IN_PROGRESS: |
| case EmailServiceStatus.MESSAGE_NOT_FOUND: |
| case EmailServiceStatus.ATTACHMENT_NOT_FOUND: |
| case EmailServiceStatus.FOLDER_NOT_DELETED: |
| case EmailServiceStatus.FOLDER_NOT_RENAMED: |
| case EmailServiceStatus.FOLDER_NOT_CREATED: |
| case EmailServiceStatus.ACCOUNT_UNINITIALIZED: |
| case EmailServiceStatus.PROTOCOL_ERROR: |
| LogUtils.e(TAG, "Unexpected sync result %d", result); |
| return false; |
| } |
| return false; |
| } |
| |
| protected final boolean waitForService() { |
| synchronized(mConnection) { |
| if (mEasService == null) { |
| LogUtils.d(TAG, "service not yet connected"); |
| try { |
| mConnection.wait(MAX_WAIT_FOR_SERVICE_MS); |
| } catch (InterruptedException e) { |
| LogUtils.wtf(TAG, "InterrupedException waiting for EasService to connect"); |
| return false; |
| } |
| if (mEasService == null) { |
| LogUtils.wtf(TAG, "timed out waiting for EasService to connect"); |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| protected final Account getAccountFromAndroidAccount(final android.accounts.Account acct) { |
| final Account emailAccount; |
| emailAccount = Account.restoreAccountWithAddress(this, acct.name); |
| return emailAccount; |
| } |
| |
| } |
| |