Eclair CTS Test for ARM ABI Version

Bug 2923901

This is the Eclair version of the ARMEABI test added to Froyo
for http://b/2713199. Here is what needed to be tweaked to make
it work:

1. I didn't see an open NDK Eclair project, so I copied the
   cpufeatures source code to a temporary directory under CTS.

2. There was no public SDK Java method to get the second ABI
   property. However, I went ahead and used the hidden API
   due to the importance of the test.

This test already exists in Froyo and uses the proper public API.
This test will not be merged into Froyo and future releases.

Change-Id: I20fcc21096f468fb733fcf34842b218e41dabbc1
diff --git a/tests/Android.mk b/tests/Android.mk
index 64517af..838d01d 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -24,6 +24,8 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
+LOCAL_JNI_SHARED_LIBRARIES := libcts_jni
+
 # Resource unit tests use a private locale and some densities
 LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c 32dpi -c 240dpi -c 160dpi
 
diff --git a/tests/cpufeatures/Android.mk b/tests/cpufeatures/Android.mk
new file mode 100644
index 0000000..49d479a
--- /dev/null
+++ b/tests/cpufeatures/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+# If targetting ARM CPUs, build the ARMv7 detection
+# function. It will only be called by the main
+# library if we detect ARMv7 through a system property.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := cpufeatures
+LOCAL_SRC_FILES := cpu-features.c
+include $(BUILD_STATIC_LIBRARY)
diff --git a/tests/cpufeatures/cpu-features.c b/tests/cpufeatures/cpu-features.c
new file mode 100644
index 0000000..c46b884
--- /dev/null
+++ b/tests/cpufeatures/cpu-features.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/system_properties.h>
+#include <machine/cpu-features.h>
+#include <pthread.h>
+#include "cpu-features.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+
+static  pthread_once_t     g_once;
+static  AndroidCpuFamily   g_cpuFamily;
+static  uint64_t           g_cpuFeatures;
+
+static const int  android_cpufeatures_debug = 0;
+
+#define  D(...) \
+    do { \
+        if (android_cpufeatures_debug) { \
+            printf(__VA_ARGS__); fflush(stdout); \
+        } \
+    } while (0)
+
+/* Read the content of /proc/cpuinfo into a user-provided buffer.
+ * Return the length of the data, or -1 on error. Does *not*
+ * zero-terminate the content. Will not read more
+ * than 'buffsize' bytes.
+ */
+static int
+read_file(const char*  pathname, char*  buffer, size_t  buffsize)
+{
+    int  fd, len;
+
+    fd = open(pathname, O_RDONLY);
+    if (fd < 0)
+        return -1;
+
+    do {
+        len = read(fd, buffer, buffsize);
+    } while (len < 0 && errno == EINTR);
+
+    close(fd);
+
+    return len;
+}
+
+/* Extract the content of a the first occurence of a given field in
+ * the content of /proc/cpuinfo and return it as a heap-allocated
+ * string that must be freed by the caller.
+ *
+ * Return NULL if not found
+ */
+static char*
+extract_cpuinfo_field(char* buffer, int buflen, const char* field)
+{
+    int  fieldlen = strlen(field);
+    char* bufend = buffer + buflen;
+    char* result = NULL;
+    int len, ignore;
+    const char *p, *q;
+
+    /* Look for first field occurence, and ensures it starts the line.
+     */
+    p = buffer;
+    bufend = buffer + buflen;
+    for (;;) {
+        p = memmem(p, bufend-p, field, fieldlen);
+        if (p == NULL)
+            goto EXIT;
+
+        if (p == buffer || p[-1] == '\n')
+            break;
+
+        p += fieldlen;
+    }
+
+    /* Skip to the first column followed by a space */
+    p += fieldlen;
+    p  = memchr(p, ':', bufend-p);
+    if (p == NULL || p[1] != ' ')
+        goto EXIT;
+
+    /* Find the end of the line */
+    p += 2;
+    q = memchr(p, '\n', bufend-p);
+    if (q == NULL)
+        q = bufend;
+
+    /* Copy the line into a heap-allocated buffer */
+    len = q-p;
+    result = malloc(len+1);
+    if (result == NULL)
+        goto EXIT;
+
+    memcpy(result, p, len);
+    result[len] = '\0';
+
+EXIT:
+    return result;
+}
+
+
+/* Checks that a space-separated list of items contains one given 'item'.
+ * Returns 1 if found, 0 otherwise.
+ */
+static int
+has_list_item(const char* list, const char* item)
+{
+    const char*  p = list;
+    int itemlen = strlen(item);
+
+    if (list == NULL)
+        return 0;
+
+    while (*p) {
+        const char*  q;
+
+        /* skip spaces */
+        while (*p == ' ' || *p == '\t')
+            p++;
+
+        /* find end of current list item */
+        q = p;
+        while (*q && *q != ' ' && *q != '\t')
+            q++;
+
+        if (itemlen == q-p && !memcmp(p, item, itemlen))
+            return 1;
+
+        /* skip to next item */
+        p = q;
+    }
+    return 0;
+}
+
+
+static void
+android_cpuInit(void)
+{
+    g_cpuFamily   = ANDROID_CPU_FAMILY_UNKNOWN;
+    g_cpuFeatures = 0;
+
+#ifdef __ARM_ARCH__
+    {
+        char   cpuinfo[4096];
+        int    cpuinfo_len = read_file("/proc/cpuinfo", cpuinfo, sizeof cpuinfo);
+        char*  features = NULL;
+        char*  architecture = NULL;
+
+        g_cpuFamily = ANDROID_CPU_FAMILY_ARM;
+
+        D("cpuinfo_len is (%d):\n%.*s\n", cpuinfo_len, cpuinfo_len, cpuinfo);
+
+        if (cpuinfo_len < 0)  /* should not happen */ {
+            return;
+        }
+
+        /* Extract architecture from the "CPU Architecture" field,
+         * Which can be something like 5JTE, 7, or something else.
+         * We cannot rely on the 'Processor' field here.
+         */
+        {
+            char* cpuArch = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "CPU architecture");
+
+            if (cpuArch != NULL) {
+                char*  end;
+                long   archNumber;
+
+                D("found cpuArch = '%s'\n", cpuArch);
+
+                /* read the initial decimal number, ignore the rest */
+                archNumber = strtol(cpuArch, &end, 10);
+
+                /* Here we assume that ARMv8 will be upwards compatible with v7
+                 * in the future. Unfortunately, there is no 'Features' field to
+                 * indicate that Thumb-2 is supported.
+                 */
+                if (end > cpuArch && archNumber >= 7) {
+                    g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_ARMv7;
+                }
+                free(cpuArch);
+            }
+        }
+
+        /* Extract the list of CPU features from 'Features' field */
+        {
+            char* cpuFeatures = extract_cpuinfo_field(cpuinfo, cpuinfo_len, "Features");
+
+            if (cpuFeatures != NULL) {
+
+                D("found cpuFeatures = '%s'\n", cpuFeatures);
+
+                if (has_list_item(cpuFeatures, "vfpv3"))
+                    g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_VFPv3;
+
+                if (has_list_item(cpuFeatures, "neon"))
+                    g_cpuFeatures |= ANDROID_CPU_ARM_FEATURE_NEON;
+
+                free(cpuFeatures);
+            }
+        }
+    }
+#endif /* __ARM_ARCH__ */
+}
+
+
+AndroidCpuFamily
+android_getCpuFamily(void)
+{
+    pthread_once(&g_once, android_cpuInit);
+    return g_cpuFamily;
+}
+
+
+uint64_t
+android_getCpuFeatures(void)
+{
+    pthread_once(&g_once, android_cpuInit);
+    return g_cpuFeatures;
+}
diff --git a/tests/cpufeatures/cpu-features.h b/tests/cpufeatures/cpu-features.h
new file mode 100644
index 0000000..e6a7db7
--- /dev/null
+++ b/tests/cpufeatures/cpu-features.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef CPU_FEATURES_H
+#define CPU_FEATURES_H
+
+#include <sys/cdefs.h>
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+typedef enum {
+    ANDROID_CPU_FAMILY_UNKNOWN = 0,
+    ANDROID_CPU_FAMILY_ARM,
+    ANDROID_CPU_FAMILY_X86,
+
+    ANDROID_CPU_FAMILY_MAX  /* do not remove */
+
+} AndroidCpuFamily;
+
+/* Return family of the device's CPU */
+extern AndroidCpuFamily   android_getCpuFamily(void);
+
+enum {
+    ANDROID_CPU_ARM_FEATURE_ARMv7 = (1 << 0),
+    ANDROID_CPU_ARM_FEATURE_VFPv3 = (1 << 1),
+    ANDROID_CPU_ARM_FEATURE_NEON  = (1 << 2),
+};
+
+extern uint64_t    android_getCpuFeatures(void);
+
+__END_DECLS
+
+#endif /* CPU_FEATURES_H */
diff --git a/tests/jni/Android.mk b/tests/jni/Android.mk
new file mode 100644
index 0000000..f00b5de
--- /dev/null
+++ b/tests/jni/Android.mk
@@ -0,0 +1,42 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcts_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+# This isn't part of the system, so don't prelink it.
+LOCAL_PRELINK_MODULE := false
+
+LOCAL_SRC_FILES := \
+		CtsJniOnLoad.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) 
+
+LOCAL_SHARED_LIBRARIES := libnativehelper liblog
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_SRC_FILES += android_os_cts_CpuFeatures.cpp
+LOCAL_C_INCLUDES += cts/tests/cpufeatures
+LOCAL_STATIC_LIBRARIES := cpufeatures
+else
+LOCAL_CFLAGS += -DCTS_TARGET_SIMULATOR
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/jni/CtsJniOnLoad.cpp b/tests/jni/CtsJniOnLoad.cpp
new file mode 100644
index 0000000..b6177a1
--- /dev/null
+++ b/tests/jni/CtsJniOnLoad.cpp
@@ -0,0 +1,38 @@
+/* 
+ * 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.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+#ifndef CTS_TARGET_SIMULATOR
+extern int register_android_os_cts_CpuFeatures(JNIEnv*);
+#endif
+
+jint JNI_OnLoad(JavaVM *vm, void *reserved) {
+    JNIEnv *env = NULL;
+
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+#ifndef CTS_TARGET_SIMULATOR
+    if (register_android_os_cts_CpuFeatures(env)) {
+        return JNI_ERR;
+    }
+#endif
+
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/jni/android_os_cts_CpuFeatures.cpp b/tests/jni/android_os_cts_CpuFeatures.cpp
new file mode 100644
index 0000000..95a0d2f
--- /dev/null
+++ b/tests/jni/android_os_cts_CpuFeatures.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ */
+#include <cpu-features.h>
+#include <jni.h>
+#include <string.h>
+
+jboolean android_os_cts_CpuFeatures_isArmCpu(JNIEnv* env, jobject thiz)
+{
+    AndroidCpuFamily cpuFamily = android_getCpuFamily();
+    return cpuFamily == ANDROID_CPU_FAMILY_ARM;
+}
+
+jboolean android_os_cts_CpuFeatures_isArm7Compatible(JNIEnv* env, jobject thiz)
+{
+    uint64_t cpuFeatures = android_getCpuFeatures();
+    return (cpuFeatures & ANDROID_CPU_ARM_FEATURE_ARMv7) == ANDROID_CPU_ARM_FEATURE_ARMv7;
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "isArmCpu", "()Z",
+            (void *) android_os_cts_CpuFeatures_isArmCpu  },
+    {  "isArm7Compatible", "()Z",
+            (void *) android_os_cts_CpuFeatures_isArm7Compatible  },
+};
+
+int register_android_os_cts_CpuFeatures(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/os/cts/CpuFeatures");
+
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/src/android/os/cts/CpuFeatures.java b/tests/src/android/os/cts/CpuFeatures.java
new file mode 100644
index 0000000..4d84d8e
--- /dev/null
+++ b/tests/src/android/os/cts/CpuFeatures.java
@@ -0,0 +1,16 @@
+package android.os.cts;
+
+public class CpuFeatures {
+
+    public static final String ARMEABI_V7 = "armeabi-v7a";
+
+    public static final String ARMEABI = "armeabi";
+
+    static {
+        System.loadLibrary("cts_jni");
+    }
+
+    public static native boolean isArmCpu();
+
+    public static native boolean isArm7Compatible();
+}
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index c25cf22..affd97d 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -23,7 +23,11 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
+ifneq ($(TARGET_SIMULATOR),true)
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+else
+LOCAL_SRC_FILES := $(filter-out %BuildTest.java,$(call all-java-files-under, src))
+endif
 
 LOCAL_PACKAGE_NAME := CtsOsTestCases
 
@@ -33,4 +37,3 @@
 #LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
-
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
new file mode 100644
index 0000000..c9cd54d
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.os.cts;
+
+import dalvik.annotation.TestTargetClass;
+
+import android.os.Build;
+import android.os.SystemProperties;
+
+import junit.framework.TestCase;
+
+@TestTargetClass(Build.class)
+public class BuildTest extends TestCase {
+
+    /** Tests that check the values of {@link Build#CPU_ABI} and the ABI2 system property. */
+    public void testCpuAbi() throws Exception {
+        if (CpuFeatures.isArmCpu()) {
+            assertArmCpuAbiConstants();
+        }
+    }
+
+    private void assertArmCpuAbiConstants() {
+        if (CpuFeatures.isArm7Compatible()) {
+            String message = String.format("CPU is ARM v7 compatible, so Build.CPU_ABI must be %s"
+                    + " and Build.CPU_ABI2 must be %s.", CpuFeatures.ARMEABI_V7,
+                            CpuFeatures.ARMEABI);
+            assertEquals(message, CpuFeatures.ARMEABI_V7, Build.CPU_ABI);
+            assertEquals(message, CpuFeatures.ARMEABI, getCpuAbi2());
+        } else {
+            String message = String.format("CPU is not ARM v7 compatible, so Build.CPU_ABI must "
+                    + "be %s and Build.CPU_ABI2 must be 'unknown'.", CpuFeatures.ARMEABI);
+            assertEquals(message, CpuFeatures.ARMEABI, Build.CPU_ABI);
+            assertEquals(message, "unknown", getCpuAbi2());
+        }
+    }
+
+    private String getCpuAbi2() {
+        // The property will be replaced by a SDK constant Build.CPU_ABI_2 in future releases.
+        return SystemProperties.get("ro.product.cpu.abi2");
+    }
+}