Clear error states on network reconnect; add temporary notifications; see details

* When we get a network connect broadcast, clear error states so sync can
  restart for any boxes in an error state
* Add temporary notification code for the testers
* Add file-based debug logger
* Add Exchange logging to debug screen (adds additional exchange debugging)
* Add Exchange sd card logging to debug screen (logs to sd card)
* Change setLogging service API to send an int rather than a boolean
* Make sure push mailboxes are set up again when account changes to push
* Make sure push mailboxes are set up again when account mailbox starts
* (Fixed contacts sync bug found during debugging these changes)
This commit is contained in:
Marc Blank 2009-08-08 21:58:54 -07:00
parent 71132ff6b7
commit cc402e42ab
17 changed files with 349 additions and 63 deletions

View File

@ -37,4 +37,16 @@
android:layout_height="wrap_content"
android:text="@string/debug_enable_sensitive_logging_label"
/>
<CheckBox
android:id="@+id/exchange_logging"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/debug_enable_exchange_logging_label"
/>
<CheckBox
android:id="@+id/exchange_file_logging"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/debug_enable_exchange_file_logging_label"
/>
</LinearLayout>

View File

@ -154,6 +154,10 @@
<string name="debug_enable_debug_logging_label">Enable extra debug logging?</string>
<!-- Checkbox label, shown only on debug screen -->
<string name="debug_enable_sensitive_logging_label">Enable sensitive information debug logging? (May show passwords in logs.)</string>
<!-- Checkbox label, shown only on debug screen -->
<string name="debug_enable_exchange_logging_label">Enable exchange debug logging?</string>
<!-- Checkbox label, shown only on debug screen -->
<string name="debug_enable_exchange_file_logging_label">Enable exchange sd card logging?</string>
<!-- The text in the small separator between smart folders and the accounts -->
<string name="account_folder_list_separator_accounts">Accounts</string>

View File

@ -124,7 +124,7 @@ public class Controller {
*
* Generally this should be called by anybody who changes Email.DEBUG
*/
public void serviceLogging(boolean debugEnabled) {
public void serviceLogging(int debugEnabled) {
IEmailService service =
new EmailServiceProxy(mContext, SyncManager.class, mServiceCallback);
try {

View File

@ -22,6 +22,7 @@ import com.android.email.mail.internet.BinaryTempFileBody;
import com.android.email.provider.EmailContent;
import com.android.email.service.BootReceiver;
import com.android.email.service.MailService;
import com.android.exchange.Eas;
import android.app.Application;
import android.content.ComponentName;
@ -57,7 +58,7 @@ public class Email extends Application {
*/
public static boolean DEBUG_SENSITIVE = false;
/**
/**
* Set this to 'true' to enable as much Email logging as possible.
* Do not check-in with it set to 'true'!
*/
@ -68,7 +69,7 @@ public class Email extends Application {
* to open a chooser with a list of filter types, so the chooser is only opened with the first
* item in the list. The entire list will be used to filter down attachments that are added
* with Intent.ACTION_SEND.
*
*
* TODO: It should be legal to send anything requested by another app. This would provide
* parity with Gmail's behavior.
*/
@ -146,7 +147,7 @@ public class Email extends Application {
Cursor c = null;
try {
c = context.getContentResolver().query(
EmailContent.Account.CONTENT_URI,
EmailContent.Account.CONTENT_URI,
EmailContent.Account.ID_PROJECTION,
null, null, null);
boolean enable = c.getCount() > 0;
@ -204,12 +205,12 @@ public class Email extends Application {
Preferences prefs = Preferences.getPreferences(this);
DEBUG = prefs.geteEnableDebugLogging();
DEBUG_SENSITIVE = prefs.getEnableSensitiveLogging();
// Reset all accounts to default visible window
Cursor c = null;
try {
c = getContentResolver().query(
EmailContent.Account.CONTENT_URI,
EmailContent.Account.CONTENT_URI,
EmailContent.Account.CONTENT_PROJECTION,
null, null, null);
while (c.moveToNext()) {
@ -227,9 +228,13 @@ public class Email extends Application {
* doesn't work in Android and MimeMessage does not have access to a Context.
*/
BinaryTempFileBody.setTempDirectory(getCacheDir());
// Enable logging in the EAS service, so it starts up as early as possible.
Controller.getInstance(this).serviceLogging(DEBUG);
int debugLogging = prefs.geteEnableDebugLogging() ? Eas.DEBUG_BIT : 0;
int exchangeLogging = prefs.getEnableExchangeLogging() ? Eas.DEBUG_EXCHANGE_BIT : 0;
int fileLogging = prefs.getEnableExchangeFileLogging() ? Eas.DEBUG_FILE_BIT : 0;
int debugBits = debugLogging + exchangeLogging + fileLogging;
Controller.getInstance(this).serviceLogging(debugBits);
}
/**

View File

@ -16,12 +16,9 @@
package com.android.email;
import java.util.Arrays;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Config;
import android.util.Log;
public class Preferences {
@ -142,6 +139,22 @@ public class Preferences {
return mSharedPreferences.getBoolean("enableSensitiveLogging", false);
}
public void setEnableExchangeLogging(boolean value) {
mSharedPreferences.edit().putBoolean("enableExchangeLogging", value).commit();
}
public boolean getEnableExchangeLogging() {
return mSharedPreferences.getBoolean("enableExchgangeLogging", false);
}
public void setEnableExchangeFileLogging(boolean value) {
mSharedPreferences.edit().putBoolean("enableExchangeFileLogging", value).commit();
}
public boolean getEnableExchangeFileLogging() {
return mSharedPreferences.getBoolean("enableExchgangeFileLogging", false);
}
public void save() {
}

View File

@ -16,6 +16,13 @@
package com.android.email.activity;
import com.android.email.Controller;
import com.android.email.Email;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.exchange.Eas;
import com.android.exchange.utility.FileLogger;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
@ -25,15 +32,12 @@ import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.CompoundButton.OnCheckedChangeListener;
import com.android.email.Controller;
import com.android.email.Email;
import com.android.email.Preferences;
import com.android.email.R;
public class Debug extends Activity implements OnCheckedChangeListener {
private TextView mVersionView;
private CheckBox mEnableDebugLoggingView;
private CheckBox mEnableSensitiveLoggingView;
private CheckBox mEnableExchangeLoggingView;
private CheckBox mEnableExchangeFileLoggingView;
private Preferences mPreferences;
@ -48,26 +52,59 @@ public class Debug extends Activity implements OnCheckedChangeListener {
mVersionView = (TextView)findViewById(R.id.version);
mEnableDebugLoggingView = (CheckBox)findViewById(R.id.debug_logging);
mEnableSensitiveLoggingView = (CheckBox)findViewById(R.id.sensitive_logging);
mEnableExchangeLoggingView = (CheckBox)findViewById(R.id.exchange_logging);
mEnableExchangeFileLoggingView = (CheckBox)findViewById(R.id.exchange_file_logging);
mEnableDebugLoggingView.setOnCheckedChangeListener(this);
mEnableSensitiveLoggingView.setOnCheckedChangeListener(this);
mEnableExchangeLoggingView.setOnCheckedChangeListener(this);
mEnableExchangeFileLoggingView.setOnCheckedChangeListener(this);
mVersionView.setText(String.format(getString(R.string.debug_version_fmt).toString(),
getString(R.string.build_number)));
mEnableDebugLoggingView.setChecked(Email.DEBUG);
mEnableSensitiveLoggingView.setChecked(Email.DEBUG_SENSITIVE);
mEnableExchangeLoggingView.setChecked(Eas.USER_LOG);
mEnableExchangeFileLoggingView.setChecked(Eas.FILE_LOG);
}
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int debugLogging = mPreferences.geteEnableDebugLogging() ? Eas.DEBUG_BIT : 0;
int exchangeLogging = mPreferences.getEnableExchangeLogging() ? Eas.DEBUG_EXCHANGE_BIT : 0;
int fileLogging = mPreferences.getEnableExchangeFileLogging() ? Eas.DEBUG_FILE_BIT : 0;
int debugBits = debugLogging + exchangeLogging + fileLogging;
if (buttonView.getId() == R.id.debug_logging) {
Email.DEBUG = isChecked;
mPreferences.setEnableDebugLogging(Email.DEBUG);
Controller.getInstance(getApplication()).serviceLogging(Email.DEBUG);
if (isChecked) {
debugBits |= Eas.DEBUG_BIT;
} else {
debugBits &= ~Eas.DEBUG_BIT;
}
} else if (buttonView.getId() == R.id.sensitive_logging) {
Email.DEBUG_SENSITIVE = isChecked;
mPreferences.setEnableSensitiveLogging(Email.DEBUG_SENSITIVE);
} else if (buttonView.getId() == R.id.exchange_logging) {
mPreferences.setEnableExchangeLogging(isChecked);
if (isChecked) {
debugBits |= Eas.DEBUG_EXCHANGE_BIT;
} else {
debugBits &= ~Eas.DEBUG_EXCHANGE_BIT;
}
} else if (buttonView.getId() == R.id.exchange_file_logging) {
if (!isChecked) {
FileLogger.close();
}
mPreferences.setEnableExchangeFileLogging(isChecked);
if (isChecked) {
debugBits |= Eas.DEBUG_FILE_BIT;
} else {
debugBits &= ~Eas.DEBUG_FILE_BIT;
}
}
Controller.getInstance(getApplication()).serviceLogging(debugBits);
}
@Override

View File

@ -24,12 +24,10 @@ import com.android.email.Utility;
import com.android.email.activity.Debug;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.XmlResourceParser;
@ -67,7 +65,7 @@ public class AccountSetupBasics extends Activity
private final static int DIALOG_NOTE = 1;
private final static String STATE_KEY_PROVIDER =
"com.android.email.AccountSetupBasics.provider";
// NOTE: If you change this value, confirm that the new interval exists in arrays.xml
private final static int DEFAULT_ACCOUNT_CHECK_INTERVAL = 15;
@ -109,7 +107,7 @@ public class AccountSetupBasics extends Activity
Cursor c = null;
try {
c = getContentResolver().query(
EmailContent.Account.CONTENT_URI,
EmailContent.Account.CONTENT_URI,
EmailContent.Account.ID_PROJECTION,
null, null, null);
if (c.getCount() > 0) {
@ -320,11 +318,13 @@ public class AccountSetupBasics extends Activity
String[] emailParts = email.split("@");
String user = emailParts[0].trim();
String domain = emailParts[1].trim();
// Alternate entry to the debug options screen (for devices without a physical keyboard:
// Username: d@d.d
// Password: debug
if (ENTER_DEBUG_SCREEN && "d@d.d".equals(email) && "debug".equals(password)) {
mEmailView.setText("");
mPasswordView.setText("");
startActivity(new Intent(this, Debug.class));
return;
}
@ -387,9 +387,9 @@ public class AccountSetupBasics extends Activity
* Search the list of known Email providers looking for one that matches the user's email
* domain. We look in providers_product.xml first, followed by the entries in
* platform providers.xml. This provides a nominal override capability.
*
*
* A match is defined as any provider entry for which the "domain" attribute matches.
*
*
* @param domain The domain portion of the user's email address
* @return suitable Provider definition, or null if no match found
*/

View File

@ -215,7 +215,7 @@ public class EmailServiceProxy implements IEmailService {
});
}
public void setLogging(final boolean on) throws RemoteException {
public void setLogging(final int on) throws RemoteException {
setTask(new Runnable () {
public void run() {
try {

View File

@ -20,6 +20,7 @@ package com.android.exchange;
import com.android.email.mail.MessagingException;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.exchange.utility.FileLogger;
import android.content.Context;
import android.net.ConnectivityManager;
@ -208,6 +209,9 @@ public abstract class AbstractSyncService implements Runnable {
public void userLog(String str) {
if (Eas.USER_LOG) {
Log.i(TAG, str);
if (Eas.FILE_LOG) {
FileLogger.log(TAG, str);
}
}
}
@ -217,6 +221,9 @@ public abstract class AbstractSyncService implements Runnable {
*/
public void errorLog(String str) {
Log.e(TAG, str);
if (Eas.FILE_LOG) {
FileLogger.log(TAG, str);
}
}
/**

View File

@ -17,6 +17,8 @@
package com.android.exchange;
import android.util.Log;
/**
* Constants used throughout the EAS implementation are stored here.
*
@ -24,13 +26,18 @@ package com.android.exchange;
public class Eas {
// For debugging
public static boolean WAIT_DEBUG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
public static boolean DEBUG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
public static boolean DEBUG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
// The following two are for user logging (the second providing more detail)
public static boolean USER_LOG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
public static boolean PARSER_LOG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
public static boolean FILE_LOG = false; // DO NOT CHECK IN WITH THIS SET TO TRUE
public static final String VERSION = "0.2";
public static final int DEBUG_BIT = 1;
public static final int DEBUG_EXCHANGE_BIT = 2;
public static final int DEBUG_FILE_BIT = 4;
public static final String VERSION = "0.3";
public static final String ACCOUNT_MANAGER_TYPE = "com.android.exchange";
public static final String ACCOUNT_MAILBOX = "__eas";
@ -63,10 +70,20 @@ public class Eas {
public static final int EXCHANGE_ERROR_NOTIFICATION = 0x10;
public static void setUserDebug(boolean state) {
public static void setUserDebug(int state) {
// DEBUG takes precedence and is never true in a user build
if (!DEBUG) {
USER_LOG = state;
USER_LOG = (state & DEBUG_BIT) != 0;
PARSER_LOG = (state & DEBUG_EXCHANGE_BIT) != 0;
FILE_LOG = (state & DEBUG_FILE_BIT) != 0;
if (FILE_LOG) {
PARSER_LOG = true;
USER_LOG = true;
} else if (PARSER_LOG) {
USER_LOG = true;
}
Log.d("Eas Debug", "Logging: " + (USER_LOG ? "User " : "") +
(PARSER_LOG ? "Parser " : "") + (FILE_LOG ? "File" : ""));
}
}
}

View File

@ -62,6 +62,7 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.os.RemoteException;
import android.util.Log;
import java.io.ByteArrayInputStream;
import java.io.File;
@ -116,7 +117,7 @@ public class EasSyncService extends AbstractSyncService {
public ContentResolver mContentResolver;
String[] mBindArguments = new String[2];
InputStream mPendingPartInputStream = null;
private boolean mTriedReloadFolderList = false;
//private boolean mTriedReloadFolderList = false;
public EasSyncService(Context _context, Mailbox _mailbox) {
super(_context, _mailbox);
@ -154,7 +155,8 @@ public class EasSyncService extends AbstractSyncService {
}
private boolean isAuthError(int code) {
return (code == HttpURLConnection.HTTP_UNAUTHORIZED || code == HttpURLConnection.HTTP_FORBIDDEN
return (code == HttpURLConnection.HTTP_UNAUTHORIZED
|| code == HttpURLConnection.HTTP_FORBIDDEN
|| code == HttpURLConnection.HTTP_INTERNAL_ERROR);
}
@ -450,11 +452,22 @@ public class EasSyncService extends AbstractSyncService {
}
}
while (!mStop) {
// Change all pushable boxes to push when we start the account mailbox
if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
cv = new ContentValues();
cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
SyncManager.WHERE_IN_ACCOUNT_AND_PUSHABLE,
new String[] {Long.toString(mAccount.mId)}) > 0) {
userLog("Push account; set pushable boxes to push...");
}
}
while (!mStop) {
userLog("Sending Account syncKey: " + mAccount.mSyncKey);
Serializer s = new Serializer();
s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
.text(mAccount.mSyncKey).end().end().done();
.text(mAccount.mSyncKey).end().end().done();
HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
if (mStop) break;
int code = resp.getStatusLine().getStatusCode();
@ -521,12 +534,12 @@ public class EasSyncService extends AbstractSyncService {
void pushFallback() {
// We'll try reloading folders first; this has been observed to work in some cases
if (!mTriedReloadFolderList) {
errorLog("*** PING LOOP: Trying to reload folder list...");
SyncManager.reloadFolderList(mContext, mAccount.mId, true);
mTriedReloadFolderList = true;
// If we've tried that, set all mailboxes (except the account mailbox) to 5 minute sync
} else {
// if (!mTriedReloadFolderList) {
// errorLog("*** PING LOOP: Trying to reload folder list...");
// SyncManager.reloadFolderList(mContext, mAccount.mId, true);
// mTriedReloadFolderList = true;
// // If we've tried that, set all mailboxes (except the account mailbox) to 5 minute sync
// } else {
errorLog("*** PING LOOP: Turning off push due to ping loop...");
ContentValues cv = new ContentValues();
cv.put(Mailbox.SYNC_INTERVAL, 5);
@ -538,6 +551,9 @@ public class EasSyncService extends AbstractSyncService {
cv.put(Account.SYNC_INTERVAL, 5);
mContentResolver.update(ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId),
cv, null, null);
// Let the SyncManager know that something's changed
SyncManager.kick("push fallback");
// TODO Discuss the best way to alert the user
// Alert the user about what we've done
NotificationManager nm = (NotificationManager)mContext
@ -552,7 +568,7 @@ public class EasSyncService extends AbstractSyncService {
mContext.getString(R.string.notification_ping_loop_title),
mContext.getString(R.string.notification_ping_loop_text), pi);
nm.notify(Eas.EXCHANGE_ERROR_NOTIFICATION, note);
}
// }
}
void runPingLoop() throws IOException, StaleFolderListException {
@ -582,9 +598,12 @@ public class EasSyncService extends AbstractSyncService {
// 1) SyncManager tells us the mailbox is syncable (not running, not stopped)
// 2) The syncKey isn't "0" (i.e. it's synced at least once)
long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
if (SyncManager.canSync(mailboxId)) {
int pingStatus = SyncManager.pingStatus(mailboxId);
String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
if (pingStatus == SyncManager.PING_STATUS_OK) {
String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
if (syncKey == null || syncKey.equals("0")) {
pushCount--;
continue;
}
@ -630,13 +649,16 @@ public class EasSyncService extends AbstractSyncService {
.data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN))
.data(Tags.PING_CLASS, folderClass)
.end();
userLog("Ping ready for: " + folderClass + ", " +
c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN) + " (" +
c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN) + ')');
userLog("Ping ready for: " + folderClass + ", " + mailboxName + " (" +
c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN) + ')');
pushBoxes.add(new Mailbox().restore(c));
} else {
userLog(c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN) +
" not ready for ping");
} else if (pingStatus == SyncManager.PING_STATUS_RUNNING ||
pingStatus == SyncManager.PING_STATUS_WAITING) {
userLog(mailboxName + " not ready for ping");
} else if (pingStatus == SyncManager.PING_STATUS_UNABLE) {
pushCount--;
userLog(mailboxName + " in error state; ignore");
continue;
}
}
} finally {
@ -689,7 +711,9 @@ public class EasSyncService extends AbstractSyncService {
sleep(10*SECS);
} else {
// We've got nothing to do, so let's hang out for a while
sleep(20*MINS);
SyncManager.runAsleep(mMailboxId, 30*MINS);
sleep(30*MINS);
SyncManager.runAwake(mMailboxId);
}
}
}
@ -957,7 +981,7 @@ public class EasSyncService extends AbstractSyncService {
userLog("Caught IOException");
mExitStatus = EXIT_IO_ERROR;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Uncaught exception in EasSyncService", e);
} finally {
if (!mStop) {
userLog(mMailbox.mDisplayName + ": sync finished");
@ -978,6 +1002,7 @@ public class EasSyncService extends AbstractSyncService {
break;
default:
status = EmailServiceStatus.REMOTE_EXCEPTION;
errorLog("Sync ended due to an exception.");
break;
}
try {

View File

@ -37,5 +37,5 @@ interface IEmailService {
void setCallback(IEmailServiceCallback cb);
void setLogging(boolean on);
void setLogging(int on);
}

View File

@ -27,6 +27,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.exchange.utility.FileLogger;
import android.app.AlarmManager;
import android.app.PendingIntent;
@ -86,6 +87,7 @@ public class SyncManager extends Service implements Runnable {
public static final int SECS = 1000;
public static final int MINS = 60 * SECS;
// Reason codes when SyncManager.kick is called (mainly for debugging)
public static final int SYNC_UPSYNC = 0;
public static final int SYNC_SCHEDULED = 1;
public static final int SYNC_PUSH = 2;
@ -98,6 +100,9 @@ public class SyncManager extends Service implements Runnable {
MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.TYPE + "!=" +
Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + " and " + MailboxColumns.SYNC_INTERVAL +
" IN (" + Account.CHECK_INTERVAL_PING + ',' + Account.CHECK_INTERVAL_PUSH + ')';
public static final String WHERE_IN_ACCOUNT_AND_PUSHABLE =
MailboxColumns.ACCOUNT_KEY + "=? and type in (" + Mailbox.TYPE_INBOX + ','
/*+ Mailbox.TYPE_CALENDAR + ','*/ + Mailbox.TYPE_CONTACTS + ')';
// Offsets into the syncStatus data for EAS that indicate type, exit status, and change count
// The format is S<type_char>:<exit_char>:<change_count>
@ -105,6 +110,15 @@ public class SyncManager extends Service implements Runnable {
public static final int STATUS_EXIT_CHAR = 3;
public static final int STATUS_CHANGE_COUNT_OFFSET = 5;
// Ready for ping
public static final int PING_STATUS_OK = 0;
// Service already running (can't ping)
public static final int PING_STATUS_RUNNING = 1;
// Service waiting after I/O error (can't ping)
public static final int PING_STATUS_WAITING = 2;
// Service had a fatal error; can't run
public static final int PING_STATUS_UNABLE = 3;
static SyncManager INSTANCE;
static Object mSyncToken = new Object();
static Thread mServiceThread = null;
@ -208,7 +222,7 @@ public class SyncManager extends Service implements Runnable {
reloadFolderList(SyncManager.this, accountId, false);
}
public void setLogging(boolean on) throws RemoteException {
public void setLogging(int on) throws RemoteException {
Eas.setUserDebug(on);
}
@ -312,10 +326,23 @@ public class SyncManager extends Service implements Runnable {
// Here's one that has...
INSTANCE.log("Account " + account.mDisplayName +
" changed; stopping running syncs...");
// If account is push, set contacts and inbox to push
Account updatedAccount =
Account.restoreAccountWithId(getContext(), account.mId);
if (updatedAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
ContentValues cv = new ContentValues();
cv.put(MailboxColumns.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
getContext().getContentResolver().update(Mailbox.CONTENT_URI, cv,
WHERE_IN_ACCOUNT_AND_PUSHABLE,
new String[] {Long.toString(account.mId)});
}
// Stop all current syncs; the appropriate ones will restart
stopAccountSyncs(account.mId, false);
}
}
}
// Look for new accounts
for (Account account: currentAccounts) {
if (!mAccounts.contains(account.mId)) {
// This is an addition; create our magic hidden mailbox...
@ -323,7 +350,10 @@ public class SyncManager extends Service implements Runnable {
mAccounts.add(account);
}
}
} finally {
// Finally, make sure mAccounts is up to date
mAccounts = currentAccounts;
} finally {
c.close();
}
@ -485,6 +515,9 @@ public class SyncManager extends Service implements Runnable {
public void log(String str) {
if (Eas.USER_LOG) {
Log.d(TAG, str);
if (Eas.FILE_LOG) {
FileLogger.log(TAG, str);
}
}
}
@ -778,6 +811,8 @@ public class SyncManager extends Service implements Runnable {
if (state == State.CONNECTED) {
info += " CONNECTED";
kick("connected");
// Clear our sync error map when we get connected
mSyncErrorMap.clear();
} else if (state == State.CONNECTING) {
info += " CONNECTING";
} else if (state == State.DISCONNECTED) {
@ -1124,16 +1159,21 @@ public class SyncManager extends Service implements Runnable {
* @param mailboxId
* @return whether or not the Mailbox is available for syncing (i.e. is a valid push target)
*/
static public boolean canSync(long mailboxId) {
static public int pingStatus(long mailboxId) {
// Already syncing...
if (INSTANCE.mServiceMap.get(mailboxId) != null) {
return false;
return PING_STATUS_RUNNING;
}
// Blocked from syncing (transient or permanent)
if (INSTANCE.mSyncErrorMap.get(mailboxId) != null) {
return false;
// No errors or a transient error, don't ping...
SyncError error = INSTANCE.mSyncErrorMap.get(mailboxId);
if (error != null) {
if (error.fatal) {
return PING_STATUS_UNABLE;
} else {
return PING_STATUS_WAITING;
}
}
return true;
return PING_STATUS_OK;
}
static public int getSyncStatus(long mailboxId) {
@ -1243,8 +1283,11 @@ public class SyncManager extends Service implements Runnable {
case AbstractSyncService.EXIT_IO_ERROR:
if (syncError != null) {
syncError.escalate();
INSTANCE.log("Mailbox " + mailboxId + " now held for "
+ syncError.holdDelay + "s");
} else {
errorMap.put(mailboxId, INSTANCE.new SyncError(exitStatus, false));
INSTANCE.log("Mailbox " + mailboxId + " added to syncErrorMap");
}
kick("i/o error in sync");
break;

View File

@ -1046,11 +1046,13 @@ public class ContactsSyncAdapter extends AbstractSyncAdapter {
}
public void addNickname(Entity entity, String name) {
SmartBuilder builder = createBuilder(entity, Nickname.CONTENT_ITEM_TYPE, -1);
SmartBuilder builder =
createBuilder(entity, Nickname.CONTENT_ITEM_TYPE, Nickname.TYPE_DEFAULT);
ContentValues cv = builder.cv;
if (cv != null && cvCompareString(cv, Nickname.NAME, name)) {
return;
}
builder.withValue(Nickname.TYPE, Nickname.TYPE_DEFAULT);
builder.withValue(Nickname.NAME, name);
add(builder.build());
}

View File

@ -17,7 +17,10 @@
package com.android.exchange.adapter;
import com.android.email.R;
import com.android.email.activity.MessageList;
import com.android.email.mail.Address;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailProvider;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.Mailbox;
@ -27,14 +30,20 @@ import com.android.email.provider.EmailContent.SyncColumns;
import com.android.exchange.Eas;
import com.android.exchange.EasSyncService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.text.TextUtils;
import android.webkit.MimeTypeMap;
import java.io.IOException;
@ -389,6 +398,7 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
ArrayList<Message> newEmails = new ArrayList<Message>();
ArrayList<Long> deletedEmails = new ArrayList<Long>();
ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>();
int notifyCount = 0;
while (nextTag(Tags.SYNC_COMMANDS) != END) {
if (tag == Tags.SYNC_ADD) {
@ -407,8 +417,11 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
// Use a batch operation to handle the changes
// TODO New mail notifications? Who looks for these?
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
for (Message content : newEmails) {
content.addSaveOps(ops);
for (Message msg: newEmails) {
if (!msg.mFlagRead) {
notifyCount++;
}
msg.addSaveOps(ops);
}
for (Long id : deletedEmails) {
ops.add(ContentProviderOperation.newDelete(
@ -450,8 +463,30 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
// There is nothing to be done here; fail by returning null
}
}
}
// TODO Remove this temporary notification code
if (notifyCount > 0) {
NotificationManager notifMgr =
(NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notif = new Notification(R.drawable.stat_notify_email_generic,
mContext.getString(R.string.notification_new_title),
System.currentTimeMillis());
Intent i = MessageList.actionHandleAccountIntent(mContext, mAccount.mId,
Mailbox.TYPE_INBOX);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, i, 0);
notif.setLatestEventInfo(mContext,
mContext.getString(R.string.notification_new_title),
"You've got new mail!", pi);
boolean vibrate = ((mAccount.getFlags() & EmailContent.Account.FLAGS_VIBRATE) != 0);
String ringtone = mAccount.getRingtone();
notif.defaults = Notification.DEFAULT_LIGHTS;
notif.sound = TextUtils.isEmpty(ringtone) ? null : Uri.parse(ringtone);
if (vibrate) {
notif.defaults |= Notification.DEFAULT_VIBRATE;
}
notifMgr.notify(1, notif);
}
}
}
@Override

View File

@ -47,7 +47,9 @@ public class PingParser extends Parser {
while (nextTag(Tags.PING_FOLDERS) != END) {
if (tag == Tags.PING_FOLDER) {
// Here we'll keep track of which mailboxes need syncing
syncList.add(getValue());
String serverId = getValue();
syncList.add(serverId);
mService.userLog("Changes found in: " + serverId);
} else {
skipTag();
}

View File

@ -0,0 +1,84 @@
package com.android.exchange.utility;
import android.content.Context;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
public class FileLogger {
private static FileLogger LOGGER = null;
private static FileWriter mLogWriter = null;
public static String LOG_FILE_NAME = "/sdcard/emaillog.txt";
private static Object mLock = new Object();
public synchronized static FileLogger getLogger (Context c) {
LOGGER = new FileLogger();
return LOGGER;
}
private FileLogger () {
synchronized (mLock) {
try {
mLogWriter = new FileWriter(LOG_FILE_NAME, true);
} catch (IOException e) {
// Doesn't matter
}
}
}
static public synchronized void close() {
if (mLogWriter != null) {
try {
mLogWriter.close();
} catch (IOException e) {
// Doesn't matter
}
mLogWriter = null;
}
}
@SuppressWarnings("deprecation")
static public synchronized void log (String prefix, String str) {
if (LOGGER == null) {
LOGGER = new FileLogger();
log("Logger", "\r\n\r\n --- New Log ---");
}
Date d = new Date();
int hr = d.getHours();
int min = d.getMinutes();
int sec = d.getSeconds();
StringBuffer sb = new StringBuffer(256);
sb.append('[');
sb.append(hr);
sb.append(':');
if (min < 10)
sb.append('0');
sb.append(min);
sb.append(':');
if (sec < 10) {
sb.append('0');
}
sb.append(sec);
sb.append("] ");
if (prefix != null) {
sb.append(prefix);
sb.append("| ");
}
sb.append(str);
sb.append("\r\n");
String s = sb.toString();
synchronized (mLock) {
if (mLogWriter != null) {
try {
mLogWriter.write(s);
mLogWriter.flush();
} catch (IOException e) {
// Doesn't matter
}
}
}
}
}