Merge "Remove java.io.tmpdir assignment in AndroidRuntime."
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 7fc364f..3d9daca 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1569,10 +1569,7 @@
String locale = null;
if (mConfiguration.locale != null) {
- locale = mConfiguration.locale.getLanguage();
- if (mConfiguration.locale.getCountry() != null) {
- locale += "-" + mConfiguration.locale.getCountry();
- }
+ locale = mConfiguration.locale.toLanguageTag();
}
int width, height;
if (mMetrics.widthPixels >= mMetrics.heightPixels) {
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index f2a86c9..7d1c6c4 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -546,6 +546,9 @@
public void skipValue() throws IOException {
skipping = true;
try {
+ if (!hasNext() || peek() == JsonToken.END_DOCUMENT) {
+ throw new IllegalStateException("No element left to skip");
+ }
int count = 0;
do {
JsonToken token = advance();
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
index 0dfd94a..3cf5af4 100644
--- a/core/java/android/view/SurfaceSession.java
+++ b/core/java/android/view/SurfaceSession.java
@@ -24,11 +24,11 @@
*/
public final class SurfaceSession {
// Note: This field is accessed by native code.
- private int mNativeClient; // SurfaceComposerClient*
+ private long mNativeClient; // SurfaceComposerClient*
- private static native int nativeCreate();
- private static native void nativeDestroy(int ptr);
- private static native void nativeKill(int ptr);
+ private static native long nativeCreate();
+ private static native void nativeDestroy(long ptr);
+ private static native void nativeKill(long ptr);
/** Create a new connection with the surface flinger. */
public SurfaceSession() {
diff --git a/core/jni/android_view_SurfaceSession.cpp b/core/jni/android_view_SurfaceSession.cpp
index 87e339c..609c565 100644
--- a/core/jni/android_view_SurfaceSession.cpp
+++ b/core/jni/android_view_SurfaceSession.cpp
@@ -35,22 +35,22 @@
sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
JNIEnv* env, jobject surfaceSessionObj) {
return reinterpret_cast<SurfaceComposerClient*>(
- env->GetIntField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
+ env->GetLongField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
}
-static jint nativeCreate(JNIEnv* env, jclass clazz) {
+static jlong nativeCreate(JNIEnv* env, jclass clazz) {
SurfaceComposerClient* client = new SurfaceComposerClient();
client->incStrong((void*)nativeCreate);
- return reinterpret_cast<jint>(client);
+ return reinterpret_cast<jlong>(client);
}
-static void nativeDestroy(JNIEnv* env, jclass clazz, jint ptr) {
+static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->decStrong((void*)nativeCreate);
}
-static void nativeKill(JNIEnv* env, jclass clazz, jint ptr) {
+static void nativeKill(JNIEnv* env, jclass clazz, jlong ptr) {
SurfaceComposerClient* client = reinterpret_cast<SurfaceComposerClient*>(ptr);
client->dispose();
}
@@ -58,11 +58,11 @@
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "nativeCreate", "()I",
+ { "nativeCreate", "()J",
(void*)nativeCreate },
- { "nativeDestroy", "(I)V",
+ { "nativeDestroy", "(J)V",
(void*)nativeDestroy },
- { "nativeKill", "(I)V",
+ { "nativeKill", "(J)V",
(void*)nativeKill }
};
@@ -72,7 +72,7 @@
LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
jclass clazz = env->FindClass("android/view/SurfaceSession");
- gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "I");
+ gSurfaceSessionClassInfo.mNativeClient = env->GetFieldID(clazz, "mNativeClient", "J");
return 0;
}
diff --git a/core/tests/coretests/src/android/util/JsonReaderTest.java b/core/tests/coretests/src/android/util/JsonReaderTest.java
deleted file mode 100644
index 42b7640..0000000
--- a/core/tests/coretests/src/android/util/JsonReaderTest.java
+++ /dev/null
@@ -1,909 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Arrays;
-import junit.framework.TestCase;
-
-public final class JsonReaderTest extends TestCase {
-
- private static final int READER_BUFFER_SIZE = 1024;
-
- public void testReadArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true, true]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(true, reader.nextBoolean());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testReadEmptyArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[]"));
- reader.beginArray();
- assertFalse(reader.hasNext());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testReadObject() throws IOException {
- JsonReader reader = new JsonReader(new StringReader(
- "{\"a\": \"android\", \"b\": \"banana\"}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals("android", reader.nextString());
- assertEquals("b", reader.nextName());
- assertEquals("banana", reader.nextString());
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testReadEmptyObject() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{}"));
- reader.beginObject();
- assertFalse(reader.hasNext());
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testSkipObject() throws IOException {
- JsonReader reader = new JsonReader(new StringReader(
- "{\"a\": { \"c\": [], \"d\": [true, true, {}] }, \"b\": \"banana\"}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- reader.skipValue();
- assertEquals("b", reader.nextName());
- reader.skipValue();
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testHelloWorld() throws IOException {
- String json = "{\n" +
- " \"hello\": true,\n" +
- " \"foo\": [\"world\"]\n" +
- "}";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginObject();
- assertEquals("hello", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- assertEquals("foo", reader.nextName());
- reader.beginArray();
- assertEquals("world", reader.nextString());
- reader.endArray();
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testNulls() {
- try {
- new JsonReader(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testEmptyString() throws IOException {
- try {
- new JsonReader(new StringReader("")).beginArray();
- } catch (IOException expected) {
- }
- try {
- new JsonReader(new StringReader("")).beginObject();
- } catch (IOException expected) {
- }
- }
-
- public void testNoTopLevelObject() throws IOException {
- try {
- new JsonReader(new StringReader("true")).nextBoolean();
- } catch (IOException expected) {
- }
- }
-
- public void testCharacterUnescaping() throws IOException {
- String json = "[\"a\","
- + "\"a\\\"\","
- + "\"\\\"\","
- + "\":\","
- + "\",\","
- + "\"\\b\","
- + "\"\\f\","
- + "\"\\n\","
- + "\"\\r\","
- + "\"\\t\","
- + "\" \","
- + "\"\\\\\","
- + "\"{\","
- + "\"}\","
- + "\"[\","
- + "\"]\","
- + "\"\\u0000\","
- + "\"\\u0019\","
- + "\"\\u20AC\""
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals("a", reader.nextString());
- assertEquals("a\"", reader.nextString());
- assertEquals("\"", reader.nextString());
- assertEquals(":", reader.nextString());
- assertEquals(",", reader.nextString());
- assertEquals("\b", reader.nextString());
- assertEquals("\f", reader.nextString());
- assertEquals("\n", reader.nextString());
- assertEquals("\r", reader.nextString());
- assertEquals("\t", reader.nextString());
- assertEquals(" ", reader.nextString());
- assertEquals("\\", reader.nextString());
- assertEquals("{", reader.nextString());
- assertEquals("}", reader.nextString());
- assertEquals("[", reader.nextString());
- assertEquals("]", reader.nextString());
- assertEquals("\0", reader.nextString());
- assertEquals("\u0019", reader.nextString());
- assertEquals("\u20AC", reader.nextString());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testIntegersWithFractionalPartSpecified() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[1.0,1.0,1.0]"));
- reader.beginArray();
- assertEquals(1.0, reader.nextDouble());
- assertEquals(1, reader.nextInt());
- assertEquals(1L, reader.nextLong());
- }
-
- public void testDoubles() throws IOException {
- String json = "[-0.0,"
- + "1.0,"
- + "1.7976931348623157E308,"
- + "4.9E-324,"
- + "0.0,"
- + "-0.5,"
- + "2.2250738585072014E-308,"
- + "3.141592653589793,"
- + "2.718281828459045,"
- + "\"1.0\","
- + "\"011.0\","
- + "\"NaN\","
- + "\"Infinity\","
- + "\"-Infinity\""
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(-0.0, reader.nextDouble());
- assertEquals(1.0, reader.nextDouble());
- assertEquals(1.7976931348623157E308, reader.nextDouble());
- assertEquals(4.9E-324, reader.nextDouble());
- assertEquals(0.0, reader.nextDouble());
- assertEquals(-0.5, reader.nextDouble());
- assertEquals(2.2250738585072014E-308, reader.nextDouble());
- assertEquals(3.141592653589793, reader.nextDouble());
- assertEquals(2.718281828459045, reader.nextDouble());
- assertEquals(1,0, reader.nextDouble());
- assertEquals(11.0, reader.nextDouble());
- assertTrue(Double.isNaN(reader.nextDouble()));
- assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
- assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testLenientDoubles() throws IOException {
- String json = "["
- + "011.0,"
- + "NaN,"
- + "NAN,"
- + "Infinity,"
- + "INFINITY,"
- + "-Infinity"
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(11.0, reader.nextDouble());
- assertTrue(Double.isNaN(reader.nextDouble()));
- try {
- reader.nextDouble();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals("NAN", reader.nextString());
- assertEquals(Double.POSITIVE_INFINITY, reader.nextDouble());
- try {
- reader.nextDouble();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals("INFINITY", reader.nextString());
- assertEquals(Double.NEGATIVE_INFINITY, reader.nextDouble());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testBufferBoundary() throws IOException {
- char[] pad = new char[READER_BUFFER_SIZE - 8];
- Arrays.fill(pad, '5');
- String json = "[\"" + new String(pad) + "\",33333]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(JsonToken.STRING, reader.peek());
- assertEquals(new String(pad), reader.nextString());
- assertEquals(JsonToken.NUMBER, reader.peek());
- assertEquals(33333, reader.nextInt());
- }
-
- public void testTruncatedBufferBoundary() throws IOException {
- char[] pad = new char[READER_BUFFER_SIZE - 8];
- Arrays.fill(pad, '5');
- String json = "[\"" + new String(pad) + "\",33333";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(JsonToken.STRING, reader.peek());
- assertEquals(new String(pad), reader.nextString());
- assertEquals(JsonToken.NUMBER, reader.peek());
- assertEquals(33333, reader.nextInt());
- try {
- reader.endArray();
- fail();
- } catch (IOException e) {
- }
- }
-
- public void testLongestSupportedNumericLiterals() throws IOException {
- testLongNumericLiterals(READER_BUFFER_SIZE - 1, JsonToken.NUMBER);
- }
-
- public void testLongerNumericLiterals() throws IOException {
- testLongNumericLiterals(READER_BUFFER_SIZE, JsonToken.STRING);
- }
-
- private void testLongNumericLiterals(int length, JsonToken expectedToken) throws IOException {
- char[] longNumber = new char[length];
- Arrays.fill(longNumber, '9');
- longNumber[0] = '1';
- longNumber[1] = '.';
-
- String json = "[" + new String(longNumber) + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(expectedToken, reader.peek());
- assertEquals(2.0d, reader.nextDouble());
- reader.endArray();
- }
-
- public void testLongs() throws IOException {
- String json = "[0,0,0,"
- + "1,1,1,"
- + "-1,-1,-1,"
- + "-9223372036854775808,"
- + "9223372036854775807,"
- + "5.0,"
- + "1.0e2,"
- + "\"011\","
- + "\"5.0\","
- + "\"1.0e2\""
- + "]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(0L, reader.nextLong());
- assertEquals(0, reader.nextInt());
- assertEquals(0.0, reader.nextDouble());
- assertEquals(1L, reader.nextLong());
- assertEquals(1, reader.nextInt());
- assertEquals(1.0, reader.nextDouble());
- assertEquals(-1L, reader.nextLong());
- assertEquals(-1, reader.nextInt());
- assertEquals(-1.0, reader.nextDouble());
- try {
- reader.nextInt();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals(Long.MIN_VALUE, reader.nextLong());
- try {
- reader.nextInt();
- fail();
- } catch (NumberFormatException expected) {
- }
- assertEquals(Long.MAX_VALUE, reader.nextLong());
- assertEquals(5, reader.nextLong());
- assertEquals(100, reader.nextLong());
- assertEquals(11, reader.nextLong());
- assertEquals(5, reader.nextLong());
- assertEquals(100, reader.nextLong());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- /**
- * This test fails because there's no double for 9223372036854775806, and
- * our long parsing uses Double.parseDouble() for fractional values.
- */
- public void testHighPrecisionLong() throws IOException {
- String json = "[9223372036854775806.000]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- assertEquals(9223372036854775806L, reader.nextLong());
- reader.endArray();
- }
-
- public void testMatchingValidNumbers() throws IOException {
- String json = "[-1,99,-0,0,0e1,0e+1,0e-1,0E1,0E+1,0E-1,0.0,1.0,-1.0,1.0e0,1.0e+1,1.0e-1]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- for (int i = 0; i < 16; i++) {
- assertEquals(JsonToken.NUMBER, reader.peek());
- reader.nextDouble();
- }
- reader.endArray();
- }
-
- public void testRecognizingInvalidNumbers() throws IOException {
- String json = "[-00,00,001,+1,1f,0x,0xf,0x0,0f1,0ee1,1..0,1e0.1,1.-01,1.+1,1.0x,1.0+]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.setLenient(true);
- reader.beginArray();
- for (int i = 0; i < 16; i++) {
- assertEquals(JsonToken.STRING, reader.peek());
- reader.nextString();
- }
- reader.endArray();
- }
-
- public void testNonFiniteDouble() throws IOException {
- String json = "[NaN]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- try {
- reader.nextDouble();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testNumberWithHexPrefix() throws IOException {
- String json = "[0x11]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- try {
- reader.nextLong();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testNumberWithOctalPrefix() throws IOException {
- String json = "[01]";
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- try {
- reader.nextInt();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testBooleans() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true,false]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(false, reader.nextBoolean());
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testMixedCaseLiterals() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[True,TruE,False,FALSE,NULL,nulL]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(true, reader.nextBoolean());
- assertEquals(false, reader.nextBoolean());
- assertEquals(false, reader.nextBoolean());
- reader.nextNull();
- reader.nextNull();
- reader.endArray();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testMissingValue() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextString();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testPrematureEndOfInput() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true,"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testPrematurelyClosed() throws IOException {
- try {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":[]}"));
- reader.beginObject();
- reader.close();
- reader.nextName();
- fail();
- } catch (IllegalStateException expected) {
- }
-
- try {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":[]}"));
- reader.close();
- reader.beginObject();
- fail();
- } catch (IllegalStateException expected) {
- }
-
- try {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true}"));
- reader.beginObject();
- reader.nextName();
- reader.peek();
- reader.close();
- reader.nextBoolean();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNextFailuresDoNotAdvance() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true}"));
- reader.beginObject();
- try {
- reader.nextString();
- fail();
- } catch (IllegalStateException expected) {
- }
- assertEquals("a", reader.nextName());
- try {
- reader.nextName();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.beginArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.endArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.beginObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.endObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextString();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.nextName();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.beginArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- try {
- reader.endArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- reader.close();
- }
-
- public void testStringNullIsNotNull() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[\"null\"]"));
- reader.beginArray();
- try {
- reader.nextNull();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNullLiteralIsNotAString() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[null]"));
- reader.beginArray();
- try {
- reader.nextString();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testStrictNameValueSeparator() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("{\"a\"=>true}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientNameValueSeparator() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\"=true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
-
- reader = new JsonReader(new StringReader("{\"a\"=>true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictComments() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[# comment \n true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[/* comment */ true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientComments() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[// comment \n true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
-
- reader = new JsonReader(new StringReader("[# comment \n true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
-
- reader = new JsonReader(new StringReader("[/* comment */ true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictUnquotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{a:true}"));
- reader.beginObject();
- try {
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientUnquotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{a:true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- }
-
- public void testStrictSingleQuotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
- reader.beginObject();
- try {
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSingleQuotedNames() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{'a':true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- }
-
- public void testStrictUnquotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[a]"));
- reader.beginArray();
- try {
- reader.nextString();
- fail();
- } catch (MalformedJsonException expected) {
- }
- }
-
- public void testLenientUnquotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[a]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals("a", reader.nextString());
- }
-
- public void testStrictSingleQuotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("['a']"));
- reader.beginArray();
- try {
- reader.nextString();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSingleQuotedStrings() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("['a']"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals("a", reader.nextString());
- }
-
- public void testStrictSemicolonDelimitedArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true;true]"));
- reader.beginArray();
- try {
- reader.nextBoolean();
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSemicolonDelimitedArray() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true;true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictSemicolonDelimitedNameValuePair() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
- reader.beginObject();
- assertEquals("a", reader.nextName());
- try {
- reader.nextBoolean();
- reader.nextName();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientSemicolonDelimitedNameValuePair() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("{\"a\":true;\"b\":true}"));
- reader.setLenient(true);
- reader.beginObject();
- assertEquals("a", reader.nextName());
- assertEquals(true, reader.nextBoolean());
- assertEquals("b", reader.nextName());
- }
-
- public void testStrictUnnecessaryArraySeparators() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[,true]"));
- reader.beginArray();
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[true,]"));
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
-
- reader = new JsonReader(new StringReader("[,]"));
- reader.beginArray();
- try {
- reader.nextNull();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientUnnecessaryArraySeparators() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[true,,true]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- reader.nextNull();
- assertEquals(true, reader.nextBoolean());
- reader.endArray();
-
- reader = new JsonReader(new StringReader("[,true]"));
- reader.setLenient(true);
- reader.beginArray();
- reader.nextNull();
- assertEquals(true, reader.nextBoolean());
- reader.endArray();
-
- reader = new JsonReader(new StringReader("[true,]"));
- reader.setLenient(true);
- reader.beginArray();
- assertEquals(true, reader.nextBoolean());
- reader.nextNull();
- reader.endArray();
-
- reader = new JsonReader(new StringReader("[,]"));
- reader.setLenient(true);
- reader.beginArray();
- reader.nextNull();
- reader.nextNull();
- reader.endArray();
- }
-
- public void testStrictMultipleTopLevelValues() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[] []"));
- reader.beginArray();
- reader.endArray();
- try {
- reader.peek();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientMultipleTopLevelValues() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[] true {}"));
- reader.setLenient(true);
- reader.beginArray();
- reader.endArray();
- assertEquals(true, reader.nextBoolean());
- reader.beginObject();
- reader.endObject();
- assertEquals(JsonToken.END_DOCUMENT, reader.peek());
- }
-
- public void testStrictTopLevelValueType() {
- JsonReader reader = new JsonReader(new StringReader("true"));
- try {
- reader.nextBoolean();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testLenientTopLevelValueType() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("true"));
- reader.setLenient(true);
- assertEquals(true, reader.nextBoolean());
- }
-
- public void testStrictNonExecutePrefix() {
- JsonReader reader = new JsonReader(new StringReader(")]}'\n []"));
- try {
- reader.beginArray();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testBomIgnoredAsFirstCharacterOfDocument() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("\ufeff[]"));
- reader.beginArray();
- reader.endArray();
- }
-
- public void testBomForbiddenAsOtherCharacterInDocument() throws IOException {
- JsonReader reader = new JsonReader(new StringReader("[\ufeff]"));
- reader.beginArray();
- try {
- reader.endArray();
- fail();
- } catch (IOException expected) {
- }
- }
-
- public void testFailWithPosition() throws IOException {
- testFailWithPosition("Expected literal value at line 6 column 3",
- "[\n\n\n\n\n0,}]");
- }
-
- public void testFailWithPositionIsOffsetByBom() throws IOException {
- testFailWithPosition("Expected literal value at line 1 column 4",
- "\ufeff[0,}]");
- }
-
- public void testFailWithPositionGreaterThanBufferSize() throws IOException {
- String spaces = repeat(' ', 8192);
- testFailWithPosition("Expected literal value at line 6 column 3",
- "[\n\n" + spaces + "\n\n\n0,}]");
- }
-
- private void testFailWithPosition(String message, String json) throws IOException {
- JsonReader reader = new JsonReader(new StringReader(json));
- reader.beginArray();
- reader.nextInt();
- try {
- reader.peek();
- fail();
- } catch (IOException expected) {
- assertEquals(message, expected.getMessage());
- }
- }
-
- private String repeat(char c, int count) {
- char[] array = new char[count];
- Arrays.fill(array, c);
- return new String(array);
- }
-}
diff --git a/core/tests/coretests/src/android/util/JsonWriterTest.java b/core/tests/coretests/src/android/util/JsonWriterTest.java
deleted file mode 100644
index 1239a3c..0000000
--- a/core/tests/coretests/src/android/util/JsonWriterTest.java
+++ /dev/null
@@ -1,466 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import junit.framework.TestCase;
-
-public final class JsonWriterTest extends TestCase {
-
- public void testWrongTopLevelType() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- try {
- jsonWriter.value("a");
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testTwoNames() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- try {
- jsonWriter.name("a");
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNameWithoutValue() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- try {
- jsonWriter.endObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testValueWithoutName() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- try {
- jsonWriter.value(true);
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testMultipleTopLevelValues() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray().endArray();
- try {
- jsonWriter.beginArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testBadNestingObject() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.beginObject();
- try {
- jsonWriter.endArray();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testBadNestingArray() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.beginArray();
- try {
- jsonWriter.endObject();
- fail();
- } catch (IllegalStateException expected) {
- }
- }
-
- public void testNullName() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- try {
- jsonWriter.name(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testNullStringValue() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- jsonWriter.value((String) null);
- jsonWriter.endObject();
- assertEquals("{\"a\":null}", stringWriter.toString());
- }
-
- public void testNonFiniteDoubles() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- try {
- jsonWriter.value(Double.NaN);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(Double.NEGATIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(Double.POSITIVE_INFINITY);
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testNonFiniteBoxedDoubles() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- try {
- jsonWriter.value(new Double(Double.NaN));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(new Double(Double.NEGATIVE_INFINITY));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- try {
- jsonWriter.value(new Double(Double.POSITIVE_INFINITY));
- fail();
- } catch (IllegalArgumentException expected) {
- }
- }
-
- public void testDoubles() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(-0.0);
- jsonWriter.value(1.0);
- jsonWriter.value(Double.MAX_VALUE);
- jsonWriter.value(Double.MIN_VALUE);
- jsonWriter.value(0.0);
- jsonWriter.value(-0.5);
- jsonWriter.value(Double.MIN_NORMAL);
- jsonWriter.value(Math.PI);
- jsonWriter.value(Math.E);
- jsonWriter.endArray();
- jsonWriter.close();
- assertEquals("[-0.0,"
- + "1.0,"
- + "1.7976931348623157E308,"
- + "4.9E-324,"
- + "0.0,"
- + "-0.5,"
- + "2.2250738585072014E-308,"
- + "3.141592653589793,"
- + "2.718281828459045]", stringWriter.toString());
- }
-
- public void testLongs() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(0);
- jsonWriter.value(1);
- jsonWriter.value(-1);
- jsonWriter.value(Long.MIN_VALUE);
- jsonWriter.value(Long.MAX_VALUE);
- jsonWriter.endArray();
- jsonWriter.close();
- assertEquals("[0,"
- + "1,"
- + "-1,"
- + "-9223372036854775808,"
- + "9223372036854775807]", stringWriter.toString());
- }
-
- public void testNumbers() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(new BigInteger("0"));
- jsonWriter.value(new BigInteger("9223372036854775808"));
- jsonWriter.value(new BigInteger("-9223372036854775809"));
- jsonWriter.value(new BigDecimal("3.141592653589793238462643383"));
- jsonWriter.endArray();
- jsonWriter.close();
- assertEquals("[0,"
- + "9223372036854775808,"
- + "-9223372036854775809,"
- + "3.141592653589793238462643383]", stringWriter.toString());
- }
-
- public void testBooleans() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value(true);
- jsonWriter.value(false);
- jsonWriter.endArray();
- assertEquals("[true,false]", stringWriter.toString());
- }
-
- public void testNulls() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.nullValue();
- jsonWriter.endArray();
- assertEquals("[null]", stringWriter.toString());
- }
-
- public void testStrings() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value("a");
- jsonWriter.value("a\"");
- jsonWriter.value("\"");
- jsonWriter.value(":");
- jsonWriter.value(",");
- jsonWriter.value("\b");
- jsonWriter.value("\f");
- jsonWriter.value("\n");
- jsonWriter.value("\r");
- jsonWriter.value("\t");
- jsonWriter.value(" ");
- jsonWriter.value("\\");
- jsonWriter.value("{");
- jsonWriter.value("}");
- jsonWriter.value("[");
- jsonWriter.value("]");
- jsonWriter.value("\0");
- jsonWriter.value("\u0019");
- jsonWriter.endArray();
- assertEquals("[\"a\","
- + "\"a\\\"\","
- + "\"\\\"\","
- + "\":\","
- + "\",\","
- + "\"\\b\","
- + "\"\\f\","
- + "\"\\n\","
- + "\"\\r\","
- + "\"\\t\","
- + "\" \","
- + "\"\\\\\","
- + "\"{\","
- + "\"}\","
- + "\"[\","
- + "\"]\","
- + "\"\\u0000\","
- + "\"\\u0019\"]", stringWriter.toString());
- }
-
- public void testUnicodeLineBreaksEscaped() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.value("\u2028 \u2029");
- jsonWriter.endArray();
- assertEquals("[\"\\u2028 \\u2029\"]", stringWriter.toString());
- }
-
- public void testEmptyArray() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.endArray();
- assertEquals("[]", stringWriter.toString());
- }
-
- public void testEmptyObject() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.endObject();
- assertEquals("{}", stringWriter.toString());
- }
-
- public void testObjectsInArrays() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginArray();
- jsonWriter.beginObject();
- jsonWriter.name("a").value(5);
- jsonWriter.name("b").value(false);
- jsonWriter.endObject();
- jsonWriter.beginObject();
- jsonWriter.name("c").value(6);
- jsonWriter.name("d").value(true);
- jsonWriter.endObject();
- jsonWriter.endArray();
- assertEquals("[{\"a\":5,\"b\":false},"
- + "{\"c\":6,\"d\":true}]", stringWriter.toString());
- }
-
- public void testArraysInObjects() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a");
- jsonWriter.beginArray();
- jsonWriter.value(5);
- jsonWriter.value(false);
- jsonWriter.endArray();
- jsonWriter.name("b");
- jsonWriter.beginArray();
- jsonWriter.value(6);
- jsonWriter.value(true);
- jsonWriter.endArray();
- jsonWriter.endObject();
- assertEquals("{\"a\":[5,false],"
- + "\"b\":[6,true]}", stringWriter.toString());
- }
-
- public void testDeepNestingArrays() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- for (int i = 0; i < 20; i++) {
- jsonWriter.beginArray();
- }
- for (int i = 0; i < 20; i++) {
- jsonWriter.endArray();
- }
- assertEquals("[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]", stringWriter.toString());
- }
-
- public void testDeepNestingObjects() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- for (int i = 0; i < 20; i++) {
- jsonWriter.name("a");
- jsonWriter.beginObject();
- }
- for (int i = 0; i < 20; i++) {
- jsonWriter.endObject();
- }
- jsonWriter.endObject();
- assertEquals("{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":"
- + "{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{\"a\":{"
- + "}}}}}}}}}}}}}}}}}}}}}", stringWriter.toString());
- }
-
- public void testRepeatedName() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.beginObject();
- jsonWriter.name("a").value(true);
- jsonWriter.name("a").value(false);
- jsonWriter.endObject();
- // JsonWriter doesn't attempt to detect duplicate names
- assertEquals("{\"a\":true,\"a\":false}", stringWriter.toString());
- }
-
- public void testPrettyPrintObject() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.setIndent(" ");
-
- jsonWriter.beginObject();
- jsonWriter.name("a").value(true);
- jsonWriter.name("b").value(false);
- jsonWriter.name("c").value(5.0);
- jsonWriter.name("e").nullValue();
- jsonWriter.name("f").beginArray();
- jsonWriter.value(6.0);
- jsonWriter.value(7.0);
- jsonWriter.endArray();
- jsonWriter.name("g").beginObject();
- jsonWriter.name("h").value(8.0);
- jsonWriter.name("i").value(9.0);
- jsonWriter.endObject();
- jsonWriter.endObject();
-
- String expected = "{\n"
- + " \"a\": true,\n"
- + " \"b\": false,\n"
- + " \"c\": 5.0,\n"
- + " \"e\": null,\n"
- + " \"f\": [\n"
- + " 6.0,\n"
- + " 7.0\n"
- + " ],\n"
- + " \"g\": {\n"
- + " \"h\": 8.0,\n"
- + " \"i\": 9.0\n"
- + " }\n"
- + "}";
- assertEquals(expected, stringWriter.toString());
- }
-
- public void testPrettyPrintArray() throws IOException {
- StringWriter stringWriter = new StringWriter();
- JsonWriter jsonWriter = new JsonWriter(stringWriter);
- jsonWriter.setIndent(" ");
-
- jsonWriter.beginArray();
- jsonWriter.value(true);
- jsonWriter.value(false);
- jsonWriter.value(5.0);
- jsonWriter.nullValue();
- jsonWriter.beginObject();
- jsonWriter.name("a").value(6.0);
- jsonWriter.name("b").value(7.0);
- jsonWriter.endObject();
- jsonWriter.beginArray();
- jsonWriter.value(8.0);
- jsonWriter.value(9.0);
- jsonWriter.endArray();
- jsonWriter.endArray();
-
- String expected = "[\n"
- + " true,\n"
- + " false,\n"
- + " 5.0,\n"
- + " null,\n"
- + " {\n"
- + " \"a\": 6.0,\n"
- + " \"b\": 7.0\n"
- + " },\n"
- + " [\n"
- + " 8.0,\n"
- + " 9.0\n"
- + " ]\n"
- + "]";
- assertEquals(expected, stringWriter.toString());
- }
-}
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 05411ea..a0bae12 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -808,6 +808,19 @@
uint32_t lastPublicKey;
};
+// The most specific locale can consist of:
+//
+// - a 3 char language code
+// - a 3 char region code prefixed by a 'r'
+// - a 4 char script code prefixed by a 's'
+// - a 8 char variant code prefixed by a 'v'
+//
+// each separated by a single char separator, which sums up to a total of 24
+// chars, (25 include the string terminator) rounded up to 28 to be 4 byte
+// aligned.
+#define RESTABLE_MAX_LOCALE_LEN 28
+
+
/**
* Describes a particular resource configuration.
*/
@@ -828,10 +841,42 @@
union {
struct {
- // \0\0 means "any". Otherwise, en, fr, etc.
+ // This field can take three different forms:
+ // - \0\0 means "any".
+ //
+ // - Two 7 bit ascii values interpreted as ISO-639-1 language
+ // codes ('fr', 'en' etc. etc.). The high bit for both bytes is
+ // zero.
+ //
+ // - A single 16 bit little endian packed value representing an
+ // ISO-639-2 3 letter language code. This will be of the form:
+ //
+ // {1, t, t, t, t, t, s, s, s, s, s, f, f, f, f, f}
+ //
+ // bit[0, 4] = first letter of the language code
+ // bit[5, 9] = second letter of the language code
+ // bit[10, 14] = third letter of the language code.
+ // bit[15] = 1 always
+ //
+ // For backwards compatibility, languages that have unambiguous
+ // two letter codes are represented in that format.
+ //
+ // The layout is always bigendian irrespective of the runtime
+ // architecture.
char language[2];
- // \0\0 means "any". Otherwise, US, CA, etc.
+ // This field can take three different forms:
+ // - \0\0 means "any".
+ //
+ // - Two 7 bit ascii values interpreted as 2 letter region
+ // codes ('US', 'GB' etc.). The high bit for both bytes is zero.
+ //
+ // - An UN M.49 3 digit region code. For simplicity, these are packed
+ // in the same manner as the language codes, though we should need
+ // only 10 bits to represent them, instead of the 15.
+ //
+ // The layout is always bigendian irrespective of the runtime
+ // architecture.
char country[2];
};
uint32_t locale;
@@ -933,7 +978,7 @@
SDKVERSION_ANY = 0
};
- enum {
+ enum {
MINORVERSION_ANY = 0
};
@@ -1006,6 +1051,15 @@
uint32_t screenSizeDp;
};
+ // The ISO-15924 short name for the script corresponding to this
+ // configuration. (eg. Hant, Latn, etc.). Interpreted in conjunction with
+ // the locale field.
+ char localeScript[4];
+
+ // A single BCP-47 variant subtag. Will vary in length between 5 and 8
+ // chars. Interpreted in conjunction with the locale field.
+ char localeVariant[8];
+
void copyFromDeviceNoSwap(const ResTable_config& o);
void copyFromDtoH(const ResTable_config& o);
@@ -1063,7 +1117,46 @@
// settings is the requested settings
bool match(const ResTable_config& settings) const;
- void getLocale(char str[6]) const;
+ // Get the string representation of the locale component of this
+ // Config. The maximum size of this representation will be
+ // |RESTABLE_MAX_LOCALE_LEN| (including a terminating '\0').
+ //
+ // Example: en-US, en-Latn-US, en-POSIX.
+ void getBcp47Locale(char* out) const;
+
+ // Sets the values of language, region, script and variant to the
+ // well formed BCP-47 locale contained in |in|. The input locale is
+ // assumed to be valid and no validation is performed.
+ void setBcp47Locale(const char* in);
+
+ inline void clearLocale() {
+ locale = 0;
+ memset(localeScript, 0, sizeof(localeScript));
+ memset(localeVariant, 0, sizeof(localeVariant));
+ }
+
+ // Get the 2 or 3 letter language code of this configuration. Trailing
+ // bytes are set to '\0'.
+ size_t unpackLanguage(char language[4]) const;
+ // Get the 2 or 3 letter language code of this configuration. Trailing
+ // bytes are set to '\0'.
+ size_t unpackRegion(char region[4]) const;
+
+ // Sets the language code of this configuration to the first three
+ // chars at |language|.
+ //
+ // If |language| is a 2 letter code, the trailing byte must be '\0' or
+ // the BCP-47 separator '-'.
+ void packLanguage(const char* language);
+ // Sets the region code of this configuration to the first three bytes
+ // at |region|. If |region| is a 2 letter code, the trailing byte must be '\0'
+ // or the BCP-47 separator '-'.
+ void packRegion(const char* region);
+
+ // Returns a positive integer if this config is more specific than |o|
+ // with respect to their locales, a negative integer if |o| is more specific
+ // and 0 if they're equally specific.
+ int isLocaleMoreSpecificThan(const ResTable_config &o) const;
String8 toString() const;
};
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 5069958..b4d482a 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -386,17 +386,8 @@
if (locale) {
setLocaleLocked(locale);
} else if (config.language[0] != 0) {
- char spec[9];
- spec[0] = config.language[0];
- spec[1] = config.language[1];
- if (config.country[0] != 0) {
- spec[2] = '_';
- spec[3] = config.country[0];
- spec[4] = config.country[1];
- spec[5] = 0;
- } else {
- spec[3] = 0;
- }
+ char spec[RESTABLE_MAX_LOCALE_LEN];
+ config.getBcp47Locale(spec);
setLocaleLocked(spec);
} else {
updateResourceParamsLocked();
@@ -668,20 +659,11 @@
return;
}
- size_t llen = mLocale ? strlen(mLocale) : 0;
- mConfig->language[0] = 0;
- mConfig->language[1] = 0;
- mConfig->country[0] = 0;
- mConfig->country[1] = 0;
- if (llen >= 2) {
- mConfig->language[0] = mLocale[0];
- mConfig->language[1] = mLocale[1];
+ if (mLocale) {
+ mConfig->setBcp47Locale(mLocale);
+ } else {
+ mConfig->clearLocale();
}
- if (llen >= 5) {
- mConfig->country[0] = mLocale[3];
- mConfig->country[1] = mLocale[4];
- }
- mConfig->size = sizeof(*mConfig);
res->setParameters(mConfig);
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8cc98af..efb589a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -66,11 +66,6 @@
// size measured in sizeof(uint32_t)
#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
-static void printToLogFunc(int32_t cookie, const char* txt)
-{
- ALOGV("[cookie=%d] %s", cookie, txt);
-}
-
// Standard C isspace() is only required to look at the low byte of its input, so
// produces incorrect results for UTF-16 characters. For safety's sake, assume that
// any high-byte UTF-16 code point is not whitespace.
@@ -1565,6 +1560,71 @@
}
}
+/* static */ size_t unpackLanguageOrRegion(const char in[2], const char base,
+ char out[4]) {
+ if (in[0] & 0x80) {
+ // The high bit is "1", which means this is a packed three letter
+ // language code.
+
+ // The smallest 5 bits of the second char are the first alphabet.
+ const uint8_t first = in[1] & 0x1f;
+ // The last three bits of the second char and the first two bits
+ // of the first char are the second alphabet.
+ const uint8_t second = ((in[1] & 0xe0) >> 5) + ((in[0] & 0x03) << 3);
+ // Bits 3 to 7 (inclusive) of the first char are the third alphabet.
+ const uint8_t third = (in[0] & 0x7c) >> 2;
+
+ out[0] = first + base;
+ out[1] = second + base;
+ out[2] = third + base;
+ out[3] = 0;
+
+ return 3;
+ }
+
+ if (in[0]) {
+ memcpy(out, in, 2);
+ memset(out + 2, 0, 2);
+ return 2;
+ }
+
+ memset(out, 0, 4);
+ return 0;
+}
+
+/* static */ void packLanguageOrRegion(const char* in, const char base,
+ char out[2]) {
+ if (in[2] == 0 || in[2] == '-') {
+ out[0] = in[0];
+ out[1] = in[1];
+ } else {
+ uint8_t first = (in[0] - base) & 0x00ef;
+ uint8_t second = (in[1] - base) & 0x00ef;
+ uint8_t third = (in[2] - base) & 0x00ef;
+
+ out[0] = (0x80 | (third << 2) | (second >> 3));
+ out[1] = ((second << 5) | first);
+ }
+}
+
+
+void ResTable_config::packLanguage(const char* language) {
+ packLanguageOrRegion(language, 'a', this->language);
+}
+
+void ResTable_config::packRegion(const char* region) {
+ packLanguageOrRegion(region, '0', this->country);
+}
+
+size_t ResTable_config::unpackLanguage(char language[4]) const {
+ return unpackLanguageOrRegion(this->language, 'a', language);
+}
+
+size_t ResTable_config::unpackRegion(char region[4]) const {
+ return unpackLanguageOrRegion(this->country, '0', region);
+}
+
+
void ResTable_config::copyFromDtoH(const ResTable_config& o) {
copyFromDeviceNoSwap(o);
size = sizeof(ResTable_config);
@@ -1594,10 +1654,30 @@
screenHeightDp = htods(screenHeightDp);
}
+/* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) {
+ if (l.locale != r.locale) {
+ // NOTE: This is the old behaviour with respect to comparison orders.
+ // The diff value here doesn't make much sense (given our bit packing scheme)
+ // but it's stable, and that's all we need.
+ return l.locale - r.locale;
+ }
+
+ // The language & region are equal, so compare the scripts and variants.
+ int script = memcmp(l.localeScript, r.localeScript, sizeof(l.localeScript));
+ if (script) {
+ return script;
+ }
+
+ // The language, region and script are equal, so compare variants.
+ //
+ // This should happen very infrequently (if at all.)
+ return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant));
+}
+
int ResTable_config::compare(const ResTable_config& o) const {
int32_t diff = (int32_t)(imsi - o.imsi);
if (diff != 0) return diff;
- diff = (int32_t)(locale - o.locale);
+ diff = compareLocales(*this, o);
if (diff != 0) return diff;
diff = (int32_t)(screenType - o.screenType);
if (diff != 0) return diff;
@@ -1624,18 +1704,15 @@
if (mnc != o.mnc) {
return mnc < o.mnc ? -1 : 1;
}
- if (language[0] != o.language[0]) {
- return language[0] < o.language[0] ? -1 : 1;
+
+ int diff = compareLocales(*this, o);
+ if (diff < 0) {
+ return -1;
}
- if (language[1] != o.language[1]) {
- return language[1] < o.language[1] ? -1 : 1;
+ if (diff > 0) {
+ return 1;
}
- if (country[0] != o.country[0]) {
- return country[0] < o.country[0] ? -1 : 1;
- }
- if (country[1] != o.country[1]) {
- return country[1] < o.country[1] ? -1 : 1;
- }
+
if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) {
return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1;
}
@@ -1682,7 +1759,6 @@
int diffs = 0;
if (mcc != o.mcc) diffs |= CONFIG_MCC;
if (mnc != o.mnc) diffs |= CONFIG_MNC;
- if (locale != o.locale) diffs |= CONFIG_LOCALE;
if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION;
if (density != o.density) diffs |= CONFIG_DENSITY;
if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN;
@@ -1697,9 +1773,44 @@
if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE;
if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE;
if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE;
+
+ const int diff = compareLocales(*this, o);
+ if (diff) diffs |= CONFIG_LOCALE;
+
return diffs;
}
+int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const {
+ if (locale || o.locale) {
+ if (language[0] != o.language[0]) {
+ if (!language[0]) return -1;
+ if (!o.language[0]) return 1;
+ }
+
+ if (country[0] != o.country[0]) {
+ if (!country[0]) return -1;
+ if (!o.country[0]) return 1;
+ }
+ }
+
+ // There isn't a well specified "importance" order between variants and
+ // scripts. We can't easily tell whether, say "en-Latn-US" is more or less
+ // specific than "en-US-POSIX".
+ //
+ // We therefore arbitrarily decide to give priority to variants over
+ // scripts since it seems more useful to do so. We will consider
+ // "en-US-POSIX" to be more specific than "en-Latn-US".
+
+ const int score = ((localeScript[0] != 0) ? 1 : 0) +
+ ((localeVariant[0] != 0) ? 2 : 0);
+
+ const int oScore = ((o.localeScript[0] != 0) ? 1 : 0) +
+ ((o.localeVariant[0] != 0) ? 2 : 0);
+
+ return score - oScore;
+
+}
+
bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
// The order of the following tests defines the importance of one
// configuration parameter over another. Those tests first are more
@@ -1717,14 +1828,13 @@
}
if (locale || o.locale) {
- if (language[0] != o.language[0]) {
- if (!language[0]) return false;
- if (!o.language[0]) return true;
+ const int diff = isLocaleMoreSpecificThan(o);
+ if (diff < 0) {
+ return false;
}
- if (country[0] != o.country[0]) {
- if (!country[0]) return false;
- if (!o.country[0]) return true;
+ if (diff > 0) {
+ return true;
}
}
@@ -1860,6 +1970,18 @@
}
}
+ if (localeScript[0] || o.localeScript[0]) {
+ if (localeScript[0] != o.localeScript[0] && requested->localeScript[0]) {
+ return localeScript[0];
+ }
+ }
+
+ if (localeVariant[0] || o.localeVariant[0]) {
+ if (localeVariant[0] != o.localeVariant[0] && requested->localeVariant[0]) {
+ return localeVariant[0];
+ }
+ }
+
if (screenLayout || o.screenLayout) {
if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0
&& (requested->screenLayout & MASK_LAYOUTDIR)) {
@@ -2080,17 +2202,23 @@
}
}
if (locale != 0) {
+ // Don't consider the script & variants when deciding matches.
+ //
+ // If we two configs differ only in their script or language, they
+ // can be weeded out in the isMoreSpecificThan test.
if (language[0] != 0
&& (language[0] != settings.language[0]
|| language[1] != settings.language[1])) {
return false;
}
+
if (country[0] != 0
&& (country[0] != settings.country[0]
|| country[1] != settings.country[1])) {
return false;
}
}
+
if (screenConfig != 0) {
const int layoutDir = screenLayout&MASK_LAYOUTDIR;
const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR;
@@ -2192,17 +2320,92 @@
return true;
}
-void ResTable_config::getLocale(char str[6]) const {
- memset(str, 0, 6);
- if (language[0]) {
- str[0] = language[0];
- str[1] = language[1];
- if (country[0]) {
- str[2] = '_';
- str[3] = country[0];
- str[4] = country[1];
- }
+void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN]) const {
+ memset(str, 0, RESTABLE_MAX_LOCALE_LEN);
+
+ // This represents the "any" locale value, which has traditionally been
+ // represented by the empty string.
+ if (!language[0] && !country[0]) {
+ return;
}
+
+ size_t charsWritten = 0;
+ if (language[0]) {
+ charsWritten += unpackLanguage(str);
+ }
+
+ if (localeScript[0]) {
+ if (charsWritten) {
+ str[charsWritten++] = '-';
+ }
+ memcpy(str + charsWritten, localeScript, sizeof(localeScript));
+ charsWritten += sizeof(localeScript);
+ }
+
+ if (country[0]) {
+ if (charsWritten) {
+ str[charsWritten++] = '-';
+ }
+ charsWritten += unpackRegion(str + charsWritten);
+ }
+
+ if (localeVariant[0]) {
+ if (charsWritten) {
+ str[charsWritten++] = '-';
+ }
+ memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
+ }
+}
+
+/* static */ inline bool assignLocaleComponent(ResTable_config* config,
+ const char* start, size_t size) {
+
+ switch (size) {
+ case 0:
+ return false;
+ case 2:
+ case 3:
+ config->language[0] ? config->packRegion(start) : config->packLanguage(start);
+ break;
+ case 4:
+ config->localeScript[0] = toupper(start[0]);
+ for (size_t i = 1; i < 4; ++i) {
+ config->localeScript[i] = tolower(start[i]);
+ }
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ for (size_t i = 0; i < size; ++i) {
+ config->localeVariant[i] = tolower(start[i]);
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void ResTable_config::setBcp47Locale(const char* in) {
+ locale = 0;
+ memset(localeScript, 0, sizeof(localeScript));
+ memset(localeVariant, 0, sizeof(localeVariant));
+
+ const char* separator = in;
+ const char* start = in;
+ while ((separator = strchr(start, '-')) != NULL) {
+ const size_t size = separator - start;
+ if (!assignLocaleComponent(this, start, size)) {
+ fprintf(stderr, "Invalid BCP-47 locale string: %s", in);
+ }
+
+ start = (separator + 1);
+ }
+
+ const size_t size = in + strlen(in) - start;
+ assignLocaleComponent(this, start, size);
}
String8 ResTable_config::toString() const {
@@ -2216,14 +2419,10 @@
if (res.size() > 0) res.append("-");
res.appendFormat("%dmnc", dtohs(mnc));
}
- if (language[0] != 0) {
- if (res.size() > 0) res.append("-");
- res.append(language, 2);
- }
- if (country[0] != 0) {
- if (res.size() > 0) res.append("-");
- res.append(country, 2);
- }
+ char localeStr[RESTABLE_MAX_LOCALE_LEN];
+ getBcp47Locale(localeStr);
+ res.append(localeStr);
+
if ((screenLayout&MASK_LAYOUTDIR) != 0) {
if (res.size() > 0) res.append("-");
switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) {
@@ -4976,18 +5175,20 @@
const size_t L = type->configs.size();
for (size_t l=0; l<L; l++) {
const ResTable_type* config = type->configs[l];
- const ResTable_config* cfg = &config->config;
+ ResTable_config cfg;
+ memset(&cfg, 0, sizeof(ResTable_config));
+ cfg.copyFromDtoH(config->config);
// only insert unique
const size_t M = configs->size();
size_t m;
for (m=0; m<M; m++) {
- if (0 == (*configs)[m].compare(*cfg)) {
+ if (0 == (*configs)[m].compare(cfg)) {
break;
}
}
// if we didn't find it
if (m == M) {
- configs->add(*cfg);
+ configs->add(cfg);
}
}
}
@@ -5002,9 +5203,10 @@
getConfigurations(&configs);
ALOGV("called getConfigurations size=%d", (int)configs.size());
const size_t I = configs.size();
+
+ char locale[RESTABLE_MAX_LOCALE_LEN];
for (size_t i=0; i<I; i++) {
- char locale[6];
- configs[i].getLocale(locale);
+ configs[i].getBcp47Locale(locale);
const size_t J = locales->size();
size_t j;
for (j=0; j<J; j++) {
@@ -5663,9 +5865,9 @@
printf("mError=0x%x (%s)\n", mError, strerror(mError));
}
#if 0
- printf("mParams=%c%c-%c%c,\n",
- mParams.language[0], mParams.language[1],
- mParams.country[0], mParams.country[1]);
+ char localeStr[RESTABLE_MAX_LOCALE_LEN];
+ mParams.getBcp47Locale(localeStr);
+ printf("mParams=%s,\n" localeStr);
#endif
size_t pgCount = mPackageGroups.size();
printf("Package Groups (%d)\n", (int)pgCount);
@@ -5794,7 +5996,7 @@
continue;
}
- uint16_t esize = dtohs(ent->size);
+ uintptr_t esize = dtohs(ent->size);
if ((esize&0x3) != 0) {
printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize);
continue;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 3c55375..977ba80 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -5,7 +5,8 @@
# Build the unit tests.
test_src_files := \
ObbFile_test.cpp \
- ZipUtils_test.cpp
+ ZipUtils_test.cpp \
+ ResourceTypes_test.cpp
shared_libraries := \
libandroidfw \
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
new file mode 100644
index 0000000..4888b4a
--- /dev/null
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+namespace android {
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterLanguage) {
+ ResTable_config config;
+ config.packLanguage("en");
+
+ EXPECT_EQ('e', config.language[0]);
+ EXPECT_EQ('n', config.language[1]);
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackLanguage(out);
+ EXPECT_EQ('e', out[0]);
+ EXPECT_EQ('n', out[1]);
+ EXPECT_EQ(0, out[2]);
+ EXPECT_EQ(0, out[3]);
+
+ memset(out, 1, sizeof(out));
+ config.locale = 0;
+ config.unpackLanguage(out);
+ EXPECT_EQ(0, out[0]);
+ EXPECT_EQ(0, out[1]);
+ EXPECT_EQ(0, out[2]);
+ EXPECT_EQ(0, out[3]);
+}
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack2LetterRegion) {
+ ResTable_config config;
+ config.packRegion("US");
+
+ EXPECT_EQ('U', config.country[0]);
+ EXPECT_EQ('S', config.country[1]);
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackRegion(out);
+ EXPECT_EQ('U', out[0]);
+ EXPECT_EQ('S', out[1]);
+ EXPECT_EQ(0, out[2]);
+ EXPECT_EQ(0, out[3]);
+}
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) {
+ ResTable_config config;
+ config.packLanguage("eng");
+
+ // 1-00110-01 101-00100
+ EXPECT_EQ(0x99, config.language[0]);
+ EXPECT_EQ(0xa4, config.language[1]);
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackLanguage(out);
+ EXPECT_EQ('e', out[0]);
+ EXPECT_EQ('n', out[1]);
+ EXPECT_EQ('g', out[2]);
+ EXPECT_EQ(0, out[3]);
+}
+
+TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterRegion) {
+ ResTable_config config;
+ config.packRegion("419");
+
+ char out[4] = { 1, 1, 1, 1};
+ config.unpackRegion(out);
+
+ EXPECT_EQ('4', out[0]);
+ EXPECT_EQ('1', out[1]);
+ EXPECT_EQ('9', out[2]);
+}
+
+/* static */ void fillIn(const char* lang, const char* country,
+ const char* script, const char* variant, ResTable_config* out) {
+ memset(out, 0, sizeof(ResTable_config));
+ if (lang != NULL) {
+ out->packLanguage(lang);
+ }
+
+ if (country != NULL) {
+ out->packRegion(country);
+ }
+
+ if (script != NULL) {
+ memcpy(out->localeScript, script, 4);
+ }
+
+ if (variant != NULL) {
+ memcpy(out->localeVariant, variant, strlen(variant));
+ }
+}
+
+TEST(ResourceTypesTest, IsMoreSpecificThan) {
+ ResTable_config l;
+ ResTable_config r;
+
+ fillIn("en", NULL, NULL, NULL, &l);
+ fillIn(NULL, NULL, NULL, NULL, &r);
+
+ EXPECT_TRUE(l.isMoreSpecificThan(r));
+ EXPECT_FALSE(r.isMoreSpecificThan(l));
+
+ fillIn("eng", NULL, NULL, NULL, &l);
+ EXPECT_TRUE(l.isMoreSpecificThan(r));
+ EXPECT_FALSE(r.isMoreSpecificThan(l));
+
+ fillIn("eng", "419", NULL, NULL, &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", NULL, NULL, NULL, &l);
+ fillIn("en", "US", NULL, NULL, &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", "US", NULL, NULL, &l);
+ fillIn("en", "US", "Latn", NULL, &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", "US", NULL, NULL, &l);
+ fillIn("en", "US", NULL, "POSIX", &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+ fillIn("en", "US", "Latn", NULL, &l);
+ fillIn("en", "US", NULL, "POSIX", &r);
+ EXPECT_FALSE(l.isMoreSpecificThan(r));
+ EXPECT_TRUE(r.isMoreSpecificThan(l));
+}
+
+TEST(ResourceTypesTest, setLocale) {
+ ResTable_config test;
+ test.setBcp47Locale("en-US");
+ EXPECT_EQ('e', test.language[0]);
+ EXPECT_EQ('n', test.language[1]);
+ EXPECT_EQ('U', test.country[0]);
+ EXPECT_EQ('S', test.country[1]);
+ EXPECT_EQ(0, test.localeScript[0]);
+ EXPECT_EQ(0, test.localeVariant[0]);
+
+ test.setBcp47Locale("eng-419");
+ char out[4] = { 1, 1, 1, 1};
+ test.unpackLanguage(out);
+ EXPECT_EQ('e', out[0]);
+ EXPECT_EQ('n', out[1]);
+ EXPECT_EQ('g', out[2]);
+ EXPECT_EQ(0, out[3]);
+ memset(out, 1, 4);
+ test.unpackRegion(out);
+ EXPECT_EQ('4', out[0]);
+ EXPECT_EQ('1', out[1]);
+ EXPECT_EQ('9', out[2]);
+
+
+ test.setBcp47Locale("en-Latn-419");
+ memset(out, 1, 4);
+ EXPECT_EQ('e', test.language[0]);
+ EXPECT_EQ('n', test.language[1]);
+
+ EXPECT_EQ(0, memcmp("Latn", test.localeScript, 4));
+ test.unpackRegion(out);
+ EXPECT_EQ('4', out[0]);
+ EXPECT_EQ('1', out[1]);
+ EXPECT_EQ('9', out[2]);
+}
+
+} // namespace android.
diff --git a/rs/java/android/renderscript/AllocationAdapter.java b/rs/java/android/renderscript/AllocationAdapter.java
index b77d087..fd20cae 100644
--- a/rs/java/android/renderscript/AllocationAdapter.java
+++ b/rs/java/android/renderscript/AllocationAdapter.java
@@ -224,7 +224,6 @@
}
static public AllocationAdapter create2D(RenderScript rs, Allocation a) {
- android.util.Log.e("rs", "create2d " + a);
rs.validate();
AllocationAdapter aa = new AllocationAdapter(0, rs, a);
aa.mConstrainedLOD = true;
diff --git a/rs/java/android/renderscript/BaseObj.java b/rs/java/android/renderscript/BaseObj.java
index 842aa23..b386dd7 100644
--- a/rs/java/android/renderscript/BaseObj.java
+++ b/rs/java/android/renderscript/BaseObj.java
@@ -122,7 +122,8 @@
// must include nObjDestroy in the critical section
ReentrantReadWriteLock.ReadLock rlock = mRS.mRWLock.readLock();
rlock.lock();
- if(mRS.isAlive()) {
+ // AllocationAdapters are BaseObjs with an ID of 0 but should not be passed to nObjDestroy
+ if(mRS.isAlive() && mID != 0) {
mRS.nObjDestroy(mID);
}
rlock.unlock();
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 8618764..b211c5a 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -103,6 +103,20 @@
static File mCacheDir;
+ // this should be a monotonically increasing ID
+ // used in conjunction with the API version of a device
+ static final long sMinorID = 1;
+
+ /**
+ * Returns an identifier that can be used to identify a particular
+ * minor version of RS.
+ *
+ * @hide
+ */
+ public static long getMinorID() {
+ return sMinorID;
+ }
+
/**
* Sets the directory to use as a persistent storage for the
* renderscript object file cache.
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 816ae69..f73a92b 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -91,6 +91,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -384,18 +385,37 @@
}
class ShutdownCallBack extends UnmountCallBack {
- IMountShutdownObserver observer;
- ShutdownCallBack(String path, IMountShutdownObserver observer) {
+ MountShutdownLatch mMountShutdownLatch;
+ ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
super(path, true, false);
- this.observer = observer;
+ mMountShutdownLatch = mountShutdownLatch;
}
@Override
void handleFinished() {
int ret = doUnmountVolume(path, true, removeEncryption);
- if (observer != null) {
+ Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
+ mMountShutdownLatch.countDown();
+ }
+ }
+
+ static class MountShutdownLatch {
+ private IMountShutdownObserver mObserver;
+ private AtomicInteger mCount;
+
+ MountShutdownLatch(final IMountShutdownObserver observer, int count) {
+ mObserver = observer;
+ mCount = new AtomicInteger(count);
+ }
+
+ void countDown() {
+ boolean sendShutdown = false;
+ if (mCount.decrementAndGet() == 0) {
+ sendShutdown = true;
+ }
+ if (sendShutdown && mObserver != null) {
try {
- observer.onShutDownComplete(ret);
+ mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
} catch (RemoteException e) {
Slog.w(TAG, "RemoteException when shutting down");
}
@@ -1426,6 +1446,10 @@
Slog.i(TAG, "Shutting down");
synchronized (mVolumesLock) {
+ // Get all volumes to be unmounted.
+ MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
+ mVolumeStates.size());
+
for (String path : mVolumeStates.keySet()) {
String state = mVolumeStates.get(path);
@@ -1461,19 +1485,16 @@
if (state.equals(Environment.MEDIA_MOUNTED)) {
// Post a unmount message.
- ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
+ ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
} else if (observer != null) {
/*
- * Observer is waiting for onShutDownComplete when we are done.
- * Since nothing will be done send notification directly so shutdown
- * sequence can continue.
+ * Count down, since nothing will be done. The observer will be
+ * notified when we are done so shutdown sequence can continue.
*/
- try {
- observer.onShutDownComplete(StorageResultCode.OperationSucceeded);
- } catch (RemoteException e) {
- Slog.w(TAG, "RemoteException when shutting down");
- }
+ mountShutdownLatch.countDown();
+ Slog.i(TAG, "Unmount completed: " + path +
+ ", result code: " + StorageResultCode.OperationSucceeded);
}
}
}
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index ad7ec99..e6c5422 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -82,6 +82,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
@@ -1022,6 +1023,15 @@
}
}
+ private List<InterfaceAddress> excludeLinkLocal(List<InterfaceAddress> addresses) {
+ ArrayList<InterfaceAddress> filtered = new ArrayList<InterfaceAddress>(addresses.size());
+ for (InterfaceAddress ia : addresses) {
+ if (!ia.getAddress().isLinkLocalAddress())
+ filtered.add(ia);
+ }
+ return filtered;
+ }
+
private void modifyNat(String action, String internalInterface, String externalInterface)
throws SocketException {
final Command cmd = new Command("nat", action, internalInterface, externalInterface);
@@ -1031,8 +1041,10 @@
if (internalNetworkInterface == null) {
cmd.appendArg("0");
} else {
- Collection<InterfaceAddress> interfaceAddresses = internalNetworkInterface
- .getInterfaceAddresses();
+ // Don't touch link-local routes, as link-local addresses aren't routable,
+ // kernel creates link-local routes on all interfaces automatically
+ List<InterfaceAddress> interfaceAddresses = excludeLinkLocal(
+ internalNetworkInterface.getInterfaceAddresses());
cmd.appendArg(interfaceAddresses.size());
for (InterfaceAddress ia : interfaceAddresses) {
InetAddress addr = NetworkUtils.getNetworkPart(
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
index aaa0b58..6df1dbd 100644
--- a/services/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -268,11 +268,13 @@
}
File[] files = rulesDir.listFiles();
- for (int i=0; i<files.length; i++) {
- File file = files[i];
+ if (files != null) {
+ for (int i=0; i<files.length; i++) {
+ File file = files[i];
- if (file.getName().endsWith(".xml")) {
- readRules(file, resolvers);
+ if (file.getName().endsWith(".xml")) {
+ readRules(file, resolvers);
+ }
}
}
diff --git a/services/jni/com_android_server_UsbHostManager.cpp b/services/jni/com_android_server_UsbHostManager.cpp
index f1fa6cf..fc6de60 100644
--- a/services/jni/com_android_server_UsbHostManager.cpp
+++ b/services/jni/com_android_server_UsbHostManager.cpp
@@ -163,8 +163,10 @@
return NULL;
int fd = usb_device_get_fd(device);
- if (fd < 0)
+ if (fd < 0) {
+ usb_device_close(device);
return NULL;
+ }
int newFD = dup(fd);
usb_device_close(device);
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index ebf4538..19532e8 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -149,205 +149,506 @@
// =========================================================================
// =========================================================================
-status_t
-AaptGroupEntry::parseNamePart(const String8& part, int* axis, uint32_t* value)
+/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
+ Vector<String8>* parts, const char separator) {
+ const char *p = chars;
+ const char *q;
+ while (NULL != (q = strchr(p, separator))) {
+ String8 val(p, q - p);
+ val.toLower();
+ parts->add(val);
+ p = q+1;
+ }
+
+ if (p < chars + strlen(chars)) {
+ String8 val(p);
+ val.toLower();
+ parts->add(val);
+ }
+}
+
+/* static */
+inline bool isAlpha(const String8& string) {
+ const size_t length = string.length();
+ for (size_t i = 0; i < length; ++i) {
+ if (!isalpha(string[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */
+inline bool isNumber(const String8& string) {
+ const size_t length = string.length();
+ for (size_t i = 0; i < length; ++i) {
+ if (!isdigit(string[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void AaptLocaleValue::setLanguage(const char* languageChars) {
+ size_t i = 0;
+ while ((*languageChars) != '\0') {
+ language[i++] = tolower(*languageChars);
+ languageChars++;
+ }
+}
+
+void AaptLocaleValue::setRegion(const char* regionChars) {
+ size_t i = 0;
+ while ((*regionChars) != '\0') {
+ region[i++] = toupper(*regionChars);
+ regionChars++;
+ }
+}
+
+void AaptLocaleValue::setScript(const char* scriptChars) {
+ size_t i = 0;
+ while ((*scriptChars) != '\0') {
+ if (i == 0) {
+ script[i++] = toupper(*scriptChars);
+ } else {
+ script[i++] = tolower(*scriptChars);
+ }
+ scriptChars++;
+ }
+}
+
+void AaptLocaleValue::setVariant(const char* variantChars) {
+ size_t i = 0;
+ while ((*variantChars) != '\0') {
+ variant[i++] = *variantChars;
+ variantChars++;
+ }
+}
+
+bool AaptLocaleValue::initFromFilterString(const String8& str) {
+ // A locale (as specified in the filter) is an underscore separated name such
+ // as "en_US", "en_Latn_US", or "en_US_POSIX".
+ Vector<String8> parts;
+ splitAndLowerCase(str.string(), &parts, '_');
+
+ const int numTags = parts.size();
+ bool valid = false;
+ if (numTags >= 1) {
+ const String8& lang = parts[0];
+ if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
+ setLanguage(lang.string());
+ valid = true;
+ }
+ }
+
+ if (!valid || numTags == 1) {
+ return valid;
+ }
+
+ // At this point, valid == true && numTags > 1.
+ const String8& part2 = parts[1];
+ if ((part2.length() == 2 && isAlpha(part2)) ||
+ (part2.length() == 3 && isNumber(part2))) {
+ setRegion(part2.string());
+ } else if (part2.length() == 4 && isAlpha(part2)) {
+ setScript(part2.string());
+ } else if (part2.length() >= 5 && part2.length() <= 8) {
+ setVariant(part2.string());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || numTags == 2) {
+ return valid;
+ }
+
+ // At this point, valid == true && numTags > 1.
+ const String8& part3 = parts[2];
+ if (((part3.length() == 2 && isAlpha(part3)) ||
+ (part3.length() == 3 && isNumber(part3))) && script[0]) {
+ setRegion(part3.string());
+ } else if (part3.length() >= 5 && part3.length() <= 8) {
+ setVariant(part3.string());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || numTags == 3) {
+ return valid;
+ }
+
+ const String8& part4 = parts[3];
+ if (part4.length() >= 5 && part4.length() <= 8) {
+ setVariant(part4.string());
+ } else {
+ valid = false;
+ }
+
+ if (!valid || numTags > 4) {
+ return false;
+ }
+
+ return true;
+}
+
+int AaptLocaleValue::initFromDirName(const Vector<String8>& parts, const int startIndex) {
+ const int size = parts.size();
+ int currentIndex = startIndex;
+
+ String8 part = parts[currentIndex];
+ if (part[0] == 'b' && part[1] == '+') {
+ // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
+ // except that the separator is "+" and not "-".
+ Vector<String8> subtags;
+ AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+ subtags.removeItemsAt(0);
+ if (subtags.size() == 1) {
+ setLanguage(subtags[0]);
+ } else if (subtags.size() == 2) {
+ setLanguage(subtags[0]);
+
+ // The second tag can either be a region, a variant or a script.
+ switch (subtags[1].size()) {
+ case 2:
+ case 3:
+ setRegion(subtags[1]);
+ break;
+ case 4:
+ setScript(subtags[1]);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ setVariant(subtags[1]);
+ break;
+ default:
+ fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n",
+ part.string());
+ return -1;
+ }
+ } else if (subtags.size() == 3) {
+ // The language is always the first subtag.
+ setLanguage(subtags[0]);
+
+ // The second subtag can either be a script or a region code.
+ // If its size is 4, it's a script code, else it's a region code.
+ bool hasRegion = false;
+ if (subtags[1].size() == 4) {
+ setScript(subtags[1]);
+ } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+ setRegion(subtags[1]);
+ hasRegion = true;
+ } else {
+ fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name %s\n", part.string());
+ return -1;
+ }
+
+ // The third tag can either be a region code (if the second tag was
+ // a script), else a variant code.
+ if (subtags[2].size() > 4) {
+ setVariant(subtags[2]);
+ } else {
+ setRegion(subtags[2]);
+ }
+ } else if (subtags.size() == 4) {
+ setLanguage(subtags[0]);
+ setScript(subtags[1]);
+ setRegion(subtags[2]);
+ setVariant(subtags[3]);
+ } else {
+ fprintf(stderr, "ERROR: Invalid BCP-47 tag in directory name: %s\n", part.string());
+ return -1;
+ }
+
+ return ++currentIndex;
+ } else {
+ if ((part.length() == 2 || part.length() == 3) && isAlpha(part)) {
+ setLanguage(part);
+ if (++currentIndex == size) {
+ return size;
+ }
+ } else {
+ return currentIndex;
+ }
+
+ part = parts[currentIndex];
+ if (part.string()[0] == 'r' && part.length() == 3) {
+ setRegion(part.string() + 1);
+ if (++currentIndex == size) {
+ return size;
+ }
+ }
+ }
+
+ return currentIndex;
+}
+
+
+String8 AaptLocaleValue::toDirName() const {
+ String8 dirName("");
+ if (language[0]) {
+ dirName += language;
+ } else {
+ return dirName;
+ }
+
+ if (script[0]) {
+ dirName += "-s";
+ dirName += script;
+ }
+
+ if (region[0]) {
+ dirName += "-r";
+ dirName += region;
+ }
+
+ if (variant[0]) {
+ dirName += "-v";
+ dirName += variant;
+ }
+
+ return dirName;
+}
+
+void AaptLocaleValue::initFromResTable(const ResTable_config& config) {
+ config.unpackLanguage(language);
+ config.unpackRegion(region);
+ if (config.localeScript[0]) {
+ memcpy(script, config.localeScript, sizeof(config.localeScript));
+ }
+
+ if (config.localeVariant[0]) {
+ memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
+ }
+}
+
+void AaptLocaleValue::writeTo(ResTable_config* out) const {
+ out->packLanguage(language);
+ out->packRegion(region);
+
+ if (script[0]) {
+ memcpy(out->localeScript, script, sizeof(out->localeScript));
+ }
+
+ if (variant[0]) {
+ memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
+ }
+}
+
+
+/* static */ bool
+AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
{
ResTable_config config;
+ memset(&config, 0, sizeof(ResTable_config));
// IMSI - MCC
if (getMccName(part.string(), &config)) {
*axis = AXIS_MCC;
- *value = config.mcc;
- return 0;
+ value->intValue = config.mcc;
+ return true;
}
// IMSI - MNC
if (getMncName(part.string(), &config)) {
*axis = AXIS_MNC;
- *value = config.mnc;
- return 0;
+ value->intValue = config.mnc;
+ return true;
}
// locale - language
- if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
- *axis = AXIS_LANGUAGE;
- *value = part[1] << 8 | part[0];
- return 0;
- }
-
- // locale - language_REGION
- if (part.length() == 5 && isalpha(part[0]) && isalpha(part[1])
- && part[2] == '_' && isalpha(part[3]) && isalpha(part[4])) {
- *axis = AXIS_LANGUAGE;
- *value = (part[4] << 24) | (part[3] << 16) | (part[1] << 8) | (part[0]);
- return 0;
+ if (value->localeValue.initFromFilterString(part)) {
+ *axis = AXIS_LOCALE;
+ return true;
}
// layout direction
if (getLayoutDirectionName(part.string(), &config)) {
*axis = AXIS_LAYOUTDIR;
- *value = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
- return 0;
+ value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
+ return true;
}
// smallest screen dp width
if (getSmallestScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SMALLESTSCREENWIDTHDP;
- *value = config.smallestScreenWidthDp;
- return 0;
+ value->intValue = config.smallestScreenWidthDp;
+ return true;
}
// screen dp width
if (getScreenWidthDpName(part.string(), &config)) {
*axis = AXIS_SCREENWIDTHDP;
- *value = config.screenWidthDp;
- return 0;
+ value->intValue = config.screenWidthDp;
+ return true;
}
// screen dp height
if (getScreenHeightDpName(part.string(), &config)) {
*axis = AXIS_SCREENHEIGHTDP;
- *value = config.screenHeightDp;
- return 0;
+ value->intValue = config.screenHeightDp;
+ return true;
}
// screen layout size
if (getScreenLayoutSizeName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTSIZE;
- *value = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
- return 0;
+ value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
+ return true;
}
// screen layout long
if (getScreenLayoutLongName(part.string(), &config)) {
*axis = AXIS_SCREENLAYOUTLONG;
- *value = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
- return 0;
+ value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
+ return true;
}
// orientation
if (getOrientationName(part.string(), &config)) {
*axis = AXIS_ORIENTATION;
- *value = config.orientation;
- return 0;
+ value->intValue = config.orientation;
+ return true;
}
// ui mode type
if (getUiModeTypeName(part.string(), &config)) {
*axis = AXIS_UIMODETYPE;
- *value = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
- return 0;
+ value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ return true;
}
// ui mode night
if (getUiModeNightName(part.string(), &config)) {
*axis = AXIS_UIMODENIGHT;
- *value = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
- return 0;
+ value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ return true;
}
// density
if (getDensityName(part.string(), &config)) {
*axis = AXIS_DENSITY;
- *value = config.density;
- return 0;
+ value->intValue = config.density;
+ return true;
}
// touchscreen
if (getTouchscreenName(part.string(), &config)) {
*axis = AXIS_TOUCHSCREEN;
- *value = config.touchscreen;
- return 0;
+ value->intValue = config.touchscreen;
+ return true;
}
// keyboard hidden
if (getKeysHiddenName(part.string(), &config)) {
*axis = AXIS_KEYSHIDDEN;
- *value = config.inputFlags;
- return 0;
+ value->intValue = config.inputFlags;
+ return true;
}
// keyboard
if (getKeyboardName(part.string(), &config)) {
*axis = AXIS_KEYBOARD;
- *value = config.keyboard;
- return 0;
+ value->intValue = config.keyboard;
+ return true;
}
// navigation hidden
if (getNavHiddenName(part.string(), &config)) {
*axis = AXIS_NAVHIDDEN;
- *value = config.inputFlags;
+ value->intValue = config.inputFlags;
return 0;
}
// navigation
if (getNavigationName(part.string(), &config)) {
*axis = AXIS_NAVIGATION;
- *value = config.navigation;
- return 0;
+ value->intValue = config.navigation;
+ return true;
}
// screen size
if (getScreenSizeName(part.string(), &config)) {
*axis = AXIS_SCREENSIZE;
- *value = config.screenSize;
- return 0;
+ value->intValue = config.screenSize;
+ return true;
}
// version
if (getVersionName(part.string(), &config)) {
*axis = AXIS_VERSION;
- *value = config.version;
- return 0;
+ value->intValue = config.version;
+ return true;
}
- return 1;
+ return false;
}
-uint32_t
+AxisValue
AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
{
+ AxisValue value;
switch (axis) {
case AXIS_MCC:
- return config.mcc;
+ value.intValue = config.mcc;
+ break;
case AXIS_MNC:
- return config.mnc;
- case AXIS_LANGUAGE:
- return (((uint32_t)config.country[1]) << 24) | (((uint32_t)config.country[0]) << 16)
- | (((uint32_t)config.language[1]) << 8) | (config.language[0]);
+ value.intValue = config.mnc;
+ break;
+ case AXIS_LOCALE:
+ value.localeValue.initFromResTable(config);
+ break;
case AXIS_LAYOUTDIR:
- return config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
+ value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
+ break;
case AXIS_SCREENLAYOUTSIZE:
- return config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+ value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
+ break;
case AXIS_ORIENTATION:
- return config.orientation;
+ value.intValue = config.orientation;
+ break;
case AXIS_UIMODETYPE:
- return (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
+ break;
case AXIS_UIMODENIGHT:
- return (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
+ break;
case AXIS_DENSITY:
- return config.density;
+ value.intValue = config.density;
+ break;
case AXIS_TOUCHSCREEN:
- return config.touchscreen;
+ value.intValue = config.touchscreen;
+ break;
case AXIS_KEYSHIDDEN:
- return config.inputFlags;
+ value.intValue = config.inputFlags;
+ break;
case AXIS_KEYBOARD:
- return config.keyboard;
+ value.intValue = config.keyboard;
+ break;
case AXIS_NAVIGATION:
- return config.navigation;
+ value.intValue = config.navigation;
+ break;
case AXIS_SCREENSIZE:
- return config.screenSize;
+ value.intValue = config.screenSize;
+ break;
case AXIS_SMALLESTSCREENWIDTHDP:
- return config.smallestScreenWidthDp;
+ value.intValue = config.smallestScreenWidthDp;
+ break;
case AXIS_SCREENWIDTHDP:
- return config.screenWidthDp;
+ value.intValue = config.screenWidthDp;
+ break;
case AXIS_SCREENHEIGHTDP:
- return config.screenHeightDp;
+ value.intValue = config.screenHeightDp;
+ break;
case AXIS_VERSION:
- return config.version;
+ value.intValue = config.version;
+ break;
}
- return 0;
+
+ return value;
}
bool
@@ -371,24 +672,14 @@
mParamsChanged = true;
Vector<String8> parts;
+ AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
- String8 mcc, mnc, loc, layoutsize, layoutlong, orient, den;
+ String8 mcc, mnc, layoutsize, layoutlong, orient, den;
String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
- const char *p = dir;
- const char *q;
- while (NULL != (q = strchr(p, '-'))) {
- String8 val(p, q-p);
- val.toLower();
- parts.add(val);
- //printf("part: %s\n", parts[parts.size()-1].string());
- p = q+1;
- }
- String8 val(p);
- val.toLower();
- parts.add(val);
- //printf("part: %s\n", parts[parts.size()-1].string());
+ AaptLocaleValue locale;
+ int numLocaleComponents = 0;
const int N = parts.size();
int index = 0;
@@ -429,38 +720,18 @@
}
part = parts[index];
} else {
- //printf("not mcc: %s\n", part.string());
+ //printf("not mnc: %s\n", part.string());
}
- // locale - language
- if (part.length() == 2 && isalpha(part[0]) && isalpha(part[1])) {
- loc = part;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not language: %s\n", part.string());
+ index = locale.initFromDirName(parts, index);
+ if (index == -1) {
+ return false;
+ }
+ if (index >= N){
+ goto success;
}
- // locale - region
- if (loc.length() > 0
- && part.length() == 3 && part[0] == 'r' && part[0] && part[1]) {
- loc += "-";
- part.toUpper();
- loc += part.string() + 1;
-
- index++;
- if (index == N) {
- goto success;
- }
- part = parts[index];
- } else {
- //printf("not region: %s\n", part.string());
- }
-
+ part = parts[index];
if (getLayoutDirectionName(part.string())) {
layoutDir = part;
@@ -679,7 +950,7 @@
success:
this->mcc = mcc;
this->mnc = mnc;
- this->locale = loc;
+ this->locale = locale;
this->screenLayoutSize = layoutsize;
this->screenLayoutLong = layoutlong;
this->smallestScreenWidthDp = smallestwidthdp;
@@ -711,7 +982,7 @@
s += ",";
s += this->mnc;
s += ",";
- s += this->locale;
+ s += locale.toDirName();
s += ",";
s += layoutDirection;
s += ",";
@@ -765,12 +1036,15 @@
}
s += mnc;
}
- if (this->locale != "") {
- if (s.length() > 0) {
- s += "-";
- }
- s += locale;
+
+ const String8 localeComponent = locale.toDirName();
+ if (localeComponent != "") {
+ if (s.length() > 0) {
+ s += "-";
+ }
+ s += localeComponent;
}
+
if (this->layoutDirection != "") {
if (s.length() > 0) {
s += "-";
@@ -942,55 +1216,6 @@
return true;
}
-/*
- * Does this directory name fit the pattern of a locale dir ("en-rUS" or
- * "default")?
- *
- * TODO: Should insist that the first two letters are lower case, and the
- * second two are upper.
- */
-bool AaptGroupEntry::getLocaleName(const char* fileName,
- ResTable_config* out)
-{
- if (strcmp(fileName, kWildcardName) == 0
- || strcmp(fileName, kDefaultLocale) == 0) {
- if (out) {
- out->language[0] = 0;
- out->language[1] = 0;
- out->country[0] = 0;
- out->country[1] = 0;
- }
- return true;
- }
-
- if (strlen(fileName) == 2 && isalpha(fileName[0]) && isalpha(fileName[1])) {
- if (out) {
- out->language[0] = fileName[0];
- out->language[1] = fileName[1];
- out->country[0] = 0;
- out->country[1] = 0;
- }
- return true;
- }
-
- if (strlen(fileName) == 5 &&
- isalpha(fileName[0]) &&
- isalpha(fileName[1]) &&
- fileName[2] == '-' &&
- isalpha(fileName[3]) &&
- isalpha(fileName[4])) {
- if (out) {
- out->language[0] = fileName[0];
- out->language[1] = fileName[1];
- out->country[0] = fileName[3];
- out->country[1] = fileName[4];
- }
- return true;
- }
-
- return false;
-}
-
bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
{
if (strcmp(name, kWildcardName) == 0) {
@@ -1496,18 +1721,18 @@
return v;
}
-const ResTable_config& AaptGroupEntry::toParams() const
+const ResTable_config AaptGroupEntry::toParams() const
{
if (!mParamsChanged) {
return mParams;
}
mParamsChanged = false;
- ResTable_config& params(mParams);
- memset(¶ms, 0, sizeof(params));
+ ResTable_config& params = mParams;
+ memset(¶ms, 0, sizeof(ResTable_config));
getMccName(mcc.string(), ¶ms);
getMncName(mnc.string(), ¶ms);
- getLocaleName(locale.string(), ¶ms);
+ locale.writeTo(¶ms);
getLayoutDirectionName(layoutDirection.string(), ¶ms);
getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), ¶ms);
getScreenWidthDpName(screenWidthDp.string(), ¶ms);
@@ -1982,7 +2207,9 @@
AaptAssets::AaptAssets()
: AaptDir(String8(), String8()),
- mChanged(false), mHaveIncludedAssets(false), mRes(NULL)
+ mHavePrivateSymbols(false),
+ mChanged(false), mHaveIncludedAssets(false),
+ mRes(NULL)
{
}
@@ -2491,9 +2718,9 @@
// If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
// pick xhdpi.
uint32_t preferredDensity = 0;
- const SortedVector<uint32_t>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
+ const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
- preferredDensity = (*preferredConfigs)[0];
+ preferredDensity = (*preferredConfigs)[0].intValue;
}
// Now deal with preferred configurations.
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 5cfa913..9733b6d 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -13,7 +13,6 @@
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
-#include <utils/String8.h>
#include <utils/Vector.h>
#include "ZipFile.h"
@@ -34,8 +33,7 @@
AXIS_NONE = 0,
AXIS_MCC = 1,
AXIS_MNC,
- AXIS_LANGUAGE,
- AXIS_REGION,
+ AXIS_LOCALE,
AXIS_SCREENLAYOUTSIZE,
AXIS_SCREENLAYOUTLONG,
AXIS_ORIENTATION,
@@ -58,6 +56,73 @@
AXIS_END = AXIS_VERSION,
};
+struct AaptLocaleValue {
+ char language[4];
+ char region[4];
+ char script[4];
+ char variant[8];
+
+ AaptLocaleValue() {
+ memset(this, 0, sizeof(AaptLocaleValue));
+ }
+
+ // Initialize this AaptLocaleValue from a config string.
+ bool initFromFilterString(const String8& config);
+
+ int initFromDirName(const Vector<String8>& parts, const int startIndex);
+
+ // Initialize this AaptLocaleValue from a ResTable_config.
+ void initFromResTable(const ResTable_config& config);
+
+ void writeTo(ResTable_config* out) const;
+
+ String8 toDirName() const;
+
+ int compare(const AaptLocaleValue& other) const {
+ return memcmp(this, &other, sizeof(AaptLocaleValue));
+ }
+
+ static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
+ const char separator);
+
+ inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
+ inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
+ inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
+ inline bool operator!=(const AaptLocaleValue& o) const { return compare(o) != 0; }
+ inline bool operator>=(const AaptLocaleValue& o) const { return compare(o) >= 0; }
+ inline bool operator>(const AaptLocaleValue& o) const { return compare(o) > 0; }
+private:
+ void setLanguage(const char* language);
+ void setRegion(const char* language);
+ void setScript(const char* script);
+ void setVariant(const char* variant);
+};
+
+struct AxisValue {
+ // Used for all axes except AXIS_LOCALE, which is represented
+ // as a AaptLocaleValue value.
+ int intValue;
+ AaptLocaleValue localeValue;
+
+ AxisValue() : intValue(0) {
+ }
+
+ inline int compare(const AxisValue &other) const {
+ if (intValue != other.intValue) {
+ return intValue - other.intValue;
+ }
+
+ return localeValue.compare(other.localeValue);
+ }
+
+ inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
+ inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
+ inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
+ inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
+ inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
+ inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
+};
+
/**
* This structure contains a specific variation of a single file out
* of all the variations it can have that we can have.
@@ -65,22 +130,38 @@
struct AaptGroupEntry
{
public:
- AaptGroupEntry() : mParamsChanged(true) { }
- AaptGroupEntry(const String8& _locale, const String8& _vendor)
- : locale(_locale), vendor(_vendor), mParamsChanged(true) { }
+ AaptGroupEntry() : mParamsChanged(true) {
+ memset(&mParams, 0, sizeof(ResTable_config));
+ }
bool initFromDirName(const char* dir, String8* resType);
- static status_t parseNamePart(const String8& part, int* axis, uint32_t* value);
+ static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
- static uint32_t getConfigValueForAxis(const ResTable_config& config, int axis);
+ static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
static bool configSameExcept(const ResTable_config& config,
const ResTable_config& otherConfig, int axis);
+ int compare(const AaptGroupEntry& o) const;
+
+ const ResTable_config toParams() const;
+
+ inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
+ inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
+ inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
+ inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
+ inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
+ inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
+
+ String8 toString() const;
+ String8 toDirName(const String8& resType) const;
+
+ const String8& getVersionString() const { return version; }
+
+private:
static bool getMccName(const char* name, ResTable_config* out = NULL);
static bool getMncName(const char* name, ResTable_config* out = NULL);
- static bool getLocaleName(const char* name, ResTable_config* out = NULL);
static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
static bool getOrientationName(const char* name, ResTable_config* out = NULL);
@@ -99,26 +180,9 @@
static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
static bool getVersionName(const char* name, ResTable_config* out = NULL);
- int compare(const AaptGroupEntry& o) const;
-
- const ResTable_config& toParams() const;
-
- inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
- inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
- inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
- inline bool operator!=(const AaptGroupEntry& o) const { return compare(o) != 0; }
- inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
- inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
-
- String8 toString() const;
- String8 toDirName(const String8& resType) const;
-
- const String8& getVersionString() const { return version; }
-
-private:
String8 mcc;
String8 mnc;
- String8 locale;
+ AaptLocaleValue locale;
String8 vendor;
String8 smallestScreenWidthDp;
String8 screenWidthDp;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 8a6faed..c7cce96 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -517,6 +517,7 @@
// the API version because key resources like icons will have an implicit
// version if they are using newer config types like density.
ResTable_config config;
+ memset(&config, 0, sizeof(ResTable_config));
config.language[0] = 'e';
config.language[1] = 'n';
config.country[0] = 'U';
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 57d44f2..4d29ff7 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -80,6 +80,7 @@
ResourceDirIterator(const sp<ResourceTypeSet>& set, const String8& resType)
: mResType(resType), mSet(set), mSetPos(0), mGroupPos(0)
{
+ memset(&mParams, 0, sizeof(ResTable_config));
}
inline const sp<AaptGroup>& getGroup() const { return mGroup; }
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8cfd2a5..e8a2be4 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -28,8 +28,8 @@
mContainsPseudo = true;
}
int axis;
- uint32_t value;
- if (AaptGroupEntry::parseNamePart(part, &axis, &value)) {
+ AxisValue value;
+ if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
fprintf(stderr, "Invalid configuration: %s\n", arg);
fprintf(stderr, " ");
for (int i=0; i<p-arg; i++) {
@@ -44,15 +44,20 @@
ssize_t index = mData.indexOfKey(axis);
if (index < 0) {
- mData.add(axis, SortedVector<uint32_t>());
+ mData.add(axis, SortedVector<AxisValue>());
}
- SortedVector<uint32_t>& sv = mData.editValueFor(axis);
+ SortedVector<AxisValue>& sv = mData.editValueFor(axis);
sv.add(value);
- // if it's a locale with a region, also match an unmodified locale of the
- // same language
- if (axis == AXIS_LANGUAGE) {
- if (value & 0xffff0000) {
- sv.add(value & 0x0000ffff);
+
+ // If it's a locale with a region, script or variant, we should also match an
+ // unmodified locale of the same language
+ if (axis == AXIS_LOCALE) {
+ if (value.localeValue.region[0] || value.localeValue.script[0] ||
+ value.localeValue.variant[0]) {
+ AxisValue copy;
+ memcpy(copy.localeValue.language, value.localeValue.language,
+ sizeof(value.localeValue.language));
+ sv.add(copy);
}
}
p = q;
@@ -70,9 +75,9 @@
}
bool
-ResourceFilter::match(int axis, uint32_t value) const
+ResourceFilter::match(int axis, const AxisValue& value) const
{
- if (value == 0) {
+ if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
// they didn't specify anything so take everything
return true;
}
@@ -81,7 +86,7 @@
// we didn't request anything on this axis so take everything
return true;
}
- const SortedVector<uint32_t>& sv = mData.valueAt(index);
+ const SortedVector<AxisValue>& sv = mData.valueAt(index);
return sv.indexOf(value) >= 0;
}
@@ -102,7 +107,7 @@
return true;
}
-const SortedVector<uint32_t>* ResourceFilter::configsForAxis(int axis) const
+const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
{
ssize_t index = mData.indexOfKey(axis);
if (index < 0) {
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index 647b7bb..0d127ba 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -19,14 +19,15 @@
ResourceFilter() : mData(), mContainsPseudo(false) {}
status_t parse(const char* arg);
bool isEmpty() const;
- bool match(int axis, uint32_t value) const;
bool match(int axis, const ResTable_config& config) const;
bool match(const ResTable_config& config) const;
- const SortedVector<uint32_t>* configsForAxis(int axis) const;
+ const SortedVector<AxisValue>* configsForAxis(int axis) const;
inline bool containsPseudo() const { return mContainsPseudo; }
private:
- KeyedVector<int,SortedVector<uint32_t> > mData;
+ bool match(int axis, const AxisValue& value) const;
+
+ KeyedVector<int,SortedVector<AxisValue> > mData;
bool mContainsPseudo;
};
diff --git a/tools/aapt/ResourceIdCache.h b/tools/aapt/ResourceIdCache.h
index 65f7781..e6bcda2 100644
--- a/tools/aapt/ResourceIdCache.h
+++ b/tools/aapt/ResourceIdCache.h
@@ -7,7 +7,6 @@
#define RESOURCE_ID_CACHE_H
namespace android {
-class android::String16;
class ResourceIdCache {
public:
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6ced8b3..38bf540 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1292,8 +1292,8 @@
curIsStyled = true;
} else if (strcmp16(block.getElementName(&len), string16.string()) == 0) {
// Note the existence and locale of every string we process
- char rawLocale[16];
- curParams.getLocale(rawLocale);
+ char rawLocale[RESTABLE_MAX_LOCALE_LEN];
+ curParams.getBcp47Locale(rawLocale);
String8 locale(rawLocale);
String16 name;
String16 translatable;