Prevent MailService from potential looping due to EAS accounts
* The code assumed that all accounts used the scheduler in MailService whereas only those using MessagingController do so (i.e. EAS does not) * Change setupSyncReportsLocked to set the syncInterval for accounts that don't use MessagingController to Account.CHECK_INTERVAL_NEVER * Add unit test for the changed code Change-Id: I74a3dae21d9ec16f9903bdf2a1c28092ae89cc50
This commit is contained in:
parent
42e3f10a95
commit
42ff939e3a
|
@ -31,6 +31,7 @@ import android.app.Notification;
|
|||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
|
@ -77,14 +78,16 @@ public class MailService extends Service {
|
|||
private static final String[] NEW_MESSAGE_COUNT_PROJECTION =
|
||||
new String[] {AccountColumns.NEW_MESSAGE_COUNT};
|
||||
|
||||
/*package*/ Controller mController;
|
||||
private final Controller.Result mControllerCallback = new ControllerResults();
|
||||
private ContentResolver mContentResolver;
|
||||
|
||||
private int mStartId;
|
||||
|
||||
/**
|
||||
* Access must be synchronized, because there are accesses from the Controller callback
|
||||
*/
|
||||
private static HashMap<Long,AccountSyncReport> mSyncReports =
|
||||
/*package*/ static HashMap<Long,AccountSyncReport> mSyncReports =
|
||||
new HashMap<Long,AccountSyncReport>();
|
||||
|
||||
/**
|
||||
|
@ -161,8 +164,9 @@ public class MailService extends Service {
|
|||
this.mStartId = startId;
|
||||
String action = intent.getAction();
|
||||
|
||||
Controller controller = Controller.getInstance(getApplication());
|
||||
controller.addResultCallback(mControllerCallback);
|
||||
mController = Controller.getInstance(this);
|
||||
mController.addResultCallback(mControllerCallback);
|
||||
mContentResolver = getContentResolver();
|
||||
|
||||
if (ACTION_CHECK_MAIL.equals(action)) {
|
||||
// If we have the data, restore the last-sync-times for each account
|
||||
|
@ -182,7 +186,7 @@ public class MailService extends Service {
|
|||
// Start sync if account is given and background data is enabled.
|
||||
boolean syncStarted = false;
|
||||
if (checkAccountId != -1 && isBackgroundDataEnabled()) {
|
||||
syncStarted = syncOneAccount(controller, checkAccountId, startId);
|
||||
syncStarted = syncOneAccount(mController, checkAccountId, startId);
|
||||
}
|
||||
|
||||
// Reschedule if we didn't start sync.
|
||||
|
@ -224,7 +228,7 @@ public class MailService extends Service {
|
|||
} else if (ACTION_NOTIFY_MAIL.equals(action)) {
|
||||
long accountId = intent.getLongExtra(EXTRA_CHECK_ACCOUNT, -1);
|
||||
// Get the current new message count
|
||||
Cursor c = getContentResolver().query(
|
||||
Cursor c = mContentResolver.query(
|
||||
ContentUris.withAppendedId(Account.CONTENT_URI, accountId),
|
||||
NEW_MESSAGE_COUNT_PROJECTION, null, null, null);
|
||||
int newMessageCount = 0;
|
||||
|
@ -284,7 +288,7 @@ public class MailService extends Service {
|
|||
|
||||
// Delete the sync reports to force a refresh from live account db data
|
||||
mSyncReports.clear();
|
||||
setupSyncReportsLocked(-1);
|
||||
setupSyncReportsLocked(-1, mContentResolver);
|
||||
|
||||
// Restore prev-sync & next-sync times for any reports in the new list
|
||||
for (AccountSyncReport newReport : mSyncReports.values()) {
|
||||
|
@ -319,9 +323,10 @@ public class MailService extends Service {
|
|||
long timeNow = SystemClock.elapsedRealtime();
|
||||
|
||||
for (AccountSyncReport report : mSyncReports.values()) {
|
||||
if (report.syncInterval <= 0) { // no timed checks - skip
|
||||
if (report.syncInterval <= 0) { // no timed checks - skip
|
||||
continue;
|
||||
}
|
||||
|
||||
long prevSyncTime = report.prevSyncTime;
|
||||
long nextSyncTime = report.nextSyncTime;
|
||||
|
||||
|
@ -417,7 +422,7 @@ public class MailService extends Service {
|
|||
/**
|
||||
* Note: Times are relative to SystemClock.elapsedRealtime()
|
||||
*/
|
||||
private static class AccountSyncReport {
|
||||
/*package*/ static class AccountSyncReport {
|
||||
long accountId;
|
||||
long prevSyncTime; // 0 == unknown
|
||||
long nextSyncTime; // 0 == ASAP -1 == don't sync
|
||||
|
@ -448,14 +453,14 @@ public class MailService extends Service {
|
|||
*/
|
||||
/* package */ void setupSyncReports(long accountId) {
|
||||
synchronized (mSyncReports) {
|
||||
setupSyncReportsLocked(accountId);
|
||||
setupSyncReportsLocked(accountId, mContentResolver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the work of setupSyncReports. Must be synchronized on mSyncReports.
|
||||
*/
|
||||
private void setupSyncReportsLocked(long accountId) {
|
||||
/*package*/ void setupSyncReportsLocked(long accountId, ContentResolver resolver) {
|
||||
if (accountId == -1) {
|
||||
// -1 == reload the list if empty, otherwise exit immediately
|
||||
if (mSyncReports.size() > 0) {
|
||||
|
@ -477,17 +482,19 @@ public class MailService extends Service {
|
|||
}
|
||||
|
||||
// TODO use a narrower projection here
|
||||
Cursor c = getContentResolver().query(uri, Account.CONTENT_PROJECTION,
|
||||
null, null, null);
|
||||
Cursor c = resolver.query(uri, Account.CONTENT_PROJECTION, null, null, null);
|
||||
try {
|
||||
while (c.moveToNext()) {
|
||||
AccountSyncReport report = new AccountSyncReport();
|
||||
int syncInterval = c.getInt(Account.CONTENT_SYNC_INTERVAL_COLUMN);
|
||||
int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN);
|
||||
long id = c.getInt(Account.CONTENT_ID_COLUMN);
|
||||
String ringtoneString = c.getString(Account.CONTENT_RINGTONE_URI_COLUMN);
|
||||
|
||||
// For debugging only
|
||||
if (DEBUG_FORCE_QUICK_REFRESH && syncInterval >= 0) {
|
||||
// If we're not using MessagingController (EAS at this point), don't schedule syncs
|
||||
if (!mController.isMessagingController(id)) {
|
||||
syncInterval = Account.CHECK_INTERVAL_NEVER;
|
||||
} else if (DEBUG_FORCE_QUICK_REFRESH && syncInterval >= 0) {
|
||||
syncInterval = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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 com.android.email.Controller;
|
||||
import com.android.email.provider.EmailProvider;
|
||||
import com.android.email.provider.ProviderTestUtils;
|
||||
import com.android.email.provider.EmailContent.Account;
|
||||
import com.android.email.provider.EmailContent.HostAuth;
|
||||
import com.android.email.service.MailService.AccountSyncReport;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.test.ProviderTestCase2;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Tests of the Email provider.
|
||||
*
|
||||
* You can run this entire test case with:
|
||||
* runtest -c com.android.email.service.MailServiceTests email
|
||||
*/
|
||||
public class MailServiceTests extends ProviderTestCase2<EmailProvider> {
|
||||
|
||||
EmailProvider mProvider;
|
||||
Context mMockContext;
|
||||
|
||||
public MailServiceTests() {
|
||||
super(EmailProvider.class, EmailProvider.EMAIL_AUTHORITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mMockContext = getMockContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tearDown() throws Exception {
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightweight subclass of the Controller class allows injection of mock context
|
||||
*/
|
||||
public static class TestController extends Controller {
|
||||
|
||||
protected TestController(Context providerContext, Context systemContext) {
|
||||
super(systemContext);
|
||||
setProviderContext(providerContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simple HostAuth with protocol
|
||||
*/
|
||||
private HostAuth setupSimpleHostAuth(String protocol) {
|
||||
HostAuth hostAuth = new HostAuth();
|
||||
hostAuth.mProtocol = protocol;
|
||||
return hostAuth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initial testing on setupSyncReportsLocked, making sure that EAS accounts aren't scheduled
|
||||
*/
|
||||
public void testSetupSyncReportsLocked() {
|
||||
// TODO Test other functionality within setupSyncReportsLocked
|
||||
// Setup accounts of each type, all with manual sync at different intervals
|
||||
Account easAccount = ProviderTestUtils.setupAccount("account1", false, mMockContext);
|
||||
easAccount.mHostAuthRecv = setupSimpleHostAuth("eas");
|
||||
easAccount.mSyncInterval = 30;
|
||||
easAccount.save(mMockContext);
|
||||
Account imapAccount = ProviderTestUtils.setupAccount("account2", false, mMockContext);
|
||||
imapAccount.mHostAuthRecv = setupSimpleHostAuth("imap");
|
||||
imapAccount.mSyncInterval = 60;
|
||||
imapAccount.save(mMockContext);
|
||||
Account pop3Account = ProviderTestUtils.setupAccount("account3", false, mMockContext);
|
||||
pop3Account.mHostAuthRecv = setupSimpleHostAuth("pop3");
|
||||
pop3Account.mSyncInterval = 90;
|
||||
pop3Account.save(mMockContext);
|
||||
|
||||
// Setup the SyncReport's for these Accounts
|
||||
MailService mailService = new MailService();
|
||||
mailService.mController = new TestController(mMockContext, getContext());
|
||||
mailService.setupSyncReportsLocked(-1, mMockContext.getContentResolver());
|
||||
|
||||
// Get back the map created by MailService
|
||||
HashMap<Long, AccountSyncReport> syncReportMap = MailService.mSyncReports;
|
||||
// Check the SyncReport's for correctness of sync interval
|
||||
AccountSyncReport syncReport = syncReportMap.get(easAccount.mId);
|
||||
assertNotNull(syncReport);
|
||||
// EAS sync interval should have been changed to "never"
|
||||
assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval);
|
||||
syncReport = syncReportMap.get(imapAccount.mId);
|
||||
assertNotNull(syncReport);
|
||||
assertEquals(60, syncReport.syncInterval);
|
||||
syncReport = syncReportMap.get(pop3Account.mId);
|
||||
assertNotNull(syncReport);
|
||||
assertEquals(90, syncReport.syncInterval);
|
||||
|
||||
// Change the EAS account to push
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put(Account.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
|
||||
easAccount.update(mMockContext, cv);
|
||||
syncReportMap.clear();
|
||||
mailService.setupSyncReportsLocked(easAccount.mId, mMockContext.getContentResolver());
|
||||
syncReport = syncReportMap.get(easAccount.mId);
|
||||
assertNotNull(syncReport);
|
||||
// EAS sync interval should be "never" in this case as well
|
||||
assertEquals(Account.CHECK_INTERVAL_NEVER, syncReport.syncInterval);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue