summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Shrauner <shrauner@google.com>2014-05-10 23:27:20 -0700
committerJay Shrauner <shrauner@google.com>2014-05-16 10:22:41 -0700
commit3d52ff729b82a20802bb6d5a8952040f4d961cef (patch)
tree8357c464bf40133e8a5a4fb4d90e75cd9c876c4c
parent0347367531674a83ff36a4fbc222319d5e1693d9 (diff)
downloadandroid_packages_apps_Exchange-3d52ff729b82a20802bb6d5a8952040f4d961cef.tar.gz
android_packages_apps_Exchange-3d52ff729b82a20802bb6d5a8952040f4d961cef.tar.bz2
android_packages_apps_Exchange-3d52ff729b82a20802bb6d5a8952040f4d961cef.zip
Fix handling of encoded ints
Fix encoding of ints to handle ints with high bit set. Modify Parser to detect when it is decoding a run-on badly encoded int (ie, >5 bytes). Bug:14817987 Change-Id: I4a21b126d03e2e244b0fd4c4fb4821d165897ad6
-rw-r--r--src/com/android/exchange/EasOutboxService.java4
-rw-r--r--src/com/android/exchange/adapter/Parser.java174
-rw-r--r--src/com/android/exchange/adapter/Serializer.java162
-rw-r--r--src/com/android/exchange/adapter/Tags.java20
-rw-r--r--src/com/android/exchange/eas/EasOutboxSync.java4
-rw-r--r--tests/src/com/android/exchange/TagsTests.java40
-rw-r--r--tests/src/com/android/exchange/adapter/FolderSyncParserTests.java2
-rw-r--r--tests/src/com/android/exchange/adapter/ParserTest.java22
-rw-r--r--tests/src/com/android/exchange/adapter/SerializerTests.java11
9 files changed, 228 insertions, 211 deletions
diff --git a/src/com/android/exchange/EasOutboxService.java b/src/com/android/exchange/EasOutboxService.java
index 8c8a43f6..84d6108d 100644
--- a/src/com/android/exchange/EasOutboxService.java
+++ b/src/com/android/exchange/EasOutboxService.java
@@ -182,9 +182,9 @@ public class EasOutboxService extends EasSyncService {
s.start(Tags.COMPOSE_MIME);
// Send opaque data from the file stream
if (withData) {
- s.opaque(mFileStream, (int)mFileLength);
+ s.opaque(mFileStream, (int) mFileLength);
} else {
- s.opaqueWithoutData((int)mFileLength);
+ s.writeOpaqueHeader((int) mFileLength);
}
// And we're done
s.end().end().done();
diff --git a/src/com/android/exchange/adapter/Parser.java b/src/com/android/exchange/adapter/Parser.java
index 960e9dd1..cc8f747f 100644
--- a/src/com/android/exchange/adapter/Parser.java
+++ b/src/com/android/exchange/adapter/Parser.java
@@ -68,9 +68,6 @@ public abstract class Parser {
private static final int NOT_ENDED = Integer.MIN_VALUE;
private static final int EOF_BYTE = -1;
- // Where tags start in a page
- private static final int TAG_BASE = 5;
-
private boolean logging = false;
private boolean capture = false;
@@ -82,50 +79,55 @@ public abstract class Parser {
// The current tag depth
private int depth;
- // The current tag table (i.e. the tag table for the current page)
- private String[] tagTable;
-
- // An array of tag tables, as defined in EasTags
- static private String[][] tagTables = new String[Tags.pages.length + 1][];
-
// The stack of names of tags being processed; used when debug = true
private String[] nameArray = new String[32];
public class Tag {
- public int index;
+ private final int mPage;
+ private final int mIndex;
// Whether the tag is associated with content (a value)
- public boolean noContent;
- public String name;
+ public final boolean mNoContent;
+ private final String mName;
- public Tag(int id) {
+ public Tag(final int page, final int id) {
+ mPage = page;
// The tag is in the low 6 bits
- index = id & Tags.PAGE_MASK;
+ mIndex = id & Tags.PAGE_MASK;
// If the high bit is set, there is content (a value) to be read
- noContent = (id & Wbxml.WITH_CONTENT) == 0;
- if (index < TAG_BASE) {
- name = "unsupported-WBXML";
- } else if (tagTable == null || index - TAG_BASE >= tagTable.length) {
- name = "unknown";
+ mNoContent = (id & Wbxml.WITH_CONTENT) == 0;
+ if (Tags.isGlobalTag(mIndex)) {
+ mName = "unsupported-WBXML";
+ } else if (!Tags.isValidTag(mPage, mIndex)) {
+ mName = "unknown";
} else {
- name = tagTable[index - TAG_BASE];
+ mName = Tags.getTagName(mPage, mIndex);
}
}
+
+ public int getTagNum() {
+ return (mPage << Tags.PAGE_SHIFT) | mIndex;
+ }
+
+ @Override
+ public String toString() {
+ return mName;
+ }
}
// The stack of tags being processed
- private Deque<Tag> startTagArray = new ArrayDeque<Tag>();
+ private final Deque<Tag> startTagArray = new ArrayDeque<Tag>();
private Tag startTag;
- private Tag endTag;
-
- // The type of the last token read
+ // The type of the last token read (eg, TEXT, OPAQUE, END, etc).
private int type;
- // The current page
+ // The current page. As of EAS 14.1, this is a value 0-24.
private int page;
- // The current tag
+ // The current tag. The low order 6 bits contain the tag index and the
+ // higher order bits the page number. The format matches that used for
+ // the tag enums defined in Tags.java.
public int tag;
// Whether the current tag is associated with content (a value)
@@ -165,7 +167,7 @@ public abstract class Parser {
super("WBXML format error");
}
- EasParserException(String reason) {
+ EasParserException(final String reason) {
super(reason);
}
}
@@ -174,21 +176,7 @@ public abstract class Parser {
return false;
}
- /**
- * Initialize the tag tables; they are constant
- *
- */
- {
- String[][] pages = Tags.pages;
- for (int i = 0; i < pages.length; i++) {
- String[] page = pages[i];
- if (page.length > 0) {
- tagTables[i] = page;
- }
- }
- }
-
- public Parser(InputStream in) throws IOException {
+ public Parser(final InputStream in) throws IOException {
setInput(in, true);
logging = Eas.PARSER_LOG;
}
@@ -198,7 +186,7 @@ public abstract class Parser {
* @param parser an existing, initialized parser
* @throws IOException
*/
- public Parser(Parser parser) throws IOException {
+ public Parser(final Parser parser) throws IOException {
setInput(parser.in, false);
logging = Eas.PARSER_LOG;
}
@@ -210,7 +198,7 @@ public abstract class Parser {
* @param val the desired state for debug output
*/
@VisibleForTesting
- public void setDebug(boolean val) {
+ public void setDebug(final boolean val) {
logging = val;
}
@@ -230,9 +218,10 @@ public abstract class Parser {
/**
* Turns off data capture; writes the captured data to a specified file.
*/
- public void captureOff(Context context, String file) {
+ public void captureOff(final Context context, final String file) {
try {
- FileOutputStream out = context.openFileOutput(file, Context.MODE_WORLD_WRITEABLE);
+ final FileOutputStream out = context.openFileOutput(file,
+ Context.MODE_WORLD_WRITEABLE);
out.write(captureArray.toString().getBytes());
out.close();
} catch (FileNotFoundException e) {
@@ -251,7 +240,7 @@ public abstract class Parser {
* @throws IOException
*/
public byte[] getValueBytes() throws IOException {
- final String name = startTag.name;
+ final String name = startTag.toString();
getNext();
// This means there was no value given, just <Foo/>; we'll return empty array
@@ -281,7 +270,7 @@ public abstract class Parser {
* @throws IOException
*/
public String getValue() throws IOException {
- final String name = startTag.name;
+ final String name = startTag.toString();
getNext();
// This means there was no value given, just <Foo/>; we'll return empty string for now
@@ -312,17 +301,16 @@ public abstract class Parser {
* @throws IOException
*/
public int getValueInt() throws IOException {
- String val = getValue();
+ final String val = getValue();
if (val.length() == 0) {
return 0;
}
- final String name = startTag.name;
int num;
try {
num = Integer.parseInt(val);
} catch (NumberFormatException e) {
- throw new EasParserException("Tag " + name + ": " + e.getMessage());
+ throw new EasParserException("Tag " + startTag + ": " + e.getMessage());
}
return num;
}
@@ -338,21 +326,19 @@ public abstract class Parser {
* @return the next tag found
* @throws IOException
*/
- public int nextTag(int endingTag) throws IOException {
- // Lose the page information
- final int endTagIndex = endingTag & Tags.PAGE_MASK;
+ public int nextTag(final int endingTag) throws IOException {
while (getNext() != DONE) {
// If we're a start, set tag to include the page and return it
if (type == START) {
- tag = page | startTag.index;
+ tag = startTag.getTagNum();
return tag;
// If we're at the ending tag we're looking for, return the END signal
- } else if (type == END && startTag.index == endTagIndex) {
+ } else if (type == END && startTag.getTagNum() == endingTag) {
return END;
}
}
// We're at end of document here. If we're looking for it, return END_DOCUMENT
- if (endTagIndex == START_DOCUMENT) {
+ if (endingTag == START_DOCUMENT) {
return END_DOCUMENT;
}
// Otherwise, we've prematurely hit end of document, so exception out
@@ -368,10 +354,10 @@ public abstract class Parser {
* @throws IOException
*/
public void skipTag() throws IOException {
- int thisTag = startTag.index;
+ final int thisTag = startTag.getTagNum();
// Just loop until we hit the end of the current tag
while (getNext() != DONE) {
- if (type == END && startTag.index == thisTag) {
+ if (type == END && startTag.getTagNum() == thisTag) {
return;
}
}
@@ -388,15 +374,12 @@ public abstract class Parser {
* @param in the InputStream associated with this parser
* @throws IOException
*/
- public void setInput(InputStream in, boolean initialize) throws IOException {
+ public void setInput(final InputStream in, final boolean initialize) throws IOException {
this.in = in;
if ((in != null) && initialize) {
// If we fail on the very first byte, report an empty stream
try {
final int version = readByte(); // version
- if (version > 3) {
- log("WBXML version " + version + " is newer than supported version 3");
- }
} catch (EofException e) {
throw new EmptyStreamException();
}
@@ -407,11 +390,10 @@ public abstract class Parser {
throw new EasParserException("WBXML string table unsupported");
}
}
- tagTable = tagTables[0];
}
@VisibleForTesting
- void resetInput(InputStream in) {
+ void resetInput(final InputStream in) {
this.in = in;
try {
// Read leading zero
@@ -420,20 +402,23 @@ public abstract class Parser {
}
}
- void log(String str) {
+ void log(final String str) {
if (!logging) {
return;
}
+ final String logStr;
int cr = str.indexOf('\n');
if (cr > 0) {
- str = str.substring(0, cr);
+ logStr = str.substring(0, cr);
+ } else {
+ logStr = str;
}
final char [] charArray = new char[startTagArray.size() * 2];
Arrays.fill(charArray, ' ');
final String indent = new String(charArray);
- LogUtils.v(LOG_TAG, indent + str);
+ LogUtils.v(LOG_TAG, "%s", indent + logStr);
if (Eas.FILE_LOG) {
- FileLogger.log(LOG_TAG, str);
+ FileLogger.log(LOG_TAG, logStr);
}
}
@@ -443,22 +428,21 @@ public abstract class Parser {
}
}
- protected void pushTag(int id) {
- page = id >> Tags.PAGE_SHIFT;
- tagTable = tagTables[page];
+ protected void pushTag(final int id) {
+ page = id >>> Tags.PAGE_SHIFT;
push(id);
}
private void pop() {
// Retrieve the now-current startTag from our stack
startTag = startTagArray.removeFirst();
- log("</" + startTag.name + '>');
+ log("</" + startTag + '>');
}
- private void push(int id) {
- startTag = new Tag(id);
- noContent = startTag.noContent;
- log("<" + startTag.name + (startTag.noContent ? '/' : "") + '>');
+ private void push(final int id) {
+ startTag = new Tag(page, id);
+ noContent = startTag.mNoContent;
+ log("<" + startTag + (noContent ? '/' : "") + '>');
// Save the startTag to our stack
startTagArray.addFirst(startTag);
}
@@ -485,18 +469,14 @@ public abstract class Parser {
int id = read();
while (id == Wbxml.SWITCH_PAGE) {
// Get the new page number
- int pg = readByte();
- // Save the shifted page to add into the startTag in nextTag
- page = pg << Tags.PAGE_SHIFT;
+ page = readByte();
// Retrieve the current tag table
- if (pg >= tagTables.length) {
+ if (!Tags.isValidPage(page)) {
// Unknown code page. These seem to happen mostly because of
// invalid data from the server so throw an exception here.
- throw new EasParserException("Unknown code page " + pg);
- } else {
- tagTable = tagTables[pg];
+ throw new EasParserException("Unknown code page " + page);
}
- logVerbose((tagTable == null ? "Unknown " : "") + "Page: " + page);
+ logVerbose("Page: " + page);
id = read();
}
@@ -515,22 +495,22 @@ public abstract class Parser {
// Inline string
type = TEXT;
text = readInlineString();
- log(startTag.name + ": " + text);
+ log(startTag + ": " + text);
break;
case Wbxml.OPAQUE:
// Integer length + opaque data
type = OPAQUE;
- int length = readInt();
+ final int length = readInt();
bytes = new byte[length];
for (int i = 0; i < length; i++) {
bytes[i] = (byte)readByte();
}
- log(startTag.name + ": (opaque:" + length + ") ");
+ log(startTag + ": (opaque:" + length + ") ");
break;
default:
- if ((id & Tags.PAGE_MASK) < TAG_BASE) {
+ if (Tags.isGlobalTag(id & Tags.PAGE_MASK)) {
throw new EasParserException(String.format(
"Unhandled WBXML global token 0x%02X", id));
}
@@ -571,11 +551,21 @@ public abstract class Parser {
return i;
}
+ /**
+ * Throws EasParserException if detects integer encoded with more than 5
+ * bytes. A uint_32 needs 5 bytes to fully encode 32 bits so if the high
+ * bit is set for more than 4 bytes, something is wrong with the data
+ * stream.
+ */
private int readInt() throws IOException {
int result = 0;
int i;
+ int numBytes = 0;
do {
+ if (++numBytes > 5) {
+ throw new EasParserException("Invalid integer encoding, too many bytes");
+ }
i = readByte();
result = (result << 7) | (i & 0x7f);
} while ((i & 0x80) != 0);
@@ -590,9 +580,9 @@ public abstract class Parser {
* @throws IOException
*/
private String readInlineString() throws IOException {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256);
while (true) {
- int i = read();
+ final int i = read();
if (i == 0) {
break;
} else if (i == EOF_BYTE) {
@@ -601,7 +591,7 @@ public abstract class Parser {
outputStream.write(i);
}
outputStream.flush();
- String res = outputStream.toString("UTF-8");
+ final String res = outputStream.toString("UTF-8");
outputStream.close();
return res;
}
diff --git a/src/com/android/exchange/adapter/Serializer.java b/src/com/android/exchange/adapter/Serializer.java
index 4cd187ed..d28e4c2c 100644
--- a/src/com/android/exchange/adapter/Serializer.java
+++ b/src/com/android/exchange/adapter/Serializer.java
@@ -24,6 +24,7 @@
package com.android.exchange.adapter;
import android.content.ContentValues;
+import android.text.TextUtils;
import com.android.exchange.Eas;
import com.android.exchange.utility.FileLogger;
@@ -34,6 +35,9 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
public class Serializer {
private static final String TAG = Eas.LOG_TAG;
@@ -42,10 +46,9 @@ public class Serializer {
private final OutputStream mOutput;
private int mPendingTag = NOT_PENDING;
- private int mDepth;
- private String[] mNameStack = new String[20];
+ private final Deque<String> mNameStack = new ArrayDeque<String>();
private int mTagPage = 0;
- private boolean mLogging = LogUtils.isLoggable(TAG, LogUtils.VERBOSE);
+ private final boolean mLogging = LogUtils.isLoggable(TAG, LogUtils.VERBOSE);
public Serializer() throws IOException {
this(new ByteArrayOutputStream(), true);
@@ -67,7 +70,8 @@ public class Serializer {
* @param _logging whether or not to log our output
* @throws IOException
*/
- public Serializer(OutputStream outputStream, boolean startDocument) throws IOException {
+ public Serializer(final OutputStream outputStream, final boolean startDocument)
+ throws IOException {
super();
mOutput = outputStream;
if (startDocument) {
@@ -77,37 +81,47 @@ public class Serializer {
}
}
- void log(String str) {
- int cr = str.indexOf('\n');
+ void log(final String str) {
+ if (!mLogging) {
+ return;
+ }
+ final String logStr;
+ final int cr = str.indexOf('\n');
if (cr > 0) {
- str = str.substring(0, cr);
+ logStr = str.substring(0, cr);
+ } else {
+ logStr = str;
}
- LogUtils.v(TAG, "%s", str);
+ final char [] charArray = new char[mNameStack.size() * 2];
+ Arrays.fill(charArray, ' ');
+ final String indent = new String(charArray);
+ LogUtils.v(TAG, "%s%s", indent, logStr);
if (Eas.FILE_LOG) {
- FileLogger.log(TAG, str);
+ FileLogger.log(TAG, logStr);
}
}
public void done() throws IOException {
- if (mDepth != 0) {
+ if (mNameStack.size() != 0 || mPendingTag != NOT_PENDING) {
throw new IOException("Done received with unclosed tags");
}
mOutput.flush();
}
- public void startDocument() throws IOException{
+ public void startDocument() throws IOException {
mOutput.write(0x03); // version 1.3
mOutput.write(0x01); // unknown or missing public identifier
mOutput.write(106); // UTF-8
mOutput.write(0); // 0 length string array
}
- public void checkPendingTag(boolean degenerated) throws IOException {
- if (mPendingTag == NOT_PENDING)
+ private void checkPendingTag(final boolean degenerated) throws IOException {
+ if (mPendingTag == NOT_PENDING) {
return;
+ }
- int page = mPendingTag >> Tags.PAGE_SHIFT;
- int tag = mPendingTag & Tags.PAGE_MASK;
+ final int page = mPendingTag >> Tags.PAGE_SHIFT;
+ final int tag = mPendingTag & Tags.PAGE_MASK;
if (page != mTagPage) {
mTagPage = page;
mOutput.write(Wbxml.SWITCH_PAGE);
@@ -115,18 +129,24 @@ public class Serializer {
}
mOutput.write(degenerated ? tag : tag | Wbxml.WITH_CONTENT);
- if (mLogging) {
- String name = Tags.pages[page][tag - 5];
- mNameStack[mDepth] = name;
- log("<" + name + '>');
+ String name = "unknown";
+ if (!Tags.isValidPage(page)) {
+ log("Unrecognized page " + page);
+ } else if (!Tags.isValidTag(page, tag)) {
+ log("Unknown tag " + tag + " on page " + page);
+ } else {
+ name = Tags.getTagName(page, tag);
+ }
+ log("<" + name + (degenerated ? "/>" : ">"));
+ if (!degenerated) {
+ mNameStack.addFirst(name);
}
mPendingTag = NOT_PENDING;
}
- public Serializer start(int tag) throws IOException {
+ public Serializer start(final int tag) throws IOException {
checkPendingTag(false);
mPendingTag = tag;
- mDepth++;
return this;
}
@@ -135,97 +155,115 @@ public class Serializer {
checkPendingTag(true);
} else {
mOutput.write(Wbxml.END);
- if (mLogging) {
- log("</" + mNameStack[mDepth] + '>');
- }
+ final String tagName = mNameStack.removeFirst();
+ log("</" + tagName + '>');
}
- mDepth--;
return this;
}
- public Serializer tag(int t) throws IOException {
- start(t);
+ public Serializer tag(final int tag) throws IOException {
+ start(tag);
end();
return this;
}
- public Serializer data(int tag, String value) throws IOException {
- if (value == null) {
- LogUtils.e(TAG, "Writing null data for tag: " + tag);
- }
+ /**
+ * Writes <tag>value</tag>. Throws IOException for null strings.
+ */
+ public Serializer data(final int tag, final String value) throws IOException {
start(tag);
text(value);
end();
return this;
}
- public Serializer text(String text) throws IOException {
+ /**
+ * Writes out inline string. Throws IOException for null strings.
+ */
+ public Serializer text(final String text) throws IOException {
if (text == null) {
- LogUtils.e(TAG, "Writing null text for pending tag: " + mPendingTag);
+ throw new IOException("Null text write for pending tag: " + mPendingTag);
}
checkPendingTag(false);
- mOutput.write(Wbxml.STR_I);
- writeLiteralString(mOutput, text);
- if (mLogging) {
- log(text);
- }
+ writeInlineString(mOutput, text);
+ log(text);
return this;
}
- public Serializer opaque(InputStream is, int length) throws IOException {
- checkPendingTag(false);
- mOutput.write(Wbxml.OPAQUE);
- writeInteger(mOutput, length);
- if (mLogging) {
- log("Opaque, length: " + length);
- }
+ /**
+ * Writes out opaque data blocks. Throws IOException for negative buffer
+ * sizes or if is unable to read sufficient bytes from input stream.
+ */
+ public Serializer opaque(final InputStream is, final int length) throws IOException {
+ writeOpaqueHeader(length);
+ log("opaque: " + length);
// Now write out the opaque data in batches
- byte[] buffer = new byte[BUFFER_SIZE];
- while (length > 0) {
- int bytesRead = is.read(buffer, 0, Math.min(BUFFER_SIZE, length));
+ final byte[] buffer = new byte[BUFFER_SIZE];
+ int totalBytesRead = 0;
+ while (totalBytesRead < length) {
+ final int bytesRead = is.read(buffer, 0, Math.min(BUFFER_SIZE, length));
if (bytesRead == -1) {
- break;
+ throw new IOException("Invalid opaque data block; read "
+ + totalBytesRead + " bytes but expected " + length);
}
mOutput.write(buffer, 0, bytesRead);
- length -= bytesRead;
+ totalBytesRead += bytesRead;
}
return this;
}
- public Serializer opaqueWithoutData(int length) throws IOException {
+ /**
+ * Writes out opaque data header, without the actual opaque data bytes.
+ * Used internally by opaque(), and externally to calculate content length
+ * without having to allocate the memory for the data copy.
+ * Throws IOException if length is negative; is a no-op for length 0.
+ */
+ public Serializer writeOpaqueHeader(final int length) throws IOException {
+ if (length < 0) {
+ throw new IOException("Invalid negative opaque data length " + length);
+ }
+ if (length == 0) {
+ return this;
+ }
checkPendingTag(false);
mOutput.write(Wbxml.OPAQUE);
writeInteger(mOutput, length);
return this;
}
- void writeInteger(OutputStream out, int i) throws IOException {
- byte[] buf = new byte[5];
+ @VisibleForTesting
+ static void writeInteger(final OutputStream out, int i) throws IOException {
+ final byte[] buf = new byte[5];
int idx = 0;
do {
buf[idx++] = (byte) (i & 0x7f);
- i = i >> 7;
+ // Use >>> to shift in 0s so loop terminates
+ i = i >>> 7;
} while (i != 0);
while (idx > 1) {
out.write(buf[--idx] | 0x80);
}
out.write(buf[0]);
- if (mLogging) {
- log(Integer.toString(i));
- }
}
- void writeLiteralString(OutputStream out, String s) throws IOException {
- byte[] data = s.getBytes("UTF-8");
+ private static void writeInlineString(final OutputStream out, final String s)
+ throws IOException {
+ out.write(Wbxml.STR_I);
+ final byte[] data = s.getBytes("UTF-8");
out.write(data);
out.write(0);
}
- public void writeStringValue (ContentValues cv, String key, int tag) throws IOException {
- String value = cv.getAsString(key);
- if (value != null && value.length() > 0) {
+ /**
+ * Looks up key in cv; if absent or empty writes out <tag/> otherwise
+ * writes out <tag>value</tag>.
+ */
+ public void writeStringValue (final ContentValues cv, final String key,
+ final int tag) throws IOException {
+ final String value = cv.getAsString(key);
+ if (!TextUtils.isEmpty(value)) {
data(tag, value);
} else {
tag(tag);
diff --git a/src/com/android/exchange/adapter/Tags.java b/src/com/android/exchange/adapter/Tags.java
index dffa079e..f93f78f0 100644
--- a/src/com/android/exchange/adapter/Tags.java
+++ b/src/com/android/exchange/adapter/Tags.java
@@ -59,6 +59,7 @@ public class Tags {
// Shift applied to page numbers to generate tag
public static final int PAGE_SHIFT = 6;
public static final int PAGE_MASK = 0x3F; // 6 bits
+ public static final int TAG_BASE = 5;
// AirSync code page 0
public static final int SYNC_PAGE = 0 << PAGE_SHIFT;
@@ -729,7 +730,24 @@ public class Tags {
public static final int RIGHTS_CONTENT_OWNER = RIGHTS_PAGE + 0x17;
public static final int RIGHTS_REMOVE_RM_DISTRIBUTION = RIGHTS_PAGE + 0x18;
- static public String[][] pages = {
+ public static boolean isValidPage(final int page) {
+ return page >= 0 && page < mPages.length;
+ }
+
+ public static boolean isValidTag(final int page, final int tag) {
+ final int tagIndex = tag - TAG_BASE;
+ return isValidPage(page) && tagIndex >= 0 && tagIndex < mPages[page].length;
+ }
+
+ public static boolean isGlobalTag(final int tag) {
+ return tag >= 0 && tag < TAG_BASE;
+ }
+
+ public static String getTagName(final int page, final int tag) {
+ return mPages[page][tag - TAG_BASE];
+ }
+
+ static final String[][] mPages = {
{ // 0x00 AirSync
"Sync", "Responses", "Add", "Change", "Delete", "Fetch", "SyncKey", "ClientId",
"ServerId", "Status", "Collection", "Class", "Version", "CollectionId", "GetChanges",
diff --git a/src/com/android/exchange/eas/EasOutboxSync.java b/src/com/android/exchange/eas/EasOutboxSync.java
index d93dae1c..ba259712 100644
--- a/src/com/android/exchange/eas/EasOutboxSync.java
+++ b/src/com/android/exchange/eas/EasOutboxSync.java
@@ -514,9 +514,9 @@ public class EasOutboxSync extends EasOperation {
s.start(Tags.COMPOSE_MIME);
// Send opaque data from the file stream
if (withData) {
- s.opaque(mFileStream, (int)mFileLength);
+ s.opaque(mFileStream, (int) mFileLength);
} else {
- s.opaqueWithoutData((int)mFileLength);
+ s.writeOpaqueHeader((int) mFileLength);
}
// And we're done
s.end().end().done();
diff --git a/tests/src/com/android/exchange/TagsTests.java b/tests/src/com/android/exchange/TagsTests.java
deleted file mode 100644
index 55b27c0d..00000000
--- a/tests/src/com/android/exchange/TagsTests.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2009 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.exchange.adapter.Tags;
-
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import java.util.HashMap;
-@SmallTest
-public class TagsTests extends AndroidTestCase {
-
- // Make sure there are no duplicates in the tags table
- // This test is no longer required - tags can be duplicated
- public void disable_testNoDuplicates() {
- String[][] allTags = Tags.pages;
- HashMap<String, Boolean> map = new HashMap<String, Boolean>();
- for (String[] page: allTags) {
- for (String tag: page) {
- assertTrue(tag, !map.containsKey(tag));
- map.put(tag, true);
- }
- }
- }
-}
diff --git a/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java b/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
index 53fdc526..3eb59e7d 100644
--- a/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
+++ b/tests/src/com/android/exchange/adapter/FolderSyncParserTests.java
@@ -170,7 +170,7 @@ public class FolderSyncParserTests extends SyncAdapterTestCase<EmailSyncAdapter>
private void initTagMap() {
mTagMap = new HashMap<String, Integer>();
int pageNum = 0;
- for (String[] page: Tags.pages) {
+ for (String[] page: Tags.mPages) {
int tagNum = 5;
for (String tag: page) {
if (mTagMap.containsKey(tag)) {
diff --git a/tests/src/com/android/exchange/adapter/ParserTest.java b/tests/src/com/android/exchange/adapter/ParserTest.java
index b6436233..8b36433d 100644
--- a/tests/src/com/android/exchange/adapter/ParserTest.java
+++ b/tests/src/com/android/exchange/adapter/ParserTest.java
@@ -109,17 +109,6 @@ public class ParserTest extends AndroidTestCase {
}
@SmallTest
- public void testParser() throws Exception {
- // Test parser with sample valid data
- final String wbxmlDataStr =
- "03 01 6A 00 45 5C 4F 50 03 43 6F 6E 74 61 63 74 73 00 01 4B 03 32 00 01 52 03 32 00 01 4E 03 " +
- "31 00 01 56 47 4D 03 32 3A 31 00 01 5D 00 11 4A 46 03 31 00 01 4C 03 30 00 01 4D 03 31 00 01 " +
- "01 00 01 5E 03 46 75 6E 6B 2C 20 44 6F 6E 00 01 5F 03 44 6F 6E 00 01 69 03 46 75 6E 6B 00 01 " +
- "00 11 56 03 31 00 01 01 01 01 01 01 01";
- testParserHelper(wbxmlDataStr);
- }
-
- @SmallTest
public void testUnsupportedWbxmlTag() throws Exception {
// Test parser with unsupported Wbxml tag (EXT_2 = 0xC2)
final String unsupportedWbxmlTag = "03 01 6A 00 45 5F C2 05 11 22 33 44 00 01 01";
@@ -229,6 +218,17 @@ public class ParserTest extends AndroidTestCase {
}
@SmallTest
+ public void testRunOnInteger() throws Exception {
+ final String runOnIntegerEncoding = "03 01 6A 00 45 4D C3 81 82 83 84 85 06 11 22 33 01 01";
+ try {
+ testParserHelper(runOnIntegerEncoding);
+ fail("Expected EasParserException for improperly encoded integer");
+ } catch (Parser.EasParserException e) {
+ // expected
+ }
+ }
+
+ @SmallTest
public void testAttributeTag() throws Exception {
// Test parser with known tag with attributes
final String tagWithAttributes = "03 01 6A 00 45 DF 06 01 03 31 00 01 01";
diff --git a/tests/src/com/android/exchange/adapter/SerializerTests.java b/tests/src/com/android/exchange/adapter/SerializerTests.java
index 921c2261..75132dc9 100644
--- a/tests/src/com/android/exchange/adapter/SerializerTests.java
+++ b/tests/src/com/android/exchange/adapter/SerializerTests.java
@@ -22,7 +22,10 @@ import android.test.MoreAsserts;
import android.test.suitebuilder.annotation.SmallTest;
import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
/** You can run this entire test case with:
* runtest -c com.android.exchange.adapter.SerializerTests exchange
@@ -93,4 +96,12 @@ public class SerializerTests extends AndroidTestCase {
// Make sure we get what's expected
MoreAsserts.assertEquals("Serializer mismatch", bytes, expectedBytes);
}
+
+ @SmallTest
+ public void testWriteInteger() throws IOException {
+ OutputStream output = new ByteArrayOutputStream();
+ Serializer.writeInteger(output, 384);
+ Serializer.writeInteger(output, 0);
+ Serializer.writeInteger(output, -1);
+ }
}