19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* //device/libs/android_runtime/android_text_AndroidCharacter.cpp 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** Copyright 2006, The Android Open Source Project 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** 569a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** Licensed under the Apache License, Version 2.0 (the "License"); 669a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** you may not use this file except in compliance with the License. 769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** You may obtain a copy of the License at 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** 969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** http://www.apache.org/licenses/LICENSE-2.0 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** 1169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** Unless required by applicable law or agreed to in writing, software 1269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** distributed under the License is distributed on an "AS IS" BASIS, 1369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes** See the License for the specific language governing permissions and 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project** limitations under the License. 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project*/ 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#define LOG_TAG "AndroidUnicode" 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 201fc4d880909362127af9873dbacae1d00eb39d8bJim Huang#include "JNIHelp.h" 2169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes#include "ScopedPrimitiveArray.h" 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include <android_runtime/AndroidRuntime.h> 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "utils/misc.h" 249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project#include "utils/Log.h" 25a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root#include "unicode/uchar.h" 26a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 27bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root#define PROPERTY_UNDEFINED (-1) 28bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 29a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root// ICU => JDK mapping 30a9886c580b299984e62303a995bf7b13276b5bc8Kenny Rootstatic int directionality_map[U_CHAR_DIRECTION_COUNT] = { 31a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 0, // U_LEFT_TO_RIGHT (0) => DIRECTIONALITY_LEFT_TO_RIGHT (0) 32a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 1, // U_RIGHT_TO_LEFT (1) => DIRECTIONALITY_RIGHT_TO_LEFT (1) 33a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 3, // U_EUROPEAN_NUMBER (2) => DIRECTIONALITY_EUROPEAN_NUMBER (3) 34a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 4, // U_EUROPEAN_NUMBER_SEPARATOR (3) => DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR (4) 35a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 5, // U_EUROPEAN_NUMBER_TERMINATOR (4) => DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR (5) 36a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 6, // U_ARABIC_NUMBER (5) => DIRECTIONALITY_ARABIC_NUMBER (6) 37a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 7, // U_COMMON_NUMBER_SEPARATOR (6) => DIRECTIONALITY_COMMON_NUMBER_SEPARATOR (7) 38a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 10, // U_BLOCK_SEPARATOR (7) => DIRECTIONALITY_PARAGRAPH_SEPARATOR (10) 39a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 11, // U_SEGMENT_SEPARATOR (8) => DIRECTIONALITY_SEGMENT_SEPARATOR (11) 40a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 12, // U_WHITE_SPACE_NEUTRAL (9) => DIRECTIONALITY_WHITESPACE (12) 41a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 13, // U_OTHER_NEUTRAL (10) => DIRECTIONALITY_OTHER_NEUTRALS (13) 42a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 14, // U_LEFT_TO_RIGHT_EMBEDDING (11) => DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING (14) 43a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 15, // U_LEFT_TO_RIGHT_OVERRIDE (12) => DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE (15) 44a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 2, // U_RIGHT_TO_LEFT_ARABIC (13) => DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC (2) 45a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 16, // U_RIGHT_TO_LEFT_EMBEDDING (14) => DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING (16) 46a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 17, // U_RIGHT_TO_LEFT_OVERRIDE (15) => DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE (17) 47a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 18, // U_POP_DIRECTIONAL_FORMAT (16) => DIRECTIONALITY_POP_DIRECTIONAL_FORMAT (18) 48a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 8, // U_DIR_NON_SPACING_MARK (17) => DIRECTIONALITY_NONSPACING_MARK (8) 49a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root 9, // U_BOUNDARY_NEUTRAL (18) => DIRECTIONALITY_BOUNDARY_NEUTRAL (9) 50a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root}; 519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectnamespace android { 5369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes 54f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhatstatic void getDirectionalities(JNIEnv* env, jobject obj, jcharArray srcArray, 55f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhat jbyteArray destArray, jint count) 569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{ 5769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes ScopedCharArrayRO src(env, srcArray); 5869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (src.get() == NULL) { 5969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 6069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 6169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes ScopedByteArrayRW dest(env, destArray); 6269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (dest.get() == NULL) { 6369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (env->GetArrayLength(srcArray) < count || env->GetArrayLength(destArray) < count) { 679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 6869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = 0; i < count; i++) { 729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (src[i] >= 0xD800 && src[i] <= 0xDBFF && 739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project i + 1 < count && 749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project src[i + 1] >= 0xDC00 && src[i + 1] <= 0xDFFF) { 759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int c = 0x00010000 + ((src[i] - 0xD800) << 10) + 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (src[i + 1] & 0x3FF); 77a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root int dir = u_charDirection(c); 78a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT) 79bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root dir = PROPERTY_UNDEFINED; 80a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root else 81a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root dir = directionality_map[dir]; 829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest[i++] = dir; 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project dest[i] = dir; 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int c = src[i]; 87a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root int dir = u_charDirection(c); 88a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root if (dir < 0 || dir >= U_CHAR_DIRECTION_COUNT) 89bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root dest[i] = PROPERTY_UNDEFINED; 90a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root else 91a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root dest[i] = directionality_map[dir]; 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 96bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Rootstatic jint getEastAsianWidth(JNIEnv* env, jobject obj, jchar input) 97bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root{ 98bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root int width = u_getIntPropertyValue(input, UCHAR_EAST_ASIAN_WIDTH); 99bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root if (width < 0 || width >= U_EA_COUNT) 100bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root width = PROPERTY_UNDEFINED; 101bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 102bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root return width; 103bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root} 104bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 105bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Rootstatic void getEastAsianWidths(JNIEnv* env, jobject obj, jcharArray srcArray, 106f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhat jint start, jint count, jbyteArray destArray) 107bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root{ 10869a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes ScopedCharArrayRO src(env, srcArray); 10969a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (src.get() == NULL) { 11069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 11169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes } 11269a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes ScopedByteArrayRW dest(env, destArray); 11369a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (dest.get() == NULL) { 11469a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 115bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root } 116bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 117bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root if (start < 0 || start > start + count 118bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root || env->GetArrayLength(srcArray) < (start + count) 119bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root || env->GetArrayLength(destArray) < count) { 120bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 12169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes return; 122bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root } 123bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 124bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root for (int i = 0; i < count; i++) { 125bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root const int srci = start + i; 126bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root if (src[srci] >= 0xD800 && src[srci] <= 0xDBFF && 127bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root i + 1 < count && 128bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root src[srci + 1] >= 0xDC00 && src[srci + 1] <= 0xDFFF) { 129bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root int c = 0x00010000 + ((src[srci] - 0xD800) << 10) + 130bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root (src[srci + 1] & 0x3FF); 131bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH); 132bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root if (width < 0 || width >= U_EA_COUNT) 133bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root width = PROPERTY_UNDEFINED; 134bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 135bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root dest[i++] = width; 136bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root dest[i] = width; 137bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root } else { 138bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root int c = src[srci]; 139bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root int width = u_getIntPropertyValue(c, UCHAR_EAST_ASIAN_WIDTH); 140bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root if (width < 0 || width >= U_EA_COUNT) 141bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root width = PROPERTY_UNDEFINED; 142bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 143bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root dest[i] = width; 144bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root } 145bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root } 146bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root} 147bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root 148f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhatstatic jboolean mirror(JNIEnv* env, jobject obj, jcharArray charArray, jint start, jint count) 1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{ 15069a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes ScopedCharArrayRW data(env, charArray); 15169a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes if (data.get() == NULL) { 152f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhat return JNI_FALSE; 1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 155073a3d56ea7505126469dd9ed4c20a7a8923690dKenny Root if (start < 0 || start > start + count 156073a3d56ea7505126469dd9ed4c20a7a8923690dKenny Root || env->GetArrayLength(charArray) < start + count) { 1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL); 158f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhat return JNI_FALSE; 1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 161f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhat jboolean ret = JNI_FALSE; 1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project for (int i = start; i < start + count; i++) { 1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // XXX this thinks it knows that surrogates are never mirrored 1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int c1 = data[i]; 166a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root int c2 = u_charMirror(c1); 1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (c1 != c2) { 1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project data[i] = c2; 170f5df700e6ce056ebfa322314d970e52d6facc35aAshok Bhat ret = JNI_TRUE; 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 173e25071650ee4761c86a1b8323de26a07a0f6ad79Raph Levien return ret; 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic jchar getMirror(JNIEnv* env, jobject obj, jchar c) 17769a017bc1d1649350f830dfada5c6ed5eac0b770Elliott Hughes{ 178a9886c580b299984e62303a995bf7b13276b5bc8Kenny Root return u_charMirror(c); 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectstatic JNINativeMethod gMethods[] = { 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project { "getDirectionalities", "([C[BI)V", 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (void*) getDirectionalities }, 184bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root { "getEastAsianWidth", "(C)I", 185bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root (void*) getEastAsianWidth }, 186bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root { "getEastAsianWidths", "([CII[B)V", 187bb9a51768d2d9dddbe2394b99a00544a3d144facKenny Root (void*) getEastAsianWidths }, 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project { "mirror", "([CII)Z", 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (void*) mirror }, 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project { "getMirror", "(C)C", 1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project (void*) getMirror } 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}; 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectint register_android_text_AndroidCharacter(JNIEnv* env) 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{ 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidCharacter", 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project gMethods, NELEM(gMethods)); 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 201