Merge "Fix bug #4796027 left/right padding regression after recent View change"
diff --git a/api/current.txt b/api/current.txt
index fc244d8..fa3130a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16782,6 +16782,9 @@
method public void addF32(android.renderscript.Float3);
method public void addF32(android.renderscript.Float4);
method public void addF64(double);
+ method public void addF64(android.renderscript.Double2);
+ method public void addF64(android.renderscript.Double3);
+ method public void addF64(android.renderscript.Double4);
method public void addI16(short);
method public void addI16(android.renderscript.Short2);
method public void addI16(android.renderscript.Short3);
@@ -16791,6 +16794,9 @@
method public void addI32(android.renderscript.Int3);
method public void addI32(android.renderscript.Int4);
method public void addI64(long);
+ method public void addI64(android.renderscript.Long2);
+ method public void addI64(android.renderscript.Long3);
+ method public void addI64(android.renderscript.Long4);
method public void addI8(byte);
method public void addI8(android.renderscript.Byte2);
method public void addI8(android.renderscript.Byte3);
@@ -16808,6 +16814,9 @@
method public void addU32(android.renderscript.Long3);
method public void addU32(android.renderscript.Long4);
method public void addU64(long);
+ method public void addU64(android.renderscript.Long2);
+ method public void addU64(android.renderscript.Long3);
+ method public void addU64(android.renderscript.Long4);
method public void addU8(short);
method public void addU8(android.renderscript.Short2);
method public void addU8(android.renderscript.Short3);
diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java
index 56c4f7a..cde7dac 100644
--- a/core/java/android/pim/EventRecurrence.java
+++ b/core/java/android/pim/EventRecurrence.java
@@ -18,36 +18,18 @@
import android.text.TextUtils;
import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
import java.util.Calendar;
+import java.util.HashMap;
-public class EventRecurrence
-{
- /**
- * Thrown when a recurrence string provided can not be parsed according
- * to RFC2445.
- */
- public static class InvalidFormatException extends RuntimeException
- {
- InvalidFormatException(String s) {
- super(s);
- }
- }
+/**
+ * Event recurrence utility functions.
+ */
+public class EventRecurrence {
+ private static String TAG = "EventRecur";
- public EventRecurrence()
- {
- wkst = MO;
- }
-
- /**
- * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10.
- */
- public native void parse(String recur);
-
- public void setStartDate(Time date) {
- startDate = date;
- }
-
public static final int SECONDLY = 1;
public static final int MINUTELY = 2;
public static final int HOURLY = 3;
@@ -64,13 +46,15 @@
public static final int FR = 0x00200000;
public static final int SA = 0x00400000;
- public Time startDate;
- public int freq;
+ public Time startDate; // set by setStartDate(), not parse()
+
+ public int freq; // SECONDLY, MINUTELY, etc.
public String until;
public int count;
public int interval;
- public int wkst;
+ public int wkst; // SU, MO, TU, etc.
+ /* lists with zero entries may be null references */
public int[] bysecond;
public int bysecondCount;
public int[] byminute;
@@ -79,7 +63,7 @@
public int byhourCount;
public int[] byday;
public int[] bydayNum;
- public int bydayCount;
+ public int bydayCount;
public int[] bymonthday;
public int bymonthdayCount;
public int[] byyearday;
@@ -91,6 +75,134 @@
public int[] bysetpos;
public int bysetposCount;
+ /** maps a part string to a parser object */
+ private static HashMap<String,PartParser> sParsePartMap;
+ static {
+ sParsePartMap = new HashMap<String,PartParser>();
+ sParsePartMap.put("FREQ", new ParseFreq());
+ sParsePartMap.put("UNTIL", new ParseUntil());
+ sParsePartMap.put("COUNT", new ParseCount());
+ sParsePartMap.put("INTERVAL", new ParseInterval());
+ sParsePartMap.put("BYSECOND", new ParseBySecond());
+ sParsePartMap.put("BYMINUTE", new ParseByMinute());
+ sParsePartMap.put("BYHOUR", new ParseByHour());
+ sParsePartMap.put("BYDAY", new ParseByDay());
+ sParsePartMap.put("BYMONTHDAY", new ParseByMonthDay());
+ sParsePartMap.put("BYYEARDAY", new ParseByYearDay());
+ sParsePartMap.put("BYWEEKNO", new ParseByWeekNo());
+ sParsePartMap.put("BYMONTH", new ParseByMonth());
+ sParsePartMap.put("BYSETPOS", new ParseBySetPos());
+ sParsePartMap.put("WKST", new ParseWkst());
+ }
+
+ /* values for bit vector that keeps track of what we have already seen */
+ private static final int PARSED_FREQ = 1 << 0;
+ private static final int PARSED_UNTIL = 1 << 1;
+ private static final int PARSED_COUNT = 1 << 2;
+ private static final int PARSED_INTERVAL = 1 << 3;
+ private static final int PARSED_BYSECOND = 1 << 4;
+ private static final int PARSED_BYMINUTE = 1 << 5;
+ private static final int PARSED_BYHOUR = 1 << 6;
+ private static final int PARSED_BYDAY = 1 << 7;
+ private static final int PARSED_BYMONTHDAY = 1 << 8;
+ private static final int PARSED_BYYEARDAY = 1 << 9;
+ private static final int PARSED_BYWEEKNO = 1 << 10;
+ private static final int PARSED_BYMONTH = 1 << 11;
+ private static final int PARSED_BYSETPOS = 1 << 12;
+ private static final int PARSED_WKST = 1 << 13;
+
+ /** maps a FREQ value to an integer constant */
+ private static final HashMap<String,Integer> sParseFreqMap = new HashMap<String,Integer>();
+ static {
+ sParseFreqMap.put("SECONDLY", SECONDLY);
+ sParseFreqMap.put("MINUTELY", MINUTELY);
+ sParseFreqMap.put("HOURLY", HOURLY);
+ sParseFreqMap.put("DAILY", DAILY);
+ sParseFreqMap.put("WEEKLY", WEEKLY);
+ sParseFreqMap.put("MONTHLY", MONTHLY);
+ sParseFreqMap.put("YEARLY", YEARLY);
+ }
+
+ /** maps a two-character weekday string to an integer constant */
+ private static final HashMap<String,Integer> sParseWeekdayMap = new HashMap<String,Integer>();
+ static {
+ sParseWeekdayMap.put("SU", SU);
+ sParseWeekdayMap.put("MO", MO);
+ sParseWeekdayMap.put("TU", TU);
+ sParseWeekdayMap.put("WE", WE);
+ sParseWeekdayMap.put("TH", TH);
+ sParseWeekdayMap.put("FR", FR);
+ sParseWeekdayMap.put("SA", SA);
+ }
+
+ /** If set, allow lower-case recurrence rule strings. Minor performance impact. */
+ private static final boolean ALLOW_LOWER_CASE = false;
+
+ /** If set, validate the value of UNTIL parts. Minor performance impact. */
+ private static final boolean VALIDATE_UNTIL = false;
+
+ /** If set, require that only one of {UNTIL,COUNT} is present. Breaks compat w/ old parser. */
+ private static final boolean ONLY_ONE_UNTIL_COUNT = false;
+
+
+ /**
+ * Thrown when a recurrence string provided can not be parsed according
+ * to RFC2445.
+ */
+ public static class InvalidFormatException extends RuntimeException {
+ InvalidFormatException(String s) {
+ super(s);
+ }
+ }
+
+ /**
+ * Parse an iCalendar/RFC2445 recur type according to Section 4.3.10. The string is
+ * parsed twice, by the old and new parsers, and the results are compared.
+ * <p>
+ * TODO: this will go away, and what is now parse2() will simply become parse().
+ */
+ public void parse(String recur) {
+ InvalidFormatException newExcep = null;
+ try {
+ parse2(recur);
+ } catch (InvalidFormatException ife) {
+ newExcep = ife;
+ }
+
+ boolean oldThrew = false;
+ try {
+ EventRecurrence check = new EventRecurrence();
+ check.parseNative(recur);
+ if (newExcep == null) {
+ // Neither threw, check to see if results match.
+ if (!equals(check)) {
+ throw new InvalidFormatException("Recurrence rule parse does not match [" +
+ recur + "]");
+ }
+ }
+ } catch (InvalidFormatException ife) {
+ oldThrew = true;
+ if (newExcep == null) {
+ // Old threw, but new didn't. Log a warning, but don't throw.
+ Log.d(TAG, "NOTE: old parser rejected [" + recur + "]: " + ife.getMessage());
+ }
+ }
+
+ if (newExcep != null) {
+ if (!oldThrew) {
+ // New threw, but old didn't. Log a warning and throw the exception.
+ Log.d(TAG, "NOTE: new parser rejected [" + recur + "]: " + newExcep.getMessage());
+ }
+ throw newExcep;
+ }
+ }
+
+ native void parseNative(String recur);
+
+ public void setStartDate(Time date) {
+ startDate = date;
+ }
+
/**
* Converts one of the Calendar.SUNDAY constants to the SU, MO, etc.
* constants. btw, I think we should switch to those here too, to
@@ -118,7 +230,7 @@
throw new RuntimeException("bad day of week: " + day);
}
}
-
+
public static int timeDay2Day(int day)
{
switch (day)
@@ -191,16 +303,16 @@
throw new RuntimeException("bad day of week: " + day);
}
}
-
+
/**
* Converts one of the internal day constants (SU, MO, etc.) to the
* two-letter string representing that constant.
- *
- * @throws IllegalArgumentException Thrown if the day argument is not one of
- * the defined day constants.
- *
+ *
* @param day one the internal constants SU, MO, etc.
* @return the two-letter string for the day ("SU", "MO", etc.)
+ *
+ * @throws IllegalArgumentException Thrown if the day argument is not one of
+ * the defined day constants.
*/
private static String day2String(int day) {
switch (day) {
@@ -283,7 +395,7 @@
s.append(";UNTIL=");
s.append(until);
}
-
+
if (this.count != 0) {
s.append(";COUNT=");
s.append(this.count);
@@ -323,36 +435,484 @@
return s.toString();
}
-
+
public boolean repeatsOnEveryWeekDay() {
if (this.freq != WEEKLY) {
- return false;
+ return false;
}
-
+
int count = this.bydayCount;
if (count != 5) {
return false;
}
-
+
for (int i = 0 ; i < count ; i++) {
int day = byday[i];
if (day == SU || day == SA) {
return false;
}
}
-
+
return true;
}
-
+
public boolean repeatsMonthlyOnDayCount() {
if (this.freq != MONTHLY) {
return false;
}
-
+
if (bydayCount != 1 || bymonthdayCount != 0) {
return false;
}
-
+
return true;
}
+
+ /**
+ * Determines whether two integer arrays contain identical elements.
+ * <p>
+ * The native implementation over-allocated the arrays (and may have stuff left over from
+ * a previous run), so we can't just check the arrays -- the separately-maintained count
+ * field also matters. We assume that a null array will have a count of zero, and that the
+ * array can hold as many elements as the associated count indicates.
+ * <p>
+ * TODO: replace this with Arrays.equals() when the old parser goes away.
+ */
+ private static boolean arraysEqual(int[] array1, int count1, int[] array2, int count2) {
+ if (count1 != count2) {
+ return false;
+ }
+
+ for (int i = 0; i < count1; i++) {
+ if (array1[i] != array2[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof EventRecurrence)) {
+ return false;
+ }
+
+ EventRecurrence er = (EventRecurrence) obj;
+ return (startDate == null ?
+ er.startDate == null : Time.compare(startDate, er.startDate) == 0) &&
+ freq == er.freq &&
+ (until == null ? er.until == null : until.equals(er.until)) &&
+ count == er.count &&
+ interval == er.interval &&
+ wkst == er.wkst &&
+ arraysEqual(bysecond, bysecondCount, er.bysecond, er.bysecondCount) &&
+ arraysEqual(byminute, byminuteCount, er.byminute, er.byminuteCount) &&
+ arraysEqual(byhour, byhourCount, er.byhour, er.byhourCount) &&
+ arraysEqual(byday, bydayCount, er.byday, er.bydayCount) &&
+ arraysEqual(bydayNum, bydayCount, er.bydayNum, er.bydayCount) &&
+ arraysEqual(bymonthday, bymonthdayCount, er.bymonthday, er.bymonthdayCount) &&
+ arraysEqual(byyearday, byyeardayCount, er.byyearday, er.byyeardayCount) &&
+ arraysEqual(byweekno, byweeknoCount, er.byweekno, er.byweeknoCount) &&
+ arraysEqual(bymonth, bymonthCount, er.bymonth, er.bymonthCount) &&
+ arraysEqual(bysetpos, bysetposCount, er.bysetpos, er.bysetposCount);
+ }
+
+ @Override public int hashCode() {
+ // We overrode equals, so we must override hashCode(). Nobody seems to need this though.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Resets parser-modified fields to their initial state. Does not alter startDate.
+ * <p>
+ * The original parser always set all of the "count" fields, "wkst", and "until",
+ * essentially allowing the same object to be used multiple times by calling parse().
+ * It's unclear whether this behavior was intentional. For now, be paranoid and
+ * preserve the existing behavior by resetting the fields.
+ * <p>
+ * We don't need to touch the integer arrays; they will either be ignored or
+ * overwritten. The "startDate" field is not set by the parser, so we ignore it here.
+ */
+ private void resetFields() {
+ until = null;
+ freq = count = interval = bysecondCount = byminuteCount = byhourCount =
+ bydayCount = bymonthdayCount = byyeardayCount = byweeknoCount = bymonthCount =
+ bysetposCount = 0;
+ }
+
+ /**
+ * Parses an rfc2445 recurrence rule string into its component pieces. Attempting to parse
+ * malformed input will result in an EventRecurrence.InvalidFormatException.
+ *
+ * @param recur The recurrence rule to parse (in un-folded form).
+ */
+ void parse2(String recur) {
+ /*
+ * From RFC 2445 section 4.3.10:
+ *
+ * recur = "FREQ"=freq *(
+ * ; either UNTIL or COUNT may appear in a 'recur',
+ * ; but UNTIL and COUNT MUST NOT occur in the same 'recur'
+ *
+ * ( ";" "UNTIL" "=" enddate ) /
+ * ( ";" "COUNT" "=" 1*DIGIT ) /
+ *
+ * ; the rest of these keywords are optional,
+ * ; but MUST NOT occur more than once
+ *
+ * ( ";" "INTERVAL" "=" 1*DIGIT ) /
+ * ( ";" "BYSECOND" "=" byseclist ) /
+ * ( ";" "BYMINUTE" "=" byminlist ) /
+ * ( ";" "BYHOUR" "=" byhrlist ) /
+ * ( ";" "BYDAY" "=" bywdaylist ) /
+ * ( ";" "BYMONTHDAY" "=" bymodaylist ) /
+ * ( ";" "BYYEARDAY" "=" byyrdaylist ) /
+ * ( ";" "BYWEEKNO" "=" bywknolist ) /
+ * ( ";" "BYMONTH" "=" bymolist ) /
+ * ( ";" "BYSETPOS" "=" bysplist ) /
+ * ( ";" "WKST" "=" weekday ) /
+ * ( ";" x-name "=" text )
+ * )
+ *
+ * Examples:
+ * FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU
+ * FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8
+ *
+ * Strategy:
+ * (1) Split the string at ';' boundaries to get an array of rule "parts".
+ * (2) For each part, find substrings for left/right sides of '=' (name/value).
+ * (3) Call a <name>-specific parsing function to parse the <value> into an
+ * output field.
+ *
+ * By keeping track of which names we've seen in a bit vector, we can verify the
+ * constraints indicated above (FREQ appears first, none of them appear more than once --
+ * though x-[name] would require special treatment), and we have either UNTIL or COUNT
+ * but not both.
+ *
+ * In general, RFC 2445 property names (e.g. "FREQ") and enumerations ("TU") must
+ * be handled in a case-insensitive fashion, but case may be significant for other
+ * properties. We don't have any case-sensitive values in RRULE, except possibly
+ * for the custom "X-" properties, but we ignore those anyway. Thus, we can trivially
+ * convert the entire string to upper case and then use simple comparisons.
+ *
+ * Differences from previous version:
+ * - allows lower-case property and enumeration values [optional]
+ * - enforces that FREQ appears first
+ * - enforces that only one of UNTIL and COUNT may be specified
+ * - allows (but ignores) X-* parts
+ * - improved validation on various values (e.g. UNTIL timestamps)
+ * - error messages are more specific
+ */
+
+ /* TODO: replace with "if (freq != 0) throw" if nothing requires this */
+ resetFields();
+
+ int parseFlags = 0;
+ String[] parts;
+ if (ALLOW_LOWER_CASE) {
+ parts = recur.toUpperCase().split(";");
+ } else {
+ parts = recur.split(";");
+ }
+ for (String part : parts) {
+ int equalIndex = part.indexOf('=');
+ if (equalIndex <= 0) {
+ /* no '=' or no LHS */
+ throw new InvalidFormatException("Missing LHS in " + part);
+ }
+
+ String lhs = part.substring(0, equalIndex);
+ String rhs = part.substring(equalIndex + 1);
+ if (rhs.length() == 0) {
+ throw new InvalidFormatException("Missing RHS in " + part);
+ }
+
+ /*
+ * In lieu of a "switch" statement that allows string arguments, we use a
+ * map from strings to parsing functions.
+ */
+ PartParser parser = sParsePartMap.get(lhs);
+ if (parser == null) {
+ if (lhs.startsWith("X-")) {
+ //Log.d(TAG, "Ignoring custom part " + lhs);
+ continue;
+ }
+ throw new InvalidFormatException("Couldn't find parser for " + lhs);
+ } else {
+ int flag = parser.parsePart(rhs, this);
+ if ((parseFlags & flag) != 0) {
+ throw new InvalidFormatException("Part " + lhs + " was specified twice");
+ }
+ if (parseFlags == 0 && flag != PARSED_FREQ) {
+ throw new InvalidFormatException("FREQ must be specified first");
+ }
+ parseFlags |= flag;
+ }
+ }
+
+ // If not specified, week starts on Monday.
+ if ((parseFlags & PARSED_WKST) == 0) {
+ wkst = MO;
+ }
+
+ // FREQ is mandatory.
+ if ((parseFlags & PARSED_FREQ) == 0) {
+ throw new InvalidFormatException("Must specify a FREQ value");
+ }
+
+ // Can't have both UNTIL and COUNT.
+ if ((parseFlags & (PARSED_UNTIL | PARSED_COUNT)) == (PARSED_UNTIL | PARSED_COUNT)) {
+ if (ONLY_ONE_UNTIL_COUNT) {
+ throw new InvalidFormatException("Must not specify both UNTIL and COUNT: " + recur);
+ } else {
+ Log.w(TAG, "Warning: rrule has both UNTIL and COUNT: " + recur);
+ }
+ }
+ }
+
+ /**
+ * Base class for the RRULE part parsers.
+ */
+ abstract static class PartParser {
+ /**
+ * Parses a single part.
+ *
+ * @param value The right-hand-side of the part.
+ * @param er The EventRecurrence into which the result is stored.
+ * @return A bit value indicating which part was parsed.
+ */
+ public abstract int parsePart(String value, EventRecurrence er);
+
+ /**
+ * Parses an integer, with range-checking.
+ *
+ * @param str The string to parse.
+ * @param minVal Minimum allowed value.
+ * @param maxVal Maximum allowed value.
+ * @param allowZero Is 0 allowed?
+ * @return The parsed value.
+ */
+ public static int parseIntRange(String str, int minVal, int maxVal, boolean allowZero) {
+ try {
+ if (str.charAt(0) == '+') {
+ // Integer.parseInt does not allow a leading '+', so skip it manually.
+ str = str.substring(1);
+ }
+ int val = Integer.parseInt(str);
+ if (val < minVal || val > maxVal || (val == 0 && !allowZero)) {
+ throw new InvalidFormatException("Integer value out of range: " + str);
+ }
+ return val;
+ } catch (NumberFormatException nfe) {
+ throw new InvalidFormatException("Invalid integer value: " + str);
+ }
+ }
+
+ /**
+ * Parses a comma-separated list of integers, with range-checking.
+ *
+ * @param listStr The string to parse.
+ * @param minVal Minimum allowed value.
+ * @param maxVal Maximum allowed value.
+ * @param allowZero Is 0 allowed?
+ * @return A new array with values, sized to hold the exact number of elements.
+ */
+ public static int[] parseNumberList(String listStr, int minVal, int maxVal,
+ boolean allowZero) {
+ int[] values;
+
+ if (listStr.indexOf(",") < 0) {
+ // Common case: only one entry, skip split() overhead.
+ values = new int[1];
+ values[0] = parseIntRange(listStr, minVal, maxVal, allowZero);
+ } else {
+ String[] valueStrs = listStr.split(",");
+ int len = valueStrs.length;
+ values = new int[len];
+ for (int i = 0; i < len; i++) {
+ values[i] = parseIntRange(valueStrs[i], minVal, maxVal, allowZero);
+ }
+ }
+ return values;
+ }
+ }
+
+ /** parses FREQ={SECONDLY,MINUTELY,...} */
+ private static class ParseFreq extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ Integer freq = sParseFreqMap.get(value);
+ if (freq == null) {
+ throw new InvalidFormatException("Invalid FREQ value: " + value);
+ }
+ er.freq = freq;
+ return PARSED_FREQ;
+ }
+ }
+ /** parses UNTIL=enddate, e.g. "19970829T021400" */
+ private static class ParseUntil extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ if (VALIDATE_UNTIL) {
+ try {
+ // Parse the time to validate it. The result isn't retained.
+ Time until = new Time();
+ until.parse(value);
+ } catch (TimeFormatException tfe) {
+ throw new InvalidFormatException("Invalid UNTIL value: " + value);
+ }
+ }
+ er.until = value;
+ return PARSED_UNTIL;
+ }
+ }
+ /** parses COUNT=[non-negative-integer] */
+ private static class ParseCount extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ er.count = parseIntRange(value, 0, Integer.MAX_VALUE, true);
+ return PARSED_COUNT;
+ }
+ }
+ /** parses INTERVAL=[non-negative-integer] */
+ private static class ParseInterval extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ er.interval = parseIntRange(value, 1, Integer.MAX_VALUE, false);
+ return PARSED_INTERVAL;
+ }
+ }
+ /** parses BYSECOND=byseclist */
+ private static class ParseBySecond extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bysecond = parseNumberList(value, 0, 59, true);
+ er.bysecond = bysecond;
+ er.bysecondCount = bysecond.length;
+ return PARSED_BYSECOND;
+ }
+ }
+ /** parses BYMINUTE=byminlist */
+ private static class ParseByMinute extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byminute = parseNumberList(value, 0, 59, true);
+ er.byminute = byminute;
+ er.byminuteCount = byminute.length;
+ return PARSED_BYMINUTE;
+ }
+ }
+ /** parses BYHOUR=byhrlist */
+ private static class ParseByHour extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byhour = parseNumberList(value, 0, 23, true);
+ er.byhour = byhour;
+ er.byhourCount = byhour.length;
+ return PARSED_BYHOUR;
+ }
+ }
+ /** parses BYDAY=bywdaylist, e.g. "1SU,-1SU" */
+ private static class ParseByDay extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byday;
+ int[] bydayNum;
+ int bydayCount;
+
+ if (value.indexOf(",") < 0) {
+ /* only one entry, skip split() overhead */
+ bydayCount = 1;
+ byday = new int[1];
+ bydayNum = new int[1];
+ parseWday(value, byday, bydayNum, 0);
+ } else {
+ String[] wdays = value.split(",");
+ int len = wdays.length;
+ bydayCount = len;
+ byday = new int[len];
+ bydayNum = new int[len];
+ for (int i = 0; i < len; i++) {
+ parseWday(wdays[i], byday, bydayNum, i);
+ }
+ }
+ er.byday = byday;
+ er.bydayNum = bydayNum;
+ er.bydayCount = bydayCount;
+ return PARSED_BYDAY;
+ }
+
+ /** parses [int]weekday, putting the pieces into parallel array entries */
+ private static void parseWday(String str, int[] byday, int[] bydayNum, int index) {
+ int wdayStrStart = str.length() - 2;
+ String wdayStr;
+
+ if (wdayStrStart > 0) {
+ /* number is included; parse it out and advance to weekday */
+ String numPart = str.substring(0, wdayStrStart);
+ int num = parseIntRange(numPart, -53, 53, false);
+ bydayNum[index] = num;
+ wdayStr = str.substring(wdayStrStart);
+ } else {
+ /* just the weekday string */
+ wdayStr = str;
+ }
+ Integer wday = sParseWeekdayMap.get(wdayStr);
+ if (wday == null) {
+ throw new InvalidFormatException("Invalid BYDAY value: " + str);
+ }
+ byday[index] = wday;
+ }
+ }
+ /** parses BYMONTHDAY=bymodaylist */
+ private static class ParseByMonthDay extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bymonthday = parseNumberList(value, -31, 31, false);
+ er.bymonthday = bymonthday;
+ er.bymonthdayCount = bymonthday.length;
+ return PARSED_BYMONTHDAY;
+ }
+ }
+ /** parses BYYEARDAY=byyrdaylist */
+ private static class ParseByYearDay extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byyearday = parseNumberList(value, -366, 366, false);
+ er.byyearday = byyearday;
+ er.byyeardayCount = byyearday.length;
+ return PARSED_BYYEARDAY;
+ }
+ }
+ /** parses BYWEEKNO=bywknolist */
+ private static class ParseByWeekNo extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] byweekno = parseNumberList(value, -53, 53, false);
+ er.byweekno = byweekno;
+ er.byweeknoCount = byweekno.length;
+ return PARSED_BYWEEKNO;
+ }
+ }
+ /** parses BYMONTH=bymolist */
+ private static class ParseByMonth extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bymonth = parseNumberList(value, 1, 12, false);
+ er.bymonth = bymonth;
+ er.bymonthCount = bymonth.length;
+ return PARSED_BYMONTH;
+ }
+ }
+ /** parses BYSETPOS=bysplist */
+ private static class ParseBySetPos extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ int[] bysetpos = parseNumberList(value, Integer.MIN_VALUE, Integer.MAX_VALUE, true);
+ er.bysetpos = bysetpos;
+ er.bysetposCount = bysetpos.length;
+ return PARSED_BYSETPOS;
+ }
+ }
+ /** parses WKST={SU,MO,...} */
+ private static class ParseWkst extends PartParser {
+ @Override public int parsePart(String value, EventRecurrence er) {
+ Integer wkst = sParseWeekdayMap.get(value);
+ if (wkst == null) {
+ throw new InvalidFormatException("Invalid WKST value: " + value);
+ }
+ er.wkst = wkst;
+ return PARSED_WKST;
+ }
+ }
}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 18ef38a..555667b 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -252,7 +252,8 @@
/**
* Finds {@link AccessibilityNodeInfo}s by text. The match is case
- * insensitive containment.
+ * insensitive containment. The search is relative to this info i.e.
+ * this info is the root of the traversed tree.
*
* @param text The searched text.
* @return A list of node info.
@@ -260,7 +261,7 @@
public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
enforceSealed();
if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
- return null;
+ return Collections.emptyList();
}
try {
return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId,
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index c56e6db..9f632d1 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5707,8 +5707,8 @@
return false;
}
WebViewCore.CursorData data = cursorDataNoPosition();
- data.mX = viewToContentX((int) event.getX());
- data.mY = viewToContentY((int) event.getY());
+ data.mX = viewToContentX((int) event.getX() + mScrollX);
+ data.mY = viewToContentY((int) event.getY() + mScrollY);
mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
return true;
}
diff --git a/core/jni/android_pim_EventRecurrence.cpp b/core/jni/android_pim_EventRecurrence.cpp
index 44e898d..3e11569 100644
--- a/core/jni/android_pim_EventRecurrence.cpp
+++ b/core/jni/android_pim_EventRecurrence.cpp
@@ -147,7 +147,7 @@
*/
static JNINativeMethod METHODS[] = {
/* name, signature, funcPtr */
- { "parse", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }
+ { "parseNative", "(Ljava/lang/String;)V", (void*)EventRecurrence_parse }
};
static const char*const CLASS_NAME = "android/pim/EventRecurrence";
diff --git a/core/tests/coretests/src/android/pim/EventRecurrenceTest.java b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
new file mode 100644
index 0000000..05000f1
--- /dev/null
+++ b/core/tests/coretests/src/android/pim/EventRecurrenceTest.java
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2006 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 android.pim;
+
+import android.pim.EventRecurrence.InvalidFormatException;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+
+/**
+ * Test android.pim.EventRecurrence.
+ *
+ * adb shell am instrument -w -e class android.pim.EventRecurrenceTest \
+ * com.android.frameworks.coretests/android.test.InstrumentationTestRunner
+ */
+public class EventRecurrenceTest extends TestCase {
+
+ @SmallTest
+ public void test0() throws Exception {
+ verifyRecurType("FREQ=SECONDLY",
+ /* int freq */ EventRecurrence.SECONDLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test1() throws Exception {
+ verifyRecurType("FREQ=MINUTELY",
+ /* int freq */ EventRecurrence.MINUTELY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test2() throws Exception {
+ verifyRecurType("FREQ=HOURLY",
+ /* int freq */ EventRecurrence.HOURLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test3() throws Exception {
+ verifyRecurType("FREQ=DAILY",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test4() throws Exception {
+ verifyRecurType("FREQ=WEEKLY",
+ /* int freq */ EventRecurrence.WEEKLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test5() throws Exception {
+ verifyRecurType("FREQ=MONTHLY",
+ /* int freq */ EventRecurrence.MONTHLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test6() throws Exception {
+ verifyRecurType("FREQ=YEARLY",
+ /* int freq */ EventRecurrence.YEARLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test7() throws Exception {
+ // with an until
+ verifyRecurType("FREQ=DAILY;UNTIL=112233T223344Z",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ "112233T223344Z",
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test8() throws Exception {
+ // with a count
+ verifyRecurType("FREQ=DAILY;COUNT=334",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 334,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test9() throws Exception {
+ // with a count
+ verifyRecurType("FREQ=DAILY;INTERVAL=5000",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 5000,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @SmallTest
+ public void test10() throws Exception {
+ // verifyRecurType all of the BY* ones with one element
+ verifyRecurType("FREQ=DAILY"
+ + ";BYSECOND=0"
+ + ";BYMINUTE=1"
+ + ";BYHOUR=2"
+ + ";BYMONTHDAY=30"
+ + ";BYYEARDAY=300"
+ + ";BYWEEKNO=53"
+ + ";BYMONTH=12"
+ + ";BYSETPOS=-15"
+ + ";WKST=SU",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ new int[]{0},
+ /* int[] byminute */ new int[]{1},
+ /* int[] byhour */ new int[]{2},
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ new int[]{30},
+ /* int[] byyearday */ new int[]{300},
+ /* int[] byweekno */ new int[]{53},
+ /* int[] bymonth */ new int[]{12},
+ /* int[] bysetpos */ new int[]{-15},
+ /* int wkst */ EventRecurrence.SU
+ );
+ }
+
+ @SmallTest
+ public void test11() throws Exception {
+ // verifyRecurType all of the BY* ones with one element
+ verifyRecurType("FREQ=DAILY"
+ + ";BYSECOND=0,30,59"
+ + ";BYMINUTE=0,41,59"
+ + ";BYHOUR=0,4,23"
+ + ";BYMONTHDAY=-31,-1,1,31"
+ + ";BYYEARDAY=-366,-1,1,366"
+ + ";BYWEEKNO=-53,-1,1,53"
+ + ";BYMONTH=1,12"
+ + ";BYSETPOS=1,2,3,4,500,10000"
+ + ";WKST=SU",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ new int[]{0, 30, 59},
+ /* int[] byminute */ new int[]{0, 41, 59},
+ /* int[] byhour */ new int[]{0, 4, 23},
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ new int[]{-31, -1, 1, 31},
+ /* int[] byyearday */ new int[]{-366, -1, 1, 366},
+ /* int[] byweekno */ new int[]{-53, -1, 1, 53},
+ /* int[] bymonth */ new int[]{1, 12},
+ /* int[] bysetpos */ new int[]{1, 2, 3, 4, 500, 10000},
+ /* int wkst */ EventRecurrence.SU
+ );
+ }
+
+ private static class Check {
+ Check(String k, int... v) {
+ key = k;
+ values = v;
+ }
+
+ String key;
+ int[] values;
+ }
+
+ // this is a negative verifyRecurType case to verifyRecurType the range of the numbers accepted
+ @SmallTest
+ public void test12() throws Exception {
+ Check[] checks = new Check[]{
+ new Check("BYSECOND", -100, -1, 60, 100),
+ new Check("BYMINUTE", -100, -1, 60, 100),
+ new Check("BYHOUR", -100, -1, 24, 100),
+ new Check("BYMONTHDAY", -100, -32, 0, 32, 100),
+ new Check("BYYEARDAY", -400, -367, 0, 367, 400),
+ new Check("BYWEEKNO", -100, -54, 0, 54, 100),
+ new Check("BYMONTH", -100, -5, 0, 13, 100)
+ };
+
+ for (Check ck : checks) {
+ for (int n : ck.values) {
+ String recur = "FREQ=DAILY;" + ck.key + "=" + n;
+ try {
+ EventRecurrence er = new EventRecurrence();
+ er.parse(recur);
+ fail("Negative verifyRecurType failed. "
+ + " parse failed to throw an exception for '"
+ + recur + "'");
+ } catch (EventRecurrence.InvalidFormatException e) {
+ // expected
+ }
+ }
+ }
+ }
+
+ // verifyRecurType BYDAY
+ @SmallTest
+ public void test13() throws Exception {
+ verifyRecurType("FREQ=DAILY;BYDAY=1SU,-2MO,+33TU,WE,TH,FR,SA",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.SU,
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ EventRecurrence.TH,
+ EventRecurrence.FR,
+ EventRecurrence.SA
+ },
+ /* int[] bydayNum */ new int[]{1, -2, 33, 0, 0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ @Suppress
+ // Repro bug #2331761 - this should fail because of the last comma into BYDAY
+ public void test14() throws Exception {
+ verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;BYDAY=MO,TU,WE,",
+ /* int freq */ EventRecurrence.WEEKLY,
+ /* String until */ "20100129T130000Z",
+ /* int count */ 0,
+ /* int interval */ 1,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ },
+ /* int[] bydayNum */ new int[]{0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // This test should pass
+ public void test15() throws Exception {
+ verifyRecurType("FREQ=WEEKLY;WKST=MO;UNTIL=20100129T130000Z;INTERVAL=1;"
+ + "BYDAY=MO,TU,WE,TH,FR,SA,SU",
+ /* int freq */ EventRecurrence.WEEKLY,
+ /* String until */ "20100129T130000Z",
+ /* int count */ 0,
+ /* int interval */ 1,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ EventRecurrence.TH,
+ EventRecurrence.FR,
+ EventRecurrence.SA,
+ EventRecurrence.SU
+ },
+ /* int[] bydayNum */ new int[]{0, 0, 0, 0, 0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from RFC2445
+ public void test16() throws Exception {
+ verifyRecurType("FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1",
+ /* int freq */ EventRecurrence.MONTHLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.MO,
+ EventRecurrence.TU,
+ EventRecurrence.WE,
+ EventRecurrence.TH,
+ EventRecurrence.FR
+ },
+ /* int[] bydayNum */ new int[] {0, 0, 0, 0, 0},
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ new int[] { -1 },
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from RFC2445
+ public void test17() throws Exception {
+ verifyRecurType("FREQ=DAILY;COUNT=10;INTERVAL=2",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 10,
+ /* int interval */ 2,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from RFC2445
+ public void test18() throws Exception {
+ verifyRecurType("FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10",
+ /* int freq */ EventRecurrence.YEARLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.SU
+ },
+ /* int[] bydayNum */ new int[] { -1 },
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ new int[] { 10 },
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // Sample coming from bug #1640517
+ public void test19() throws Exception {
+ verifyRecurType("FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
+ /* int freq */ EventRecurrence.YEARLY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ new int[] {
+ EventRecurrence.TH
+ },
+ /* int[] bydayNum */ new int[] { 0 },
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ new int[] { 3 },
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ // for your copying pleasure
+ public void fakeTestXX() throws Exception {
+ verifyRecurType("FREQ=DAILY;",
+ /* int freq */ EventRecurrence.DAILY,
+ /* String until */ null,
+ /* int count */ 0,
+ /* int interval */ 0,
+ /* int[] bysecond */ null,
+ /* int[] byminute */ null,
+ /* int[] byhour */ null,
+ /* int[] byday */ null,
+ /* int[] bydayNum */ null,
+ /* int[] bymonthday */ null,
+ /* int[] byyearday */ null,
+ /* int[] byweekno */ null,
+ /* int[] bymonth */ null,
+ /* int[] bysetpos */ null,
+ /* int wkst */ EventRecurrence.MO
+ );
+ }
+
+ private static void cmp(int vlen, int[] v, int[] correct, String name) {
+ if ((correct == null && v != null)
+ || (correct != null && v == null)) {
+ throw new RuntimeException("One is null, one isn't for " + name
+ + ": correct=" + Arrays.toString(correct)
+ + " actual=" + Arrays.toString(v));
+ }
+ if ((correct == null && vlen != 0)
+ || (vlen != (correct == null ? 0 : correct.length))) {
+ throw new RuntimeException("Reported length mismatch for " + name
+ + ": correct=" + ((correct == null) ? "null" : correct.length)
+ + " actual=" + vlen);
+ }
+ if (correct == null) {
+ return;
+ }
+ if (v.length < correct.length) {
+ throw new RuntimeException("Array length mismatch for " + name
+ + ": correct=" + Arrays.toString(correct)
+ + " actual=" + Arrays.toString(v));
+ }
+ for (int i = 0; i < correct.length; i++) {
+ if (v[i] != correct[i]) {
+ throw new RuntimeException("Array value mismatch for " + name
+ + ": correct=" + Arrays.toString(correct)
+ + " actual=" + Arrays.toString(v));
+ }
+ }
+ }
+
+ private static boolean eq(String a, String b) {
+ if ((a == null && b != null) || (a != null && b == null)) {
+ return false;
+ } else {
+ return a == b || a.equals(b);
+ }
+ }
+
+ private static void verifyRecurType(String recur,
+ int freq, String until, int count, int interval,
+ int[] bysecond, int[] byminute, int[] byhour,
+ int[] byday, int[] bydayNum, int[] bymonthday,
+ int[] byyearday, int[] byweekno, int[] bymonth,
+ int[] bysetpos, int wkst) {
+ EventRecurrence eventRecurrence = new EventRecurrence();
+ eventRecurrence.parse(recur);
+ if (eventRecurrence.freq != freq
+ || !eq(eventRecurrence.until, until)
+ || eventRecurrence.count != count
+ || eventRecurrence.interval != interval
+ || eventRecurrence.wkst != wkst) {
+ System.out.println("Error... got:");
+ print(eventRecurrence);
+ System.out.println("expected:");
+ System.out.println("{");
+ System.out.println(" freq=" + freq);
+ System.out.println(" until=" + until);
+ System.out.println(" count=" + count);
+ System.out.println(" interval=" + interval);
+ System.out.println(" wkst=" + wkst);
+ System.out.println(" bysecond=" + Arrays.toString(bysecond));
+ System.out.println(" byminute=" + Arrays.toString(byminute));
+ System.out.println(" byhour=" + Arrays.toString(byhour));
+ System.out.println(" byday=" + Arrays.toString(byday));
+ System.out.println(" bydayNum=" + Arrays.toString(bydayNum));
+ System.out.println(" bymonthday=" + Arrays.toString(bymonthday));
+ System.out.println(" byyearday=" + Arrays.toString(byyearday));
+ System.out.println(" byweekno=" + Arrays.toString(byweekno));
+ System.out.println(" bymonth=" + Arrays.toString(bymonth));
+ System.out.println(" bysetpos=" + Arrays.toString(bysetpos));
+ System.out.println("}");
+ throw new RuntimeException("Mismatch in fields");
+ }
+ cmp(eventRecurrence.bysecondCount, eventRecurrence.bysecond, bysecond, "bysecond");
+ cmp(eventRecurrence.byminuteCount, eventRecurrence.byminute, byminute, "byminute");
+ cmp(eventRecurrence.byhourCount, eventRecurrence.byhour, byhour, "byhour");
+ cmp(eventRecurrence.bydayCount, eventRecurrence.byday, byday, "byday");
+ cmp(eventRecurrence.bydayCount, eventRecurrence.bydayNum, bydayNum, "bydayNum");
+ cmp(eventRecurrence.bymonthdayCount, eventRecurrence.bymonthday, bymonthday, "bymonthday");
+ cmp(eventRecurrence.byyeardayCount, eventRecurrence.byyearday, byyearday, "byyearday");
+ cmp(eventRecurrence.byweeknoCount, eventRecurrence.byweekno, byweekno, "byweekno");
+ cmp(eventRecurrence.bymonthCount, eventRecurrence.bymonth, bymonth, "bymonth");
+ cmp(eventRecurrence.bysetposCount, eventRecurrence.bysetpos, bysetpos, "bysetpos");
+ }
+
+ private static void print(EventRecurrence er) {
+ System.out.println("{");
+ System.out.println(" freq=" + er.freq);
+ System.out.println(" until=" + er.until);
+ System.out.println(" count=" + er.count);
+ System.out.println(" interval=" + er.interval);
+ System.out.println(" wkst=" + er.wkst);
+ System.out.println(" bysecond=" + Arrays.toString(er.bysecond));
+ System.out.println(" bysecondCount=" + er.bysecondCount);
+ System.out.println(" byminute=" + Arrays.toString(er.byminute));
+ System.out.println(" byminuteCount=" + er.byminuteCount);
+ System.out.println(" byhour=" + Arrays.toString(er.byhour));
+ System.out.println(" byhourCount=" + er.byhourCount);
+ System.out.println(" byday=" + Arrays.toString(er.byday));
+ System.out.println(" bydayNum=" + Arrays.toString(er.bydayNum));
+ System.out.println(" bydayCount=" + er.bydayCount);
+ System.out.println(" bymonthday=" + Arrays.toString(er.bymonthday));
+ System.out.println(" bymonthdayCount=" + er.bymonthdayCount);
+ System.out.println(" byyearday=" + Arrays.toString(er.byyearday));
+ System.out.println(" byyeardayCount=" + er.byyeardayCount);
+ System.out.println(" byweekno=" + Arrays.toString(er.byweekno));
+ System.out.println(" byweeknoCount=" + er.byweeknoCount);
+ System.out.println(" bymonth=" + Arrays.toString(er.bymonth));
+ System.out.println(" bymonthCount=" + er.bymonthCount);
+ System.out.println(" bysetpos=" + Arrays.toString(er.bysetpos));
+ System.out.println(" bysetposCount=" + er.bysetposCount);
+ System.out.println("}");
+ }
+
+
+ /** A list of valid rules. The parser must accept these. */
+ private static final String[] GOOD_RRULES = {
+ /* extracted wholesale from from RFC 2445 section 4.8.5.4 */
+ "FREQ=DAILY;COUNT=10",
+ "FREQ=DAILY;UNTIL=19971224T000000Z",
+ "FREQ=DAILY;INTERVAL=2",
+ "FREQ=DAILY;INTERVAL=10;COUNT=5",
+ "FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA",
+ "FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1",
+ "FREQ=WEEKLY;COUNT=10",
+ "FREQ=WEEKLY;UNTIL=19971224T000000Z",
+ "FREQ=WEEKLY;INTERVAL=2;WKST=SU",
+ "FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH",
+ "FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH",
+ "FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR",
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH",
+ "FREQ=MONTHLY;COUNT=10;BYDAY=1FR",
+ "FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR",
+ "FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU",
+ "FREQ=MONTHLY;COUNT=6;BYDAY=-2MO",
+ "FREQ=MONTHLY;BYMONTHDAY=-3",
+ "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15",
+ "FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1",
+ "FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15",
+ "FREQ=MONTHLY;INTERVAL=2;BYDAY=TU",
+ "FREQ=YEARLY;COUNT=10;BYMONTH=6,7",
+ "FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3",
+ "FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200",
+ "FREQ=YEARLY;BYDAY=20MO",
+ "FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO",
+ "FREQ=YEARLY;BYMONTH=3;BYDAY=TH",
+ "FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8",
+ "FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13",
+ "FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13",
+ "FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8",
+ "FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3",
+ "FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2",
+ "FREQ=HOURLY;INTERVAL=3;UNTIL=19970902T170000Z",
+ "FREQ=MINUTELY;INTERVAL=15;COUNT=6",
+ "FREQ=MINUTELY;INTERVAL=90;COUNT=4",
+ "FREQ=DAILY;BYHOUR=9,10,11,12,13,14,15,16;BYMINUTE=0,20,40",
+ "FREQ=MINUTELY;INTERVAL=20;BYHOUR=9,10,11,12,13,14,15,16",
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=MO",
+ "FREQ=WEEKLY;INTERVAL=2;COUNT=4;BYDAY=TU,SU;WKST=SU",
+ /* a few more */
+ "FREQ=SECONDLY;BYSECOND=0,15,59",
+ "FREQ=MINUTELY;BYMINUTE=0,15,59",
+ "FREQ=HOURLY;BYHOUR=+0,+15,+23",
+ "FREQ=DAILY;X-WHATEVER=blah", // fails on old parser
+ //"freq=daily;wkst=su", // fails on old parser
+ };
+
+ /** The parser must reject these. */
+ private static final String[] BAD_RRULES = {
+ "INTERVAL=4;FREQ=YEARLY", // FREQ must come first
+ "FREQ=MONTHLY;FREQ=MONTHLY", // can't specify twice
+ "FREQ=MONTHLY;COUNT=1;COUNT=1", // can't specify twice
+ "FREQ=SECONDLY;BYSECOND=60", // range
+ "FREQ=MINUTELY;BYMINUTE=-1", // range
+ "FREQ=HOURLY;BYHOUR=24", // range
+ "FREQ=YEARLY;BYMONTHDAY=0", // zero not valid
+ //"FREQ=YEARLY;COUNT=1;UNTIL=12345", // can't have both COUNT and UNTIL
+ //"FREQ=DAILY;UNTIL=19970829T021400e", // invalid date
+ };
+
+ /**
+ * Simple test of good/bad rules.
+ */
+ @SmallTest
+ public void testBasicParse() {
+ for (String rule : GOOD_RRULES) {
+ EventRecurrence recur = new EventRecurrence();
+ recur.parse(rule);
+ }
+
+ for (String rule : BAD_RRULES) {
+ EventRecurrence recur = new EventRecurrence();
+ boolean didThrow = false;
+
+ try {
+ recur.parse(rule);
+ } catch (InvalidFormatException ife) {
+ didThrow = true;
+ }
+
+ assertTrue("Expected throw on " + rule, didThrow);
+ }
+ }
+}
diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java
index 61f2e1f..77dd86a 100644
--- a/graphics/java/android/renderscript/AllocationAdapter.java
+++ b/graphics/java/android/renderscript/AllocationAdapter.java
@@ -27,8 +27,14 @@
*
**/
public class AllocationAdapter extends Allocation {
+ private boolean mConstrainedLOD;
+ private boolean mConstrainedFace;
+ private boolean mConstrainedY;
+ private boolean mConstrainedZ;
+
private int mSelectedDimX;
private int mSelectedDimY;
+ private int mSelectedDimZ;
private int mSelectedCount;
private Allocation mAlloc;
@@ -37,13 +43,14 @@
AllocationAdapter(int id, RenderScript rs, Allocation alloc) {
super(id, rs, null, alloc.mUsage);
- Type t = alloc.getType();
- mSelectedDimX = t.getX();
- mSelectedDimY = t.getY();
- mSelectedCount = t.getCount();
+ mAlloc = alloc;
}
+ int getID() {
+ return mAlloc.getID();
+ }
+
public void copyFrom(BaseObj[] d) {
mRS.validate();
if (d.length != mSelectedCount) {
@@ -54,7 +61,7 @@
for (int ct=0; ct < d.length; ct++) {
i[ct] = d[ct].getID();
}
- subData1D(0, mType.getCount(), i);
+ subData1D(0, mAlloc.mType.getCount(), i);
}
void validateBitmap(Bitmap b) {
@@ -93,7 +100,7 @@
public void subData(int xoff, FieldPacker fp) {
- int eSize = mType.mElement.getSizeBytes();
+ int eSize = mAlloc.mType.mElement.getSizeBytes();
final byte[] data = fp.getData();
int count = data.length / eSize;
@@ -107,7 +114,7 @@
public void subElementData(int xoff, int component_number, FieldPacker fp) {
- if (component_number >= mType.mElement.mElements.length) {
+ if (component_number >= mAlloc.mType.mElement.mElements.length) {
throw new RSIllegalArgumentException("Component_number " + component_number + " out of range.");
}
if(xoff < 0) {
@@ -115,7 +122,7 @@
}
final byte[] data = fp.getData();
- int eSize = mType.mElement.mElements[component_number].getSizeBytes();
+ int eSize = mAlloc.mType.mElement.mElements[component_number].getSizeBytes();
if (data.length != eSize) {
throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
@@ -133,12 +140,13 @@
if(count < 1) {
throw new RSIllegalArgumentException("Count must be >= 1.");
}
- if((off + count) > mSelectedDimX * mSelectedDimY) {
- throw new RSIllegalArgumentException("Overflow, Available count " + mType.getCount() +
+ if((off + count) > mSelectedCount) {
+ throw new RSIllegalArgumentException("Overflow, Available count " + mAlloc.mType.getCount() +
", got " + count + " at offset " + off + ".");
}
if((len) < dataSize) {
- throw new RSIllegalArgumentException("Array too small for allocation type.");
+ throw new RSIllegalArgumentException("Array too small for allocation type. len = " +
+ len + ", dataSize = " + dataSize);
}
}
@@ -223,8 +231,51 @@
mRS.nAllocationRead(getID(), d);
}
+ private void initLOD(int lod) {
+ if (lod < 0) {
+ throw new RSIllegalArgumentException("Attempting to set negative lod (" + lod + ").");
+ }
+
+ int tx = mAlloc.mType.getX();
+ int ty = mAlloc.mType.getY();
+ int tz = mAlloc.mType.getZ();
+
+ for (int ct=0; ct < lod; ct++) {
+ if ((tx==1) && (ty == 1) && (tz == 1)) {
+ throw new RSIllegalArgumentException("Attempting to set lod (" + lod + ") out of range.");
+ }
+
+ if (tx > 1) tx >>= 1;
+ if (ty > 1) ty >>= 1;
+ if (tz > 1) tz >>= 1;
+ }
+
+ mSelectedDimX = tx;
+ mSelectedDimY = ty;
+ mSelectedCount = tx;
+ if (ty > 1) {
+ mSelectedCount *= ty;
+ }
+ if (tz > 1) {
+ mSelectedCount *= tz;
+ }
+ }
+
+ /**
+ * Set the active LOD. The LOD must be within the range for the
+ * type being adapted.
+ *
+ * @param lod The LOD to make active.
+ */
public void setLOD(int lod) {
- mSelectedLOD = lod;
+ if (!mAlloc.getType().hasMipmaps()) {
+ throw new RSInvalidStateException("Cannot set LOD when the allocation type does not include mipmaps.");
+ }
+ if (!mConstrainedLOD) {
+ throw new RSInvalidStateException("Cannot set LOD when the adapter includes mipmaps.");
+ }
+
+ initLOD(lod);
}
public void setFace(Type.CubemapFace cf) {
@@ -245,6 +296,11 @@
static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
rs.validate();
AllocationAdapter aa = new AllocationAdapter(0, rs, a);
+ aa.mConstrainedLOD = true;
+ aa.mConstrainedFace = true;
+ aa.mConstrainedY = false;
+ aa.mConstrainedZ = true;
+ aa.initLOD(0);
return aa;
}
diff --git a/graphics/java/android/renderscript/FieldPacker.java b/graphics/java/android/renderscript/FieldPacker.java
index fac7144..2739a4b8 100644
--- a/graphics/java/android/renderscript/FieldPacker.java
+++ b/graphics/java/android/renderscript/FieldPacker.java
@@ -165,6 +165,22 @@
addF32(v.w);
}
+ public void addF64(Double2 v) {
+ addF64(v.x);
+ addF64(v.y);
+ }
+ public void addF64(Double3 v) {
+ addF64(v.x);
+ addF64(v.y);
+ addF64(v.z);
+ }
+ public void addF64(Double4 v) {
+ addF64(v.x);
+ addF64(v.y);
+ addF64(v.z);
+ addF64(v.w);
+ }
+
public void addI8(Byte2 v) {
addI8(v.x);
addI8(v.y);
@@ -261,6 +277,38 @@
addU32(v.w);
}
+ public void addI64(Long2 v) {
+ addI64(v.x);
+ addI64(v.y);
+ }
+ public void addI64(Long3 v) {
+ addI64(v.x);
+ addI64(v.y);
+ addI64(v.z);
+ }
+ public void addI64(Long4 v) {
+ addI64(v.x);
+ addI64(v.y);
+ addI64(v.z);
+ addI64(v.w);
+ }
+
+ public void addU64(Long2 v) {
+ addU64(v.x);
+ addU64(v.y);
+ }
+ public void addU64(Long3 v) {
+ addU64(v.x);
+ addU64(v.y);
+ addU64(v.z);
+ }
+ public void addU64(Long4 v) {
+ addU64(v.x);
+ addU64(v.y);
+ addU64(v.z);
+ addU64(v.w);
+ }
+
public void addMatrix(Matrix4f v) {
for (int i=0; i < v.mMat.length; i++) {
addF32(v.mMat[i]);
diff --git a/libs/rs/driver/rsdRuntimeMath.cpp b/libs/rs/driver/rsdRuntimeMath.cpp
index acb990d..d29da7e 100644
--- a/libs/rs/driver/rsdRuntimeMath.cpp
+++ b/libs/rs/driver/rsdRuntimeMath.cpp
@@ -268,6 +268,87 @@
}
+static int32_t SC_AtomicCas(volatile int32_t *ptr, int32_t expectedValue, int32_t newValue) {
+ int32_t prev;
+
+ do {
+ int32_t ret = android_atomic_release_cas(expectedValue, newValue, ptr);
+ if (!ret) {
+ // The android cas return 0 if it wrote the value. This means the
+ // previous value was the expected value and we can return.
+ return expectedValue;
+ }
+ // We didn't write the value and need to load the "previous" value.
+ prev = *ptr;
+
+ // A race condition exists where the expected value could appear after our cas failed
+ // above. In this case loop until we have a legit previous value or the
+ // write passes.
+ } while (prev == expectedValue);
+ return prev;
+}
+
+
+static int32_t SC_AtomicInc(volatile int32_t *ptr) {
+ return android_atomic_inc(ptr);
+}
+
+static int32_t SC_AtomicDec(volatile int32_t *ptr) {
+ return android_atomic_dec(ptr);
+}
+
+static int32_t SC_AtomicAdd(volatile int32_t *ptr, int32_t value) {
+ return android_atomic_add(value, ptr);
+}
+
+static int32_t SC_AtomicSub(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_release_cas(prev, prev - value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+static int32_t SC_AtomicAnd(volatile int32_t *ptr, int32_t value) {
+ return android_atomic_and(value, ptr);
+}
+
+static int32_t SC_AtomicOr(volatile int32_t *ptr, int32_t value) {
+ return android_atomic_or(value, ptr);
+}
+
+static int32_t SC_AtomicXor(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ status = android_atomic_release_cas(prev, prev ^ value, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+static int32_t SC_AtomicMin(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ int32_t n = rsMin(value, prev);
+ status = android_atomic_release_cas(prev, n, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+static int32_t SC_AtomicMax(volatile int32_t *ptr, int32_t value) {
+ int32_t prev, status;
+ do {
+ prev = *ptr;
+ int32_t n = rsMax(value, prev);
+ status = android_atomic_release_cas(prev, n, ptr);
+ } while (__builtin_expect(status != 0, 0));
+ return prev;
+}
+
+
+
//////////////////////////////////////////////////////////////////////////////
// Class implementation
//////////////////////////////////////////////////////////////////////////////
@@ -425,6 +506,28 @@
{ "_Z6rsRandff", (void *)&SC_randf2, true },
{ "_Z6rsFracf", (void *)&SC_frac, true },
+ // Atomics
+ { "_Z11rsAtomicIncPVi", (void *)&SC_AtomicInc, true },
+ { "_Z11rsAtomicIncPVj", (void *)&SC_AtomicInc, true },
+ { "_Z11rsAtomicDecPVi", (void *)&SC_AtomicDec, true },
+ { "_Z11rsAtomicDecPVj", (void *)&SC_AtomicDec, true },
+ { "_Z11rsAtomicAddPVii", (void *)&SC_AtomicAdd, true },
+ { "_Z11rsAtomicAddPVjj", (void *)&SC_AtomicAdd, true },
+ { "_Z11rsAtomicSubPVii", (void *)&SC_AtomicSub, true },
+ { "_Z11rsAtomicSubPVjj", (void *)&SC_AtomicSub, true },
+ { "_Z11rsAtomicAndPVii", (void *)&SC_AtomicAnd, true },
+ { "_Z11rsAtomicAndPVjj", (void *)&SC_AtomicAnd, true },
+ { "_Z10rsAtomicOrPVii", (void *)&SC_AtomicOr, true },
+ { "_Z10rsAtomicOrPVjj", (void *)&SC_AtomicOr, true },
+ { "_Z11rsAtomicXorPVii", (void *)&SC_AtomicXor, true },
+ { "_Z11rsAtomicXorPVjj", (void *)&SC_AtomicXor, true },
+ { "_Z11rsAtomicMinPVii", (void *)&SC_AtomicMin, true },
+ { "_Z11rsAtomicMinPVjj", (void *)&SC_AtomicMin, true },
+ { "_Z11rsAtomicMaxPVii", (void *)&SC_AtomicMax, true },
+ { "_Z11rsAtomicMaxPVjj", (void *)&SC_AtomicMax, true },
+ { "_Z11rsAtomicCasPViii", (void *)&SC_AtomicCas, true },
+ { "_Z11rsAtomicCasPVjjj", (void *)&SC_AtomicCas, true },
+
{ NULL, NULL, false }
};
diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh
index 584317e..f38f72c 100644
--- a/libs/rs/scriptc/rs_math.rsh
+++ b/libs/rs/scriptc/rs_math.rsh
@@ -258,4 +258,226 @@
rs_allocation output, const void * usrData,
const rs_script_call_t *);
+
+
+/**
+ * Atomic add one to the value at addr.
+ * Equal to rsAtomicAdd(addr, 1)
+ *
+ * @param addr Address of value to increment
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicInc(volatile int32_t* addr);
+/**
+ * Atomic add one to the value at addr.
+ * Equal to rsAtomicAdd(addr, 1)
+ *
+ * @param addr Address of value to increment
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicInc(volatile uint32_t* addr);
+
+/**
+ * Atomic subtract one from the value at addr. Equal to rsAtomicSub(addr, 1)
+ *
+ * @param addr Address of value to decrement
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicDec(volatile int32_t* addr);
+/**
+ * Atomic subtract one from the value at addr. Equal to rsAtomicSub(addr, 1)
+ *
+ * @param addr Address of value to decrement
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicDec(volatile uint32_t* addr);
+
+/**
+ * Atomic add a value to the value at addr. addr[0] += value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to add to the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicAdd(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic add a value to the value at addr. addr[0] += value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to add to the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicAdd(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Subtract a value from the value at addr. addr[0] -= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to subtract from the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicSub(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Subtract a value from the value at addr. addr[0] -= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to subtract from the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicSub(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise and a value from the value at addr. addr[0] &= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to and with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicAnd(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Bitwise and a value from the value at addr. addr[0] &= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to and with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicAnd(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise or a value from the value at addr. addr[0] |= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to or with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicOr(volatile int32_t* addr, int32_t value);
+/**
+ * Atomic Bitwise or a value from the value at addr. addr[0] |= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to or with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicOr(volatile uint32_t* addr, uint32_t value);
+
+/**
+ * Atomic Bitwise xor a value from the value at addr. addr[0] ^= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to xor with the value at addr
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicXor(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Bitwise xor a value from the value at addr. addr[0] ^= value
+ *
+ * @param addr Address of value to modify
+ * @param value Amount to xor with the value at addr
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicXor(volatile int32_t* addr, int32_t value);
+
+/**
+ * Atomic Set the value at addr to the min of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicMin(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Set the value at addr to the min of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicMin(volatile int32_t* addr, int32_t value);
+
+/**
+ * Atomic Set the value at addr to the max of addr and value
+ * addr[0] = rsMax(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicMax(volatile uint32_t* addr, uint32_t value);
+/**
+ * Atomic Set the value at addr to the max of addr and value
+ * addr[0] = rsMin(addr[0], value)
+ *
+ * @param addr Address of value to modify
+ * @param value comparison value
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicMax(volatile int32_t* addr, int32_t value);
+
+/**
+ * Compare-and-set operation with a full memory barrier.
+ *
+ * If the value at addr matches compareValue then newValue is written.
+ *
+ * @param addr The address to compare and replace if the compare passes.
+ * @param compareValue The value to test addr[0] against.
+ * @param newValue The value to write if the test passes.
+ *
+ * @return old value
+ */
+extern int32_t __attribute__((overloadable))
+ rsAtomicCas(volatile int32_t* addr, int32_t compareValue, int32_t newValue);
+
+/**
+ * Compare-and-set operation with a full memory barrier.
+ *
+ * If the value at addr matches compareValue then newValue is written.
+ *
+ * @param addr The address to compare and replace if the compare passes.
+ * @param compareValue The value to test addr[0] against.
+ * @param newValue The value to write if the test passes.
+ *
+ * @return old value
+ */
+extern uint32_t __attribute__((overloadable))
+ rsAtomicCas(volatile uint32_t* addr, int32_t compareValue, int32_t newValue);
+
+
#endif
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 58f6699..ea9911c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -47,10 +47,6 @@
static const uint8_t kNalUnitTypePicParamSet = 0x08;
static const int64_t kInitialDelayTimeUs = 700000LL;
-// Using longer adjustment period to suppress fluctuations in
-// the audio encoding paths
-static const int64_t kVideoMediaTimeAdjustPeriodTimeUs = 600000000LL; // 10 minutes
-
class MPEG4Writer::Track {
public:
Track(MPEG4Writer *owner, const sp<MediaSource> &source, size_t trackId);
@@ -88,8 +84,6 @@
int64_t mTrackDurationUs;
int64_t mMaxChunkDurationUs;
- // For realtime applications, we need to adjust the media clock
- // for video track based on the audio media clock
bool mIsRealTimeRecording;
int64_t mMaxTimeStampUs;
int64_t mEstimatedTrackSizeBytes;
@@ -175,28 +169,9 @@
int64_t mPreviousTrackTimeUs;
int64_t mTrackEveryTimeDurationUs;
- // Has the media time adjustment for video started?
- bool mIsMediaTimeAdjustmentOn;
- // The time stamp when previous media time adjustment period starts
- int64_t mPrevMediaTimeAdjustTimestampUs;
- // Number of vidoe frames whose time stamp may be adjusted
- int64_t mMediaTimeAdjustNumFrames;
- // The sample number when previous meida time adjustmnet period starts
- int64_t mPrevMediaTimeAdjustSample;
- // The total accumulated drift time within a period of
- // kVideoMediaTimeAdjustPeriodTimeUs.
- int64_t mTotalDriftTimeToAdjustUs;
- // The total accumalated drift time since the start of the recording
- // excluding the current time adjustment period
- int64_t mPrevTotalAccumDriftTimeUs;
-
// Update the audio track's drift information.
void updateDriftTime(const sp<MetaData>& meta);
- // Adjust the time stamp of the video track according to
- // the drift time information from the audio track.
- void adjustMediaTime(int64_t *timestampUs);
-
static void *ThreadWrapper(void *me);
status_t threadEntry();
@@ -1512,12 +1487,7 @@
mNumSttsTableEntries = 0;
mNumCttsTableEntries = 0;
mMdatSizeBytes = 0;
- mIsMediaTimeAdjustmentOn = false;
- mPrevMediaTimeAdjustTimestampUs = 0;
- mMediaTimeAdjustNumFrames = 0;
- mPrevMediaTimeAdjustSample = 0;
- mTotalDriftTimeToAdjustUs = 0;
- mPrevTotalAccumDriftTimeUs = 0;
+
mMaxChunkDurationUs = 0;
mHasNegativeCttsDeltaDuration = false;
@@ -1816,128 +1786,6 @@
}
/*
-* The video track's media time adjustment for real-time applications
-* is described as follows:
-*
-* First, the media time adjustment is done for every period of
-* kVideoMediaTimeAdjustPeriodTimeUs. kVideoMediaTimeAdjustPeriodTimeUs
-* is currently a fixed value chosen heuristically. The value of
-* kVideoMediaTimeAdjustPeriodTimeUs should not be very large or very small
-* for two considerations: on one hand, a relatively large value
-* helps reduce large fluctuation of drift time in the audio encoding
-* path; while on the other hand, a relatively small value helps keep
-* restoring synchronization in audio/video more frequently. Note for the
-* very first period of kVideoMediaTimeAdjustPeriodTimeUs, there is
-* no media time adjustment for the video track.
-*
-* Second, the total accumulated audio track time drift found
-* in a period of kVideoMediaTimeAdjustPeriodTimeUs is distributed
-* over a stream of incoming video frames. The number of video frames
-* affected is determined based on the number of recorded video frames
-* within the past kVideoMediaTimeAdjustPeriodTimeUs period.
-* We choose to distribute the drift time over only a portion
-* (rather than all) of the total number of recorded video frames
-* in order to make sure that the video track media time adjustment is
-* completed for the current period before the next video track media
-* time adjustment period starts. Currently, the portion chosen is a
-* half (0.5).
-*
-* Last, various additional checks are performed to ensure that
-* the actual audio encoding path does not have too much drift.
-* In particular, 1) we want to limit the average incremental time
-* adjustment for each video frame to be less than a threshold
-* for a single period of kVideoMediaTimeAdjustPeriodTimeUs.
-* Currently, the threshold is set to 5 ms. If the average incremental
-* media time adjustment for a video frame is larger than the
-* threshold, the audio encoding path has too much time drift.
-* 2) We also want to limit the total time drift in the audio
-* encoding path to be less than a threshold for a period of
-* kVideoMediaTimeAdjustPeriodTimeUs. Currently, the threshold
-* is 0.5% of kVideoMediaTimeAdjustPeriodTimeUs. If the time drift of
-* the audio encoding path is larger than the threshold, the audio
-* encoding path has too much time drift. We treat the large time
-* drift of the audio encoding path as errors, since there is no
-* way to keep audio/video in synchronization for real-time
-* applications if the time drift is too large unless we drop some
-* video frames, which has its own problems that we don't want
-* to get into for the time being.
-*/
-void MPEG4Writer::Track::adjustMediaTime(int64_t *timestampUs) {
- if (*timestampUs - mPrevMediaTimeAdjustTimestampUs >=
- kVideoMediaTimeAdjustPeriodTimeUs) {
-
- LOGV("New media time adjustment period at %lld us", *timestampUs);
- mIsMediaTimeAdjustmentOn = true;
- mMediaTimeAdjustNumFrames =
- (mNumSamples - mPrevMediaTimeAdjustSample) >> 1;
-
- mPrevMediaTimeAdjustTimestampUs = *timestampUs;
- mPrevMediaTimeAdjustSample = mNumSamples;
- int64_t totalAccumDriftTimeUs = mOwner->getDriftTimeUs();
- mTotalDriftTimeToAdjustUs =
- totalAccumDriftTimeUs - mPrevTotalAccumDriftTimeUs;
-
- mPrevTotalAccumDriftTimeUs = totalAccumDriftTimeUs;
-
- // Check on incremental adjusted time per frame
- int64_t adjustTimePerFrameUs =
- mTotalDriftTimeToAdjustUs / mMediaTimeAdjustNumFrames;
-
- if (adjustTimePerFrameUs < 0) {
- adjustTimePerFrameUs = -adjustTimePerFrameUs;
- }
- if (adjustTimePerFrameUs >= 5000) {
- LOGE("Adjusted time per video frame is %lld us",
- adjustTimePerFrameUs);
- CHECK(!"Video frame time adjustment is too large!");
- }
-
- // Check on total accumulated time drift within a period of
- // kVideoMediaTimeAdjustPeriodTimeUs.
- int64_t driftPercentage = (mTotalDriftTimeToAdjustUs * 1000)
- / kVideoMediaTimeAdjustPeriodTimeUs;
-
- if (driftPercentage < 0) {
- driftPercentage = -driftPercentage;
- }
- if (driftPercentage > 5) {
- LOGE("Audio track has time drift %lld us over %lld us",
- mTotalDriftTimeToAdjustUs,
- kVideoMediaTimeAdjustPeriodTimeUs);
-
- CHECK(!"The audio track media time drifts too much!");
- }
-
- }
-
- if (mIsMediaTimeAdjustmentOn) {
- if (mNumSamples - mPrevMediaTimeAdjustSample <=
- mMediaTimeAdjustNumFrames) {
-
- // Do media time incremental adjustment
- int64_t incrementalAdjustTimeUs =
- (mTotalDriftTimeToAdjustUs *
- (mNumSamples - mPrevMediaTimeAdjustSample))
- / mMediaTimeAdjustNumFrames;
-
- *timestampUs +=
- (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs);
-
- LOGV("Incremental video frame media time adjustment: %lld us",
- (incrementalAdjustTimeUs + mPrevTotalAccumDriftTimeUs));
- } else {
- // Within the remaining adjustment period,
- // no incremental adjustment is needed.
- *timestampUs +=
- (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs);
-
- LOGV("Fixed video frame media time adjustment: %lld us",
- (mTotalDriftTimeToAdjustUs + mPrevTotalAccumDriftTimeUs));
- }
- }
-}
-
-/*
* Updates the drift time from the audio track so that
* the video track can get the updated drift time information
* from the file writer. The fluctuation of the drift time of the audio
@@ -2080,32 +1928,6 @@
int32_t isSync = false;
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
-
- /*
- * The original timestamp found in the data buffer will be modified as below:
- *
- * There is a playback offset into this track if the track's start time
- * is not the same as the movie start time, which will be recorded in edst
- * box of the output file. The playback offset is to make sure that the
- * starting time of the audio/video tracks are synchronized. Although the
- * track's media timestamp may be subject to various modifications
- * as outlined below, the track's playback offset time remains unchanged
- * once the first data buffer of the track is received.
- *
- * The media time stamp will be calculated by subtracting the playback offset
- * (and potential pause durations) from the original timestamp in the buffer.
- *
- * If this track is a video track for a real-time recording application with
- * both audio and video tracks, its media timestamp will subject to further
- * modification based on the media clock of the audio track. This modification
- * is needed for the purpose of maintaining good audio/video synchronization.
- *
- * If the recording session is paused and resumed multiple times, the track
- * media timestamp will be modified as if the recording session had never been
- * paused at all during playback of the recorded output file. In other words,
- * the output file will have no memory of pause/resume durations.
- *
- */
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
////////////////////////////////////////////////////////////////////////////////
@@ -2146,31 +1968,13 @@
timestampUs, cttsDeltaTimeUs);
}
- // Media time adjustment for real-time applications
if (mIsRealTimeRecording) {
if (mIsAudio) {
updateDriftTime(meta_data);
- } else {
- adjustMediaTime(×tampUs);
}
}
CHECK(timestampUs >= 0);
- if (mNumSamples > 1) {
- if (timestampUs <= lastTimestampUs) {
- LOGW("Frame arrives too late!");
- // Don't drop the late frame, since dropping a frame may cause
- // problems later during playback
-
- // The idea here is to avoid having two or more samples with the
- // same timestamp in the output file.
- if (mTimeScale >= 1000000LL) {
- timestampUs = lastTimestampUs + 1;
- } else {
- timestampUs = lastTimestampUs + (1000000LL + (mTimeScale >> 1)) / mTimeScale;
- }
- }
- }
LOGV("%s media time stamp: %lld and previous paused duration %lld",
mIsAudio? "Audio": "Video", timestampUs, previousPausedDurationUs);
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
index 541bf22..e77998e 100644
--- a/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/RSTestCore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2011 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.
@@ -65,6 +65,7 @@
unitTests = new ArrayList<UnitTest>();
unitTests.add(new UT_primitives(this, mRes, mCtx));
+ unitTests.add(new UT_vector(this, mRes, mCtx));
unitTests.add(new UT_rsdebug(this, mRes, mCtx));
unitTests.add(new UT_rstime(this, mRes, mCtx));
unitTests.add(new UT_rstypes(this, mRes, mCtx));
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java
new file mode 100644
index 0000000..748701d
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/UT_vector.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2011 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.rs.test;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.renderscript.*;
+
+public class UT_vector extends UnitTest {
+ private Resources mRes;
+
+ protected UT_vector(RSTestCore rstc, Resources res, Context ctx) {
+ super(rstc, "Vector", ctx);
+ mRes = res;
+ }
+
+ private boolean initializeGlobals(ScriptC_vector s) {
+ Float2 F2 = s.get_f2();
+ if (F2.x != 1.0f || F2.y != 2.0f) {
+ return false;
+ }
+ F2.x = 2.99f;
+ F2.y = 3.99f;
+ s.set_f2(F2);
+
+ Float3 F3 = s.get_f3();
+ if (F3.x != 1.0f || F3.y != 2.0f || F3.z != 3.0f) {
+ return false;
+ }
+ F3.x = 2.99f;
+ F3.y = 3.99f;
+ F3.z = 4.99f;
+ s.set_f3(F3);
+
+ Float4 F4 = s.get_f4();
+ if (F4.x != 1.0f || F4.y != 2.0f || F4.z != 3.0f || F4.w != 4.0f) {
+ return false;
+ }
+ F4.x = 2.99f;
+ F4.y = 3.99f;
+ F4.z = 4.99f;
+ F4.w = 5.99f;
+ s.set_f4(F4);
+
+ Double2 D2 = s.get_d2();
+ if (D2.x != 1.0 || D2.y != 2.0) {
+ return false;
+ }
+ D2.x = 2.99;
+ D2.y = 3.99;
+ s.set_d2(D2);
+
+ Double3 D3 = s.get_d3();
+ if (D3.x != 1.0 || D3.y != 2.0 || D3.z != 3.0) {
+ return false;
+ }
+ D3.x = 2.99;
+ D3.y = 3.99;
+ D3.z = 4.99;
+ s.set_d3(D3);
+
+ Double4 D4 = s.get_d4();
+ if (D4.x != 1.0 || D4.y != 2.0 || D4.z != 3.0 || D4.w != 4.0) {
+ return false;
+ }
+ D4.x = 2.99;
+ D4.y = 3.99;
+ D4.z = 4.99;
+ D4.w = 5.99;
+ s.set_d4(D4);
+
+ Byte2 B2 = s.get_i8_2();
+ if (B2.x != 1 || B2.y != 2) {
+ return false;
+ }
+ B2.x = 2;
+ B2.y = 3;
+ s.set_i8_2(B2);
+
+ Byte3 B3 = s.get_i8_3();
+ if (B3.x != 1 || B3.y != 2 || B3.z != 3) {
+ return false;
+ }
+ B3.x = 2;
+ B3.y = 3;
+ B3.z = 4;
+ s.set_i8_3(B3);
+
+ Byte4 B4 = s.get_i8_4();
+ if (B4.x != 1 || B4.y != 2 || B4.z != 3 || B4.w != 4) {
+ return false;
+ }
+ B4.x = 2;
+ B4.y = 3;
+ B4.z = 4;
+ B4.w = 5;
+ s.set_i8_4(B4);
+
+ Short2 S2 = s.get_u8_2();
+ if (S2.x != 1 || S2.y != 2) {
+ return false;
+ }
+ S2.x = 2;
+ S2.y = 3;
+ s.set_u8_2(S2);
+
+ Short3 S3 = s.get_u8_3();
+ if (S3.x != 1 || S3.y != 2 || S3.z != 3) {
+ return false;
+ }
+ S3.x = 2;
+ S3.y = 3;
+ S3.z = 4;
+ s.set_u8_3(S3);
+
+ Short4 S4 = s.get_u8_4();
+ if (S4.x != 1 || S4.y != 2 || S4.z != 3 || S4.w != 4) {
+ return false;
+ }
+ S4.x = 2;
+ S4.y = 3;
+ S4.z = 4;
+ S4.w = 5;
+ s.set_u8_4(S4);
+
+ S2 = s.get_i16_2();
+ if (S2.x != 1 || S2.y != 2) {
+ return false;
+ }
+ S2.x = 2;
+ S2.y = 3;
+ s.set_i16_2(S2);
+
+ S3 = s.get_i16_3();
+ if (S3.x != 1 || S3.y != 2 || S3.z != 3) {
+ return false;
+ }
+ S3.x = 2;
+ S3.y = 3;
+ S3.z = 4;
+ s.set_i16_3(S3);
+
+ S4 = s.get_i16_4();
+ if (S4.x != 1 || S4.y != 2 || S4.z != 3 || S4.w != 4) {
+ return false;
+ }
+ S4.x = 2;
+ S4.y = 3;
+ S4.z = 4;
+ S4.w = 5;
+ s.set_i16_4(S4);
+
+ Int2 I2 = s.get_u16_2();
+ if (I2.x != 1 || I2.y != 2) {
+ return false;
+ }
+ I2.x = 2;
+ I2.y = 3;
+ s.set_u16_2(I2);
+
+ Int3 I3 = s.get_u16_3();
+ if (I3.x != 1 || I3.y != 2 || I3.z != 3) {
+ return false;
+ }
+ I3.x = 2;
+ I3.y = 3;
+ I3.z = 4;
+ s.set_u16_3(I3);
+
+ Int4 I4 = s.get_u16_4();
+ if (I4.x != 1 || I4.y != 2 || I4.z != 3 || I4.w != 4) {
+ return false;
+ }
+ I4.x = 2;
+ I4.y = 3;
+ I4.z = 4;
+ I4.w = 5;
+ s.set_u16_4(I4);
+
+ I2 = s.get_i32_2();
+ if (I2.x != 1 || I2.y != 2) {
+ return false;
+ }
+ I2.x = 2;
+ I2.y = 3;
+ s.set_i32_2(I2);
+
+ I3 = s.get_i32_3();
+ if (I3.x != 1 || I3.y != 2 || I3.z != 3) {
+ return false;
+ }
+ I3.x = 2;
+ I3.y = 3;
+ I3.z = 4;
+ s.set_i32_3(I3);
+
+ I4 = s.get_i32_4();
+ if (I4.x != 1 || I4.y != 2 || I4.z != 3 || I4.w != 4) {
+ return false;
+ }
+ I4.x = 2;
+ I4.y = 3;
+ I4.z = 4;
+ I4.w = 5;
+ s.set_i32_4(I4);
+
+ Long2 L2 = s.get_u32_2();
+ if (L2.x != 1 || L2.y != 2) {
+ return false;
+ }
+ L2.x = 2;
+ L2.y = 3;
+ s.set_u32_2(L2);
+
+ Long3 L3 = s.get_u32_3();
+ if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+ return false;
+ }
+ L3.x = 2;
+ L3.y = 3;
+ L3.z = 4;
+ s.set_u32_3(L3);
+
+ Long4 L4 = s.get_u32_4();
+ if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+ return false;
+ }
+ L4.x = 2;
+ L4.y = 3;
+ L4.z = 4;
+ L4.w = 5;
+ s.set_u32_4(L4);
+
+ L2 = s.get_i64_2();
+ if (L2.x != 1 || L2.y != 2) {
+ return false;
+ }
+ L2.x = 2;
+ L2.y = 3;
+ s.set_i64_2(L2);
+
+ L3 = s.get_i64_3();
+ if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+ return false;
+ }
+ L3.x = 2;
+ L3.y = 3;
+ L3.z = 4;
+ s.set_i64_3(L3);
+
+ L4 = s.get_i64_4();
+ if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+ return false;
+ }
+ L4.x = 2;
+ L4.y = 3;
+ L4.z = 4;
+ L4.w = 5;
+ s.set_i64_4(L4);
+
+ L2 = s.get_u64_2();
+ if (L2.x != 1 || L2.y != 2) {
+ return false;
+ }
+ L2.x = 2;
+ L2.y = 3;
+ s.set_u64_2(L2);
+
+ L3 = s.get_u64_3();
+ if (L3.x != 1 || L3.y != 2 || L3.z != 3) {
+ return false;
+ }
+ L3.x = 2;
+ L3.y = 3;
+ L3.z = 4;
+ s.set_u64_3(L3);
+
+ L4 = s.get_u64_4();
+ if (L4.x != 1 || L4.y != 2 || L4.z != 3 || L4.w != 4) {
+ return false;
+ }
+ L4.x = 2;
+ L4.y = 3;
+ L4.z = 4;
+ L4.w = 5;
+ s.set_u64_4(L4);
+
+ return true;
+ }
+
+ public void run() {
+ RenderScript pRS = RenderScript.create(mCtx);
+ ScriptC_vector s = new ScriptC_vector(pRS, mRes, R.raw.vector);
+ pRS.setMessageHandler(mRsMessage);
+ if (!initializeGlobals(s)) {
+ result = -1;
+ } else {
+ s.invoke_vector_test();
+ pRS.finish();
+ waitForMessage();
+ }
+ pRS.destroy();
+ }
+}
diff --git a/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs b/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs
new file mode 100644
index 0000000..0430a2f
--- /dev/null
+++ b/tests/RenderScriptTests/tests/src/com/android/rs/test/vector.rs
@@ -0,0 +1,198 @@
+#include "shared.rsh"
+
+// Testing vector types
+float2 f2 = { 1.0f, 2.0f };
+float3 f3 = { 1.0f, 2.0f, 3.0f };
+float4 f4 = { 1.0f, 2.0f, 3.0f, 4.0f };
+
+double2 d2 = { 1.0, 2.0 };
+double3 d3 = { 1.0, 2.0, 3.0 };
+double4 d4 = { 1.0, 2.0, 3.0, 4.0 };
+
+char2 i8_2 = { 1, 2 };
+char3 i8_3 = { 1, 2, 3 };
+char4 i8_4 = { 1, 2, 3, 4 };
+
+uchar2 u8_2 = { 1, 2 };
+uchar3 u8_3 = { 1, 2, 3 };
+uchar4 u8_4 = { 1, 2, 3, 4 };
+
+short2 i16_2 = { 1, 2 };
+short3 i16_3 = { 1, 2, 3 };
+short4 i16_4 = { 1, 2, 3, 4 };
+
+ushort2 u16_2 = { 1, 2 };
+ushort3 u16_3 = { 1, 2, 3 };
+ushort4 u16_4 = { 1, 2, 3, 4 };
+
+int2 i32_2 = { 1, 2 };
+int3 i32_3 = { 1, 2, 3 };
+int4 i32_4 = { 1, 2, 3, 4 };
+
+uint2 u32_2 = { 1, 2 };
+uint3 u32_3 = { 1, 2, 3 };
+uint4 u32_4 = { 1, 2, 3, 4 };
+
+long2 i64_2 = { 1, 2 };
+long3 i64_3 = { 1, 2, 3 };
+long4 i64_4 = { 1, 2, 3, 4 };
+
+ulong2 u64_2 = { 1, 2 };
+ulong3 u64_3 = { 1, 2, 3 };
+ulong4 u64_4 = { 1, 2, 3, 4 };
+
+static bool test_vector_types() {
+ bool failed = false;
+
+ rsDebug("Testing F32", 0);
+ _RS_ASSERT(f2.x == 2.99f);
+ _RS_ASSERT(f2.y == 3.99f);
+
+ _RS_ASSERT(f3.x == 2.99f);
+ _RS_ASSERT(f3.y == 3.99f);
+ _RS_ASSERT(f3.z == 4.99f);
+
+ _RS_ASSERT(f4.x == 2.99f);
+ _RS_ASSERT(f4.y == 3.99f);
+ _RS_ASSERT(f4.z == 4.99f);
+ _RS_ASSERT(f4.w == 5.99f);
+
+ rsDebug("Testing F64", 0);
+ _RS_ASSERT(d2.x == 2.99);
+ _RS_ASSERT(d2.y == 3.99);
+
+ _RS_ASSERT(d3.x == 2.99);
+ _RS_ASSERT(d3.y == 3.99);
+ _RS_ASSERT(d3.z == 4.99);
+
+ _RS_ASSERT(d4.x == 2.99);
+ _RS_ASSERT(d4.y == 3.99);
+ _RS_ASSERT(d4.z == 4.99);
+ _RS_ASSERT(d4.w == 5.99);
+
+ rsDebug("Testing I8", 0);
+ _RS_ASSERT(i8_2.x == 2);
+ _RS_ASSERT(i8_2.y == 3);
+
+ _RS_ASSERT(i8_3.x == 2);
+ _RS_ASSERT(i8_3.y == 3);
+ _RS_ASSERT(i8_3.z == 4);
+
+ _RS_ASSERT(i8_4.x == 2);
+ _RS_ASSERT(i8_4.y == 3);
+ _RS_ASSERT(i8_4.z == 4);
+ _RS_ASSERT(i8_4.w == 5);
+
+ rsDebug("Testing U8", 0);
+ _RS_ASSERT(u8_2.x == 2);
+ _RS_ASSERT(u8_2.y == 3);
+
+ _RS_ASSERT(u8_3.x == 2);
+ _RS_ASSERT(u8_3.y == 3);
+ _RS_ASSERT(u8_3.z == 4);
+
+ _RS_ASSERT(u8_4.x == 2);
+ _RS_ASSERT(u8_4.y == 3);
+ _RS_ASSERT(u8_4.z == 4);
+ _RS_ASSERT(u8_4.w == 5);
+
+ rsDebug("Testing I16", 0);
+ _RS_ASSERT(i16_2.x == 2);
+ _RS_ASSERT(i16_2.y == 3);
+
+ _RS_ASSERT(i16_3.x == 2);
+ _RS_ASSERT(i16_3.y == 3);
+ _RS_ASSERT(i16_3.z == 4);
+
+ _RS_ASSERT(i16_4.x == 2);
+ _RS_ASSERT(i16_4.y == 3);
+ _RS_ASSERT(i16_4.z == 4);
+ _RS_ASSERT(i16_4.w == 5);
+
+ rsDebug("Testing U16", 0);
+ _RS_ASSERT(u16_2.x == 2);
+ _RS_ASSERT(u16_2.y == 3);
+
+ _RS_ASSERT(u16_3.x == 2);
+ _RS_ASSERT(u16_3.y == 3);
+ _RS_ASSERT(u16_3.z == 4);
+
+ _RS_ASSERT(u16_4.x == 2);
+ _RS_ASSERT(u16_4.y == 3);
+ _RS_ASSERT(u16_4.z == 4);
+ _RS_ASSERT(u16_4.w == 5);
+
+ rsDebug("Testing I32", 0);
+ _RS_ASSERT(i32_2.x == 2);
+ _RS_ASSERT(i32_2.y == 3);
+
+ _RS_ASSERT(i32_3.x == 2);
+ _RS_ASSERT(i32_3.y == 3);
+ _RS_ASSERT(i32_3.z == 4);
+
+ _RS_ASSERT(i32_4.x == 2);
+ _RS_ASSERT(i32_4.y == 3);
+ _RS_ASSERT(i32_4.z == 4);
+ _RS_ASSERT(i32_4.w == 5);
+
+ rsDebug("Testing U32", 0);
+ _RS_ASSERT(u32_2.x == 2);
+ _RS_ASSERT(u32_2.y == 3);
+
+ _RS_ASSERT(u32_3.x == 2);
+ _RS_ASSERT(u32_3.y == 3);
+ _RS_ASSERT(u32_3.z == 4);
+
+ _RS_ASSERT(u32_4.x == 2);
+ _RS_ASSERT(u32_4.y == 3);
+ _RS_ASSERT(u32_4.z == 4);
+ _RS_ASSERT(u32_4.w == 5);
+
+ rsDebug("Testing I64", 0);
+ _RS_ASSERT(i64_2.x == 2);
+ _RS_ASSERT(i64_2.y == 3);
+
+ _RS_ASSERT(i64_3.x == 2);
+ _RS_ASSERT(i64_3.y == 3);
+ _RS_ASSERT(i64_3.z == 4);
+
+ _RS_ASSERT(i64_4.x == 2);
+ _RS_ASSERT(i64_4.y == 3);
+ _RS_ASSERT(i64_4.z == 4);
+ _RS_ASSERT(i64_4.w == 5);
+
+ rsDebug("Testing U64", 0);
+ _RS_ASSERT(u64_2.x == 2);
+ _RS_ASSERT(u64_2.y == 3);
+
+ _RS_ASSERT(u64_3.x == 2);
+ _RS_ASSERT(u64_3.y == 3);
+ _RS_ASSERT(u64_3.z == 4);
+
+ _RS_ASSERT(u64_4.x == 2);
+ _RS_ASSERT(u64_4.y == 3);
+ _RS_ASSERT(u64_4.z == 4);
+ _RS_ASSERT(u64_4.w == 5);
+
+ if (failed) {
+ rsDebug("test_vector FAILED", 0);
+ }
+ else {
+ rsDebug("test_vector PASSED", 0);
+ }
+
+ return failed;
+}
+
+void vector_test() {
+ bool failed = false;
+ failed |= test_vector_types();
+
+ if (failed) {
+ rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+ }
+ else {
+ rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+ }
+}
+