Merge "adb: win32: define UNICODE/_UNICODE, Unicode error messages, misc"
diff --git a/adb/Android.mk b/adb/Android.mk
index e911bc3..e2d0bb1 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -76,7 +76,6 @@
     usb_linux.cpp \
 
 LIBADB_windows_SRC_FILES := \
-    get_my_path_windows.cpp \
     sysdeps_win32.cpp \
     usb_windows.cpp \
 
@@ -133,7 +132,9 @@
 LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
-ifneq ($(HOST_OS),windows)
+# adb_test
+# =========================================================
+
 include $(CLEAR_VARS)
 LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_test
@@ -154,9 +155,13 @@
     LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
 endif
 
-include $(BUILD_HOST_NATIVE_TEST)
+ifeq ($(HOST_OS),windows)
+    LOCAL_LDLIBS += -lws2_32 -luserenv
+    LOCAL_STATIC_LIBRARIES += AdbWinApi
 endif
 
+include $(BUILD_HOST_NATIVE_TEST)
+
 # adb device tracker (used by ddms) test tool
 # =========================================================
 
diff --git a/adb/adb.cpp b/adb/adb.cpp
index c5ab7b0..fd46dea 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -580,8 +580,8 @@
                            FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     if (nul_read == INVALID_HANDLE_VALUE) {
-        fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failure, error %ld\n",
-                GetLastError());
+        fprintf(stderr, "CreateFileW(nul, GENERIC_READ) failed: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
 
@@ -589,8 +589,8 @@
                             FILE_SHARE_READ | FILE_SHARE_WRITE, &sa,
                             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     if (nul_write == INVALID_HANDLE_VALUE) {
-        fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failure, error %ld\n",
-                GetLastError());
+        fprintf(stderr, "CreateFileW(nul, GENERIC_WRITE) failed: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         CloseHandle(nul_read);
         return -1;
     }
@@ -598,7 +598,8 @@
     /* create pipe, and ensure its read handle isn't inheritable */
     ret = CreatePipe( &pipe_read, &pipe_write, &sa, 0 );
     if (!ret) {
-        fprintf(stderr, "CreatePipe() failure, error %ld\n", GetLastError() );
+        fprintf(stderr, "CreatePipe() failed: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         CloseHandle(nul_read);
         CloseHandle(nul_write);
         return -1;
@@ -640,8 +641,8 @@
                                              arraysize(program_path));
     if ((module_result == arraysize(program_path)) || (module_result == 0)) {
         // String truncation or some other error.
-        fprintf(stderr, "GetModuleFileNameW() failure, error %ld\n",
-                GetLastError());
+        fprintf(stderr, "GetModuleFileNameW() failed: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         return -1;
     }
     WCHAR args[64];
@@ -666,7 +667,8 @@
     CloseHandle( pipe_write );
 
     if (!ret) {
-        fprintf(stderr, "CreateProcess failure, error %ld\n", GetLastError() );
+        fprintf(stderr, "CreateProcess failed: %s\n",
+                SystemErrorCodeToString(GetLastError()).c_str());
         CloseHandle( pipe_read );
         return -1;
     }
@@ -682,7 +684,8 @@
         ret = ReadFile( pipe_read, temp, 3, &count, NULL );
         CloseHandle( pipe_read );
         if ( !ret ) {
-            fprintf(stderr, "could not read ok from ADB Server, error = %ld\n", GetLastError() );
+            fprintf(stderr, "could not read ok from ADB Server, error: %s\n",
+                    SystemErrorCodeToString(GetLastError()).c_str());
             return -1;
         }
         if (count != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 966e61d..e7f82a9 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -305,7 +305,10 @@
     home = getenv("ANDROID_SDK_HOME");
     if (!home) {
         WCHAR path[MAX_PATH];
-        if (FAILED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path))) {
+        const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+        if (FAILED(hr)) {
+            D("SHGetFolderPathW failed: %s\n",
+              SystemErrorCodeToString(hr).c_str());
             return -1;
         }
         home_str = narrow(path);
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 0ae21db..f637073 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -30,6 +30,12 @@
 #include "base/file.h"
 #include "base/test_utils.h"
 
+// All of these tests fail on Windows because they use the C Runtime open(),
+// but the adb_io APIs expect file descriptors from adb_open(). Also, the
+// android::base file APIs use the C Runtime which uses CR/LF translation by
+// default (changeable with _setmode()), but the adb_io APIs use adb_read()
+// and adb_write() which do no translation.
+
 TEST(io, ReadFdExactly_whole) {
   const char expected[] = "Foobar";
   TemporaryFile tf;
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 12208cd..ca843bd 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -18,6 +18,7 @@
 
 #include "adb_utils.h"
 
+#include <libgen.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -32,6 +33,8 @@
 #include "adb_trace.h"
 #include "sysdeps.h"
 
+ADB_MUTEX_DEFINE(dirname_lock);
+
 bool getcwd(std::string* s) {
   char* cwd = getcwd(nullptr, 0);
   if (cwd != nullptr) *s = cwd;
@@ -66,35 +69,86 @@
 }
 
 std::string adb_basename(const std::string& path) {
-    size_t base = path.find_last_of(OS_PATH_SEPARATORS);
-    return (base != std::string::npos) ? path.substr(base + 1) : path;
+  size_t base = path.find_last_of(OS_PATH_SEPARATORS);
+  return (base != std::string::npos) ? path.substr(base + 1) : path;
 }
 
-static bool real_mkdirs(const std::string& path) {
-    std::vector<std::string> path_components = android::base::Split(path, OS_PATH_SEPARATOR_STR);
-    // TODO: all the callers do unlink && mkdirs && adb_creat ---
-    // that's probably the operation we should expose.
-    path_components.pop_back();
-    std::string partial_path;
-    for (const auto& path_component : path_components) {
-        if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
-        partial_path += path_component;
-        if (adb_mkdir(partial_path.c_str(), 0775) == -1 && errno != EEXIST) {
-            return false;
-        }
-    }
-    return true;
+std::string adb_dirname(const std::string& path) {
+  // Copy path because dirname may modify the string passed in.
+  std::string parent_storage(path);
+
+  // Use lock because dirname() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock.
+  adb_mutex_lock(&dirname_lock);
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* parent = dirname(&parent_storage[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  const std::string result(parent);
+
+  adb_mutex_unlock(&dirname_lock);
+
+  return result;
 }
 
+// Given a relative or absolute filepath, create the parent directory hierarchy
+// as needed. Returns true if the hierarchy is/was setup.
 bool mkdirs(const std::string& path) {
-#if defined(_WIN32)
-    // Replace '/' with '\\' so we can share the code.
-    std::string clean_path = path;
-    std::replace(clean_path.begin(), clean_path.end(), '/', '\\');
-    return real_mkdirs(clean_path);
-#else
-    return real_mkdirs(path);
-#endif
+  // TODO: all the callers do unlink && mkdirs && adb_creat ---
+  // that's probably the operation we should expose.
+
+  // Implementation Notes:
+  //
+  // Pros:
+  // - Uses dirname, so does not need to deal with OS_PATH_SEPARATOR.
+  // - On Windows, uses mingw dirname which accepts '/' and '\\', drive letters
+  //   (C:\foo), UNC paths (\\server\share\dir\dir\file), and Unicode (when
+  //   combined with our adb_mkdir() which takes UTF-8).
+  // - Is optimistic wrt thinking that a deep directory hierarchy will exist.
+  //   So it does as few stat()s as possible before doing mkdir()s.
+  // Cons:
+  // - Recursive, so it uses stack space relative to number of directory
+  //   components.
+
+  const std::string parent(adb_dirname(path));
+
+  if (directory_exists(parent)) {
+    return true;
+  }
+
+  // If dirname returned the same path as what we passed in, don't go recursive.
+  // This can happen on Windows when walking up the directory hierarchy and not
+  // finding anything that already exists (unlike POSIX that will eventually
+  // find . or /).
+  if (parent == path) {
+    errno = ENOENT;
+    return false;
+  }
+
+  // Recursively make parent directories of 'parent'.
+  if (!mkdirs(parent)) {
+    return false;
+  }
+
+  // Now that the parent directory hierarchy of 'parent' has been ensured,
+  // create parent itself.
+  if (adb_mkdir(parent, 0775) == -1) {
+    // Can't just check for errno == EEXIST because it might be a file that
+    // exists.
+    const int saved_errno = errno;
+    if (directory_exists(parent)) {
+      return true;
+    }
+    errno = saved_errno;
+    return false;
+  }
+
+  return true;
 }
 
 void dump_hex(const void* data, size_t byte_count) {
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 8c5208c..739efcc 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,7 +21,11 @@
 
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
+
+// Like the regular basename and dirname, but thread-safe on all
+// platforms and capable of correctly handling exotic Windows paths.
 std::string adb_basename(const std::string& path);
+std::string adb_dirname(const std::string& path);
 
 bool mkdirs(const std::string& path);
 
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 309ac02..34b54e7 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -16,6 +16,13 @@
 
 #include "adb_utils.h"
 
+#ifdef _WIN32
+#include <windows.h>
+#include <userenv.h>
+#endif
+
+#include <string>
+
 #include <gtest/gtest.h>
 
 #include <stdlib.h>
@@ -23,12 +30,46 @@
 
 #include "sysdeps.h"
 
+#include <base/macros.h>
 #include <base/test_utils.h>
 
+#ifdef _WIN32
+static std::string subdir(const char* parent, const char* child) {
+  std::string str(parent);
+  str += OS_PATH_SEPARATOR;
+  str += child;
+  return str;
+}
+#endif
+
 TEST(adb_utils, directory_exists) {
+#ifdef _WIN32
+  char profiles_dir[MAX_PATH];
+  DWORD cch = arraysize(profiles_dir);
+
+  // On typical Windows 7, returns C:\Users
+  ASSERT_TRUE(GetProfilesDirectory(profiles_dir, &cch));
+
+  ASSERT_TRUE(directory_exists(profiles_dir));
+
+  // On modern (English?) Windows, this is a directory symbolic link to
+  // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+  // a special permission (by default granted to Administrative users) to
+  // create symbolic links.
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+  // On modern (English?) Windows, this is a directory junction to
+  // C:\Users\Default. Junctions are used throughout user profile directories
+  // for backwards compatibility and they don't require any special permissions
+  // to create.
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+
+  ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
+#else
   ASSERT_TRUE(directory_exists("/proc"));
   ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
   ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
+#endif
 }
 
 TEST(adb_utils, escape_arg) {
@@ -145,10 +186,19 @@
   EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
 }
 
+void test_mkdirs(const std::string basepath) {
+  EXPECT_TRUE(mkdirs(basepath));
+  EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
+  EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+}
+
 TEST(adb_utils, mkdirs) {
   TemporaryDir td;
-  std::string path = std::string(td.path) + "/dir/subdir/file";
-  EXPECT_TRUE(mkdirs(path));
-  EXPECT_NE(-1, adb_creat(path.c_str(), 0600));
-  EXPECT_FALSE(mkdirs(path + "/subdir/"));
+
+  // Absolute paths.
+  test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+
+  // Relative paths.
+  ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
+  test_mkdirs(std::string("relative/subrel/file"));
 }
diff --git a/adb/get_my_path_windows.cpp b/adb/get_my_path_windows.cpp
deleted file mode 100644
index ed92270..0000000
--- a/adb/get_my_path_windows.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 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 "sysdeps.h"
-
-#include <assert.h>
-#include <limits.h>
-#include <windows.h>
-
-#include <base/macros.h>
-
-#include "adb.h"
-
-// This is not currently called on Windows. Code that only runs on Windows
-// should probably deal with UTF-16 WCHAR/wchar_t since Windows APIs natively
-// work in that format.
-void get_my_path(char *exe, size_t maxLen) {
-    WCHAR wexe[MAX_PATH];
-
-    DWORD module_result = GetModuleFileNameW(NULL, wexe, arraysize(wexe));
-    if ((module_result == arraysize(wexe)) || (module_result == 0)) {
-        // String truncation or other error.
-        wexe[0] = '\0';
-    }
-
-    // Convert from UTF-16 to UTF-8.
-    const std::string exe_str(narrow(wexe));
-
-    if (exe_str.length() + 1 <= maxLen) {
-        strcpy(exe, exe_str.c_str());
-    } else {
-        exe[0] = '\0';
-    }
-}
-
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
index ff72751..9003361 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -6,6 +6,7 @@
 #ifndef ADB_MUTEX
 #error ADB_MUTEX not defined when including this file
 #endif
+ADB_MUTEX(dirname_lock)
 ADB_MUTEX(socket_list_lock)
 ADB_MUTEX(transport_lock)
 #if ADB_HOST
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 803a172..ec28ccd 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -427,7 +427,8 @@
                 return -1;
 
             default:
-                D( "unknown error: %ld\n", err );
+                D( "unknown error: %s\n",
+                   SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
@@ -470,7 +471,8 @@
                 return -1;
 
             default:
-                D( "unknown error: %ld\n", err );
+                D( "unknown error: %s\n",
+                   SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
@@ -2324,7 +2326,7 @@
         memset(input_record, 0, sizeof(*input_record));
         if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
             D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
-              "failure, error %ld\n", GetLastError());
+              "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str());
             errno = EIO;
             return false;
         }
@@ -3138,8 +3140,8 @@
         if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
             ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
             // This really should not fail.
-            D("stdin_raw_init: SetConsoleMode() failure, error %ld\n",
-                GetLastError());
+            D("stdin_raw_init: SetConsoleMode() failed: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
         }
 
         // Once this is set, it means that stdin has been configured for
@@ -3160,8 +3162,8 @@
 
             if (!SetConsoleMode(in, _old_console_mode)) {
                 // This really should not fail.
-                D("stdin_raw_restore: SetConsoleMode() failure, error %ld\n",
-                    GetLastError());
+                D("stdin_raw_restore: SetConsoleMode() failed: %s\n",
+                  SystemErrorCodeToString(GetLastError()).c_str());
             }
         }
     }
diff --git a/adb/test_device.py b/adb/test_device.py
index 81c4a94..48a3f6c 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -333,107 +333,96 @@
 
     def _test_push(self, local_file, checksum):
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
-        try:
-            self.device.push(
-                local=local_file, remote=self.DEVICE_TEMP_FILE)
-            dev_md5, _ = self.device.shell(
-                [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
-            self.assertEqual(checksum, dev_md5)
-        finally:
-            self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
+        self.device.push(local=local_file, remote=self.DEVICE_TEMP_FILE)
+        dev_md5, _ = self.device.shell([get_md5_prog(self.device),
+                                       self.DEVICE_TEMP_FILE]).split()
+        self.assertEqual(checksum, dev_md5)
+        self.device.shell(['rm', '-f', self.DEVICE_TEMP_FILE])
 
     def test_push(self):
         """Push a randomly generated file to specified device."""
         kbytes = 512
         tmp = tempfile.NamedTemporaryFile(mode='wb', delete=False)
-        try:
-            rand_str = os.urandom(1024 * kbytes)
-            tmp.write(rand_str)
-            tmp.close()
-            self._test_push(tmp.name, compute_md5(rand_str))
-        finally:
-            os.remove(tmp.name)
+        rand_str = os.urandom(1024 * kbytes)
+        tmp.write(rand_str)
+        tmp.close()
+        self._test_push(tmp.name, compute_md5(rand_str))
+        os.remove(tmp.name)
 
     # TODO: write push directory test.
 
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
-        try:
-            tmp_write.close()
-            self.device.pull(remote=remote_file, local=tmp_write.name)
-            with open(tmp_write.name, 'rb') as tmp_read:
-                host_contents = tmp_read.read()
-                host_md5 = compute_md5(host_contents)
-            self.assertEqual(checksum, host_md5)
-        finally:
-            os.remove(tmp_write.name)
+        tmp_write.close()
+        self.device.pull(remote=remote_file, local=tmp_write.name)
+        with open(tmp_write.name, 'rb') as tmp_read:
+            host_contents = tmp_read.read()
+            host_md5 = compute_md5(host_contents)
+        self.assertEqual(checksum, host_md5)
+        os.remove(tmp_write.name)
 
     def test_pull(self):
         """Pull a randomly generated file from specified device."""
         kbytes = 512
         self.device.shell(['rm', '-rf', self.DEVICE_TEMP_FILE])
-        try:
-            cmd = ['dd', 'if=/dev/urandom',
-                   'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
-                   'count={}'.format(kbytes)]
-            self.device.shell(cmd)
-            dev_md5, _ = self.device.shell(
-                [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
-            self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
-        finally:
-            self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
+        cmd = ['dd', 'if=/dev/urandom',
+               'of={}'.format(self.DEVICE_TEMP_FILE), 'bs=1024',
+               'count={}'.format(kbytes)]
+        self.device.shell(cmd)
+        dev_md5, _ = self.device.shell(
+            [get_md5_prog(self.device), self.DEVICE_TEMP_FILE]).split()
+        self._test_pull(self.DEVICE_TEMP_FILE, dev_md5)
+        self.device.shell_nocheck(['rm', self.DEVICE_TEMP_FILE])
 
     def test_pull_dir(self):
         """Pull a randomly generated directory of files from the device."""
         host_dir = tempfile.mkdtemp()
-        try:
-            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
-            self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
 
-            # Populate device directory with random files.
-            temp_files = make_random_device_files(
-                self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+        # Populate device directory with random files.
+        temp_files = make_random_device_files(
+            self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
 
-            self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
+        self.device.pull(remote=self.DEVICE_TEMP_DIR, local=host_dir)
 
-            for temp_file in temp_files:
-                host_path = os.path.join(host_dir, temp_file.base_name)
-                with open(host_path, 'rb') as host_file:
-                    host_md5 = compute_md5(host_file.read())
-                    self.assertEqual(host_md5, temp_file.checksum)
-        finally:
-            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
-            if host_dir is not None:
-                shutil.rmtree(host_dir)
+        for temp_file in temp_files:
+            host_path = os.path.join(host_dir, temp_file.base_name)
+            with open(host_path, 'rb') as host_file:
+                host_md5 = compute_md5(host_file.read())
+                self.assertEqual(host_md5, temp_file.checksum)
+
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        if host_dir is not None:
+            shutil.rmtree(host_dir)
 
     def test_sync(self):
         """Sync a randomly generated directory of files to specified device."""
         base_dir = tempfile.mkdtemp()
-        try:
-            # Create mirror device directory hierarchy within base_dir.
-            full_dir_path = base_dir + self.DEVICE_TEMP_DIR
-            os.makedirs(full_dir_path)
 
-            # Create 32 random files within the host mirror.
-            temp_files = make_random_host_files(in_dir=full_dir_path,
-                                                num_files=32)
+        # Create mirror device directory hierarchy within base_dir.
+        full_dir_path = base_dir + self.DEVICE_TEMP_DIR
+        os.makedirs(full_dir_path)
 
-            # Clean up any trash on the device.
-            device = adb.get_device(product=base_dir)
-            device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        # Create 32 random files within the host mirror.
+        temp_files = make_random_host_files(in_dir=full_dir_path, num_files=32)
 
-            device.sync('data')
+        # Clean up any trash on the device.
+        device = adb.get_device(product=base_dir)
+        device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
 
-            # Confirm that every file on the device mirrors that on the host.
-            for temp_file in temp_files:
-                device_full_path = posixpath.join(
-                    self.DEVICE_TEMP_DIR, temp_file.base_name)
-                dev_md5, _ = device.shell(
-                    [get_md5_prog(self.device), device_full_path]).split()
-                self.assertEqual(temp_file.checksum, dev_md5)
-        finally:
-            self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
-            shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
+        device.sync('data')
+
+        # Confirm that every file on the device mirrors that on the host.
+        for temp_file in temp_files:
+            device_full_path = posixpath.join(self.DEVICE_TEMP_DIR,
+                                              temp_file.base_name)
+            dev_md5, _ = device.shell(
+                [get_md5_prog(self.device), device_full_path]).split()
+            self.assertEqual(temp_file.checksum, dev_md5)
+
+        self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+        shutil.rmtree(base_dir + self.DEVICE_TEMP_DIR)
 
     def test_unicode_paths(self):
         """Ensure that we can support non-ASCII paths, even on Windows."""
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 4b74adf..49deb73 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -63,6 +63,27 @@
     }
 };
 
+class TransportSetup {
+public:
+  TransportSetup() {
+#ifdef _WIN32
+    // Use extern instead of including sysdeps.h which brings in various macros
+    // that conflict with APIs used in this file.
+    extern void adb_sysdeps_init(void);
+    adb_sysdeps_init();
+#else
+    // adb_sysdeps_init() is an inline function that we cannot link against.
+#endif
+  }
+};
+
+// Static initializer will call adb_sysdeps_init() before main() to initialize
+// the transport mutex before it is used in the tests. Alternatives would be to
+// use __attribute__((constructor)) here or to use that or a static initializer
+// for adb_sysdeps_init() itself in sysdeps_win32.cpp (caveats of unclear
+// init order), or to use a test fixture whose SetUp() could do the init once.
+static TransportSetup g_TransportSetup;
+
 TEST(transport, kick_transport) {
   TestTransport t;
 
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 4056684..2158421 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -34,17 +34,7 @@
   EXPECT_EQ("", s);  // s was cleared.
 }
 
-TEST(file, ReadFileToString_success) {
-  std::string s("hello");
-  ASSERT_TRUE(android::base::ReadFileToString("/proc/version", &s))
-    << strerror(errno);
-  EXPECT_GT(s.length(), 6U);
-  EXPECT_EQ('\n', s[s.length() - 1]);
-  s[5] = 0;
-  EXPECT_STREQ("Linux", s.c_str());
-}
-
-TEST(file, WriteStringToFile) {
+TEST(file, ReadFileToString_WriteStringToFile) {
   TemporaryFile tf;
   ASSERT_TRUE(tf.fd != -1);
   ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
@@ -89,13 +79,23 @@
 }
 
 TEST(file, ReadFully) {
-  int fd = open("/proc/version", O_RDONLY);
+#ifdef _WIN32
+  VersionFile ver;
+  ASSERT_NE(ver.filename, nullptr);
+  const char* filename = ver.filename;
+  // Note that ReadFully() does CR/LF translation, so we expect \n, not \r\n.
+  const char expected[] = "\nMicrosoft Windows";
+#else
+  const char* filename = "/proc/version";
+  const char expected[] = "Linux";
+#endif
+  int fd = open(filename, O_RDONLY);
   ASSERT_NE(-1, fd) << strerror(errno);
 
   char buf[1024];
   memset(buf, 0, sizeof(buf));
-  ASSERT_TRUE(android::base::ReadFully(fd, buf, 5));
-  ASSERT_STREQ("Linux", buf);
+  ASSERT_TRUE(android::base::ReadFully(fd, buf, sizeof(expected) - 1));
+  ASSERT_STREQ(expected, buf);
 
   ASSERT_EQ(0, lseek(fd, 0, SEEK_SET)) << strerror(errno);
 
diff --git a/base/include/base/test_utils.h b/base/include/base/test_utils.h
index 83f0f1c..402e0a5 100644
--- a/base/include/base/test_utils.h
+++ b/base/include/base/test_utils.h
@@ -35,7 +35,6 @@
   DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
 };
 
-#if !defined(_WIN32)
 class TemporaryDir {
  public:
   TemporaryDir();
@@ -48,6 +47,5 @@
 
   DISALLOW_COPY_AND_ASSIGN(TemporaryDir);
 };
-#endif
 
 #endif // TEST_UTILS_H
diff --git a/base/stringprintf_test.cpp b/base/stringprintf_test.cpp
index 54b2b6c..5cc2086 100644
--- a/base/stringprintf_test.cpp
+++ b/base/stringprintf_test.cpp
@@ -20,14 +20,11 @@
 
 #include <string>
 
-// The z size sepcifier isn't supported on Windows, so this test isn't useful.
-#if !defined(_WIN32)
 TEST(StringPrintfTest, HexSizeT) {
   size_t size = 0x00107e59;
   EXPECT_EQ("00107e59", android::base::StringPrintf("%08zx", size));
   EXPECT_EQ("0x00107e59", android::base::StringPrintf("0x%08zx", size));
 }
-#endif
 
 TEST(StringPrintfTest, StringAppendF) {
   std::string s("a");
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index dceb8b7..b0c5a12 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
+#include "base/logging.h"
 #include "base/test_utils.h"
+#include "utils/Compat.h" // For OS_PATH_SEPARATOR.
 
 #include <fcntl.h>
 #include <stdio.h>
@@ -24,17 +26,47 @@
 
 #if defined(_WIN32)
 #include <windows.h>
+#include <direct.h>
 #endif
 
 #include <string>
 
+#ifdef _WIN32
+int mkstemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return -1;
+  }
+  // Use open() to match the close() that TemporaryFile's destructor does.
+  // Note that on Windows, this does CR/LF translation and _setmode() should
+  // be used to change that if appropriate.
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+}
+
+char* mkdtemp(char* template_name) {
+  if (_mktemp(template_name) == nullptr) {
+    return nullptr;
+  }
+  if (_mkdir(template_name) == -1) {
+    return nullptr;
+  }
+  return template_name;
+}
+#endif
+
 static std::string GetSystemTempDir() {
 #if defined(__ANDROID__)
   return "/data/local/tmp";
 #elif defined(_WIN32)
-  char wd[MAX_PATH] = {};
-  _getcwd(wd, sizeof(wd));
-  return wd;
+  char tmp_dir[MAX_PATH];
+  DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir);
+  CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
+  CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+
+  // GetTempPath() returns a path with a trailing slash, but init()
+  // does not expect that, so remove it.
+  CHECK_EQ(tmp_dir[result - 1], '\\');
+  tmp_dir[result - 1] = '\0';
+  return tmp_dir;
 #else
   return "/tmp";
 #endif
@@ -50,21 +82,11 @@
 }
 
 void TemporaryFile::init(const std::string& tmp_dir) {
-  snprintf(path, sizeof(path), "%s/TemporaryFile-XXXXXX", tmp_dir.c_str());
-#if !defined(_WIN32)
+  snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
+           OS_PATH_SEPARATOR);
   fd = mkstemp(path);
-#else
-  // Windows doesn't have mkstemp, and tmpfile creates the file in the root
-  // directory, requiring root (?!) permissions. We have to settle for mktemp.
-  if (mktemp(path) == nullptr) {
-    abort();
-  }
-
-  fd = open(path, O_RDWR | O_NOINHERIT | O_CREAT, _S_IREAD | _S_IWRITE);
-#endif
 }
 
-#if !defined(_WIN32)
 TemporaryDir::TemporaryDir() {
   init(GetSystemTempDir());
 }
@@ -74,7 +96,7 @@
 }
 
 bool TemporaryDir::init(const std::string& tmp_dir) {
-  snprintf(path, sizeof(path), "%s/TemporaryDir-XXXXXX", tmp_dir.c_str());
+  snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(),
+           OS_PATH_SEPARATOR);
   return (mkdtemp(path) != nullptr);
 }
-#endif
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index e4ed179..6d9b3bc 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -67,7 +67,8 @@
 #define ATRACE_TAG_RS               (1<<15)
 #define ATRACE_TAG_BIONIC           (1<<16)
 #define ATRACE_TAG_POWER            (1<<17)
-#define ATRACE_TAG_LAST             ATRACE_TAG_POWER
+#define ATRACE_TAG_PACKAGE_MANAGER  (1<<18)
+#define ATRACE_TAG_LAST             ATRACE_TAG_PACKAGE_MANAGER
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1LL<<63)
diff --git a/init/action.cpp b/init/action.cpp
index bd5fe75..2eb809e 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -30,7 +30,7 @@
 class Action::Command
 {
 public:
-    Command(int (*f)(int nargs, char** args),
+    Command(int (*f)(const std::vector<std::string>& args),
             const std::vector<std::string>& args,
             const std::string& filename,
             int line);
@@ -40,13 +40,13 @@
     std::string BuildSourceString() const;
 
 private:
-    int (*func_)(int nargs, char** args);
+    int (*func_)(const std::vector<std::string>& args);
     const std::vector<std::string> args_;
     const std::string filename_;
     int line_;
 };
 
-Action::Command::Command(int (*f)(int nargs, char** args),
+Action::Command::Command(int (*f)(const std::vector<std::string>& args),
                          const std::vector<std::string>& args,
                          const std::string& filename,
                          int line) :
@@ -56,22 +56,17 @@
 
 int Action::Command::InvokeFunc() const
 {
-    std::vector<std::string> strs;
-    strs.resize(args_.size());
-    strs[0] = args_[0];
+    std::vector<std::string> expanded_args;
+    expanded_args.resize(args_.size());
+    expanded_args[0] = args_[0];
     for (std::size_t i = 1; i < args_.size(); ++i) {
-        if (expand_props(args_[i], &strs[i]) == -1) {
+        if (expand_props(args_[i], &expanded_args[i]) == -1) {
             ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
             return -EINVAL;
         }
     }
 
-    std::vector<char*> args;
-    for (auto& s : strs) {
-        args.push_back(&s[0]);
-    }
-
-    return func_(args.size(), &args[0]);
+    return func_(expanded_args);
 }
 
 std::string Action::Command::BuildCommandString() const
@@ -92,7 +87,7 @@
 {
 }
 
-void Action::AddCommand(int (*f)(int nargs, char** args),
+void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
                         const std::vector<std::string>& args,
                         const std::string& filename, int line)
 {
@@ -305,7 +300,7 @@
     QueuePropertyTrigger("", "");
 }
 
-void ActionManager::QueueBuiltinAction(int (*func)(int nargs, char** args),
+void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
                                        const std::string& name)
 {
     Action* act = new Action();
diff --git a/init/action.h b/init/action.h
index 8ee09b0..ae28fe1 100644
--- a/init/action.h
+++ b/init/action.h
@@ -26,7 +26,7 @@
 public:
     Action();
 
-    void AddCommand(int (*f)(int nargs, char** args),
+    void AddCommand(int (*f)(const std::vector<std::string>& args),
                     const std::vector<std::string>& args,
                     const std::string& filename = "", int line = 0);
     bool InitTriggers(const std::vector<std::string>& args, std::string* err);
@@ -60,7 +60,7 @@
     void QueueEventTrigger(const std::string& trigger);
     void QueuePropertyTrigger(const std::string& name, const std::string& value);
     void QueueAllPropertyTriggers();
-    void QueueBuiltinAction(int (*func)(int nargs, char** args),
+    void QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
                             const std::string& name);
     void ExecuteOneCommand();
     bool HasMoreCommands() const;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index e5b153a..efaee1c 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -203,7 +203,7 @@
     return count;
 }
 
-int do_bootchart_init(int nargs, char** args) {
+int do_bootchart_init(const std::vector<std::string>& args) {
     g_remaining_samples = bootchart_init();
     if (g_remaining_samples < 0) {
         ERROR("Bootcharting init failure: %s\n", strerror(errno));
diff --git a/init/builtins.cpp b/init/builtins.cpp
index df3cb02..7848e10 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -61,7 +61,7 @@
 // System call provided by bionic but not in any header file.
 extern "C" int init_module(void *, unsigned long, const char *);
 
-static int insmod(const char *filename, char *options)
+static int insmod(const char *filename, const char *options)
 {
     std::string module;
     if (!read_file(filename, &module)) {
@@ -170,37 +170,37 @@
     }
 }
 
-int do_class_start(int nargs, char **args)
+int do_class_start(const std::vector<std::string>& args)
 {
         /* Starting a class does not start services
          * which are explicitly disabled.  They must
          * be started individually.
          */
-    service_for_each_class(args[1], service_start_if_not_disabled);
+    service_for_each_class(args[1].c_str(), service_start_if_not_disabled);
     return 0;
 }
 
-int do_class_stop(int nargs, char **args)
+int do_class_stop(const std::vector<std::string>& args)
 {
-    service_for_each_class(args[1], service_stop);
+    service_for_each_class(args[1].c_str(), service_stop);
     return 0;
 }
 
-int do_class_reset(int nargs, char **args)
+int do_class_reset(const std::vector<std::string>& args)
 {
-    service_for_each_class(args[1], service_reset);
+    service_for_each_class(args[1].c_str(), service_reset);
     return 0;
 }
 
-int do_domainname(int nargs, char **args)
+int do_domainname(const std::vector<std::string>& args)
 {
-    return write_file("/proc/sys/kernel/domainname", args[1]);
+    return write_file("/proc/sys/kernel/domainname", args[1].c_str());
 }
 
-int do_enable(int nargs, char **args)
+int do_enable(const std::vector<std::string>& args)
 {
     struct service *svc;
-    svc = service_find_by_name(args[1]);
+    svc = service_find_by_name(args[1].c_str());
     if (svc) {
         svc->flags &= ~(SVC_DISABLED | SVC_RC_DISABLED);
         if (svc->flags & SVC_DISABLED_START) {
@@ -212,8 +212,13 @@
     return 0;
 }
 
-int do_exec(int nargs, char** args) {
-    service* svc = make_exec_oneshot_service(nargs, args);
+int do_exec(const std::vector<std::string>& args) {
+    std::vector<char*> strs;
+    strs.reserve(args.size());
+    for (const auto& s : args) {
+        strs.push_back(const_cast<char*>(s.c_str()));
+    }
+    service* svc = make_exec_oneshot_service(strs.size(), &strs[0]);
     if (svc == NULL) {
         return -1;
     }
@@ -221,94 +226,78 @@
     return 0;
 }
 
-int do_export(int nargs, char **args)
+int do_export(const std::vector<std::string>& args)
 {
-    return add_environment(args[1], args[2]);
+    return add_environment(args[1].c_str(), args[2].c_str());
 }
 
-int do_hostname(int nargs, char **args)
+int do_hostname(const std::vector<std::string>& args)
 {
-    return write_file("/proc/sys/kernel/hostname", args[1]);
+    return write_file("/proc/sys/kernel/hostname", args[1].c_str());
 }
 
-int do_ifup(int nargs, char **args)
+int do_ifup(const std::vector<std::string>& args)
 {
-    return __ifupdown(args[1], 1);
+    return __ifupdown(args[1].c_str(), 1);
 }
 
-
-static int do_insmod_inner(int nargs, char **args, int opt_len)
+int do_insmod(const std::vector<std::string>& args)
 {
-    char options[opt_len + 1];
-    int i;
+    std::string options;
 
-    options[0] = '\0';
-    if (nargs > 2) {
-        strcpy(options, args[2]);
-        for (i = 3; i < nargs; ++i) {
-            strcat(options, " ");
-            strcat(options, args[i]);
+    if (args.size() > 2) {
+        options += args[2];
+        for (std::size_t i = 3; i < args.size(); ++i) {
+            options += ' ';
+            options += args[i];
         }
     }
 
-    return insmod(args[1], options);
+    return insmod(args[1].c_str(), options.c_str());
 }
 
-int do_insmod(int nargs, char **args)
-{
-    int i;
-    int size = 0;
-
-    if (nargs > 2) {
-        for (i = 2; i < nargs; ++i)
-            size += strlen(args[i]) + 1;
-    }
-
-    return do_insmod_inner(nargs, args, size);
-}
-
-int do_mkdir(int nargs, char **args)
+int do_mkdir(const std::vector<std::string>& args)
 {
     mode_t mode = 0755;
     int ret;
 
     /* mkdir <path> [mode] [owner] [group] */
 
-    if (nargs >= 3) {
-        mode = strtoul(args[2], 0, 8);
+    if (args.size() >= 3) {
+        mode = std::stoul(args[2], 0, 8);
     }
 
-    ret = make_dir(args[1], mode);
+    ret = make_dir(args[1].c_str(), mode);
     /* chmod in case the directory already exists */
     if (ret == -1 && errno == EEXIST) {
-        ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+        ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
     }
     if (ret == -1) {
         return -errno;
     }
 
-    if (nargs >= 4) {
-        uid_t uid = decode_uid(args[3]);
+    if (args.size() >= 4) {
+        uid_t uid = decode_uid(args[3].c_str());
         gid_t gid = -1;
 
-        if (nargs == 5) {
-            gid = decode_uid(args[4]);
+        if (args.size() == 5) {
+            gid = decode_uid(args[4].c_str());
         }
 
-        if (lchown(args[1], uid, gid) == -1) {
+        if (lchown(args[1].c_str(), uid, gid) == -1) {
             return -errno;
         }
 
         /* chown may have cleared S_ISUID and S_ISGID, chmod again */
         if (mode & (S_ISUID | S_ISGID)) {
-            ret = fchmodat(AT_FDCWD, args[1], mode, AT_SYMLINK_NOFOLLOW);
+            ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW);
             if (ret == -1) {
                 return -errno;
             }
         }
     }
 
-    return e4crypt_set_directory_policy(args[1]);
+    return e4crypt_set_directory_policy(args[1].c_str());
 }
 
 static struct {
@@ -336,35 +325,36 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(int nargs, char **args)
+int do_mount(const std::vector<std::string>& args)
 {
     char tmp[64];
-    char *source, *target, *system;
-    char *options = NULL;
+    const char *source, *target, *system;
+    const char *options = NULL;
     unsigned flags = 0;
+    std::size_t na = 0;
     int n, i;
     int wait = 0;
 
-    for (n = 4; n < nargs; n++) {
+    for (na = 4; na < args.size(); na++) {
         for (i = 0; mount_flags[i].name; i++) {
-            if (!strcmp(args[n], mount_flags[i].name)) {
+            if (!args[na].compare(mount_flags[i].name)) {
                 flags |= mount_flags[i].flag;
                 break;
             }
         }
 
         if (!mount_flags[i].name) {
-            if (!strcmp(args[n], "wait"))
+            if (!args[na].compare("wait"))
                 wait = 1;
             /* if our last argument isn't a flag, wolf it up as an option string */
-            else if (n + 1 == nargs)
-                options = args[n];
+            else if (na + 1 == args.size())
+                options = args[na].c_str();
         }
     }
 
-    system = args[1];
-    source = args[2];
-    target = args[3];
+    system = args[1].c_str();
+    source = args[2].c_str();
+    target = args[3].c_str();
 
     if (!strncmp(source, "mtd@", 4)) {
         n = mtd_name_to_number(source + 4);
@@ -456,7 +446,7 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-int do_mount_all(int nargs, char **args)
+int do_mount_all(const std::vector<std::string>& args)
 {
     pid_t pid;
     int ret = -1;
@@ -464,10 +454,10 @@
     int status;
     struct fstab *fstab;
 
-    if (nargs != 2) {
+    if (args.size() != 2) {
         return -1;
     }
-    const char* fstabfile = args[1];
+    const char* fstabfile = args[1].c_str();
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -545,69 +535,69 @@
     return ret;
 }
 
-int do_swapon_all(int nargs, char **args)
+int do_swapon_all(const std::vector<std::string>& args)
 {
     struct fstab *fstab;
     int ret;
 
-    fstab = fs_mgr_read_fstab(args[1]);
+    fstab = fs_mgr_read_fstab(args[1].c_str());
     ret = fs_mgr_swapon_all(fstab);
     fs_mgr_free_fstab(fstab);
 
     return ret;
 }
 
-int do_setprop(int nargs, char **args)
+int do_setprop(const std::vector<std::string>& args)
 {
-    const char *name = args[1];
-    const char *value = args[2];
+    const char* name = args[1].c_str();
+    const char* value = args[2].c_str();
     property_set(name, value);
     return 0;
 }
 
-int do_setrlimit(int nargs, char **args)
+int do_setrlimit(const std::vector<std::string>& args)
 {
     struct rlimit limit;
     int resource;
-    resource = atoi(args[1]);
-    limit.rlim_cur = atoi(args[2]);
-    limit.rlim_max = atoi(args[3]);
+    resource = std::stoi(args[1]);
+    limit.rlim_cur = std::stoi(args[2]);
+    limit.rlim_max = std::stoi(args[3]);
     return setrlimit(resource, &limit);
 }
 
-int do_start(int nargs, char **args)
+int do_start(const std::vector<std::string>& args)
 {
     struct service *svc;
-    svc = service_find_by_name(args[1]);
+    svc = service_find_by_name(args[1].c_str());
     if (svc) {
         service_start(svc, NULL);
     }
     return 0;
 }
 
-int do_stop(int nargs, char **args)
+int do_stop(const std::vector<std::string>& args)
 {
     struct service *svc;
-    svc = service_find_by_name(args[1]);
+    svc = service_find_by_name(args[1].c_str());
     if (svc) {
         service_stop(svc);
     }
     return 0;
 }
 
-int do_restart(int nargs, char **args)
+int do_restart(const std::vector<std::string>& args)
 {
     struct service *svc;
-    svc = service_find_by_name(args[1]);
+    svc = service_find_by_name(args[1].c_str());
     if (svc) {
         service_restart(svc);
     }
     return 0;
 }
 
-int do_powerctl(int nargs, char **args)
+int do_powerctl(const std::vector<std::string>& args)
 {
-    const char* command = args[1];
+    const char* command = args[1].c_str();
     int len = 0;
     unsigned int cmd = 0;
     const char *reboot_target = "";
@@ -642,42 +632,42 @@
                                         callback_on_ro_remount);
 }
 
-int do_trigger(int nargs, char **args)
+int do_trigger(const std::vector<std::string>& args)
 {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
 }
 
-int do_symlink(int nargs, char **args)
+int do_symlink(const std::vector<std::string>& args)
 {
-    return symlink(args[1], args[2]);
+    return symlink(args[1].c_str(), args[2].c_str());
 }
 
-int do_rm(int nargs, char **args)
+int do_rm(const std::vector<std::string>& args)
 {
-    return unlink(args[1]);
+    return unlink(args[1].c_str());
 }
 
-int do_rmdir(int nargs, char **args)
+int do_rmdir(const std::vector<std::string>& args)
 {
-    return rmdir(args[1]);
+    return rmdir(args[1].c_str());
 }
 
-int do_sysclktz(int nargs, char **args)
+int do_sysclktz(const std::vector<std::string>& args)
 {
     struct timezone tz;
 
-    if (nargs != 2)
+    if (args.size() != 2)
         return -1;
 
     memset(&tz, 0, sizeof(tz));
-    tz.tz_minuteswest = atoi(args[1]);
+    tz.tz_minuteswest = std::stoi(args[1]);
     if (settimeofday(NULL, &tz))
         return -1;
     return 0;
 }
 
-int do_verity_load_state(int nargs, char **args) {
+int do_verity_load_state(const std::vector<std::string>& args) {
     int mode = -1;
     int rc = fs_mgr_load_verity_state(&mode);
     if (rc == 0 && mode == VERITY_MODE_LOGGING) {
@@ -691,18 +681,18 @@
                  android::base::StringPrintf("%d", mode).c_str());
 }
 
-int do_verity_update_state(int nargs, char** args) {
+int do_verity_update_state(const std::vector<std::string>& args) {
     return fs_mgr_update_verity_state(verity_update_property);
 }
 
-int do_write(int nargs, char **args)
+int do_write(const std::vector<std::string>& args)
 {
-    const char *path = args[1];
-    const char *value = args[2];
+    const char* path = args[1].c_str();
+    const char* value = args[2].c_str();
     return write_file(path, value);
 }
 
-int do_copy(int nargs, char **args)
+int do_copy(const std::vector<std::string>& args)
 {
     char *buffer = NULL;
     int rc = 0;
@@ -711,16 +701,16 @@
     int brtw, brtr;
     char *p;
 
-    if (nargs != 3)
+    if (args.size() != 3)
         return -1;
 
-    if (stat(args[1], &info) < 0)
+    if (stat(args[1].c_str(), &info) < 0)
         return -1;
 
-    if ((fd1 = open(args[1], O_RDONLY|O_CLOEXEC)) < 0)
+    if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
         goto out_err;
 
-    if ((fd2 = open(args[2], O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
+    if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
         goto out_err;
 
     if (!(buffer = (char*) malloc(info.st_size)))
@@ -764,13 +754,14 @@
     return rc;
 }
 
-int do_chown(int nargs, char **args) {
+int do_chown(const std::vector<std::string>& args) {
     /* GID is optional. */
-    if (nargs == 3) {
-        if (lchown(args[2], decode_uid(args[1]), -1) == -1)
+    if (args.size() == 3) {
+        if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
             return -errno;
-    } else if (nargs == 4) {
-        if (lchown(args[3], decode_uid(args[1]), decode_uid(args[2])) == -1)
+    } else if (args.size() == 4) {
+        if (lchown(args[3].c_str(), decode_uid(args[1].c_str()),
+                   decode_uid(args[2].c_str())) == -1)
             return -errno;
     } else {
         return -1;
@@ -791,43 +782,41 @@
     return mode;
 }
 
-int do_chmod(int nargs, char **args) {
-    mode_t mode = get_mode(args[1]);
-    if (fchmodat(AT_FDCWD, args[2], mode, AT_SYMLINK_NOFOLLOW) < 0) {
+int do_chmod(const std::vector<std::string>& args) {
+    mode_t mode = get_mode(args[1].c_str());
+    if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return -errno;
     }
     return 0;
 }
 
-int do_restorecon(int nargs, char **args) {
-    int i;
+int do_restorecon(const std::vector<std::string>& args) {
     int ret = 0;
 
-    for (i = 1; i < nargs; i++) {
-        if (restorecon(args[i]) < 0)
+    for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+        if (restorecon(it->c_str()) < 0)
             ret = -errno;
     }
     return ret;
 }
 
-int do_restorecon_recursive(int nargs, char **args) {
-    int i;
+int do_restorecon_recursive(const std::vector<std::string>& args) {
     int ret = 0;
 
-    for (i = 1; i < nargs; i++) {
-        if (restorecon_recursive(args[i]) < 0)
+    for (auto it = std::next(args.begin()); it != args.end(); ++it) {
+        if (restorecon_recursive(it->c_str()) < 0)
             ret = -errno;
     }
     return ret;
 }
 
-int do_loglevel(int nargs, char **args) {
-    if (nargs != 2) {
+int do_loglevel(const std::vector<std::string>& args) {
+    if (args.size() != 2) {
         ERROR("loglevel: missing argument\n");
         return -EINVAL;
     }
 
-    int log_level = atoi(args[1]);
+    int log_level = std::stoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
         return -EINVAL;
@@ -836,28 +825,28 @@
     return 0;
 }
 
-int do_load_persist_props(int nargs, char **args) {
-    if (nargs == 1) {
+int do_load_persist_props(const std::vector<std::string>& args) {
+    if (args.size() == 1) {
         load_persist_props();
         return 0;
     }
     return -1;
 }
 
-int do_load_all_props(int nargs, char **args) {
-    if (nargs == 1) {
+int do_load_all_props(const std::vector<std::string>& args) {
+    if (args.size() == 1) {
         load_all_props();
         return 0;
     }
     return -1;
 }
 
-int do_wait(int nargs, char **args)
+int do_wait(const std::vector<std::string>& args)
 {
-    if (nargs == 2) {
-        return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
-    } else if (nargs == 3) {
-        return wait_for_file(args[1], atoi(args[2]));
+    if (args.size() == 2) {
+        return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
+    } else if (args.size() == 3) {
+        return wait_for_file(args[1].c_str(), std::stoi(args[2]));
     } else
         return -1;
 }
@@ -874,9 +863,9 @@
     return 0;
 }
 
-int do_installkey(int nargs, char **args)
+int do_installkey(const std::vector<std::string>& args)
 {
-    if (nargs != 2) {
+    if (args.size() != 2) {
         return -1;
     }
 
@@ -885,6 +874,6 @@
         return 0;
     }
 
-    return e4crypt_create_device_key(args[1],
+    return e4crypt_create_device_key(args[1].c_str(),
                                      do_installkeys_ensure_dir_exists);
 }
diff --git a/init/init.cpp b/init/init.cpp
index 66143bf..ac4db7c 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -540,7 +540,7 @@
     }
 }
 
-static int wait_for_coldboot_done_action(int nargs, char **args) {
+static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
     Timer t;
 
     NOTICE("Waiting for %s...\n", COLDBOOT_DONE);
@@ -570,7 +570,7 @@
  * time. We do not reboot or halt on failures, as this is a best-effort
  * attempt.
  */
-static int mix_hwrng_into_linux_rng_action(int nargs, char **args)
+static int mix_hwrng_into_linux_rng_action(const std::vector<std::string>& args)
 {
     int result = -1;
     int hwrandom_fd = -1;
@@ -632,13 +632,13 @@
     return result;
 }
 
-static int keychord_init_action(int nargs, char **args)
+static int keychord_init_action(const std::vector<std::string>& args)
 {
     keychord_init();
     return 0;
 }
 
-static int console_init_action(int nargs, char **args)
+static int console_init_action(const std::vector<std::string>& args)
 {
     std::string console = property_get("ro.boot.console");
     if (!console.empty()) {
@@ -753,7 +753,7 @@
     if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
 }
 
-static int queue_property_triggers_action(int nargs, char **args)
+static int queue_property_triggers_action(const std::vector<std::string>& args)
 {
     ActionManager::GetInstance().QueueAllPropertyTriggers();
     /* enable property triggers */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index ab0dbc3..d420351 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -62,7 +62,7 @@
 
 static struct {
     const char *name;
-    int (*func)(int nargs, char **args);
+    int (*func)(const std::vector<std::string>& args);
     unsigned char nargs;
     unsigned char flags;
 } keyword_info[KEYWORD_COUNT] = {
diff --git a/init/keywords.h b/init/keywords.h
index e637d7d..922feee 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -1,43 +1,45 @@
 #ifndef KEYWORD
-int do_bootchart_init(int nargs, char **args);
-int do_class_start(int nargs, char **args);
-int do_class_stop(int nargs, char **args);
-int do_class_reset(int nargs, char **args);
-int do_domainname(int nargs, char **args);
-int do_enable(int nargs, char **args);
-int do_exec(int nargs, char **args);
-int do_export(int nargs, char **args);
-int do_hostname(int nargs, char **args);
-int do_ifup(int nargs, char **args);
-int do_insmod(int nargs, char **args);
-int do_installkey(int nargs, char **args);
-int do_mkdir(int nargs, char **args);
-int do_mount_all(int nargs, char **args);
-int do_mount(int nargs, char **args);
-int do_powerctl(int nargs, char **args);
-int do_restart(int nargs, char **args);
-int do_restorecon(int nargs, char **args);
-int do_restorecon_recursive(int nargs, char **args);
-int do_rm(int nargs, char **args);
-int do_rmdir(int nargs, char **args);
-int do_setprop(int nargs, char **args);
-int do_setrlimit(int nargs, char **args);
-int do_start(int nargs, char **args);
-int do_stop(int nargs, char **args);
-int do_swapon_all(int nargs, char **args);
-int do_trigger(int nargs, char **args);
-int do_symlink(int nargs, char **args);
-int do_sysclktz(int nargs, char **args);
-int do_write(int nargs, char **args);
-int do_copy(int nargs, char **args);
-int do_chown(int nargs, char **args);
-int do_chmod(int nargs, char **args);
-int do_loglevel(int nargs, char **args);
-int do_load_persist_props(int nargs, char **args);
-int do_load_all_props(int nargs, char **args);
-int do_verity_load_state(int nargs, char **args);
-int do_verity_update_state(int nargs, char **args);
-int do_wait(int nargs, char **args);
+#include <string>
+#include <vector>
+int do_bootchart_init(const std::vector<std::string>& args);
+int do_class_start(const std::vector<std::string>& args);
+int do_class_stop(const std::vector<std::string>& args);
+int do_class_reset(const std::vector<std::string>& args);
+int do_domainname(const std::vector<std::string>& args);
+int do_enable(const std::vector<std::string>& args);
+int do_exec(const std::vector<std::string>& args);
+int do_export(const std::vector<std::string>& args);
+int do_hostname(const std::vector<std::string>& args);
+int do_ifup(const std::vector<std::string>& args);
+int do_insmod(const std::vector<std::string>& args);
+int do_installkey(const std::vector<std::string>& args);
+int do_mkdir(const std::vector<std::string>& args);
+int do_mount_all(const std::vector<std::string>& args);
+int do_mount(const std::vector<std::string>& args);
+int do_powerctl(const std::vector<std::string>& args);
+int do_restart(const std::vector<std::string>& args);
+int do_restorecon(const std::vector<std::string>& args);
+int do_restorecon_recursive(const std::vector<std::string>& args);
+int do_rm(const std::vector<std::string>& args);
+int do_rmdir(const std::vector<std::string>& args);
+int do_setprop(const std::vector<std::string>& args);
+int do_setrlimit(const std::vector<std::string>& args);
+int do_start(const std::vector<std::string>& args);
+int do_stop(const std::vector<std::string>& args);
+int do_swapon_all(const std::vector<std::string>& args);
+int do_trigger(const std::vector<std::string>& args);
+int do_symlink(const std::vector<std::string>& args);
+int do_sysclktz(const std::vector<std::string>& args);
+int do_write(const std::vector<std::string>& args);
+int do_copy(const std::vector<std::string>& args);
+int do_chown(const std::vector<std::string>& args);
+int do_chmod(const std::vector<std::string>& args);
+int do_loglevel(const std::vector<std::string>& args);
+int do_load_persist_props(const std::vector<std::string>& args);
+int do_load_all_props(const std::vector<std::string>& args);
+int do_verity_load_state(const std::vector<std::string>& args);
+int do_verity_update_state(const std::vector<std::string>& args);
+int do_wait(const std::vector<std::string>& args);
 #define __MAKE_KEYWORD_ENUM__
 #define KEYWORD(symbol, flags, nargs, func) K_##symbol,
 enum {
diff --git a/init/perfboot.py b/init/perfboot.py
index 2cd4699..13da328 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -205,6 +205,16 @@
     atexit.register(cleanup)
 
 
+def check_dm_verity_settings(device):
+    device.wait()
+    for partition in ['system', 'vendor']:
+        verity_mode = device.get_prop('partition.%s.verified' % partition)
+        if verity_mode is None:
+            logging.warning('dm-verity is not enabled for /%s. Did you run '
+                            'adb disable-verity? That may skew the result.',
+                            partition)
+
+
 def read_event_tags(tags_file):
     """Reads event tags from |tags_file|."""
     if not tags_file:
@@ -393,6 +403,7 @@
         args.output = 'perf-%s-%s.tsv' % (
             device.get_prop('ro.build.flavor'),
             device.get_prop('ro.build.version.incremental'))
+    check_dm_verity_settings(device)
 
     record_list = []
     event_tags = read_event_tags(args.tags)