Merge "AdbdCommunicationMode: choose default from API level"
diff --git a/android/avd/util.c b/android/avd/util.c
index f7ebc35..c513d54 100644
--- a/android/avd/util.c
+++ b/android/avd/util.c
@@ -150,7 +150,6 @@
     return avdPath;
 }
 
-
 char*
 propertyFile_getTargetAbi(const FileData* data) {
     return propertyFile_getValue((const char*)data->data,
@@ -186,57 +185,75 @@
 
 
 int
-propertyFile_getApiLevel(const FileData* data) {
-    char* sdkVersion = propertyFile_getValue((const char*)data->data,
-                                             data->size,
-                                             "ro.build.version.sdk");
-    const int kMinLevel = 3;
-    const int kMaxLevel = 10000;
-    int level = kMinLevel;
-    if (!sdkVersion) {
-        D("Could not find target API sdkVersion / SDK version in build properties!");
-        level = kMaxLevel;
-        D("Default target API sdkVersion: %d", level);
-    } else {
-        char* end = NULL;
-        long levelLong = strtol(sdkVersion, &end, 10);
-        int level = (int)levelLong;
-        if (levelLong == LONG_MIN || levelLong == LONG_MAX ||
-            levelLong < 0 || !end || *end || level != levelLong) {
-            level = kMinLevel;
-            D("Invalid SDK version build property: '%s'", sdkVersion);
-            D("Defaulting to target API sdkVersion %d", level);
-        } else {
-            D("Found target API sdkVersion: %d\n", level);
-        }
-    }
-    free(sdkVersion);
-    return level;
-}
-
-
-int
-propertyFile_getAdbdCommunicationMode(const FileData* data) {
+propertyFile_getInt(const FileData* data, const char* key, int _default,
+                    SearchResult* searchResult) {
     char* prop = propertyFile_getValue((const char*)data->data,
                                        data->size,
-                                       "ro.adb.qemud");
+                                       key);
     if (!prop) {
-        // No ro.adb.qemud means 'legacy' ADBD.
-        return 0;
+        if (searchResult) {
+            *searchResult = RESULT_NOT_FOUND;
+        }
+        return _default;
     }
 
     char* end;
-    long val = strtol(prop, &end, 10);
-    if (end == NULL || *end != '\0' || val != (int)val) {
-        D("Invalid ro.adb.qemud build property: '%s'", prop);
-        val = 0;
-    } else {
-        D("Found ro.adb.qemud build property: %d", val);
+    // long is only 32 bits on windows so it isn't enough to detect int overflow
+    long long val = strtoll(prop, &end, 10);
+    if (val < INT_MIN || val > INT_MAX ||
+        end == prop || *end != '\0') {
+        D("Invalid int property: '%s:%s'", key, prop);
+        AFREE(prop);
+        if (searchResult) {
+            *searchResult = RESULT_INVALID;
+        }
+        return _default;
     }
+
     AFREE(prop);
+
+    if (searchResult) {
+        *searchResult = RESULT_FOUND;
+    }
     return (int)val;
 }
 
+int
+propertyFile_getApiLevel(const FileData* data) {
+    const int kMinLevel = 3;
+    const int kMaxLevel = 10000;
+    SearchResult searchResult;
+    int level = propertyFile_getInt(data, "ro.build.version.sdk", kMinLevel,
+                                    &searchResult);
+    if (searchResult == RESULT_NOT_FOUND) {
+        D("Could not find target API sdkVersion / SDK version in build properties!");
+        level = kMaxLevel;
+        D("Default target API sdkVersion: %d", level);
+    } else if (searchResult == RESULT_INVALID || level < 0) {
+        D("Defaulting to target API sdkVersion %d", level);
+    } else {
+        D("Found target API sdkVersion: %d\n", level);
+    }
+    return level;
+}
+
+int
+propertyFile_getAdbdCommunicationMode(const FileData* data) {
+    if ( propertyFile_getApiLevel(data) < 16 ) {
+        // QEMU pipe for ADB communication was added in android-4.1.1_r1 API 16
+        D("API < 16, forcing ro.adb.qemud==0");
+        return 0;
+    }
+
+    SearchResult searchResult;
+    int qemud = propertyFile_getInt(data, "ro.adb.qemud", 1, &searchResult);
+    if (searchResult == RESULT_FOUND) {
+        D("Found ro.adb.qemud build property: %d", qemud);
+        return qemud;
+    }
+    D("ro.adb.qemud invalid or not found, API >= 16, defaulting ro.adb.qemud==1");
+    return 1;
+}
 
 char* path_getBuildBuildProp(const char* androidOut) {
     char temp[MAX_PATH], *p = temp, *end = p + sizeof(temp);
diff --git a/android/avd/util.h b/android/avd/util.h
index 630d237..262822a 100644
--- a/android/avd/util.h
+++ b/android/avd/util.h
@@ -43,6 +43,23 @@
  */
 char* path_getAvdTargetArch( const char* avdName );
 
+typedef enum {
+    RESULT_INVALID   = -1, // key was found but value contained invalid data
+    RESULT_FOUND     =  0, // key was found and value parsed correctly
+    RESULT_NOT_FOUND =  1, // key was not found (default used)
+} SearchResult;
+
+/* Retrieves an integer value associated with the key parameter
+ *
+ * |data| is a FileData instance
+ * |key| name of key to search for
+ * |searchResult| if non-null, this is set to RESULT_INVALID, RESULT_FOUND,
+ *                or RESULT_NOT_FOUND
+ * Returns valid parsed int value if found, |default| otherwise
+ */
+int propertyFile_getInt(const FileData* data, const char* key, int _default,
+                        SearchResult* searchResult);
+
 /* Retrieves a string corresponding to the target architecture
  * extracted from a build properties file.
  *
diff --git a/android/avd/util_unittest.cpp b/android/avd/util_unittest.cpp
index c0aa213..0472bb2 100644
--- a/android/avd/util_unittest.cpp
+++ b/android/avd/util_unittest.cpp
@@ -10,6 +10,7 @@
 // GNU General Public License for more details.
 
 #include "android/avd/util.h"
+#include "android/utils/file_data.h"
 
 #include <gtest/gtest.h>
 
@@ -27,3 +28,124 @@
   EXPECT_FALSE(emulator_getBackendSuffix(NULL));
   EXPECT_FALSE(emulator_getBackendSuffix("dummy"));
 }
+
+TEST(AvdUtil, propertyFile_getInt) {
+  FileData fd;
+
+  const char* testFile =
+    "nineteen=19\n"
+    "int_min=-2147483648\n"
+    "int_max=2147483647\n"
+    "invalid=2147483648\n"
+    "invalid2=-2147483649\n"
+    "invalid3=bar\n"
+    "empty=\n";
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile, strlen(testFile)));
+
+  const int kDefault = 1138;
+  SearchResult kSearchResultGarbage = (SearchResult)0xdeadbeef;
+  SearchResult searchResult = kSearchResultGarbage;
+
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "invalid", kDefault, &searchResult));
+  EXPECT_EQ(RESULT_INVALID,searchResult);
+
+  searchResult = kSearchResultGarbage;
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "invalid2", kDefault, &searchResult));
+  EXPECT_EQ(RESULT_INVALID,searchResult);
+
+  searchResult = kSearchResultGarbage;
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "invalid3", kDefault, &searchResult));
+  EXPECT_EQ(RESULT_INVALID,searchResult);
+
+  searchResult = kSearchResultGarbage;
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "bar", kDefault, &searchResult));
+  EXPECT_EQ(RESULT_NOT_FOUND,searchResult);
+
+  searchResult = kSearchResultGarbage;
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "empty", kDefault, &searchResult));
+  EXPECT_EQ(RESULT_INVALID,searchResult);
+
+  searchResult = kSearchResultGarbage;
+  EXPECT_EQ(19,propertyFile_getInt(&fd, "nineteen", kDefault, &searchResult));
+  EXPECT_EQ(RESULT_FOUND,searchResult);
+
+  // check that null "searchResult" parameter is supported
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "bar", kDefault, NULL));
+  EXPECT_EQ(kDefault,propertyFile_getInt(&fd, "invalid", kDefault, NULL));
+  EXPECT_EQ(19,propertyFile_getInt(&fd, "nineteen", kDefault, NULL));
+}
+
+TEST(AvdUtil, propertyFile_getApiLevel) {
+  FileData fd;
+
+  const char* emptyFile =
+    "\n";
+
+  const char* testFile19 =
+    "ro.build.version.sdk=19\n";
+
+  const char* testFileBogus =
+    "ro.build.version.sdk=bogus\n";
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, emptyFile, strlen(emptyFile)));
+  EXPECT_EQ(10000,propertyFile_getApiLevel(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile19, strlen(testFile19)));
+  EXPECT_EQ(19,propertyFile_getApiLevel(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFileBogus, strlen(testFileBogus)));
+  EXPECT_EQ(3,propertyFile_getApiLevel(&fd));
+}
+
+TEST(AvdUtil, propertyFile_getAdbdCommunicationMode) {
+  FileData fd;
+
+  const char* emptyFile =
+    "\n";
+
+  const char* testFile15 =
+    "ro.build.version.sdk=15\n";
+
+  const char* testFile16 =
+    "ro.build.version.sdk=16\n";
+
+  const char* testFile15_0 =
+    "ro.build.version.sdk=15\n"
+    "ro.adb.qemud=0";
+
+  const char* testFile15_1 =
+    "ro.build.version.sdk=15\n"
+    "ro.adb.qemud=1";
+
+  const char* testFile16_0 =
+    "ro.build.version.sdk=16\n"
+    "ro.adb.qemud=0";
+
+  const char* testFile16_1 =
+    "ro.build.version.sdk=16\n"
+    "ro.adb.qemud=1";
+
+  // API unspecified -> API level == 10000
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, emptyFile, strlen(emptyFile)));
+  EXPECT_EQ(1,propertyFile_getAdbdCommunicationMode(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile15, strlen(testFile15)));
+  EXPECT_EQ(0,propertyFile_getAdbdCommunicationMode(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile16, strlen(testFile16)));
+  EXPECT_EQ(1,propertyFile_getAdbdCommunicationMode(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile15_0, strlen(testFile15_0)));
+  EXPECT_EQ(0,propertyFile_getAdbdCommunicationMode(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile15_1, strlen(testFile15_1)));
+  EXPECT_EQ(0,propertyFile_getAdbdCommunicationMode(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile16_0, strlen(testFile16_0)));
+  EXPECT_EQ(0,propertyFile_getAdbdCommunicationMode(&fd));
+
+  EXPECT_EQ(0,fileData_initFromMemory(&fd, testFile16_1, strlen(testFile16_1)));
+  EXPECT_EQ(1,propertyFile_getAdbdCommunicationMode(&fd));
+}
+