EAS implementation of "move to folder"

* Handle errors cases appropriately

Change-Id: I16060cc5c4fc648c299a2fa8f9f57d9aa5c37f56
This commit is contained in:
Marc Blank 2010-08-23 22:07:00 -07:00
parent 486761169d
commit d694a20849
5 changed files with 219 additions and 1 deletions

View File

@ -32,6 +32,7 @@ import com.android.email.provider.EmailContent.HostAuth;
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.EmailServiceConstants;
import com.android.email.service.EmailServiceProxy;
import com.android.email.service.EmailServiceStatus;
@ -43,6 +44,7 @@ import com.android.exchange.adapter.EmailSyncAdapter;
import com.android.exchange.adapter.FolderSyncParser;
import com.android.exchange.adapter.GalParser;
import com.android.exchange.adapter.MeetingResponseParser;
import com.android.exchange.adapter.MoveItemsParser;
import com.android.exchange.adapter.PingParser;
import com.android.exchange.adapter.ProvisionParser;
import com.android.exchange.adapter.Serializer;
@ -1061,6 +1063,69 @@ public class EasSyncService extends AbstractSyncService {
}
}
/**
* Responds to a move request. The MessageMoveRequest is basically our
* wrapper for the MoveItems service call
* @param req the request (message id and "to" mailbox id)
* @throws IOException
*/
protected void messageMoveRequest(MessageMoveRequest req) throws IOException {
// Retrieve the message and mailbox; punt if either are null
Message msg = Message.restoreMessageWithId(mContext, req.mMessageId);
if (msg == null) return;
Cursor c = mContentResolver.query(ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI,
msg.mId), new String[] {MessageColumns.MAILBOX_KEY}, null, null, null);
Mailbox srcMailbox = null;
try {
if (!c.moveToNext()) return;
srcMailbox = Mailbox.restoreMailboxWithId(mContext, c.getLong(0));
} finally {
c.close();
}
if (srcMailbox == null) return;
Mailbox dstMailbox = Mailbox.restoreMailboxWithId(mContext, req.mMailboxId);
if (dstMailbox == null) return;
Serializer s = new Serializer();
s.start(Tags.MOVE_MOVE_ITEMS).start(Tags.MOVE_MOVE);
s.data(Tags.MOVE_SRCMSGID, msg.mServerId);
s.data(Tags.MOVE_SRCFLDID, srcMailbox.mServerId);
s.data(Tags.MOVE_DSTFLDID, dstMailbox.mServerId);
s.end().end().done();
HttpResponse res = sendHttpClientPost("MoveItems", s.toByteArray());
int status = res.getStatusLine().getStatusCode();
if (status == HttpStatus.SC_OK) {
HttpEntity e = res.getEntity();
int len = (int)e.getContentLength();
InputStream is = res.getEntity().getContent();
if (len != 0) {
MoveItemsParser p = new MoveItemsParser(is, this);
p.parse();
int statusCode = p.getStatusCode();
if (statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
// Restore the old mailbox id
ContentValues cv = new ContentValues();
cv.put(MessageColumns.MAILBOX_KEY, srcMailbox.mServerId);
mContentResolver.update(ContentUris.withAppendedId(
Message.CONTENT_URI, req.mMessageId), cv, null, null);
}
if (statusCode == MoveItemsParser.STATUS_CODE_SUCCESS ||
statusCode == MoveItemsParser.STATUS_CODE_REVERT) {
// If we revert or if we succeeded, we no longer need the update information
mContentResolver.delete(ContentUris.withAppendedId(
Message.UPDATED_CONTENT_URI, req.mMessageId), null, null);
} else {
// In this case, we're retrying, so do nothing. The request will be handled
// next sync
}
}
} else if (isAuthError(status)) {
throw new EasAuthenticationException();
} else {
userLog("Move items request failed, code: " + status);
throw new IOException();
}
}
/**
* Responds to a meeting request. The MeetingResponseRequest is basically our
* wrapper for the meetingResponse service call
@ -2059,6 +2124,8 @@ public class EasSyncService extends AbstractSyncService {
loadAttachment((PartRequest)req);
} else if (req instanceof MeetingResponseRequest) {
sendMeetingResponse((MeetingResponseRequest)req);
} else if (req instanceof MessageMoveRequest) {
messageMoveRequest((MessageMoveRequest)req);
}
// If there's an exception handling the request, we'll throw it

View File

@ -0,0 +1,29 @@
/*
* 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;
/**
* MessageMoveRequest is the EAS wrapper for requesting a "move to folder"
*/
public class MessageMoveRequest extends Request {
public final long mMailboxId;
public MessageMoveRequest(long messageId, long mailboxId) {
mMessageId = messageId;
mMailboxId = mailboxId;
}
}

View File

@ -429,6 +429,7 @@ public class SyncManager extends Service implements Runnable {
}
public void moveMessage(long messageId, long mailboxId) throws RemoteException {
sendMessageRequest(new MessageMoveRequest(messageId, mailboxId));
}
};

View File

@ -35,6 +35,7 @@ import com.android.email.provider.EmailContent.SyncColumns;
import com.android.email.service.MailService;
import com.android.exchange.Eas;
import com.android.exchange.EasSyncService;
import com.android.exchange.MessageMoveRequest;
import com.android.exchange.utility.CalendarUtilities;
import android.content.ContentProviderOperation;
@ -775,9 +776,22 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
boolean flagChange = false;
boolean readChange = false;
int flag = 0;
long mailbox = currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN);
if (mailbox != c.getLong(Message.LIST_MAILBOX_KEY_COLUMN)) {
// The message has moved to another mailbox; add a request for this
// Note: The Sync command doesn't handle moving messages, so we need
// to handle this as a "request" (similar to meeting response and
// attachment load)
mService.addRequest(new MessageMoveRequest(id, mailbox));
// Regardless of other changes that might be made, we don't want to indicate
// that this message has been updated until the move request has been
// handled (without this, a crash between the flag upsync and the move
// would cause the move to be lost)
mUpdatedIdList.remove(id);
}
// We can only send flag changes to the server in 12.0 or later
int flag = 0;
if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
flag = currentCursor.getInt(UPDATES_FLAG_COLUMN);
if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) {

View File

@ -0,0 +1,107 @@
/* 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.adapter;
import com.android.exchange.EasSyncService;
import java.io.IOException;
import java.io.InputStream;
/**
* Parse the result of a MoveItems command.
*/
public class MoveItemsParser extends Parser {
private final EasSyncService mService;
private int mStatusCode = 0;
// These are the EAS status codes for MoveItems
private static final int STATUS_NO_SOURCE_FOLDER = 1;
private static final int STATUS_NO_DESTINATION_FOLDER = 2;
private static final int STATUS_SUCCESS = 3;
private static final int STATUS_SOURCE_DESTINATION_SAME = 4;
private static final int STATUS_INTERNAL_ERROR = 5;
private static final int STATUS_ALREADY_EXISTS = 6;
private static final int STATUS_LOCKED = 7;
// These are the status values we return to callers
public static final int STATUS_CODE_SUCCESS = 1;
public static final int STATUS_CODE_REVERT = 2;
public static final int STATUS_CODE_RETRY = 3;
public MoveItemsParser(InputStream in, EasSyncService service) throws IOException {
super(in);
mService = service;
}
public int getStatusCode() {
return mStatusCode;
}
public void parseResponse() throws IOException {
while (nextTag(Tags.MOVE_RESPONSE) != END) {
if (tag == Tags.MOVE_STATUS) {
int status = getValueInt();
// Convert the EAS status code with our external codes
switch(status) {
case STATUS_SUCCESS:
case STATUS_SOURCE_DESTINATION_SAME:
case STATUS_ALREADY_EXISTS:
// Same destination and already exists are ok with us; we'll continue as
// if the move succeeded
mStatusCode = STATUS_CODE_SUCCESS;
break;
case STATUS_LOCKED:
// This sounds like a transient error, so we can safely retry
mStatusCode = STATUS_CODE_RETRY;
break;
case STATUS_NO_SOURCE_FOLDER:
case STATUS_NO_DESTINATION_FOLDER:
case STATUS_INTERNAL_ERROR:
default:
// These are non-recoverable, so we'll revert the message to its original
// mailbox. If there's an unknown response, revert
mStatusCode = STATUS_CODE_REVERT;
break;
}
if (status != STATUS_SUCCESS) {
// There's not much to be done if this fails
mService.userLog("Error in MoveItems: " + status);
}
} else if (tag == Tags.MOVE_DSTMSGID || tag == Tags.MOVE_SRCMSGID) {
mService.userLog("Moved message id is now: " + getValue());
} else {
skipTag();
}
}
}
@Override
public boolean parse() throws IOException {
boolean res = false;
if (nextTag(START_DOCUMENT) != Tags.MOVE_MOVE_ITEMS) {
throw new IOException();
}
while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
if (tag == Tags.MOVE_RESPONSE) {
parseResponse();
} else {
skipTag();
}
}
return res;
}
}