Merge "Set hw.keyboard.lid default value to false for API level >= 12"
diff --git a/android/avd/hardware-properties.ini b/android/avd/hardware-properties.ini
index 6b5caae..7d2e7f6 100644
--- a/android/avd/hardware-properties.ini
+++ b/android/avd/hardware-properties.ini
@@ -57,6 +57,10 @@
 # (I.e. can the qwerty keyboard be closed/hidden or opened/visible)
 # this will be ignored if hw.keyboard is false
 #
+# NOTE: As a special case, the default value will be 'false' if the
+#       AVD targets API level 12 or higher. See hwConfig_init()
+#       in external/qemu/android/avd/hw-config.c for more details.
+#
 name        = hw.keyboard.lid
 type        = boolean
 default     = yes
diff --git a/android/avd/hw-config.c b/android/avd/hw-config.c
index b449bb3..65796d9 100644
--- a/android/avd/hw-config.c
+++ b/android/avd/hw-config.c
@@ -11,6 +11,7 @@
 */
 #include "android/avd/hw-config.h"
 #include "android/utils/ini.h"
+#include "android/utils/system.h"
 #include <string.h>
 #include <stdlib.h>
 
@@ -18,6 +19,61 @@
 /* the global variable containing the hardware config for this device */
 AndroidHwConfig   android_hw[1];
 
+static int
+stringToBoolean( const char* value )
+{
+    if (!strcmp(value,"1")    ||
+        !strcmp(value,"yes")  ||
+        !strcmp(value,"YES")  ||
+        !strcmp(value,"true") ||
+        !strcmp(value,"TRUE"))
+    {
+        return 1;
+    }
+    else
+        return 0;
+}
+
+static int64_t
+diskSizeToInt64( const char* diskSize )
+{
+    char*   end;
+    int64_t value;
+
+    value = strtoll(diskSize, &end, 10);
+    if (*end == 'k' || *end == 'K')
+        value *= 1024ULL;
+    else if (*end == 'm' || *end == 'M')
+        value *= 1024*1024ULL;
+    else if (*end == 'g' || *end == 'G')
+        value *= 1024*1024*1024ULL;
+
+    return value;
+}
+
+
+void
+androidHwConfig_init( AndroidHwConfig*  config,
+                      int               apiLevel )
+{
+#define   HWCFG_BOOL(n,s,d,a,t)       config->n = stringToBoolean(d);
+#define   HWCFG_INT(n,s,d,a,t)        config->n = d;
+#define   HWCFG_STRING(n,s,d,a,t)     config->n = ASTRDUP(d);
+#define   HWCFG_DOUBLE(n,s,d,a,t)     config->n = d;
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   config->n = diskSizeToInt64(d);
+
+#include "android/avd/hw-config-defs.h"
+
+    /* Special case for hw.keyboard.lid, we need to set the
+     * default to FALSE for apiLevel >= 12. This allows platform builds
+     * to get correct orientation emulation even if they don't bring
+     * a custom hardware.ini
+     */
+    if (apiLevel >= 12) {
+        config->hw_keyboard_lid = 0;
+    }
+}
+
 int
 androidHwConfig_read( AndroidHwConfig*  config,
                       IniFile*          ini )
@@ -27,11 +83,11 @@
 
     /* use the magic of macros to implement the hardware configuration loaded */
 
-#define   HWCFG_BOOL(n,s,d,a,t)       config->n = iniFile_getBoolean(ini, s, d);
-#define   HWCFG_INT(n,s,d,a,t)        config->n = iniFile_getInteger(ini, s, d);
-#define   HWCFG_STRING(n,s,d,a,t)     config->n = iniFile_getString(ini, s, d);
-#define   HWCFG_DOUBLE(n,s,d,a,t)     config->n = iniFile_getDouble(ini, s, d);
-#define   HWCFG_DISKSIZE(n,s,d,a,t)   config->n = iniFile_getDiskSize(ini, s, d);
+#define   HWCFG_BOOL(n,s,d,a,t)       if (iniFile_getValue(ini, s)) { config->n = iniFile_getBoolean(ini, s, d); }
+#define   HWCFG_INT(n,s,d,a,t)        if (iniFile_getValue(ini, s)) { config->n = iniFile_getInteger(ini, s, d); }
+#define   HWCFG_STRING(n,s,d,a,t)     if (iniFile_getValue(ini, s)) { AFREE(config->n); config->n = iniFile_getString(ini, s, d); }
+#define   HWCFG_DOUBLE(n,s,d,a,t)     if (iniFile_getValue(ini, s)) { config->n = iniFile_getDouble(ini, s, d); }
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   if (iniFile_getValue(ini, s)) { config->n = iniFile_getDiskSize(ini, s, d); }
 
 #include "android/avd/hw-config-defs.h"
 
@@ -57,3 +113,15 @@
 
     return 0;
 }
+
+void
+androidHwConfig_done( AndroidHwConfig* config )
+{
+#define   HWCFG_BOOL(n,s,d,a,t)       config->n = 0;
+#define   HWCFG_INT(n,s,d,a,t)        config->n = 0;
+#define   HWCFG_STRING(n,s,d,a,t)     AFREE(config->n);
+#define   HWCFG_DOUBLE(n,s,d,a,t)     config->n = 0.0;
+#define   HWCFG_DISKSIZE(n,s,d,a,t)   config->n = 0;
+
+#include "android/avd/hw-config-defs.h"
+}
diff --git a/android/avd/hw-config.h b/android/avd/hw-config.h
index c79ccad..3c2480a 100644
--- a/android/avd/hw-config.h
+++ b/android/avd/hw-config.h
@@ -34,6 +34,10 @@
 #include "android/avd/hw-config-defs.h"
 } AndroidHwConfig;
 
+/* Set all default values, based on the target API level */
+void androidHwConfig_init( AndroidHwConfig*  hwConfig,
+                           int               apiLevel );
+
 /* reads a hardware configuration file from disk.
  * returns -1 if the file could not be read, or 0 in case of success.
  *
@@ -50,4 +54,7 @@
 int  androidHwConfig_write( AndroidHwConfig*  hwConfig,
                             IniFile*          configFile );
 
+/* Finalize a given hardware configuration */
+void androidHwConfig_done( AndroidHwConfig* config );
+
 #endif /* _ANDROID_AVD_HW_CONFIG_H */
diff --git a/android/avd/info.c b/android/avd/info.c
index aaf1111..6f7c904 100644
--- a/android/avd/info.c
+++ b/android/avd/info.c
@@ -149,6 +149,7 @@
     IniFile*  skinHardwareIni;  /* skin-specific hardware.ini */
 
     /* for both */
+    int       apiLevel;
     char*     skinName;     /* skin name */
     char*     skinDirPath;  /* skin directory */
     char*     coreHardwareIniPath;  /* core hardware.ini path */
@@ -348,34 +349,25 @@
 }
 
 
-/* Retrieves a string corresponding to the target architecture
- * when in the Android platform tree. The only way to do that
- * properly for now is to look at $OUT/system/build.prop:
+/* Retrieves the value of a given system property defined in a .prop
+ * file. This is a text file that contains definitions of the format:
+ * <name>=<value>
  *
- *   ro.product.cpu-abi=<abi>
- *
- * Where <abi> can be 'armeabi', 'armeabi-v7a' or 'x86'.
+ * Returns NULL if property <name> is undefined or empty.
+ * Returned string must be freed by the caller.
  */
-static const char*
-_getBuildTargetArch( const char* androidOut )
+static char*
+_getSystemProperty( const char* propFile, const char* propName )
 {
-    const char* arch = "arm";
-    char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
     FILE*  file;
+    char   temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+    int    propNameLen = strlen(propName);
+    char*  result = NULL;
 
-#define ABI_PREFIX       "ro.product.cpu.abi="
-#define ABI_PREFIX_LEN   (sizeof(ABI_PREFIX)-1)
-
-    p = bufprint(temp, end, "%s/system/build.prop", androidOut);
-    if (p >= end) {
-        D("%s: ANDROID_PRODUCT_OUT too long: %s", __FUNCTION__, androidOut);
-        goto EXIT;
-    }
-    file = fopen(temp, "rb");
+    file = fopen(propFile, "rb");
     if (file == NULL) {
         D("Could not open file: %s: %s", temp, strerror(errno));
-        D("Default target architecture=%s", arch);
-        goto EXIT;
+        return NULL;
     }
 
     while (fgets(temp, sizeof temp, file) != NULL) {
@@ -393,29 +385,110 @@
         if (p == end)
             *--p = '\0';
 
-        if (memcmp(temp, ABI_PREFIX, ABI_PREFIX_LEN) != 0) {
+        /* check that the line starts with the property name */
+        if (memcmp(temp, propName, propNameLen) != 0) {
             continue;
         }
-        p = temp + ABI_PREFIX_LEN;
+        p = temp + propNameLen;
+
+        /* followed by an equal sign */
+        if (p >= end || *p != '=')
+            continue;
+        p++;
+
+        /* followed by something */
         if (p >= end || !*p)
-            goto EXIT2;
+            break;
 
-        if (!strcmp("armeabi",p))
-            arch = "arm";
-        else if (!strcmp("armeabi-v7a",p))
-            arch = "arm";
-        else
-            arch = p;
-
-        D("Found target ABI=%s, architecture=%s", p, arch);
-        goto EXIT2;
+        result = ASTRDUP(p);
+        break;
     }
-
-    D("Could not find target architecture, defaulting to %s", arch);
-EXIT2:
     fclose(file);
-EXIT:
-    return arch;
+    return result;
+}
+
+/* Return a build property. This is a system property defined in a file
+ * named $ANDROID_PRODUCT_OUT/system/build.prop
+ *
+ * Returns NULL if undefined or empty. Returned string must be freed
+ * by the caller.
+ */
+static char*
+_getBuildProperty( const char* androidOut, const char* propName )
+{
+    char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp);
+
+    p = bufprint(temp, end, "%s/system/build.prop", androidOut);
+    if (p >= end) {
+        D("%s: ANDROID_PRODUCT_OUT too long: %s", __FUNCTION__, androidOut);
+        return NULL;
+    }
+    return _getSystemProperty(temp, propName);
+}
+
+/* Retrieves a string corresponding to the target architecture
+ * when in the Android platform tree. The only way to do that
+ * properly for now is to look at $OUT/system/build.prop:
+ *
+ *   ro.product.cpu-abi=<abi>
+ *
+ * Where <abi> can be 'armeabi', 'armeabi-v7a' or 'x86'.
+ */
+static char*
+_getBuildTargetArch( const char* androidOut )
+{
+    const char* defaultArch = "arm";
+    char*       result = NULL;
+    char*       cpuAbi = _getBuildProperty(androidOut, "ro.product.cpu.abi");
+
+    if (cpuAbi == NULL) {
+        D("Coult not find CPU ABI in build properties!");
+        D("Default target architecture=%s", defaultArch);
+        result = ASTRDUP(defaultArch);
+    } else {
+        /* Translate ABI to cpu arch if necessary */
+        if (!strcmp("armeabi",cpuAbi))
+            result = "arm";
+        else if (!strcmp("armeabi-v7a", cpuAbi))
+            result = "arm";
+        else
+            result = cpuAbi;
+
+        D("Found target ABI=%s, architecture=%s", cpuAbi, result);
+        result = ASTRDUP(result);
+        AFREE(cpuAbi);
+    }
+    return result;
+}
+
+static int
+_getBuildTargetApiLevel( const char* androidOut )
+{
+    const int  defaultLevel = 1000;
+    int        level        = defaultLevel;
+    char*      sdkVersion = _getBuildProperty(androidOut, "ro.build.version.sdk");
+
+    if (sdkVersion != NULL) {
+        long  value;
+        char* end;
+        value = strtol(sdkVersion, &end, 10);
+        if (end == NULL || *end != '\0' || value != (int)value) {
+            D("Invalid SDK version build property: '%s'", sdkVersion);
+            D("Defaulting to target API level %d", level);
+        } else {
+            level = (int)value;
+            /* Sanity check, the Android SDK doesn't support anything
+             * before Android 1.5, a.k.a API level 3 */
+            if (level < 3)
+                level = 3;
+            D("Found target API level: %d", level);
+        }
+        AFREE(sdkVersion);
+    } else {
+        D("Could not find target API level / SDK version in build properties!");
+        D("Default target API level: %d", level);
+    }
+    return level;
 }
 
 /* Returns the full path of a given file.
@@ -630,6 +703,76 @@
     return 0;
 }
 
+static int
+_avdInfo_getApiLevel( AvdInfo*  i )
+{
+    char*       target;
+    const char* p;
+    const int   defaultLevel = 1000;
+    int         level        = defaultLevel;
+
+#    define ROOT_TARGET_KEY   "target"
+
+    target = iniFile_getString(i->rootIni, ROOT_TARGET_KEY, NULL);
+    if (target == NULL) {
+        D("No target field in root AVD .ini file?");
+        D("Defaulting to API level %d", level);
+        return level;
+    }
+
+    DD("Found target field in root AVD .ini file: '%s'", target);
+
+    /* There are two acceptable formats for the target key.
+     *
+     * 1/  android-<level>
+     * 2/  <vendor-name>:<add-on-name>:<level>
+     *
+     * Where <level> can be either a _name_ (for experimental/preview SDK builds)
+     * or a decimal number. Note that if a _name_, it can start with a digit.
+     */
+
+    /* First, extract the level */
+    if (!memcmp(target, "android-", 8))
+        p = target + 8;
+    else {
+        /* skip two columns */
+        p = strchr(target, ':');
+        if (p != NULL) {
+            p = strchr(p+1, ':');
+            if (p != NULL)
+                p += 1;
+        }
+    }
+    if (p == NULL || !isdigit(*p)) {
+        goto NOT_A_NUMBER;
+    } else {
+        char* end;
+        long  val = strtol(p, &end, 10);
+        if (end == NULL || *end != '\0' || val != (int)val) {
+            goto NOT_A_NUMBER;
+        }
+        level = (int)val;
+
+        /* Sanity check, we don't support anything prior to Android 1.5 */
+        if (level < 3)
+            level = 3;
+
+        D("Found AVD target API level: %d", level);
+    }
+EXIT:
+    AFREE(target);
+    return level;
+
+NOT_A_NUMBER:
+    if (p == NULL) {
+        D("Invalid target field in root AVD .ini file");
+    } else {
+        D("Target AVD api level is not a number");
+    }
+    D("Defaulting to API level %d", level);
+    goto EXIT;
+}
+
 /* Look for a named file inside the AVD's content directory.
  * Returns NULL if it doesn't exist, or a strdup() copy otherwise.
  */
@@ -803,6 +946,8 @@
          _avdInfo_getCoreHwIniPath(i, i->contentPath) < 0 )
         goto FAIL;
 
+    i->apiLevel = _avdInfo_getApiLevel(i);
+
     /* look for image search paths. handle post 1.1/pre cupcake
      * obsolete SDKs.
      */
@@ -882,7 +1027,8 @@
     i->androidBuildRoot = ASTRDUP(androidBuildRoot);
     i->androidOut       = ASTRDUP(androidOut);
     i->contentPath      = ASTRDUP(androidOut);
-    i->targetArch       = ASTRDUP(_getBuildTargetArch(i->androidOut));
+    i->targetArch       = _getBuildTargetArch(i->androidOut);
+    i->apiLevel         = _getBuildTargetApiLevel(i->androidOut);
 
     /* TODO: find a way to provide better information from the build files */
     i->deviceName = ASTRDUP("<build>");
@@ -1052,31 +1198,27 @@
 }
 
 int
-avdInfo_getHwConfig( AvdInfo*  i, AndroidHwConfig*  hw )
+avdInfo_initHwConfig( AvdInfo*  i, AndroidHwConfig*  hw )
 {
-    IniFile*   ini = i->configIni;
-    int        ret;
+    int  ret = 0;
 
-    if (ini == NULL)
-        ini = iniFile_newFromMemory("", 0);
+    androidHwConfig_init(hw, i->apiLevel);
 
-    ret = androidHwConfig_read(hw, ini);
+    /* First read the config.ini, if any */
+    if (i->configIni != NULL) {
+        ret = androidHwConfig_read(hw, i->configIni);
+    }
 
-    if (ini != i->configIni)
-        iniFile_free(ini);
-
+    /* The skin's hardware.ini can override values */
     if (ret == 0 && i->skinHardwareIni != NULL) {
         ret = androidHwConfig_read(hw, i->skinHardwareIni);
     }
 
-    /* special product-specific hardware configuration */
-    if (i->androidOut != NULL)
-    {
+    /* Auto-disable keyboard emulation on sapphire platform builds */
+    if (i->androidOut != NULL) {
         char*  p = strrchr(i->androidOut, '/');
-        if (p != NULL && p[0] != 0) {
-            if (p[1] == 's') {
-                hw->hw_keyboard = 0;
-            }
+        if (p != NULL && !strcmp(p,"sapphire")) {
+            hw->hw_keyboard = 0;
         }
     }
 
diff --git a/android/avd/info.h b/android/avd/info.h
index aa3ddcd..2469f68 100644
--- a/android/avd/info.h
+++ b/android/avd/info.h
@@ -130,6 +130,13 @@
  */
 const char*  avdInfo_getName( AvdInfo*  i );
 
+/* Return the target API level for this AVD.
+ * Note that this will be some ridiculously large
+ * value (e.g. 1000) if this value cannot be properly
+ * determined (e.g. you're using an AVD from a preview SDK)
+ */
+int    avdInfo_getApiLevel( AvdInfo*  i );
+
 /* Returns the path to various images corresponding to a given AVD.
  * NULL if the image cannot be found. Returned strings must be freed
  * by the caller.
@@ -212,7 +219,7 @@
 int          avdInfo_inAndroidBuild( AvdInfo*  i );
 
 /* Reads the AVD's hardware configuration into 'hw'. returns -1 on error, 0 otherwise */
-int          avdInfo_getHwConfig( AvdInfo*  i, AndroidHwConfig*  hw );
+int          avdInfo_initHwConfig( AvdInfo*  i, AndroidHwConfig*  hw );
 
 /* Returns a *copy* of the path used to store trace 'foo'. result must be freed by caller */
 char*        avdInfo_getTracePath( AvdInfo*  i, const char*  traceName );
diff --git a/android/main.c b/android/main.c
index 7b6ea03..0682497 100644
--- a/android/main.c
+++ b/android/main.c
@@ -305,7 +305,7 @@
 
     /* Read hardware configuration */
     hw = android_hw;
-    if (avdInfo_getHwConfig(avd, hw) < 0) {
+    if (avdInfo_initHwConfig(avd, hw) < 0) {
         derror("could not read hardware configuration ?");
         exit(1);
     }
diff --git a/vl-android.c b/vl-android.c
index e140cb2..59b7c72 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -4953,7 +4953,10 @@
     if (hw_ini == NULL) {
         PANIC("Could not find %s file.", android_op_hwini);
     }
+
+    androidHwConfig_init(android_hw, 0);
     androidHwConfig_read(android_hw, hw_ini);
+
     iniFile_free(hw_ini);
 
     {