summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cleanspec.mk1
-rw-r--r--common/Android.mk29
-rw-r--r--common/java/com/android/common/OperationScheduler.java297
-rw-r--r--common/java/com/android/common/Patterns.java209
-rw-r--r--common/tests/Android.mk26
-rw-r--r--common/tests/AndroidManifest.xml30
-rw-r--r--common/tests/src/com/android/common/OperationSchedulerTest.java116
-rw-r--r--common/tests/src/com/android/common/PatternsTest.java123
-rw-r--r--opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java175
-rw-r--r--opengl/tests/gldual/Android.mk51
-rw-r--r--opengl/tests/gldual/AndroidManifest.xml35
-rw-r--r--opengl/tests/gldual/jni/gl_code.cpp165
-rw-r--r--opengl/tests/gldual/res/layout/gldual_activity.xml30
-rw-r--r--opengl/tests/gldual/res/values/strings.xml29
-rw-r--r--opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java53
-rw-r--r--opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java299
-rw-r--r--opengl/tests/gldual/src/com/android/gldual/GLDualLib.java33
-rw-r--r--opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java149
18 files changed, 1841 insertions, 9 deletions
diff --git a/cleanspec.mk b/cleanspec.mk
new file mode 100644
index 0000000000..683e303ecb
--- /dev/null
+++ b/cleanspec.mk
@@ -0,0 +1 @@
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/os/IDropBoxService.java)
diff --git a/common/Android.mk b/common/Android.mk
new file mode 100644
index 0000000000..76091ebf7f
--- /dev/null
+++ b/common/Android.mk
@@ -0,0 +1,29 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+# Note: the source code is in java/, not src/, because this code is also part of
+# the framework library, and build/core/pathmap.mk expects a java/ subdirectory.
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := android-common
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+# Include this library in the build server's output directory
+$(call dist-for-goals, droid, $(LOCAL_BUILT_MODULE):android-common.jar)
+
+# Build the test package
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java
new file mode 100644
index 0000000000..71b22ce63e
--- /dev/null
+++ b/common/java/com/android/common/OperationScheduler.java
@@ -0,0 +1,297 @@
+/*
+ * 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.common;
+
+import android.content.SharedPreferences;
+import android.text.format.Time;
+
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * Tracks the success/failure history of a particular network operation in
+ * persistent storage and computes retry strategy accordingly. Handles
+ * exponential backoff, periodic rescheduling, event-driven triggering,
+ * retry-after moratorium intervals, etc. based on caller-specified parameters.
+ *
+ * <p>This class does not directly perform or invoke any operations,
+ * it only keeps track of the schedule. Somebody else needs to call
+ * {@link #getNextTimeMillis()} as appropriate and do the actual work.
+ */
+public class OperationScheduler {
+ /** Tunable parameter options for {@link #getNextTimeMillis}. */
+ public static class Options {
+ /** Wait this long after every error before retrying. */
+ public long backoffFixedMillis = 0;
+
+ /** Wait this long times the number of consecutive errors so far before retrying. */
+ public long backoffIncrementalMillis = 5000;
+
+ /** Maximum duration of moratorium to honor. Mostly an issue for clock rollbacks. */
+ public long maxMoratoriumMillis = 24 * 3600 * 1000;
+
+ /** Minimum duration after success to wait before allowing another trigger. */
+ public long minTriggerMillis = 0;
+
+ /** Automatically trigger this long after the last success. */
+ public long periodicIntervalMillis = 0;
+
+ @Override
+ public String toString() {
+ return String.format(
+ "OperationScheduler.Options[backoff=%.1f+%.1f max=%.1f min=%.1f period=%.1f]",
+ backoffFixedMillis / 1000.0, backoffIncrementalMillis / 1000.0,
+ maxMoratoriumMillis / 1000.0, minTriggerMillis / 1000.0,
+ periodicIntervalMillis / 1000.0);
+ }
+ }
+
+ private static final String PREFIX = "OperationScheduler_";
+ private final SharedPreferences mStorage;
+
+ /**
+ * Initialize the scheduler state.
+ * @param storage to use for recording the state of operations across restarts/reboots
+ */
+ public OperationScheduler(SharedPreferences storage) {
+ mStorage = storage;
+ }
+
+ /**
+ * Parse scheduler options supplied in this string form:
+ *
+ * <pre>
+ * backoff=(fixed)+(incremental) max=(maxmoratorium) min=(mintrigger) [period=](interval)
+ * </pre>
+ *
+ * All values are times in (possibly fractional) <em>seconds</em> (not milliseconds).
+ * Omitted settings are left at whatever existing default value was passed in.
+ *
+ * <p>
+ * The default options: <code>backoff=0+5 max=86400 min=0 period=0</code><br>
+ * Fractions are OK: <code>backoff=+2.5 period=10.0</code><br>
+ * The "period=" can be omitted: <code>3600</code><br>
+ *
+ * @param spec describing some or all scheduler options.
+ * @param options to update with parsed values.
+ * @return the options passed in (for convenience)
+ * @throws IllegalArgumentException if the syntax is invalid
+ */
+ public static Options parseOptions(String spec, Options options)
+ throws IllegalArgumentException {
+ for (String param : spec.split(" +")) {
+ if (param.length() == 0) continue;
+ if (param.startsWith("backoff=")) {
+ int plus = param.indexOf('+', 8);
+ if (plus < 0) {
+ options.backoffFixedMillis = parseSeconds(param.substring(8));
+ } else {
+ if (plus > 8) {
+ options.backoffFixedMillis = parseSeconds(param.substring(8, plus));
+ }
+ options.backoffIncrementalMillis = parseSeconds(param.substring(plus + 1));
+ }
+ } else if (param.startsWith("max=")) {
+ options.maxMoratoriumMillis = parseSeconds(param.substring(4));
+ } else if (param.startsWith("min=")) {
+ options.minTriggerMillis = parseSeconds(param.substring(4));
+ } else if (param.startsWith("period=")) {
+ options.periodicIntervalMillis = parseSeconds(param.substring(7));
+ } else {
+ options.periodicIntervalMillis = parseSeconds(param);
+ }
+ }
+ return options;
+ }
+
+ private static long parseSeconds(String param) throws NumberFormatException {
+ return (long) (Float.parseFloat(param) * 1000);
+ }
+
+ /**
+ * Compute the time of the next operation. Does not modify any state.
+ *
+ * @param options to use for this computation.
+ * @return the wall clock time ({@link System#currentTimeMillis()}) when the
+ * next operation should be attempted -- immediately, if the return value is
+ * before the current time.
+ */
+ public long getNextTimeMillis(Options options) {
+ boolean enabledState = mStorage.getBoolean(PREFIX + "enabledState", true);
+ if (!enabledState) return Long.MAX_VALUE;
+
+ boolean permanentError = mStorage.getBoolean(PREFIX + "permanentError", false);
+ if (permanentError) return Long.MAX_VALUE;
+
+ // We do quite a bit of limiting to prevent a clock rollback from totally
+ // hosing the scheduler. Times which are supposed to be in the past are
+ // clipped to the current time so we don't languish forever.
+
+ int errorCount = mStorage.getInt(PREFIX + "errorCount", 0);
+ long now = System.currentTimeMillis();
+ long lastSuccessTimeMillis = getTimeBefore(PREFIX + "lastSuccessTimeMillis", now);
+ long lastErrorTimeMillis = getTimeBefore(PREFIX + "lastErrorTimeMillis", now);
+ long triggerTimeMillis = mStorage.getLong(PREFIX + "triggerTimeMillis", Long.MAX_VALUE);
+ long moratoriumSetMillis = mStorage.getLong(PREFIX + "moratoriumSetTimeMillis", 0);
+ long moratoriumTimeMillis = getTimeBefore(PREFIX + "moratoriumTimeMillis",
+ moratoriumSetMillis + options.maxMoratoriumMillis);
+
+ long time = triggerTimeMillis;
+ if (options.periodicIntervalMillis > 0) {
+ time = Math.min(time, lastSuccessTimeMillis + options.periodicIntervalMillis);
+ }
+ if (time >= moratoriumTimeMillis - options.maxMoratoriumMillis) {
+ time = Math.max(time, moratoriumTimeMillis);
+ }
+ time = Math.max(time, lastSuccessTimeMillis + options.minTriggerMillis);
+ time = Math.max(time, lastErrorTimeMillis + options.backoffFixedMillis +
+ options.backoffIncrementalMillis * errorCount);
+ return time;
+ }
+
+ /**
+ * Fetch a {@link SharedPreferences} property, but force it to be before
+ * a certain time, updating the value if necessary. This is to recover
+ * gracefully from clock rollbacks which could otherwise strand our timers.
+ *
+ * @param name of SharedPreferences key
+ * @param max time to allow in result
+ * @return current value attached to key (default 0), limited by max
+ */
+ private long getTimeBefore(String name, long max) {
+ long time = mStorage.getLong(name, 0);
+ if (time > max) mStorage.edit().putLong(name, (time = max)).commit();
+ return time;
+ }
+
+ /**
+ * Request an operation to be performed at a certain time. The actual
+ * scheduled time may be affected by error backoff logic and defined
+ * minimum intervals.
+ *
+ * @param millis wall clock time ({@link System#currentTimeMillis()}) to
+ * trigger another operation; 0 to trigger immediately
+ */
+ public void setTriggerTimeMillis(long millis) {
+ mStorage.edit().putLong(PREFIX + "triggerTimeMillis", millis).commit();
+ }
+
+ /**
+ * Forbid any operations until after a certain (absolute) time.
+ * Commonly used when a server returns a "Retry-After:" type directive.
+ * Limited by {@link #Options.maxMoratoriumMillis}.
+ *
+ * @param millis wall clock time ({@link System#currentTimeMillis()}) to
+ * wait before attempting any more operations; 0 to remove moratorium
+ */
+ public void setMoratoriumTimeMillis(long millis) {
+ mStorage.edit()
+ .putLong(PREFIX + "moratoriumTimeMillis", millis)
+ .putLong(PREFIX + "moratoriumSetTimeMillis", System.currentTimeMillis())
+ .commit();
+ }
+
+ /**
+ * Enable or disable all operations. When disabled, all calls to
+ * {@link #getNextTimeMillis()} return {@link Long#MAX_VALUE}.
+ * Commonly used when data network availability goes up and down.
+ *
+ * @param enabled if operations can be performed
+ */
+ public void setEnabledState(boolean enabled) {
+ mStorage.edit().putBoolean(PREFIX + "enabledState", enabled).commit();
+ }
+
+ /**
+ * Report successful completion of an operation. Resets all error
+ * counters, clears any trigger directives, and records the success.
+ */
+ public void onSuccess() {
+ resetTransientError();
+ resetPermanentError();
+ long now = System.currentTimeMillis();
+ mStorage.edit()
+ .remove(PREFIX + "errorCount")
+ .remove(PREFIX + "lastErrorTimeMillis")
+ .remove(PREFIX + "permanentError")
+ .remove(PREFIX + "triggerTimeMillis")
+ .putLong(PREFIX + "lastSuccessTimeMillis", now).commit();
+ }
+
+ /**
+ * Report a transient error (usually a network failure). Increments
+ * the error count and records the time of the latest error for backoff
+ * purposes.
+ */
+ public void onTransientError() {
+ long now = System.currentTimeMillis();
+ mStorage.edit().putLong(PREFIX + "lastErrorTimeMillis", now).commit();
+ mStorage.edit().putInt(PREFIX + "errorCount",
+ mStorage.getInt(PREFIX + "errorCount", 0) + 1).commit();
+ }
+
+ /**
+ * Reset all transient error counts, allowing the next operation to proceed
+ * immediately without backoff. Commonly used on network state changes, when
+ * partial progress occurs (some data received), and in other circumstances
+ * where there is reason to hope things might start working better.
+ */
+ public void resetTransientError() {
+ mStorage.edit()
+ .remove(PREFIX + "lastErrorTimeMillis")
+ .remove(PREFIX + "errorCount").commit();
+ }
+
+ /**
+ * Report a permanent error that will not go away until further notice.
+ * No operation will be scheduled until {@link #resetPermanentError()}
+ * is called. Commonly used for authentication failures (which are reset
+ * when the accounts database is updated).
+ */
+ public void onPermanentError() {
+ mStorage.edit().putBoolean(PREFIX + "permanentError", true).commit();
+ }
+
+ /**
+ * Reset any permanent error status set by {@link #onPermanentError},
+ * allowing operations to be scheduled as normal.
+ */
+ public void resetPermanentError() {
+ mStorage.edit().remove(PREFIX + "permanentError").commit();
+ }
+
+ /**
+ * Return a string description of the scheduler state for debugging.
+ */
+ public String toString() {
+ StringBuilder out = new StringBuilder("[OperationScheduler:");
+ for (String key : new TreeSet<String>(mStorage.getAll().keySet())) { // Sort keys
+ if (key.startsWith(PREFIX)) {
+ if (key.endsWith("TimeMillis")) {
+ Time time = new Time();
+ time.set(mStorage.getLong(key, 0));
+ out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10));
+ out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S"));
+ } else {
+ out.append(" ").append(key.substring(PREFIX.length()));
+ out.append("=").append(mStorage.getAll().get(key).toString());
+ }
+ }
+ }
+ return out.append("]").toString();
+ }
+}
diff --git a/common/java/com/android/common/Patterns.java b/common/java/com/android/common/Patterns.java
new file mode 100644
index 0000000000..2eab3e1089
--- /dev/null
+++ b/common/java/com/android/common/Patterns.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2007 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.common;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Commonly used regular expression patterns.
+ */
+public class Patterns {
+ /**
+ * Regular expression pattern to match all IANA top-level domains.
+ * List accurate as of 2007/06/15. List taken from:
+ * http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+ * This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py
+ */
+ public static final Pattern TOP_LEVEL_DOMAIN
+ = Pattern.compile(
+ "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+ + "|(biz|b[abdefghijmnorstvwyz])"
+ + "|(cat|com|coop|c[acdfghiklmnoruvxyz])"
+ + "|d[ejkmoz]"
+ + "|(edu|e[cegrstu])"
+ + "|f[ijkmor]"
+ + "|(gov|g[abdefghilmnpqrstuwy])"
+ + "|h[kmnrtu]"
+ + "|(info|int|i[delmnoqrst])"
+ + "|(jobs|j[emop])"
+ + "|k[eghimnrwyz]"
+ + "|l[abcikrstuvy]"
+ + "|(mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
+ + "|(name|net|n[acefgilopruz])"
+ + "|(org|om)"
+ + "|(pro|p[aefghklmnrstwy])"
+ + "|qa"
+ + "|r[eouw]"
+ + "|s[abcdeghijklmnortuvyz]"
+ + "|(tel|travel|t[cdfghjklmnoprtvwz])"
+ + "|u[agkmsyz]"
+ + "|v[aceginu]"
+ + "|w[fs]"
+ + "|y[etu]"
+ + "|z[amw])");
+
+ /**
+ * Regular expression pattern to match RFC 1738 URLs
+ * List accurate as of 2007/06/15. List taken from:
+ * http://data.iana.org/TLD/tlds-alpha-by-domain.txt
+ * This pattern is auto-generated by //device/tools/make-iana-tld-pattern.py
+ */
+ public static final Pattern WEB_URL
+ = Pattern.compile(
+ "((?:(http|https|Http|Https|rtsp|Rtsp):\\/\\/(?:(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)"
+ + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_"
+ + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@)?)?"
+ + "((?:(?:[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}\\.)+" // named host
+ + "(?:" // plus top level domain
+ + "(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])"
+ + "|(?:biz|b[abdefghijmnorstvwyz])"
+ + "|(?:cat|com|coop|c[acdfghiklmnoruvxyz])"
+ + "|d[ejkmoz]"
+ + "|(?:edu|e[cegrstu])"
+ + "|f[ijkmor]"
+ + "|(?:gov|g[abdefghilmnpqrstuwy])"
+ + "|h[kmnrtu]"
+ + "|(?:info|int|i[delmnoqrst])"
+ + "|(?:jobs|j[emop])"
+ + "|k[eghimnrwyz]"
+ + "|l[abcikrstuvy]"
+ + "|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])"
+ + "|(?:name|net|n[acefgilopruz])"
+ + "|(?:org|om)"
+ + "|(?:pro|p[aefghklmnrstwy])"
+ + "|qa"
+ + "|r[eouw]"
+ + "|s[abcdeghijklmnortuvyz]"
+ + "|(?:tel|travel|t[cdfghjklmnoprtvwz])"
+ + "|u[agkmsyz]"
+ + "|v[aceginu]"
+ + "|w[fs]"
+ + "|y[etu]"
+ + "|z[amw]))"
+ + "|(?:(?:25[0-5]|2[0-4]" // or ip address
+ + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]"
+ + "|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1]"
+ + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ + "|[1-9][0-9]|[0-9])))"
+ + "(?:\\:\\d{1,5})?)" // plus option port number
+ + "(\\/(?:(?:[a-zA-Z0-9\\;\\/\\?\\:\\@\\&\\=\\#\\~" // plus option query params
+ + "\\-\\.\\+\\!\\*\\'\\(\\)\\,\\_])|(?:\\%[a-fA-F0-9]{2}))*)?"
+ + "(?:\\b|$)"); // and finally, a word boundary or end of
+ // input. This is to stop foo.sure from
+ // matching as foo.su
+
+ public static final Pattern IP_ADDRESS
+ = Pattern.compile(
+ "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+ + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+ + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
+ + "|[1-9][0-9]|[0-9]))");
+
+ public static final Pattern DOMAIN_NAME
+ = Pattern.compile(
+ "(((([a-zA-Z0-9][a-zA-Z0-9\\-]*)*[a-zA-Z0-9]\\.)+"
+ + TOP_LEVEL_DOMAIN + ")|"
+ + IP_ADDRESS + ")");
+
+ public static final Pattern EMAIL_ADDRESS
+ = Pattern.compile(
+ "[a-zA-Z0-9\\+\\.\\_\\%\\-]{1,256}" +
+ "\\@" +
+ "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
+ "(" +
+ "\\." +
+ "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" +
+ ")+"
+ );
+
+ /**
+ * This pattern is intended for searching for things that look like they
+ * might be phone numbers in arbitrary text, not for validating whether
+ * something is in fact a phone number. It will miss many things that
+ * are legitimate phone numbers.
+ *
+ * <p> The pattern matches the following:
+ * <ul>
+ * <li>Optionally, a + sign followed immediately by one or more digits. Spaces, dots, or dashes
+ * may follow.
+ * <li>Optionally, sets of digits in parentheses, separated by spaces, dots, or dashes.
+ * <li>A string starting and ending with a digit, containing digits, spaces, dots, and/or dashes.
+ * </ul>
+ */
+ public static final Pattern PHONE
+ = Pattern.compile( // sdd = space, dot, or dash
+ "(\\+[0-9]+[\\- \\.]*)?" // +<digits><sdd>*
+ + "(\\([0-9]+\\)[\\- \\.]*)?" // (<digits>)<sdd>*
+ + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>
+
+ /**
+ * Convenience method to take all of the non-null matching groups in a
+ * regex Matcher and return them as a concatenated string.
+ *
+ * @param matcher The Matcher object from which grouped text will
+ * be extracted
+ *
+ * @return A String comprising all of the non-null matched
+ * groups concatenated together
+ */
+ public static final String concatGroups(Matcher matcher) {
+ StringBuilder b = new StringBuilder();
+ final int numGroups = matcher.groupCount();
+
+ for (int i = 1; i <= numGroups; i++) {
+ String s = matcher.group(i);
+
+ System.err.println("Group(" + i + ") : " + s);
+
+ if (s != null) {
+ b.append(s);
+ }
+ }
+
+ return b.toString();
+ }
+
+ /**
+ * Convenience method to return only the digits and plus signs
+ * in the matching string.
+ *
+ * @param matcher The Matcher object from which digits and plus will
+ * be extracted
+ *
+ * @return A String comprising all of the digits and plus in
+ * the match
+ */
+ public static final String digitsAndPlusOnly(Matcher matcher) {
+ StringBuilder buffer = new StringBuilder();
+ String matchingRegion = matcher.group();
+
+ for (int i = 0, size = matchingRegion.length(); i < size; i++) {
+ char character = matchingRegion.charAt(i);
+
+ if (character == '+' || Character.isDigit(character)) {
+ buffer.append(character);
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Do not create this static utility class.
+ */
+ private Patterns() {}
+}
diff --git a/common/tests/Android.mk b/common/tests/Android.mk
new file mode 100644
index 0000000000..0f2c3e4688
--- /dev/null
+++ b/common/tests/Android.mk
@@ -0,0 +1,26 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_MODULE_TAGS := tests
+LOCAL_PACKAGE_NAME := AndroidCommonTests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := android-common
+
+include $(BUILD_PACKAGE)
diff --git a/common/tests/AndroidManifest.xml b/common/tests/AndroidManifest.xml
new file mode 100644
index 0000000000..151ec20ebe
--- /dev/null
+++ b/common/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.common.tests"
+ android:sharedUserId="com.android.uid.test">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- Run tests with "runtest common" -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.common.tests"
+ android:label="Android Common Library Tests" />
+
+</manifest>
diff --git a/common/tests/src/com/android/common/OperationSchedulerTest.java b/common/tests/src/com/android/common/OperationSchedulerTest.java
new file mode 100644
index 0000000000..13f710d13f
--- /dev/null
+++ b/common/tests/src/com/android/common/OperationSchedulerTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.common;
+
+import android.content.SharedPreferences;
+import android.test.AndroidTestCase;
+
+public class OperationSchedulerTest extends AndroidTestCase {
+ public void testScheduler() throws Exception {
+ String name = "OperationSchedulerTest.testScheduler";
+ SharedPreferences storage = getContext().getSharedPreferences(name, 0);
+ storage.edit().clear().commit();
+
+ OperationScheduler scheduler = new OperationScheduler(storage);
+ OperationScheduler.Options options = new OperationScheduler.Options();
+ assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+
+ long beforeTrigger = System.currentTimeMillis();
+ scheduler.setTriggerTimeMillis(beforeTrigger + 1000000);
+ assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
+
+ // It will schedule for the later of the trigger and the moratorium...
+ scheduler.setMoratoriumTimeMillis(beforeTrigger + 500000);
+ assertEquals(beforeTrigger + 1000000, scheduler.getNextTimeMillis(options));
+ scheduler.setMoratoriumTimeMillis(beforeTrigger + 1500000);
+ assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+
+ // Test enable/disable toggle
+ scheduler.setEnabledState(false);
+ assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+ scheduler.setEnabledState(true);
+ assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+
+ // Backoff interval after an error
+ long beforeError = System.currentTimeMillis();
+ scheduler.onTransientError();
+ long afterError = System.currentTimeMillis();
+ assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+ options.backoffFixedMillis = 1000000;
+ options.backoffIncrementalMillis = 500000;
+ assertTrue(beforeError + 1500000 <= scheduler.getNextTimeMillis(options));
+ assertTrue(afterError + 1500000 >= scheduler.getNextTimeMillis(options));
+
+ // Two errors: backoff interval increases
+ beforeError = System.currentTimeMillis();
+ scheduler.onTransientError();
+ afterError = System.currentTimeMillis();
+ assertTrue(beforeError + 2000000 <= scheduler.getNextTimeMillis(options));
+ assertTrue(afterError + 2000000 >= scheduler.getNextTimeMillis(options));
+
+ // Permanent error holds true even if transient errors are reset
+ // However, we remember that the transient error was reset...
+ scheduler.onPermanentError();
+ assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+ scheduler.resetTransientError();
+ assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+ scheduler.resetPermanentError();
+ assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+
+ // Success resets the trigger
+ long beforeSuccess = System.currentTimeMillis();
+ scheduler.onSuccess();
+ long afterSuccess = System.currentTimeMillis();
+ assertEquals(Long.MAX_VALUE, scheduler.getNextTimeMillis(options));
+
+ // The moratorium is not reset by success!
+ scheduler.setTriggerTimeMillis(beforeSuccess + 500000);
+ assertEquals(beforeTrigger + 1500000, scheduler.getNextTimeMillis(options));
+ scheduler.setMoratoriumTimeMillis(0);
+ assertEquals(beforeSuccess + 500000, scheduler.getNextTimeMillis(options));
+
+ // Periodic interval after success
+ options.periodicIntervalMillis = 250000;
+ assertTrue(beforeSuccess + 250000 <= scheduler.getNextTimeMillis(options));
+ assertTrue(afterSuccess + 250000 >= scheduler.getNextTimeMillis(options));
+
+ // Trigger minimum is also since the last success
+ options.minTriggerMillis = 1000000;
+ assertTrue(beforeSuccess + 1000000 <= scheduler.getNextTimeMillis(options));
+ assertTrue(afterSuccess + 1000000 >= scheduler.getNextTimeMillis(options));
+ }
+
+ public void testParseOptions() throws Exception {
+ OperationScheduler.Options options = new OperationScheduler.Options();
+ assertEquals(
+ "OperationScheduler.Options[backoff=0.0+5.0 max=86400.0 min=0.0 period=3600.0]",
+ OperationScheduler.parseOptions("3600", options).toString());
+
+ assertEquals(
+ "OperationScheduler.Options[backoff=0.0+2.5 max=86400.0 min=0.0 period=3700.0]",
+ OperationScheduler.parseOptions("backoff=+2.5 3700", options).toString());
+
+ assertEquals(
+ "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
+ OperationScheduler.parseOptions("max=12345.6 min=7 backoff=10 period=3800",
+ options).toString());
+
+ assertEquals(
+ "OperationScheduler.Options[backoff=10.0+2.5 max=12345.6 min=7.0 period=3800.0]",
+ OperationScheduler.parseOptions("", options).toString());
+ }
+}
diff --git a/common/tests/src/com/android/common/PatternsTest.java b/common/tests/src/com/android/common/PatternsTest.java
new file mode 100644
index 0000000000..7fabe5e275
--- /dev/null
+++ b/common/tests/src/com/android/common/PatternsTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2007 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.common;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class PatternsTest extends TestCase {
+
+ @SmallTest
+ public void testTldPattern() throws Exception {
+ boolean t;
+
+ t = Patterns.TOP_LEVEL_DOMAIN.matcher("com").matches();
+ assertTrue("Missed valid TLD", t);
+
+ t = Patterns.TOP_LEVEL_DOMAIN.matcher("xer").matches();
+ assertFalse("Matched invalid TLD!", t);
+ }
+
+ @SmallTest
+ public void testUrlPattern() throws Exception {
+ boolean t;
+
+ t = Patterns.WEB_URL.matcher("http://www.google.com").matches();
+ assertTrue("Valid URL", t);
+
+ t = Patterns.WEB_URL.matcher("ftp://www.example.com").matches();
+ assertFalse("Matched invalid protocol", t);
+
+ t = Patterns.WEB_URL.matcher("http://www.example.com:8080").matches();
+ assertTrue("Didn't match valid URL with port", t);
+
+ t = Patterns.WEB_URL.matcher("http://www.example.com:8080/?foo=bar").matches();
+ assertTrue("Didn't match valid URL with port and query args", t);
+
+ t = Patterns.WEB_URL.matcher("http://www.example.com:8080/~user/?foo=bar").matches();
+ assertTrue("Didn't match valid URL with ~", t);
+ }
+
+ @SmallTest
+ public void testIpPattern() throws Exception {
+ boolean t;
+
+ t = Patterns.IP_ADDRESS.matcher("172.29.86.3").matches();
+ assertTrue("Valid IP", t);
+
+ t = Patterns.IP_ADDRESS.matcher("1234.4321.9.9").matches();
+ assertFalse("Invalid IP", t);
+ }
+
+ @SmallTest
+ public void testDomainPattern() throws Exception {
+ boolean t;
+
+ t = Patterns.DOMAIN_NAME.matcher("mail.example.com").matches();
+ assertTrue("Valid domain", t);
+
+ t = Patterns.DOMAIN_NAME.matcher("__+&42.xer").matches();
+ assertFalse("Invalid domain", t);
+ }
+
+ @SmallTest
+ public void testPhonePattern() throws Exception {
+ boolean t;
+
+ t = Patterns.PHONE.matcher("(919) 555-1212").matches();
+ assertTrue("Valid phone", t);
+
+ t = Patterns.PHONE.matcher("2334 9323/54321").matches();
+ assertFalse("Invalid phone", t);
+
+ String[] tests = {
+ "Me: 16505551212 this\n",
+ "Me: 6505551212 this\n",
+ "Me: 5551212 this\n",
+
+ "Me: 1-650-555-1212 this\n",
+ "Me: (650) 555-1212 this\n",
+ "Me: +1 (650) 555-1212 this\n",
+ "Me: +1-650-555-1212 this\n",
+ "Me: 650-555-1212 this\n",
+ "Me: 555-1212 this\n",
+
+ "Me: 1.650.555.1212 this\n",
+ "Me: (650) 555.1212 this\n",
+ "Me: +1 (650) 555.1212 this\n",
+ "Me: +1.650.555.1212 this\n",
+ "Me: 650.555.1212 this\n",
+ "Me: 555.1212 this\n",
+
+ "Me: 1 650 555 1212 this\n",
+ "Me: (650) 555 1212 this\n",
+ "Me: +1 (650) 555 1212 this\n",
+ "Me: +1 650 555 1212 this\n",
+ "Me: 650 555 1212 this\n",
+ "Me: 555 1212 this\n",
+ };
+
+ for (String test : tests) {
+ Matcher m = Patterns.PHONE.matcher(test);
+
+ assertTrue("Valid phone " + test, m.find());
+ }
+ }
+}
diff --git a/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java b/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java
index 2dae090950..72b1dfb9bb 100644
--- a/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java
+++ b/opengl/tests/gl2_jni/src/com/android/gl2jni/GL2JNIView.java
@@ -56,19 +56,22 @@ import javax.microedition.khronos.opengles.GL10;
*/
class GL2JNIView extends GLSurfaceView {
private static String TAG = "GL2JNIView";
- GL2JNIView(Context context) {
+
+ public GL2JNIView(Context context) {
super(context);
- init();
+ init(false, 0, 0);
}
- public GL2JNIView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
+ public GL2JNIView(Context context, boolean translucent, int depth, int stencil) {
+ super(context);
+ init(translucent, depth, stencil);
}
- private void init() {
+ private void init(boolean translucent, int depth, int stencil) {
setEGLContextFactory(new ContextFactory());
- setEGLConfigChooser(new ConfigChooser());
+ setEGLConfigChooser( translucent ?
+ new ConfigChooser(8,8,8,8, depth, stencil) :
+ new ConfigChooser(5,6,5,0, depth, stencil));
setRenderer(new Renderer());
}
@@ -105,6 +108,16 @@ class GL2JNIView extends GLSurfaceView {
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE
};
+
+ public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+ mRedSize = r;
+ mGreenSize = g;
+ mBlueSize = b;
+ mAlphaSize = a;
+ mDepthSize = depth;
+ mStencilSize = stencil;
+ }
+
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] num_config = new int[1];
@@ -112,14 +125,158 @@ class GL2JNIView extends GLSurfaceView {
int numConfigs = num_config[0];
- Log.w(TAG, String.format("Found %d configurations", numConfigs));
if (numConfigs <= 0) {
throw new IllegalArgumentException("No configs match configSpec");
}
EGLConfig[] configs = new EGLConfig[numConfigs];
egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
- return configs[0];
+ // printConfigs(egl, display, configs);
+ return chooseConfig(egl, display, configs);
}
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private void printConfigs(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ int numConfigs = configs.length;
+ Log.w(TAG, String.format("%d configurations", numConfigs));
+ for (int i = 0; i < numConfigs; i++) {
+ Log.w(TAG, String.format("Configuration %d:\n", i));
+ printConfig(egl, display, configs[i]);
+ }
+ }
+
+ private void printConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig config) {
+ int[] attributes = {
+ EGL10.EGL_BUFFER_SIZE,
+ EGL10.EGL_ALPHA_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_RED_SIZE,
+ EGL10.EGL_DEPTH_SIZE,
+ EGL10.EGL_STENCIL_SIZE,
+ EGL10.EGL_CONFIG_CAVEAT,
+ EGL10.EGL_CONFIG_ID,
+ EGL10.EGL_LEVEL,
+ EGL10.EGL_MAX_PBUFFER_HEIGHT,
+ EGL10.EGL_MAX_PBUFFER_PIXELS,
+ EGL10.EGL_MAX_PBUFFER_WIDTH,
+ EGL10.EGL_NATIVE_RENDERABLE,
+ EGL10.EGL_NATIVE_VISUAL_ID,
+ EGL10.EGL_NATIVE_VISUAL_TYPE,
+ 0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+ EGL10.EGL_SAMPLES,
+ EGL10.EGL_SAMPLE_BUFFERS,
+ EGL10.EGL_SURFACE_TYPE,
+ EGL10.EGL_TRANSPARENT_TYPE,
+ EGL10.EGL_TRANSPARENT_RED_VALUE,
+ EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+ EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+ 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+ 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+ 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+ 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+ EGL10.EGL_LUMINANCE_SIZE,
+ EGL10.EGL_ALPHA_MASK_SIZE,
+ EGL10.EGL_COLOR_BUFFER_TYPE,
+ EGL10.EGL_RENDERABLE_TYPE,
+ 0x3042 // EGL10.EGL_CONFORMANT
+ };
+ String[] names = {
+ "EGL_BUFFER_SIZE",
+ "EGL_ALPHA_SIZE",
+ "EGL_BLUE_SIZE",
+ "EGL_GREEN_SIZE",
+ "EGL_RED_SIZE",
+ "EGL_DEPTH_SIZE",
+ "EGL_STENCIL_SIZE",
+ "EGL_CONFIG_CAVEAT",
+ "EGL_CONFIG_ID",
+ "EGL_LEVEL",
+ "EGL_MAX_PBUFFER_HEIGHT",
+ "EGL_MAX_PBUFFER_PIXELS",
+ "EGL_MAX_PBUFFER_WIDTH",
+ "EGL_NATIVE_RENDERABLE",
+ "EGL_NATIVE_VISUAL_ID",
+ "EGL_NATIVE_VISUAL_TYPE",
+ "EGL_PRESERVED_RESOURCES",
+ "EGL_SAMPLES",
+ "EGL_SAMPLE_BUFFERS",
+ "EGL_SURFACE_TYPE",
+ "EGL_TRANSPARENT_TYPE",
+ "EGL_TRANSPARENT_RED_VALUE",
+ "EGL_TRANSPARENT_GREEN_VALUE",
+ "EGL_TRANSPARENT_BLUE_VALUE",
+ "EGL_BIND_TO_TEXTURE_RGB",
+ "EGL_BIND_TO_TEXTURE_RGBA",
+ "EGL_MIN_SWAP_INTERVAL",
+ "EGL_MAX_SWAP_INTERVAL",
+ "EGL_LUMINANCE_SIZE",
+ "EGL_ALPHA_MASK_SIZE",
+ "EGL_COLOR_BUFFER_TYPE",
+ "EGL_RENDERABLE_TYPE",
+ "EGL_CONFORMANT"
+ };
+ int[] value = new int[1];
+ for (int i = 0; i < attributes.length; i++) {
+ int attribute = attributes[i];
+ String name = names[i];
+ if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+ Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
+ } else {
+ // Log.w(TAG, String.format(" %s: failed\n", name));
+ while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+ }
+ }
+ }
+
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ private int[] mValue = new int[1];
}
private static class Renderer implements GLSurfaceView.Renderer {
diff --git a/opengl/tests/gldual/Android.mk b/opengl/tests/gldual/Android.mk
new file mode 100644
index 0000000000..e73c249fef
--- /dev/null
+++ b/opengl/tests/gldual/Android.mk
@@ -0,0 +1,51 @@
+#########################################################################
+# OpenGL ES JNI sample
+# This makefile builds both an activity and a shared library.
+#########################################################################
+ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean
+
+TOP_LOCAL_PATH:= $(call my-dir)
+
+# Build activity
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := user
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := GLDual
+
+LOCAL_JNI_SHARED_LIBRARIES := libgldualjni
+
+include $(BUILD_PACKAGE)
+
+#########################################################################
+# Build JNI Shared Library
+#########################################################################
+
+LOCAL_PATH:= $(LOCAL_PATH)/jni
+
+include $(CLEAR_VARS)
+
+# Optional tag would mean it doesn't get installed by default
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS := -Werror
+
+LOCAL_SRC_FILES:= \
+ gl_code.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libutils \
+ libEGL \
+ libGLESv2
+
+LOCAL_MODULE := libgldualjni
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif # TARGET_SIMULATOR
diff --git a/opengl/tests/gldual/AndroidManifest.xml b/opengl/tests/gldual/AndroidManifest.xml
new file mode 100644
index 0000000000..06f4c4d4ee
--- /dev/null
+++ b/opengl/tests/gldual/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.gldual">
+ <application
+ android:label="@string/gldual_activity">
+ <activity android:name="GLDualActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+ android:launchMode="singleTask"
+ android:configChanges="orientation|keyboardHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/opengl/tests/gldual/jni/gl_code.cpp b/opengl/tests/gldual/jni/gl_code.cpp
new file mode 100644
index 0000000000..f1f0a1f754
--- /dev/null
+++ b/opengl/tests/gldual/jni/gl_code.cpp
@@ -0,0 +1,165 @@
+// OpenGL ES 2.0 code
+
+#include <nativehelper/jni.h>
+#define LOG_TAG "GL2JNI gl_code.cpp"
+#include <utils/Log.h>
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+static void printGLString(const char *name, GLenum s) {
+ const char *v = (const char *) glGetString(s);
+ LOGI("GL %s = %s\n", name, v);
+}
+
+static void checkGlError(const char* op) {
+ for (GLint error = glGetError(); error; error
+ = glGetError()) {
+ LOGI("after %s() glError (0x%x)\n", op, error);
+ }
+}
+
+static const char gVertexShader[] = "attribute vec4 vPosition;\n"
+ "void main() {\n"
+ " gl_Position = vPosition;\n"
+ "}\n";
+
+static const char gFragmentShader[] = "precision mediump float;\n"
+ "void main() {\n"
+ " gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
+ "}\n";
+
+GLuint loadShader(GLenum shaderType, const char* pSource) {
+ GLuint shader = glCreateShader(shaderType);
+ if (shader) {
+ glShaderSource(shader, 1, &pSource, NULL);
+ glCompileShader(shader);
+ GLint compiled = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
+ if (!compiled) {
+ GLint infoLen = 0;
+ glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen) {
+ char* buf = (char*) malloc(infoLen);
+ if (buf) {
+ glGetShaderInfoLog(shader, infoLen, NULL, buf);
+ LOGE("Could not compile shader %d:\n%s\n",
+ shaderType, buf);
+ free(buf);
+ }
+ glDeleteShader(shader);
+ shader = 0;
+ }
+ }
+ }
+ return shader;
+}
+
+GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
+ GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
+ if (!vertexShader) {
+ return 0;
+ }
+
+ GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
+ if (!pixelShader) {
+ return 0;
+ }
+
+ GLuint program = glCreateProgram();
+ if (program) {
+ glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ glLinkProgram(program);
+ GLint linkStatus = GL_FALSE;
+ glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+ if (linkStatus != GL_TRUE) {
+ GLint bufLength = 0;
+ glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
+ if (bufLength) {
+ char* buf = (char*) malloc(bufLength);
+ if (buf) {
+ glGetProgramInfoLog(program, bufLength, NULL, buf);
+ LOGE("Could not link program:\n%s\n", buf);
+ free(buf);
+ }
+ }
+ glDeleteProgram(program);
+ program = 0;
+ }
+ }
+ return program;
+}
+
+GLuint gProgram;
+GLuint gvPositionHandle;
+
+bool setupGraphics(int w, int h) {
+ printGLString("Version", GL_VERSION);
+ printGLString("Vendor", GL_VENDOR);
+ printGLString("Renderer", GL_RENDERER);
+ printGLString("Extensions", GL_EXTENSIONS);
+
+ LOGI("setupGraphics(%d, %d)", w, h);
+ gProgram = createProgram(gVertexShader, gFragmentShader);
+ if (!gProgram) {
+ LOGE("Could not create program.");
+ return false;
+ }
+ gvPositionHandle = glGetAttribLocation(gProgram, "vPosition");
+ checkGlError("glGetAttribLocation");
+ LOGI("glGetAttribLocation(\"vPosition\") = %d\n",
+ gvPositionHandle);
+
+ glViewport(0, 0, w, h);
+ checkGlError("glViewport");
+ return true;
+}
+
+const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
+ 0.5f, -0.5f };
+
+void renderFrame() {
+ static float grey;
+ grey += 0.01f;
+ if (grey > 1.0f) {
+ grey = 0.0f;
+ }
+ glClearColor(grey, grey, grey, 1.0f);
+ checkGlError("glClearColor");
+ glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+ checkGlError("glClear");
+
+ glUseProgram(gProgram);
+ checkGlError("glUseProgram");
+
+ glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
+ checkGlError("glVertexAttribPointer");
+ glEnableVertexAttribArray(gvPositionHandle);
+ checkGlError("glEnableVertexAttribArray");
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ checkGlError("glDrawArrays");
+}
+
+extern "C" {
+ JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_init(JNIEnv * env, jobject obj, jint width, jint height);
+ JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * env, jobject obj);
+};
+
+JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_init(JNIEnv * env, jobject obj, jint width, jint height)
+{
+ setupGraphics(width, height);
+}
+
+JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * env, jobject obj)
+{
+ renderFrame();
+}
+
diff --git a/opengl/tests/gldual/res/layout/gldual_activity.xml b/opengl/tests/gldual/res/layout/gldual_activity.xml
new file mode 100644
index 0000000000..f2d59c74ed
--- /dev/null
+++ b/opengl/tests/gldual/res/layout/gldual_activity.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/text"
+
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <android.opengl.GLSurfaceView android:id="@+id/gl1"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+ <com.android.gldual.GLDualGL2View android:id="@+id/gl2"
+ android:layout_width="fill_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1" />
+</LinearLayout>
diff --git a/opengl/tests/gldual/res/values/strings.xml b/opengl/tests/gldual/res/values/strings.xml
new file mode 100644
index 0000000000..4267dffd00
--- /dev/null
+++ b/opengl/tests/gldual/res/values/strings.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<!-- This file contains resource definitions for displayed strings, allowing
+ them to be changed based on the locale and options. -->
+
+<resources>
+ <!-- Simple strings. -->
+ <string name="gldual_activity">GLDual</string>
+
+</resources>
+
diff --git a/opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java b/opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java
new file mode 100644
index 0000000000..9d88f64882
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/GLDualActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2007 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.gldual;
+
+import android.app.Activity;
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.LinearLayout;
+
+
+public class GLDualActivity extends Activity {
+
+ GLSurfaceView mGLView;
+ GLDualGL2View mGL2View;
+
+ @Override protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ View root = getLayoutInflater().inflate(R.layout.gldual_activity, null);
+ mGLView = (GLSurfaceView) root.findViewById(R.id.gl1);
+ mGLView.setEGLConfigChooser(5,6,5,0,0,0);
+ mGLView.setRenderer(new TriangleRenderer());
+ mGL2View = (GLDualGL2View) root.findViewById(R.id.gl2);
+ setContentView(root);
+ }
+
+ @Override protected void onPause() {
+ super.onPause();
+ mGLView.onPause();
+ mGL2View.onPause();
+ }
+
+ @Override protected void onResume() {
+ super.onResume();
+ mGLView.onResume();
+ mGL2View.onResume();
+ }
+}
diff --git a/opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java b/opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java
new file mode 100644
index 0000000000..8f5e347ea8
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/GLDualGL2View.java
@@ -0,0 +1,299 @@
+/*
+ * 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.gldual;
+/*
+ * Copyright (C) 2008 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.
+ */
+
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.util.AttributeSet;
+import android.util.Log;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying an OpenGL animation. This allows the animation to run in a
+ * separate thread, without requiring that it be driven by the update mechanism
+ * of the view hierarchy.
+ *
+ * The application-specific rendering code is delegated to a GLView.Renderer
+ * instance.
+ */
+class GLDualGL2View extends GLSurfaceView {
+ private static String TAG = "GLDualGL2View";
+
+ public GLDualGL2View(Context context) {
+ super(context);
+ init(false, 0, 0);
+ }
+
+ public GLDualGL2View(Context context, AttributeSet set) {
+ super(context, set);
+ init(false, 0, 0);
+ }
+
+ public GLDualGL2View(Context context, boolean translucent, int depth, int stencil) {
+ super(context);
+ init(translucent, depth, stencil);
+ }
+
+ private void init(boolean translucent, int depth, int stencil) {
+ setEGLContextFactory(new ContextFactory());
+ setEGLConfigChooser( translucent ?
+ new ConfigChooser(8,8,8,8, depth, stencil) :
+ new ConfigChooser(5,6,5,0, depth, stencil));
+ setRenderer(new Renderer());
+ }
+
+ private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
+ private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
+ Log.w(TAG, "creating OpenGL ES 2.0 context");
+ checkEglError("Before eglCreateContext", egl);
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };
+ EGLContext context = egl.eglCreateContext(display, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
+ checkEglError("After eglCreateContext", egl);
+ return context;
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {
+ egl.eglDestroyContext(display, context);
+ }
+ }
+
+ private static void checkEglError(String prompt, EGL10 egl) {
+ int error;
+ while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {
+ Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));
+ }
+ }
+
+ private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {
+ private static int EGL_OPENGL_ES2_BIT = 4;
+ private static int[] s_configAttribs2 =
+ {
+ EGL10.EGL_RED_SIZE, 4,
+ EGL10.EGL_GREEN_SIZE, 4,
+ EGL10.EGL_BLUE_SIZE, 4,
+ EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL10.EGL_NONE
+ };
+
+ public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {
+ mRedSize = r;
+ mGreenSize = g;
+ mBlueSize = b;
+ mAlphaSize = a;
+ mDepthSize = depth;
+ mStencilSize = stencil;
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+
+ int[] num_config = new int[1];
+ egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException("No configs match configSpec");
+ }
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);
+ // printConfigs(egl, display, configs);
+ return chooseConfig(egl, display, configs);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private void printConfigs(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ int numConfigs = configs.length;
+ Log.w(TAG, String.format("%d configurations", numConfigs));
+ for (int i = 0; i < numConfigs; i++) {
+ Log.w(TAG, String.format("Configuration %d:\n", i));
+ printConfig(egl, display, configs[i]);
+ }
+ }
+
+ private void printConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig config) {
+ int[] attributes = {
+ EGL10.EGL_BUFFER_SIZE,
+ EGL10.EGL_ALPHA_SIZE,
+ EGL10.EGL_BLUE_SIZE,
+ EGL10.EGL_GREEN_SIZE,
+ EGL10.EGL_RED_SIZE,
+ EGL10.EGL_DEPTH_SIZE,
+ EGL10.EGL_STENCIL_SIZE,
+ EGL10.EGL_CONFIG_CAVEAT,
+ EGL10.EGL_CONFIG_ID,
+ EGL10.EGL_LEVEL,
+ EGL10.EGL_MAX_PBUFFER_HEIGHT,
+ EGL10.EGL_MAX_PBUFFER_PIXELS,
+ EGL10.EGL_MAX_PBUFFER_WIDTH,
+ EGL10.EGL_NATIVE_RENDERABLE,
+ EGL10.EGL_NATIVE_VISUAL_ID,
+ EGL10.EGL_NATIVE_VISUAL_TYPE,
+ 0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+ EGL10.EGL_SAMPLES,
+ EGL10.EGL_SAMPLE_BUFFERS,
+ EGL10.EGL_SURFACE_TYPE,
+ EGL10.EGL_TRANSPARENT_TYPE,
+ EGL10.EGL_TRANSPARENT_RED_VALUE,
+ EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+ EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+ 0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+ 0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+ 0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+ 0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+ EGL10.EGL_LUMINANCE_SIZE,
+ EGL10.EGL_ALPHA_MASK_SIZE,
+ EGL10.EGL_COLOR_BUFFER_TYPE,
+ EGL10.EGL_RENDERABLE_TYPE,
+ 0x3042 // EGL10.EGL_CONFORMANT
+ };
+ String[] names = {
+ "EGL_BUFFER_SIZE",
+ "EGL_ALPHA_SIZE",
+ "EGL_BLUE_SIZE",
+ "EGL_GREEN_SIZE",
+ "EGL_RED_SIZE",
+ "EGL_DEPTH_SIZE",
+ "EGL_STENCIL_SIZE",
+ "EGL_CONFIG_CAVEAT",
+ "EGL_CONFIG_ID",
+ "EGL_LEVEL",
+ "EGL_MAX_PBUFFER_HEIGHT",
+ "EGL_MAX_PBUFFER_PIXELS",
+ "EGL_MAX_PBUFFER_WIDTH",
+ "EGL_NATIVE_RENDERABLE",
+ "EGL_NATIVE_VISUAL_ID",
+ "EGL_NATIVE_VISUAL_TYPE",
+ "EGL_PRESERVED_RESOURCES",
+ "EGL_SAMPLES",
+ "EGL_SAMPLE_BUFFERS",
+ "EGL_SURFACE_TYPE",
+ "EGL_TRANSPARENT_TYPE",
+ "EGL_TRANSPARENT_RED_VALUE",
+ "EGL_TRANSPARENT_GREEN_VALUE",
+ "EGL_TRANSPARENT_BLUE_VALUE",
+ "EGL_BIND_TO_TEXTURE_RGB",
+ "EGL_BIND_TO_TEXTURE_RGBA",
+ "EGL_MIN_SWAP_INTERVAL",
+ "EGL_MAX_SWAP_INTERVAL",
+ "EGL_LUMINANCE_SIZE",
+ "EGL_ALPHA_MASK_SIZE",
+ "EGL_COLOR_BUFFER_TYPE",
+ "EGL_RENDERABLE_TYPE",
+ "EGL_CONFORMANT"
+ };
+ int[] value = new int[1];
+ for (int i = 0; i < attributes.length; i++) {
+ int attribute = attributes[i];
+ String name = names[i];
+ if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+ Log.w(TAG, String.format(" %s: %d\n", name, value[0]));
+ } else {
+ // Log.w(TAG, String.format(" %s: failed\n", name));
+ while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+ }
+ }
+ }
+
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ private int[] mValue = new int[1];
+ }
+
+ private static class Renderer implements GLSurfaceView.Renderer {
+ public void onDrawFrame(GL10 gl) {
+ GLDualLib.step();
+ }
+
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ GLDualLib.init(width, height);
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ // Do nothing.
+ }
+ }
+}
+
diff --git a/opengl/tests/gldual/src/com/android/gldual/GLDualLib.java b/opengl/tests/gldual/src/com/android/gldual/GLDualLib.java
new file mode 100644
index 0000000000..d8f765e495
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/GLDualLib.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 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.gldual;
+
+// Wrapper for native library
+
+public class GLDualLib {
+
+ static {
+ System.loadLibrary("gldualjni");
+ }
+
+ /**
+ * @param width the current view width
+ * @param height the current view height
+ */
+ public static native void init(int width, int height);
+ public static native void step();
+}
diff --git a/opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java b/opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java
new file mode 100644
index 0000000000..098c4d217b
--- /dev/null
+++ b/opengl/tests/gldual/src/com/android/gldual/TriangleRenderer.java
@@ -0,0 +1,149 @@
+package com.android.gldual;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+import java.nio.ShortBuffer;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.opengl.GLSurfaceView;
+import android.opengl.GLU;
+import android.os.SystemClock;
+
+public class TriangleRenderer implements GLSurfaceView.Renderer{
+
+ public TriangleRenderer() {
+ mTriangle = new Triangle();
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Some one-time OpenGL initialization can be made here
+ * probably based on features of this particular context
+ */
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+ GL10.GL_FASTEST);
+
+ gl.glClearColor(.5f, .5f, .5f, 1);
+ gl.glShadeModel(GL10.GL_SMOOTH);
+ }
+
+ public void onDrawFrame(GL10 gl) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Usually, the first thing one might want to do is to clear
+ * the screen. The most efficient way of doing this is to use
+ * glClear().
+ */
+
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+ /*
+ * Now we're ready to draw some 3D objects
+ */
+
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glLoadIdentity();
+
+ GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+ long time = SystemClock.uptimeMillis() % 4000L;
+ float angle = 0.090f * ((int) time);
+
+ gl.glRotatef(angle, 0, 0, 1.0f);
+
+ mTriangle.draw(gl);
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ gl.glViewport(0, 0, w, h);
+
+ /*
+ * Set our projection matrix. This doesn't have to be done
+ * each time we draw, but usually a new projection needs to
+ * be set when the viewport is resized.
+ */
+
+ float ratio = (float) w / h;
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glFrustumf(-ratio, ratio, -1, 1, 3, 7);
+
+ }
+
+ private Triangle mTriangle;
+}
+
+class Triangle {
+ public Triangle() {
+
+ // Buffers to be passed to gl*Pointer() functions
+ // must be direct, i.e., they must be placed on the
+ // native heap where the garbage collector cannot
+ // move them.
+ //
+ // Buffers with multi-byte datatypes (e.g., short, int, float)
+ // must have their byte order set to native order
+
+ ByteBuffer vbb = ByteBuffer.allocateDirect(VERTS * 3 * 4);
+ vbb.order(ByteOrder.nativeOrder());
+ mFVertexBuffer = vbb.asFloatBuffer();
+
+ ByteBuffer tbb = ByteBuffer.allocateDirect(VERTS * 2 * 4);
+ tbb.order(ByteOrder.nativeOrder());
+
+ ByteBuffer ibb = ByteBuffer.allocateDirect(VERTS * 2);
+ ibb.order(ByteOrder.nativeOrder());
+ mIndexBuffer = ibb.asShortBuffer();
+
+ // A unit-sided equalateral triangle centered on the origin.
+ float[] coords = {
+ // X, Y, Z
+ -0.5f, -0.25f, 0,
+ 0.5f, -0.25f, 0,
+ 0.0f, 0.559016994f, 0
+ };
+
+ for (int i = 0; i < VERTS; i++) {
+ for(int j = 0; j < 3; j++) {
+ mFVertexBuffer.put(coords[i*3+j] * 2.0f);
+ }
+ }
+
+ for(int i = 0; i < VERTS; i++) {
+ mIndexBuffer.put((short) i);
+ }
+
+ mFVertexBuffer.position(0);
+ mIndexBuffer.position(0);
+ }
+
+ public void draw(GL10 gl) {
+ gl.glFrontFace(GL10.GL_CCW);
+ gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFVertexBuffer);
+ gl.glDrawElements(GL10.GL_TRIANGLE_STRIP, VERTS,
+ GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
+ }
+
+ private final static int VERTS = 3;
+
+ private FloatBuffer mFVertexBuffer;
+ private ShortBuffer mIndexBuffer;
+}