Merge changes I082e9bb7,Ib35fe4fc,Ibc2726e1

* changes:
  merge dumpstate_headers into libdumpstateutil
  Use getpwnam()/getgrnam() instead of AID_* macros (dumpstate)
  Mark libbinder and libdumpstateutil as VNDK in Android.bp
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index bcefe1b..9a3030e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -685,6 +685,7 @@
     printf("Kernel: ");
     DumpFileToFd(STDOUT_FILENO, "", "/proc/version");
     printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
+    ds.RunCommand("UPTIME", {"uptime"}, CommandOptions::DEFAULT);
     printf("Bugreport format version: %s\n", version_.c_str());
     printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
            PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str());
@@ -1053,6 +1054,7 @@
     DurationReporter duration_reporter("DUMPSTATE");
 
     dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
+    /* TODO: Remove duplicate uptime call when tools use it from header */
     RunCommand("UPTIME", {"uptime"});
     DumpBlockStatFiles();
     dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
diff --git a/cmds/flatland/Android.mk b/cmds/flatland/Android.mk
index c295167..7aa111c 100644
--- a/cmds/flatland/Android.mk
+++ b/cmds/flatland/Android.mk
@@ -8,6 +8,8 @@
     Renderers.cpp   \
     Main.cpp        \
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_MODULE:= flatland
 
 LOCAL_MODULE_TAGS := tests
diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp
index ec1e543..3d7cac0 100644
--- a/cmds/flatland/Main.cpp
+++ b/cmds/flatland/Main.cpp
@@ -284,7 +284,6 @@
 public:
 
     Layer() :
-        mFirstFrame(true),
         mGLHelper(NULL),
         mSurface(EGL_NO_SURFACE) {
     }
@@ -358,8 +357,6 @@
     }
 
 private:
-    bool mFirstFrame;
-
     LayerDesc mDesc;
 
     GLHelper* mGLHelper;
@@ -389,7 +386,6 @@
         ATRACE_CALL();
 
         bool result;
-        EGLint resulte;
 
         float scaleFactor = float(mDesc.runHeights[mInstance]) /
             float(mDesc.height);
@@ -465,7 +461,6 @@
         ATRACE_CALL();
 
         bool result;
-        status_t err;
 
         resetColorGenerator();
 
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 20b7728..af2c527 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1385,6 +1385,8 @@
                              oat_dir.c_str(),
                              is_secondary_dex,
                              oat_path)) {
+        LOG(ERROR) << "Could not create oat out path for "
+                << apk_path << " with oat dir " << oat_dir;
         return false;
     }
     oat_file_fd->reset(open(oat_path, O_RDONLY));
@@ -1489,7 +1491,7 @@
 
 // Prepares the oat dir for the secondary dex files.
 static bool prepare_secondary_dex_oat_dir(const std::string& dex_path, int uid,
-        const char* instruction_set, std::string* oat_dir_out) {
+        const char* instruction_set) {
     unsigned long dirIndex = dex_path.rfind('/');
     if (dirIndex == std::string::npos) {
         LOG(ERROR ) << "Unexpected dir structure for secondary dex " << dex_path;
@@ -1506,10 +1508,8 @@
 
     char oat_dir[PKG_PATH_MAX];
     snprintf(oat_dir, PKG_PATH_MAX, "%s/oat", dex_dir.c_str());
-    oat_dir_out->assign(oat_dir);
 
-    // Create oat/isa output directory.
-    if (prepare_app_cache_dir(*oat_dir_out, instruction_set, oat_dir_mode, uid, uid) != 0) {
+    if (prepare_app_cache_dir(oat_dir, instruction_set, oat_dir_mode, uid, uid) != 0) {
         LOG(ERROR) << "Could not prepare oat/isa dir for secondary dex: " << dex_path;
         return false;
     }
@@ -1517,39 +1517,128 @@
     return true;
 }
 
-static int constexpr DEXOPTANALYZER_BIN_EXEC_ERROR = 200;
+// Return codes for identifying the reason why dexoptanalyzer was not invoked when processing
+// secondary dex files. This return codes are returned by the child process created for
+// analyzing secondary dex files in process_secondary_dex_dexopt.
 
-// Verifies the result of dexoptanalyzer executed for the apk_path.
+// The dexoptanalyzer was not invoked because of validation or IO errors.
+static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED = 200;
+// The dexoptanalyzer was not invoked because the dex file does not exist anymore.
+static int constexpr SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE = 201;
+
+// Verifies the result of analyzing secondary dex files from process_secondary_dex_dexopt.
 // If the result is valid returns true and sets dexopt_needed_out to a valid value.
 // Returns false for errors or unexpected result values.
-static bool process_dexoptanalyzer_result(const std::string& dex_path, int result,
+// The result is expected to be either one of SECONDARY_DEX_* codes or a valid exit code
+// of dexoptanalyzer.
+static bool process_secondary_dexoptanalyzer_result(const std::string& dex_path, int result,
             int* dexopt_needed_out) {
     // The result values are defined in dexoptanalyzer.
     switch (result) {
-        case 0:  // no_dexopt_needed
+        case 0:  // dexoptanalyzer: no_dexopt_needed
             *dexopt_needed_out = NO_DEXOPT_NEEDED; return true;
-        case 1:  // dex2oat_from_scratch
+        case 1:  // dexoptanalyzer: dex2oat_from_scratch
             *dexopt_needed_out = DEX2OAT_FROM_SCRATCH; return true;
-        case 5:  // dex2oat_for_bootimage_odex
+        case 5:  // dexoptanalyzer: dex2oat_for_bootimage_odex
             *dexopt_needed_out = -DEX2OAT_FOR_BOOT_IMAGE; return true;
-        case 6:  // dex2oat_for_filter_odex
+        case 6:  // dexoptanalyzer: dex2oat_for_filter_odex
             *dexopt_needed_out = -DEX2OAT_FOR_FILTER; return true;
-        case 7:  // dex2oat_for_relocation_odex
+        case 7:  // dexoptanalyzer: dex2oat_for_relocation_odex
             *dexopt_needed_out = -DEX2OAT_FOR_RELOCATION; return true;
-        case 2:  // dex2oat_for_bootimage_oat
-        case 3:  // dex2oat_for_filter_oat
-        case 4:  // dex2oat_for_relocation_oat
+        case 2:  // dexoptanalyzer: dex2oat_for_bootimage_oat
+        case 3:  // dexoptanalyzer: dex2oat_for_filter_oat
+        case 4:  // dexoptanalyzer: dex2oat_for_relocation_oat
             LOG(ERROR) << "Dexoptnalyzer return the status of an oat file."
                     << " Expected odex file status for secondary dex " << dex_path
                     << " : dexoptanalyzer result=" << result;
             return false;
+        case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE:
+            // If the file does not exist there's no need for dexopt.
+            *dexopt_needed_out = NO_DEXOPT_NEEDED;
+            return true;
+        case SECONDARY_DEX_DEXOPTANALYZER_SKIPPED:
+            return false;
         default:
-            LOG(ERROR) << "Unexpected result for dexoptanalyzer " << dex_path
-                    << " exec_dexoptanalyzer result=" << result;
+            LOG(ERROR) << "Unexpected result from analyzing secondary dex " << dex_path
+                    << " result=" << result;
             return false;
     }
 }
 
+enum SecondaryDexAccess {
+    kSecondaryDexAccessReadOk = 0,
+    kSecondaryDexAccessDoesNotExist = 1,
+    kSecondaryDexAccessPermissionError = 2,
+    kSecondaryDexAccessIOError = 3
+};
+
+static SecondaryDexAccess check_secondary_dex_access(const std::string& dex_path) {
+    // Check if the path exists and can be read. If not, there's nothing to do.
+    if (access(dex_path.c_str(), R_OK) == 0) {
+        return kSecondaryDexAccessReadOk;
+    } else {
+        if (errno == ENOENT) {
+            LOG(INFO) << "Secondary dex does not exist: " <<  dex_path;
+            return kSecondaryDexAccessDoesNotExist;
+        } else {
+            PLOG(ERROR) << "Could not access secondary dex " << dex_path;
+            return errno == EACCES
+                ? kSecondaryDexAccessPermissionError
+                : kSecondaryDexAccessIOError;
+        }
+    }
+}
+
+static bool is_file_public(const std::string& filename) {
+    struct stat file_stat;
+    if (stat(filename.c_str(), &file_stat) == 0) {
+        return (file_stat.st_mode & S_IROTH) != 0;
+    }
+    return false;
+}
+
+// Create the oat file structure for the secondary dex 'dex_path' and assign
+// the individual path component to the 'out_' parameters.
+static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
+        char* out_oat_dir, char* out_oat_isa_dir, char* out_oat_path) {
+    size_t dirIndex = dex_path.rfind('/');
+    if (dirIndex == std::string::npos) {
+        LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
+        return false;
+    }
+    // TODO(calin): we have similar computations in at lest 3 other places
+    // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
+    // using string append.
+    std::string apk_dir = dex_path.substr(0, dirIndex);
+    snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
+    snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
+
+    if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
+            /*is_secondary_dex*/true, out_oat_path)) {
+        LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
+        return false;
+    }
+    return true;
+}
+
+// Validate that the dexopt_flags contain a valid storage flag and convert that to an installd
+// recognized storage flags (FLAG_STORAGE_CE or FLAG_STORAGE_DE).
+static bool validate_dexopt_storage_flags(int dexopt_flags, int* out_storage_flag) {
+    if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
+        *out_storage_flag = FLAG_STORAGE_CE;
+        if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+            LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
+            return false;
+        }
+    } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
+        *out_storage_flag = FLAG_STORAGE_DE;
+    } else {
+        LOG(ERROR) << "Secondary dex storage flag must be set";
+        return false;
+    }
+    return true;
+}
+
 // Processes the dex_path as a secondary dex files and return true if the path dex file should
 // be compiled. Returns false for errors (logged) or true if the secondary dex path was process
 // successfully.
@@ -1557,126 +1646,107 @@
 //   - is_public_out: whether or not the oat file should not be made public
 //   - dexopt_needed_out: valid OatFileAsssitant::DexOptNeeded
 //   - oat_dir_out: the oat dir path where the oat file should be stored
-//   - dex_path_out: the real path of the dex file
-static bool process_secondary_dex_dexopt(const char* original_dex_path, const char* pkgname,
+static bool process_secondary_dex_dexopt(const std::string& dex_path, const char* pkgname,
         int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
         const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
-        std::string* oat_dir_out, std::string* dex_path_out, bool downgrade,
-        const char* class_loader_context) {
+        std::string* oat_dir_out, bool downgrade, const char* class_loader_context) {
+    LOG(DEBUG) << "Processing secondary dex path " << dex_path;
     int storage_flag;
-
-    if ((dexopt_flags & DEXOPT_STORAGE_CE) != 0) {
-        storage_flag = FLAG_STORAGE_CE;
-        if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
-            LOG(ERROR) << "Ambiguous secondary dex storage flag. Both, CE and DE, flags are set";
-            return false;
-        }
-    } else if ((dexopt_flags & DEXOPT_STORAGE_DE) != 0) {
-        storage_flag = FLAG_STORAGE_DE;
-    } else {
-        LOG(ERROR) << "Secondary dex storage flag must be set";
+    if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag)) {
         return false;
     }
-
-    {
-        // As opposed to the primary apk, secondary dex files might contain symlinks.
-        // Resolve the path before passing it to the validate method to
-        // make sure the verification is done on the real location.
-        UniqueCPtr<char> dex_real_path_cstr(realpath(original_dex_path, nullptr));
-        if (dex_real_path_cstr == nullptr) {
-            PLOG(ERROR) << "Could not get the real path of the secondary dex file "
-                    << original_dex_path;
-            return false;
-        } else {
-            dex_path_out->assign(dex_real_path_cstr.get());
-        }
-    }
-    const std::string& dex_path = *dex_path_out;
-    if (!validate_dex_path_size(dex_path)) {
+    // Compute the oat dir as it's not easy to extract it from the child computation.
+    char oat_path[PKG_PATH_MAX];
+    char oat_dir[PKG_PATH_MAX];
+    char oat_isa_dir[PKG_PATH_MAX];
+    if (!create_secondary_dex_oat_layout(
+            dex_path, instruction_set, oat_dir, oat_isa_dir, oat_path)) {
+        LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
         return false;
     }
-    if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
-        LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
-        return false;
-    }
-
-    // Check if the path exist. If not, there's nothing to do.
-    struct stat dex_path_stat;
-    if (stat(dex_path.c_str(), &dex_path_stat) != 0) {
-        if (errno == ENOENT) {
-            // Secondary dex files might be deleted any time by the app.
-            // Nothing to do if that's the case
-            ALOGV("Secondary dex does not exist %s", dex_path.c_str());
-            return NO_DEXOPT_NEEDED;
-        } else {
-            PLOG(ERROR) << "Could not access secondary dex " << dex_path;
-        }
-    }
-
-    // Check if we should make the oat file public.
-    // Note that if the dex file is not public the compiled code cannot be made public.
-    *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) &&
-            ((dex_path_stat.st_mode & S_IROTH) != 0);
-
-    // Prepare the oat directories.
-    if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set, oat_dir_out)) {
-        return false;
-    }
-
-    // Analyze profiles.
-    bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
-
-    unique_fd oat_file_fd;
-    unique_fd vdex_file_fd;
-    unique_fd zip_fd;
-    zip_fd.reset(open(dex_path.c_str(), O_RDONLY));
-    if (zip_fd.get() < 0) {
-        PLOG(ERROR) << "installd cannot open " << dex_path << " for input during dexopt";
-        return false;
-    }
-    if (!maybe_open_oat_and_vdex_file(dex_path,
-                                      *oat_dir_out,
-                                      instruction_set,
-                                      true /* is_secondary_dex */,
-                                      &oat_file_fd,
-                                      &vdex_file_fd)) {
-      return false;
-    }
+    oat_dir_out->assign(oat_dir);
 
     pid_t pid = fork();
     if (pid == 0) {
         // child -- drop privileges before continuing.
         drop_capabilities(uid);
-        // Run dexoptanalyzer to get dexopt_needed code.
+
+        // Validate the path structure.
+        if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid, uid, storage_flag)) {
+            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+        }
+
+        // Open the dex file.
+        unique_fd zip_fd;
+        zip_fd.reset(open(dex_path.c_str(), O_RDONLY));
+        if (zip_fd.get() < 0) {
+            if (errno == ENOENT) {
+                _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE);
+            } else {
+                _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+            }
+        }
+
+        // Prepare the oat directories.
+        if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) {
+            _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+        }
+
+        // Open the vdex/oat files if any.
+        unique_fd oat_file_fd;
+        unique_fd vdex_file_fd;
+        if (!maybe_open_oat_and_vdex_file(dex_path,
+                                          *oat_dir_out,
+                                          instruction_set,
+                                          true /* is_secondary_dex */,
+                                          &oat_file_fd,
+                                          &vdex_file_fd)) {
+            _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
+        }
+
+        // Analyze profiles.
+        bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+
+        // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return.
         exec_dexoptanalyzer(dex_path,
                             vdex_file_fd.get(),
                             oat_file_fd.get(),
                             zip_fd.get(),
                             instruction_set,
-                            compiler_filter,
-                            profile_was_updated,
-                            downgrade, class_loader_context);
-        exit(DEXOPTANALYZER_BIN_EXEC_ERROR);
+                            compiler_filter, profile_was_updated,
+                            downgrade,
+                            class_loader_context);
+        PLOG(ERROR) << "Failed to exec dexoptanalyzer";
+        _exit(SECONDARY_DEX_DEXOPTANALYZER_SKIPPED);
     }
 
     /* parent */
-
     int result = wait_child(pid);
     if (!WIFEXITED(result)) {
         LOG(ERROR) << "dexoptanalyzer failed for path " << dex_path << ": " << result;
         return false;
     }
     result = WEXITSTATUS(result);
-    bool success = process_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+    // Check that we successfully executed dexoptanalyzer.
+    bool success = process_secondary_dexoptanalyzer_result(dex_path, result, dexopt_needed_out);
+
+    LOG(DEBUG) << "Processed secondary dex file " << dex_path << " result=" << result;
+
     // Run dexopt only if needed or forced.
-    // Note that dexoptanalyzer is executed even if force compilation is enabled.
-    // We ignore its valid dexopNeeded result, but still check (in process_dexoptanalyzer_result)
-    // that we only get results for odex files (apk_dir/oat/isa/code.odex) and not
-    // for oat files from dalvik-cache.
-    if (success && ((dexopt_flags & DEXOPT_FORCE) != 0)) {
+    // Note that dexoptanalyzer is executed even if force compilation is enabled (because it
+    // makes the code simpler; force compilation is only needed during tests).
+    if (success &&
+        (result != SECONDARY_DEX_DEXOPTANALYZER_SKIPPED_NO_FILE) &&
+        ((dexopt_flags & DEXOPT_FORCE) != 0)) {
         *dexopt_needed_out = DEX2OAT_FROM_SCRATCH;
     }
 
+    // Check if we should make the oat file public.
+    // Note that if the dex file is not public the compiled code cannot be made public.
+    // It is ok to check this flag outside in the parent process.
+    *is_public_out = ((dexopt_flags & DEXOPT_PUBLIC) != 0) && is_file_public(dex_path);
+
     return success;
 }
 
@@ -1708,13 +1778,11 @@
 
     // Check if we're dealing with a secondary dex file and if we need to compile it.
     std::string oat_dir_str;
-    std::string dex_real_path;
     if (is_secondary_dex) {
         if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
                 instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
-                &dex_real_path, downgrade, class_loader_context)) {
+                downgrade, class_loader_context)) {
             oat_dir = oat_dir_str.c_str();
-            dex_path = dex_real_path.c_str();
             if (dexopt_needed == NO_DEXOPT_NEEDED) {
                 return 0;  // Nothing to do, report success.
             }
@@ -1852,29 +1920,13 @@
     return false;
 }
 
-// Create the oat file structure for the secondary dex 'dex_path' and assign
-// the individual path component to the 'out_' parameters.
-static bool create_secondary_dex_oat_layout(const std::string& dex_path, const std::string& isa,
-        /*out*/char* out_oat_dir, /*out*/char* out_oat_isa_dir, /*out*/char* out_oat_path) {
-    size_t dirIndex = dex_path.rfind('/');
-    if (dirIndex == std::string::npos) {
-        LOG(ERROR) << "Unexpected dir structure for dex file " << dex_path;
-        return false;
-    }
-    // TODO(calin): we have similar computations in at lest 3 other places
-    // (InstalldNativeService, otapropt and dexopt). Unify them and get rid of snprintf by
-    // use string append.
-    std::string apk_dir = dex_path.substr(0, dirIndex);
-    snprintf(out_oat_dir, PKG_PATH_MAX, "%s/oat", apk_dir.c_str());
-    snprintf(out_oat_isa_dir, PKG_PATH_MAX, "%s/%s", out_oat_dir, isa.c_str());
-
-    if (!create_oat_out_path(dex_path.c_str(), isa.c_str(), out_oat_dir,
-            /*is_secondary_dex*/true, out_oat_path)) {
-        LOG(ERROR) << "Could not create oat path for secondary dex " << dex_path;
-        return false;
-    }
-    return true;
-}
+enum ReconcileSecondaryDexResult {
+    kReconcileSecondaryDexExists = 0,
+    kReconcileSecondaryDexCleanedUp = 1,
+    kReconcileSecondaryDexValidationError = 2,
+    kReconcileSecondaryDexCleanUpError = 3,
+    kReconcileSecondaryDexAccessIOError = 4,
+};
 
 // Reconcile the secondary dex 'dex_path' and its generated oat files.
 // Return true if all the parameters are valid and the secondary dex file was
@@ -1888,36 +1940,15 @@
         const std::string& pkgname, int uid, const std::vector<std::string>& isas,
         const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
         /*out*/bool* out_secondary_dex_exists) {
-    // Set out to false to start with, just in case we have validation errors.
-    *out_secondary_dex_exists = false;
-    if (!validate_dex_path_size(dex_path)) {
-        return false;
-    }
-
+    *out_secondary_dex_exists = false;  // start by assuming the file does not exist.
     if (isas.size() == 0) {
         LOG(ERROR) << "reconcile_secondary_dex_file called with empty isas vector";
         return false;
     }
 
-    const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
-
-    // Note that we cannot validate the package path here because the file might not exist
-    // and we cannot call realpath to resolve system symlinks. Since /data/user/0 symlinks to
-    // /data/data/ a lot of validations will fail if we attempt to check the package path.
-    // It is still ok to be more relaxed because any file removal is done after forking and
-    // dropping capabilities.
-    if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
-            uid, storage_flag, /*validate_package_path*/ false)) {
-        LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
-        return false;
-    }
-
-    if (access(dex_path.c_str(), F_OK) == 0) {
-        // The path exists, nothing to do. The odex files (if any) will be left untouched.
-        *out_secondary_dex_exists = true;
-        return true;
-    } else if (errno != ENOENT) {
-        PLOG(ERROR) << "Failed to check access to secondary dex " << dex_path;
+    if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
+        LOG(ERROR) << "reconcile_secondary_dex_file called with invalid storage_flag: "
+                << storage_flag;
         return false;
     }
 
@@ -1925,23 +1956,39 @@
     // of the package user id. So we fork and drop capabilities in the child.
     pid_t pid = fork();
     if (pid == 0) {
-        // The secondary dex does not exist anymore. Clear any generated files.
+        /* child -- drop privileges before continuing */
+        drop_capabilities(uid);
+
+        const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+        if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+                uid, storage_flag)) {
+            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            _exit(kReconcileSecondaryDexValidationError);
+        }
+
+        SecondaryDexAccess access_check = check_secondary_dex_access(dex_path);
+        switch (access_check) {
+            case kSecondaryDexAccessDoesNotExist:
+                 // File does not exist. Proceed with cleaning.
+                break;
+            case kSecondaryDexAccessReadOk: _exit(kReconcileSecondaryDexExists);
+            case kSecondaryDexAccessIOError: _exit(kReconcileSecondaryDexAccessIOError);
+            case kSecondaryDexAccessPermissionError: _exit(kReconcileSecondaryDexValidationError);
+            default:
+                LOG(ERROR) << "Unexpected result from check_secondary_dex_access: " << access_check;
+                _exit(kReconcileSecondaryDexValidationError);
+        }
+
+        // The secondary dex does not exist anymore or it's. Clear any generated files.
         char oat_path[PKG_PATH_MAX];
         char oat_dir[PKG_PATH_MAX];
         char oat_isa_dir[PKG_PATH_MAX];
         bool result = true;
-        /* child -- drop privileges before continuing */
-        drop_capabilities(uid);
         for (size_t i = 0; i < isas.size(); i++) {
-            if (!create_secondary_dex_oat_layout(dex_path,
-                                                 isas[i],
-                                                 oat_dir,
-                                                 oat_isa_dir,
-                                                 oat_path)) {
-                LOG(ERROR) << "Could not create secondary odex layout: "
-                           << dex_path;
-                result = false;
-                continue;
+            if (!create_secondary_dex_oat_layout(
+                    dex_path,isas[i], oat_dir, oat_isa_dir, oat_path)) {
+                LOG(ERROR) << "Could not create secondary odex layout: " << dex_path;
+                _exit(kReconcileSecondaryDexValidationError);
             }
 
             // Delete oat/vdex/art files.
@@ -1966,11 +2013,43 @@
             result = rmdir_if_empty(oat_isa_dir) && result;
             result = rmdir_if_empty(oat_dir) && result;
         }
-        result ? _exit(0) : _exit(1);
+        if (!result) {
+            PLOG(ERROR) << "Failed to clean secondary dex artifacts for location " << dex_path;
+        }
+        _exit(result ? kReconcileSecondaryDexCleanedUp : kReconcileSecondaryDexAccessIOError);
     }
 
     int return_code = wait_child(pid);
-    return return_code == 0;
+    if (!WIFEXITED(return_code)) {
+        LOG(WARNING) << "reconcile dex failed for location " << dex_path << ": " << return_code;
+    } else {
+        return_code = WEXITSTATUS(return_code);
+    }
+
+    LOG(DEBUG) << "Reconcile secondary dex path " << dex_path << " result=" << return_code;
+
+    switch (return_code) {
+        case kReconcileSecondaryDexCleanedUp:
+        case kReconcileSecondaryDexValidationError:
+            // If we couldn't validate assume the dex file does not exist.
+            // This will purge the entry from the PM records.
+            *out_secondary_dex_exists = false;
+            return true;
+        case kReconcileSecondaryDexExists:
+            *out_secondary_dex_exists = true;
+            return true;
+        case kReconcileSecondaryDexAccessIOError:
+            // We had an access IO error.
+            // Return false so that we can try again.
+            // The value of out_secondary_dex_exists does not matter in this case and by convention
+            // is set to false.
+            *out_secondary_dex_exists = false;
+            return false;
+        default:
+            LOG(ERROR) << "Unexpected code from reconcile_secondary_dex_file: " << return_code;
+            *out_secondary_dex_exists = false;
+            return false;
+    }
 }
 
 // Helper for move_ab, so that we can have common failure-case cleanup.
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 89c11aa..1a22992 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -55,3 +55,23 @@
         "liblogwrap",
     ],
 }
+
+cc_test {
+    name: "installd_dexopt_test",
+    clang: true,
+    srcs: ["installd_dexopt_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libselinux",
+        "libutils",
+    ],
+    static_libs: [
+        "libdiskusage",
+        "libinstalld",
+        "liblog",
+        "liblogwrap",
+    ],
+}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
new file mode 100644
index 0000000..821b96f
--- /dev/null
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2017 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 <stdlib.h>
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <gtest/gtest.h>
+
+#include "dexopt.h"
+#include "InstalldNativeService.h"
+#include "globals.h"
+#include "tests/test_utils.h"
+#include "utils.h"
+
+namespace android {
+namespace installd {
+
+// TODO(calin): try to dedup this code.
+#if defined(__arm__)
+static const std::string kRuntimeIsa = "arm";
+#elif defined(__aarch64__)
+static const std::string kRuntimeIsa = "arm64";
+#elif defined(__mips__) && !defined(__LP64__)
+static const std::string kRuntimeIsa = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const std::string kRuntimeIsa = "mips64";
+#elif defined(__i386__)
+static const std::string kRuntimeIsa = "x86";
+#elif defined(__x86_64__)
+static const std::string kRuntimeIsa = "x86_64";
+#else
+static const std::string kRuntimeIsa = "none";
+#endif
+
+int get_property(const char *key, char *value, const char *default_value) {
+    return property_get(key, value, default_value);
+}
+
+bool calculate_oat_file_path(char path[PKG_PATH_MAX], const char *oat_dir, const char *apk_path,
+        const char *instruction_set) {
+    return calculate_oat_file_path_default(path, oat_dir, apk_path, instruction_set);
+}
+
+bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
+        const char *instruction_set) {
+    return calculate_odex_file_path_default(path, apk_path, instruction_set);
+}
+
+bool create_cache_path(char path[PKG_PATH_MAX], const char *src, const char *instruction_set) {
+    return create_cache_path_default(path, src, instruction_set);
+}
+
+static void run_cmd(const std::string& cmd) {
+    system(cmd.c_str());
+}
+
+static void mkdir(const std::string& path, uid_t owner, gid_t group, mode_t mode) {
+    ::mkdir(path.c_str(), mode);
+    ::chown(path.c_str(), owner, group);
+    ::chmod(path.c_str(), mode);
+}
+
+// Base64 encoding of a simple dex files with 2 methods.
+static const char kDexFile[] =
+    "UEsDBBQAAAAIAOiOYUs9y6BLCgEAABQCAAALABwAY2xhc3Nlcy5kZXhVVAkAA/Ns+lkOHv1ZdXgL"
+    "AAEEI+UCAASIEwAAS0mt4DIwNmX4qpn7j/2wA7v7N+ZvoQpCJRlVx5SWa4YaiDAxMBQwMDBUhJkI"
+    "MUBBDyMDAzsDRJwFxAdioBDDHAYEYAbiFUAM1M5wAIhFGCGKDIDYAogdgNgDiH2BOAiI0xghekDm"
+    "sQIxGxQzM6ACRijNhCbOhCZfyohdPYyuh8szgtVkMkLsLhAAqeCDi+ejibPZZOZlltgxsDnqZSWW"
+    "JTKwOUFoZh9HayDhZM0g5AMS0M9JzEvX90/KSk0usWZgDAMaws5nAyXBzmpoYGlgAjsAyJoBMp0b"
+    "zQ8gGhbOTEhhzYwU3qxIYc2GFN6MClC/AhUyKUDMAYU9M1Qc5F8GKBscVgIQM0FxCwBQSwECHgMU"
+    "AAAACADojmFLPcugSwoBAAAUAgAACwAYAAAAAAAAAAAAoIEAAAAAY2xhc3Nlcy5kZXhVVAUAA/Ns"
+    "+ll1eAsAAQQj5QIABIgTAABQSwUGAAAAAAEAAQBRAAAATwEAAAAA";
+
+
+class DexoptTest : public testing::Test {
+protected:
+    static constexpr bool kDebug = false;
+    static constexpr uid_t kSystemUid = 1000;
+    static constexpr uid_t kSystemGid = 1000;
+    static constexpr int32_t kOSdkVersion = 25;
+    static constexpr int32_t kAppDataFlags = FLAG_STORAGE_CE | FLAG_STORAGE_DE;
+    static constexpr uid_t kTestAppUid = 19999;
+    static constexpr gid_t kTestAppGid = 19999;
+    static constexpr int32_t kTestUserId = 0;
+
+    InstalldNativeService* service_;
+    std::unique_ptr<std::string> volume_uuid_;
+    std::string package_name_;
+    std::string app_apk_dir_;
+    std::string app_private_dir_ce_;
+    std::string app_private_dir_de_;
+    std::string se_info_;
+
+    int64_t ce_data_inode_;
+
+    std::string secondary_dex_ce_;
+    std::string secondary_dex_ce_link_;
+    std::string secondary_dex_de_;
+
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:v", 1);
+        android::base::InitLogging(nullptr);
+
+        service_ = new InstalldNativeService();
+
+        volume_uuid_ = nullptr;
+        package_name_ = "com.installd.test.dexopt";
+        se_info_ = "default";
+
+        init_globals_from_data_and_root();
+
+        app_apk_dir_ = android_app_dir + package_name_;
+
+        create_mock_app();
+    }
+
+    virtual void TearDown() {
+        if (!kDebug) {
+            service_->destroyAppData(
+                volume_uuid_, package_name_, kTestUserId, kAppDataFlags, ce_data_inode_);
+            run_cmd("rm -rf " + app_apk_dir_);
+            run_cmd("rm -rf " + app_private_dir_ce_);
+            run_cmd("rm -rf " + app_private_dir_de_);
+        }
+        delete service_;
+    }
+
+    void create_mock_app() {
+        // Create the oat dir.
+        std::string app_oat_dir = app_apk_dir_ + "/oat";
+        mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755);
+        service_->createOatDir(app_oat_dir, kRuntimeIsa);
+
+        // Copy the primary apk.
+        std::string apk_path = app_apk_dir_ + "/base.jar";
+        ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path, kSystemUid, kSystemGid, 0644));
+
+        // Create the app user data.
+        ASSERT_TRUE(service_->createAppData(
+            volume_uuid_,
+            package_name_,
+            kTestUserId,
+            kAppDataFlags,
+            kTestAppUid,
+            se_info_,
+            kOSdkVersion,
+            &ce_data_inode_).isOk());
+
+        // Create a secondary dex file on CE storage
+        const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str();
+        app_private_dir_ce_ = create_data_user_ce_package_path(
+                volume_uuid_cstr, kTestUserId, package_name_.c_str());
+        secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar";
+        ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_ce_, kTestAppUid, kTestAppGid, 0600));
+        std::string app_private_dir_ce_link = create_data_user_ce_package_path_as_user_link(
+                volume_uuid_cstr, kTestUserId, package_name_.c_str());
+        secondary_dex_ce_link_ = app_private_dir_ce_link + "/secondary_ce.jar";
+
+        // Create a secondary dex file on DE storage.
+        app_private_dir_de_ = create_data_user_de_package_path(
+                volume_uuid_cstr, kTestUserId, package_name_.c_str());
+        secondary_dex_de_ = app_private_dir_de_ + "/secondary_de.jar";
+        ASSERT_TRUE(WriteBase64ToFile(kDexFile, secondary_dex_de_, kTestAppUid, kTestAppGid, 0600));
+
+        // Fix app data uid.
+        ASSERT_TRUE(service_->fixupAppData(volume_uuid_, kTestUserId).isOk());
+    }
+
+
+    std::string get_secondary_dex_artifact(const std::string& path, const std::string& type) {
+        std::string::size_type end = path.rfind('.');
+        std::string::size_type start = path.rfind('/', end);
+        return path.substr(0, start) + "/oat/" + kRuntimeIsa + "/" +
+                path.substr(start + 1, end - start) + type;
+    }
+
+    void compile_secondary_dex(const std::string& path, int32_t dex_storage_flag,
+            bool should_binder_call_succeed, bool should_dex_be_compiled = true,
+            int uid = kTestAppUid) {
+        std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
+        int32_t dexopt_needed = 0;  // does not matter;
+        std::unique_ptr<std::string> out_path = nullptr;  // does not matter
+        int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
+        std::string compiler_filter = "speed-profile";
+        std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
+        std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+        bool downgrade = false;
+
+        binder::Status result = service_->dexopt(path,
+                                                 uid,
+                                                 package_name_ptr,
+                                                 kRuntimeIsa,
+                                                 dexopt_needed,
+                                                 out_path,
+                                                 dex_flags,
+                                                 compiler_filter,
+                                                 volume_uuid_,
+                                                 class_loader_context_ptr,
+                                                 se_info_ptr,
+                                                 downgrade);
+        ASSERT_EQ(should_binder_call_succeed, result.isOk());
+        int expected_access = should_dex_be_compiled ? 0 : -1;
+        std::string odex = get_secondary_dex_artifact(path, "odex");
+        std::string vdex = get_secondary_dex_artifact(path, "vdex");
+        std::string art = get_secondary_dex_artifact(path, "art");
+        ASSERT_EQ(expected_access, access(odex.c_str(), R_OK));
+        ASSERT_EQ(expected_access, access(vdex.c_str(), R_OK));
+        ASSERT_EQ(-1, access(art.c_str(), R_OK));  // empty profiles do not generate an image.
+    }
+
+    void reconcile_secondary_dex(const std::string& path, int32_t storage_flag,
+            bool should_binder_call_succeed, bool should_dex_exist, bool should_dex_be_deleted,
+            int uid = kTestAppUid, std::string* package_override = nullptr) {
+        std::vector<std::string> isas;
+        isas.push_back(kRuntimeIsa);
+        bool out_secondary_dex_exists = false;
+        binder::Status result = service_->reconcileSecondaryDexFile(
+            path,
+            package_override == nullptr ? package_name_ : *package_override,
+            uid,
+            isas,
+            volume_uuid_,
+            storage_flag,
+            &out_secondary_dex_exists);
+
+        ASSERT_EQ(should_binder_call_succeed, result.isOk());
+        ASSERT_EQ(should_dex_exist, out_secondary_dex_exists);
+
+        int expected_access = should_dex_be_deleted ? -1 : 0;
+        std::string odex = get_secondary_dex_artifact(path, "odex");
+        std::string vdex = get_secondary_dex_artifact(path, "vdex");
+        std::string art = get_secondary_dex_artifact(path, "art");
+        ASSERT_EQ(expected_access, access(odex.c_str(), F_OK));
+        ASSERT_EQ(expected_access, access(vdex.c_str(), F_OK));
+        ASSERT_EQ(-1, access(art.c_str(), R_OK));  // empty profiles do not generate an image.
+    }
+};
+
+
+TEST_F(DexoptTest, DexoptSecondaryCe) {
+    LOG(INFO) << "DexoptSecondaryCe";
+    compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+        /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryCeLink) {
+    LOG(INFO) << "DexoptSecondaryCeLink";
+    compile_secondary_dex(secondary_dex_ce_link_, DEXOPT_STORAGE_CE,
+        /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryDe) {
+    LOG(INFO) << "DexoptSecondaryDe";
+    compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+        /*binder_ok*/ true, /*compile_ok*/ true);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) {
+    LOG(INFO) << "DexoptSecondaryDoesNotExist";
+    // If the file validates but does not exist we do not treat it as an error.
+    compile_secondary_dex(secondary_dex_ce_ + "not.there", DEXOPT_STORAGE_CE,
+        /*binder_ok*/ true,  /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryStorageValidationError) {
+    LOG(INFO) << "DexoptSecondaryStorageValidationError";
+    compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_DE,
+        /*binder_ok*/ false,  /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) {
+    LOG(INFO) << "DexoptSecondaryAppOwnershipValidationError";
+    compile_secondary_dex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE,
+        /*binder_ok*/ false,  /*compile_ok*/ false);
+}
+
+TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) {
+    LOG(INFO) << "DexoptSecondaryAcessViaDifferentUidError";
+    compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+        /*binder_ok*/ false,  /*compile_ok*/ false, kSystemUid);
+}
+
+
+class ReconcileTest : public DexoptTest {
+    virtual void SetUp() {
+        DexoptTest::SetUp();
+        compile_secondary_dex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+            /*binder_ok*/ true, /*compile_ok*/ true);
+        compile_secondary_dex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+            /*binder_ok*/ true, /*compile_ok*/ true);
+    }
+};
+
+TEST_F(ReconcileTest, ReconcileSecondaryCeExists) {
+    LOG(INFO) << "ReconcileSecondaryCeExists";
+    reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+        /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryCeLinkExists) {
+    LOG(INFO) << "ReconcileSecondaryCeLinkExists";
+    reconcile_secondary_dex(secondary_dex_ce_link_, FLAG_STORAGE_CE,
+        /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryDeExists) {
+    LOG(INFO) << "ReconcileSecondaryDeExists";
+    reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE,
+        /*binder_ok*/ true, /*dex_ok */ true, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryDeDoesNotExist) {
+    LOG(INFO) << "ReconcileSecondaryDeDoesNotExist";
+    run_cmd("rm -rf " + secondary_dex_de_);
+    reconcile_secondary_dex(secondary_dex_de_, FLAG_STORAGE_DE,
+        /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ true);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryStorageValidationError) {
+    // Validation errors will not clean the odex/vdex/art files but will mark
+    // the file as non existent so that the PM knows it should purge it from its
+    // records.
+    LOG(INFO) << "ReconcileSecondaryStorageValidationError";
+    reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_DE,
+        /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryAppOwnershipValidationError) {
+    LOG(INFO) << "ReconcileSecondaryAppOwnershipValidationError";
+    // Attempt to reconcile the dex files of the test app from a different app.
+    std::string another_app = "another.app";
+    reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+        /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid, &another_app);
+}
+
+TEST_F(ReconcileTest, ReconcileSecondaryAcessViaDifferentUidError) {
+    LOG(INFO) << "ReconcileSecondaryAcessViaDifferentUidError";
+    reconcile_secondary_dex(secondary_dex_ce_, FLAG_STORAGE_CE,
+        /*binder_ok*/ true, /*dex_ok */ false, /*odex_deleted*/ false, kSystemUid);
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 2ca7ac2..a6a4451 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -379,6 +379,7 @@
 TEST_F(UtilsTest, ValidateSecondaryDexFilesPath) {
     std::string package_name = "com.test.app";
     std::string app_dir_ce_user_0 = "/data/data/" + package_name;
+    std::string app_dir_ce_user_0_link = "/data/user/0/" + package_name;
     std::string app_dir_ce_user_10 = "/data/user/10/" + package_name;
 
     std::string app_dir_de_user_0 = "/data/user_de/0/" + package_name;
@@ -400,6 +401,8 @@
     // Standard path for user 0 on CE storage.
     pass_secondary_dex_validation(
         package_name, app_dir_ce_user_0 + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
+    pass_secondary_dex_validation(
+        package_name, app_dir_ce_user_0_link + "/ce0.dex", app_uid_for_user_0, FLAG_STORAGE_CE);
     // Standard path for user 10 on CE storage.
     pass_secondary_dex_validation(
         package_name, app_dir_ce_user_10 + "/ce10.dex", app_uid_for_user_10, FLAG_STORAGE_CE);
diff --git a/cmds/installd/tests/test_utils.h b/cmds/installd/tests/test_utils.h
new file mode 100644
index 0000000..7d1162e
--- /dev/null
+++ b/cmds/installd/tests/test_utils.h
@@ -0,0 +1,107 @@
+#include <android-base/logging.h>
+#include <stdlib.h>
+#include <string.h>
+
+uint8_t kBase64Map[256] = {
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
+     52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
+    255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
+      7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
+     19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,
+    255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
+     37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
+     49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255
+};
+
+uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+    CHECK(dst_size != nullptr);
+    std::vector<uint8_t> tmp;
+    uint32_t t = 0, y = 0;
+    int g = 3;
+    for (size_t i = 0; src[i] != '\0'; ++i) {
+        uint8_t c = kBase64Map[src[i] & 0xFF];
+        if (c == 255) continue;
+        // the final = symbols are read and used to trim the remaining bytes
+        if (c == 254) {
+            c = 0;
+            // prevent g < 0 which would potentially allow an overflow later
+            if (--g < 0) {
+                *dst_size = 0;
+                return nullptr;
+            }
+        } else if (g != 3) {
+            // we only allow = to be at the end
+            *dst_size = 0;
+            return nullptr;
+        }
+        t = (t << 6) | c;
+        if (++y == 4) {
+            tmp.push_back((t >> 16) & 255);
+            if (g > 1) {
+                tmp.push_back((t >> 8) & 255);
+            }
+            if (g > 2) {
+                tmp.push_back(t & 255);
+            }
+            y = t = 0;
+        }
+    }
+    if (y != 0) {
+        *dst_size = 0;
+        return nullptr;
+    }
+    std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
+    *dst_size = tmp.size();
+    std::copy(tmp.begin(), tmp.end(), dst.get());
+    return dst.release();
+}
+
+bool WriteBase64ToFile(const char* base64, const std::string& file,
+        uid_t uid, gid_t gid, int mode) {
+    CHECK(base64 != nullptr);
+    size_t length;
+    std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length));
+    CHECK(bytes != nullptr);
+
+
+    int fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+
+    if (fd < 0) {
+        PLOG(ERROR) << "Could not open file " << file;
+        return false;
+    }
+
+    size_t wrote = 0;
+    while (wrote < length) {
+        ssize_t cur = write(fd, bytes.get() + wrote, length - wrote);
+        if (cur == -1) {
+            PLOG(ERROR) << "Could not write file " << file;
+            return false;
+        }
+        wrote += cur;
+    }
+
+    if (::chown(file.c_str(), uid, gid) != 0) {
+        PLOG(ERROR) << "Could not chown file " << file;
+        return false;
+    }
+    if (::chmod(file.c_str(), mode) != 0) {
+        PLOG(ERROR) << "Could not chmod file " << file;
+        return false;
+    }
+    return true;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index ca0a82e..7dca7c6 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -87,6 +87,20 @@
             create_data_user_ce_path(volume_uuid, user).c_str(), package_name);
 }
 
+/**
+ * Create the path name where package data should be stored for the given
+ * volume UUID, package name, and user ID. An empty UUID is assumed to be
+ * internal storage.
+ * Compared to create_data_user_ce_package_path this method always return the
+ * ".../user/..." directory.
+ */
+std::string create_data_user_ce_package_path_as_user_link(
+        const char* volume_uuid, userid_t userid, const char* package_name) {
+    check_package_name(package_name);
+    std::string data(create_data_path(volume_uuid));
+    return StringPrintf("%s/user/%u/%s", data.c_str(), userid, package_name);
+}
+
 std::string create_data_user_ce_package_path(const char* volume_uuid, userid_t user,
         const char* package_name, ino_t ce_data_inode) {
     // For testing purposes, rely on the inode when defined; this could be
@@ -786,7 +800,7 @@
 }
 
 bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
-        const char* volume_uuid, int uid, int storage_flag, bool validate_package_path) {
+        const char* volume_uuid, int uid, int storage_flag) {
     CHECK(storage_flag == FLAG_STORAGE_CE || storage_flag == FLAG_STORAGE_DE);
 
     // Empty paths are not allowed.
@@ -800,16 +814,20 @@
     // The path should be at most PKG_PATH_MAX long.
     if (dex_path.size() > PKG_PATH_MAX) { return false; }
 
-    if (validate_package_path) {
-        // If we are asked to validate the package path check that
-        // the dex_path is under the app data directory.
-        std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
+    // The dex_path should be under the app data directory.
+    std::string app_private_dir = storage_flag == FLAG_STORAGE_CE
             ? create_data_user_ce_package_path(
                     volume_uuid, multiuser_get_user_id(uid), pkgname.c_str())
             : create_data_user_de_package_path(
                     volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
 
-        if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+    if (strncmp(dex_path.c_str(), app_private_dir.c_str(), app_private_dir.size()) != 0) {
+        // The check above might fail if the dex file is accessed via the /data/user/0 symlink.
+        // If that's the case, attempt to validate against the user data link.
+        std::string app_private_dir_symlink = create_data_user_ce_package_path_as_user_link(
+                volume_uuid, multiuser_get_user_id(uid), pkgname.c_str());
+        if (strncmp(dex_path.c_str(), app_private_dir_symlink.c_str(),
+                app_private_dir_symlink.size()) != 0) {
             return false;
         }
     }
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 3e04af9..b90caf9 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -60,6 +60,8 @@
         userid_t user, const char* package_name, ino_t ce_data_inode);
 std::string create_data_user_de_package_path(const char* volume_uuid,
         userid_t user, const char* package_name);
+std::string create_data_user_ce_package_path_as_user_link(
+        const char* volume_uuid, userid_t userid, const char* package_name);
 
 std::string create_data_media_path(const char* volume_uuid, userid_t userid);
 std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
@@ -114,7 +116,7 @@
 
 int validate_system_app_path(const char* path);
 bool validate_secondary_dex_path(const std::string& pkgname, const std::string& dex_path,
-        const char* volume_uuid, int uid, int storage_flag, bool validate_package_path = true);
+        const char* volume_uuid, int uid, int storage_flag);
 
 int validate_apk_path(const char *path);
 int validate_apk_path_subdirs(const char *path);
diff --git a/cmds/ip-up-vpn/Android.mk b/cmds/ip-up-vpn/Android.mk
index 36bbdf5..e1e2204 100644
--- a/cmds/ip-up-vpn/Android.mk
+++ b/cmds/ip-up-vpn/Android.mk
@@ -18,6 +18,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES := ip-up-vpn.c
+LOCAL_CFLAGS := -Wall -Werror
 LOCAL_SHARED_LIBRARIES := libcutils liblog
 LOCAL_MODULE := ip-up-vpn
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/ppp
diff --git a/cmds/rawbu/Android.mk b/cmds/rawbu/Android.mk
index b580390..9322151 100644
--- a/cmds/rawbu/Android.mk
+++ b/cmds/rawbu/Android.mk
@@ -5,6 +5,8 @@
 
 LOCAL_SRC_FILES:= backup.cpp
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_SHARED_LIBRARIES := libcutils libc
 
 LOCAL_MODULE:= rawbu
diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp
index ff6719f..0072281 100644
--- a/cmds/rawbu/backup.cpp
+++ b/cmds/rawbu/backup.cpp
@@ -129,7 +129,6 @@
         }
 
         if(S_ISDIR(statBuffer.st_mode)) {
-            int i;
             char *newpath;
 
             newpath = strdup(nameBuffer);
diff --git a/cmds/surfacereplayer/proto/Android.bp b/cmds/surfacereplayer/proto/Android.bp
index dda80bb..71a5e23 100644
--- a/cmds/surfacereplayer/proto/Android.bp
+++ b/cmds/surfacereplayer/proto/Android.bp
@@ -3,6 +3,10 @@
     srcs: [
         "src/trace.proto",
     ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
     proto: {
         type: "lite",
         export_proto_headers: true,
diff --git a/libs/hwc2onfbadapter/Android.bp b/libs/hwc2onfbadapter/Android.bp
new file mode 100644
index 0000000..73a41f7
--- /dev/null
+++ b/libs/hwc2onfbadapter/Android.bp
@@ -0,0 +1,33 @@
+// Copyright 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.
+
+cc_library_shared {
+    name: "libhwc2onfbadapter",
+    vendor: true,
+
+    clang: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+
+    srcs: [
+        "HWC2OnFbAdapter.cpp",
+    ],
+
+    header_libs: ["libhardware_headers"],
+    shared_libs: ["liblog", "libsync"],
+    export_include_dirs: ["include"],
+}
diff --git a/libs/hwc2onfbadapter/HWC2OnFbAdapter.cpp b/libs/hwc2onfbadapter/HWC2OnFbAdapter.cpp
new file mode 100644
index 0000000..7c9e651
--- /dev/null
+++ b/libs/hwc2onfbadapter/HWC2OnFbAdapter.cpp
@@ -0,0 +1,887 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#define LOG_TAG "HWC2OnFbAdapter"
+
+//#define LOG_NDEBUG 0
+
+#include "hwc2onfbadapter/HWC2OnFbAdapter.h"
+
+#include <algorithm>
+#include <type_traits>
+
+#include <inttypes.h>
+#include <time.h>
+#include <sys/prctl.h>
+#include <unistd.h> // for close
+
+#include <hardware/fb.h>
+#include <log/log.h>
+#include <sync/sync.h>
+
+namespace android {
+
+namespace {
+
+void dumpHook(hwc2_device_t* device, uint32_t* outSize, char* outBuffer) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (outBuffer) {
+        *outSize = adapter.getDebugString().copy(outBuffer, *outSize);
+    } else {
+        adapter.updateDebugString();
+        *outSize = adapter.getDebugString().size();
+    }
+}
+
+int32_t registerCallbackHook(hwc2_device_t* device, int32_t descriptor,
+                             hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    switch (descriptor) {
+        case HWC2_CALLBACK_HOTPLUG:
+            if (pointer) {
+                reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer)(callbackData, adapter.getDisplayId(),
+                                                            HWC2_CONNECTION_CONNECTED);
+            }
+            break;
+        case HWC2_CALLBACK_REFRESH:
+            break;
+        case HWC2_CALLBACK_VSYNC:
+            adapter.setVsyncCallback(reinterpret_cast<HWC2_PFN_VSYNC>(pointer), callbackData);
+            break;
+        default:
+            return HWC2_ERROR_BAD_PARAMETER;
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* /*device*/) {
+    return 0;
+}
+
+int32_t createVirtualDisplayHook(hwc2_device_t* /*device*/, uint32_t /*width*/, uint32_t /*height*/,
+                                 int32_t* /*format*/, hwc2_display_t* /*outDisplay*/) {
+    return HWC2_ERROR_NO_RESOURCES;
+}
+
+int32_t destroyVirtualDisplayHook(hwc2_device_t* /*device*/, hwc2_display_t /*display*/) {
+    return HWC2_ERROR_BAD_DISPLAY;
+}
+
+int32_t setOutputBufferHook(hwc2_device_t* /*device*/, hwc2_display_t /*display*/,
+                            buffer_handle_t /*buffer*/, int32_t /*releaseFence*/) {
+    return HWC2_ERROR_BAD_DISPLAY;
+}
+
+int32_t getDisplayNameHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outSize,
+                           char* outName) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    const auto& info = adapter.getInfo();
+    if (outName) {
+        *outSize = info.name.copy(outName, *outSize);
+    } else {
+        *outSize = info.name.size();
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayTypeHook(hwc2_device_t* device, hwc2_display_t display, int32_t* outType) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    *outType = HWC2_DISPLAY_TYPE_PHYSICAL;
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getDozeSupportHook(hwc2_device_t* device, hwc2_display_t display, int32_t* outSupport) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    *outSupport = 0;
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getHdrCapabilitiesHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumTypes,
+                               int32_t* /*outTypes*/, float* /*outMaxLuminance*/,
+                               float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    *outNumTypes = 0;
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setPowerModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t /*mode*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    // pretend that it works
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setVsyncEnabledHook(hwc2_device_t* device, hwc2_display_t display, int32_t enabled) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    adapter.enableVsync(enabled == HWC2_VSYNC_ENABLE);
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getColorModesHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumModes,
+                          int32_t* outModes) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    if (outModes) {
+        if (*outNumModes > 0) {
+            outModes[0] = HAL_COLOR_MODE_NATIVE;
+            *outNumModes = 1;
+        }
+    } else {
+        *outNumModes = 1;
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setColorModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t mode) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (mode != HAL_COLOR_MODE_NATIVE) {
+        return HWC2_ERROR_BAD_PARAMETER;
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setColorTransformHook(hwc2_device_t* device, hwc2_display_t display,
+                              const float* /*matrix*/, int32_t /*hint*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    // we always force client composition
+    adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getClientTargetSupportHook(hwc2_device_t* device, hwc2_display_t display, uint32_t width,
+                                   uint32_t height, int32_t format, int32_t dataspace) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (dataspace != HAL_DATASPACE_UNKNOWN) {
+        return HWC2_ERROR_UNSUPPORTED;
+    }
+
+    const auto& info = adapter.getInfo();
+    return (info.width == width && info.height == height && info.format == format)
+            ? HWC2_ERROR_NONE
+            : HWC2_ERROR_UNSUPPORTED;
+}
+
+int32_t setClientTargetHook(hwc2_device_t* device, hwc2_display_t display, buffer_handle_t target,
+                            int32_t acquireFence, int32_t dataspace, hwc_region_t /*damage*/) {
+    if (acquireFence >= 0) {
+        sync_wait(acquireFence, -1);
+        close(acquireFence);
+    }
+
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (dataspace != HAL_DATASPACE_UNKNOWN) {
+        return HWC2_ERROR_BAD_PARAMETER;
+    }
+
+    // no state change
+    adapter.setBuffer(target);
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayConfigsHook(hwc2_device_t* device, hwc2_display_t display,
+                              uint32_t* outNumConfigs, hwc2_config_t* outConfigs) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    if (outConfigs) {
+        if (*outNumConfigs > 0) {
+            outConfigs[0] = adapter.getConfigId();
+            *outNumConfigs = 1;
+        }
+    } else {
+        *outNumConfigs = 1;
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayAttributeHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config,
+                                int32_t attribute, int32_t* outValue) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (adapter.getConfigId() != config) {
+        return HWC2_ERROR_BAD_CONFIG;
+    }
+
+    const auto& info = adapter.getInfo();
+    switch (attribute) {
+        case HWC2_ATTRIBUTE_WIDTH:
+            *outValue = int32_t(info.width);
+            break;
+        case HWC2_ATTRIBUTE_HEIGHT:
+            *outValue = int32_t(info.height);
+            break;
+        case HWC2_ATTRIBUTE_VSYNC_PERIOD:
+            *outValue = int32_t(info.vsync_period_ns);
+            break;
+        case HWC2_ATTRIBUTE_DPI_X:
+            *outValue = int32_t(info.xdpi_scaled);
+            break;
+        case HWC2_ATTRIBUTE_DPI_Y:
+            *outValue = int32_t(info.ydpi_scaled);
+            break;
+        default:
+            return HWC2_ERROR_BAD_PARAMETER;
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getActiveConfigHook(hwc2_device_t* device, hwc2_display_t display,
+                            hwc2_config_t* outConfig) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    *outConfig = adapter.getConfigId();
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setActiveConfigHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (adapter.getConfigId() != config) {
+        return HWC2_ERROR_BAD_CONFIG;
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t validateDisplayHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumTypes,
+                            uint32_t* outNumRequests) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    const auto& dirtyLayers = adapter.getDirtyLayers();
+    *outNumTypes = dirtyLayers.size();
+    *outNumRequests = 0;
+
+    if (*outNumTypes > 0) {
+        adapter.setState(HWC2OnFbAdapter::State::VALIDATED_WITH_CHANGES);
+        return HWC2_ERROR_HAS_CHANGES;
+    } else {
+        adapter.setState(HWC2OnFbAdapter::State::VALIDATED);
+        return HWC2_ERROR_NONE;
+    }
+}
+
+int32_t getChangedCompositionTypesHook(hwc2_device_t* device, hwc2_display_t display,
+                                       uint32_t* outNumElements, hwc2_layer_t* outLayers,
+                                       int32_t* outTypes) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) {
+        return HWC2_ERROR_NOT_VALIDATED;
+    }
+
+    // request client composition for all layers
+    const auto& dirtyLayers = adapter.getDirtyLayers();
+    if (outLayers && outTypes) {
+        *outNumElements = std::min(*outNumElements, uint32_t(dirtyLayers.size()));
+        auto iter = dirtyLayers.cbegin();
+        for (uint32_t i = 0; i < *outNumElements; i++) {
+            outLayers[i] = *iter++;
+            outTypes[i] = HWC2_COMPOSITION_CLIENT;
+        }
+    } else {
+        *outNumElements = dirtyLayers.size();
+    }
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getDisplayRequestsHook(hwc2_device_t* device, hwc2_display_t display,
+                               int32_t* outDisplayRequests, uint32_t* outNumElements,
+                               hwc2_layer_t* /*outLayers*/, int32_t* /*outLayerRequests*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) {
+        return HWC2_ERROR_NOT_VALIDATED;
+    }
+
+    *outDisplayRequests = 0;
+    *outNumElements = 0;
+    return HWC2_ERROR_NONE;
+}
+
+int32_t acceptDisplayChangesHook(hwc2_device_t* device, hwc2_display_t display) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) {
+        return HWC2_ERROR_NOT_VALIDATED;
+    }
+
+    adapter.clearDirtyLayers();
+    adapter.setState(HWC2OnFbAdapter::State::VALIDATED);
+    return HWC2_ERROR_NONE;
+}
+
+int32_t presentDisplayHook(hwc2_device_t* device, hwc2_display_t display,
+                           int32_t* outPresentFence) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (adapter.getState() != HWC2OnFbAdapter::State::VALIDATED) {
+        return HWC2_ERROR_NOT_VALIDATED;
+    }
+
+    adapter.postBuffer();
+    *outPresentFence = -1;
+
+    return HWC2_ERROR_NONE;
+}
+
+int32_t getReleaseFencesHook(hwc2_device_t* device, hwc2_display_t display,
+                             uint32_t* outNumElements, hwc2_layer_t* /*outLayers*/,
+                             int32_t* /*outFences*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    *outNumElements = 0;
+    return HWC2_ERROR_NONE;
+}
+
+int32_t createLayerHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t* outLayer) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    *outLayer = adapter.addLayer();
+    adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+    return HWC2_ERROR_NONE;
+}
+
+int32_t destroyLayerHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    if (adapter.removeLayer(layer)) {
+        adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+        return HWC2_ERROR_NONE;
+    } else {
+        return HWC2_ERROR_BAD_LAYER;
+    }
+}
+
+int32_t setCursorPositionHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t /*layer*/,
+                              int32_t /*x*/, int32_t /*y*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+
+    // always an error
+    return HWC2_ERROR_BAD_LAYER;
+}
+
+int32_t setLayerBufferHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer,
+                           buffer_handle_t /*buffer*/, int32_t acquireFence) {
+    if (acquireFence >= 0) {
+        sync_wait(acquireFence, -1);
+        close(acquireFence);
+    }
+
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (!adapter.hasLayer(layer)) {
+        return HWC2_ERROR_BAD_LAYER;
+    }
+
+    // no state change
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setLayerSurfaceDamageHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer,
+                                  hwc_region_t /*damage*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (!adapter.hasLayer(layer)) {
+        return HWC2_ERROR_BAD_LAYER;
+    }
+
+    // no state change
+    return HWC2_ERROR_NONE;
+}
+
+int32_t setLayerCompositionTypeHook(hwc2_device_t* device, hwc2_display_t display,
+                                    hwc2_layer_t layer, int32_t type) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (!adapter.markLayerDirty(layer, type != HWC2_COMPOSITION_CLIENT)) {
+        return HWC2_ERROR_BAD_LAYER;
+    }
+
+    adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+    return HWC2_ERROR_NONE;
+}
+
+template <typename... Args>
+int32_t setLayerStateHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer,
+                          Args... /*args*/) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    if (adapter.getDisplayId() != display) {
+        return HWC2_ERROR_BAD_DISPLAY;
+    }
+    if (!adapter.hasLayer(layer)) {
+        return HWC2_ERROR_BAD_LAYER;
+    }
+
+    adapter.setState(HWC2OnFbAdapter::State::MODIFIED);
+    return HWC2_ERROR_NONE;
+}
+
+template <typename PFN, typename T>
+static hwc2_function_pointer_t asFP(T function) {
+    static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer");
+    return reinterpret_cast<hwc2_function_pointer_t>(function);
+}
+
+hwc2_function_pointer_t getFunctionHook(hwc2_device_t* /*device*/, int32_t descriptor) {
+    switch (descriptor) {
+        // global functions
+        case HWC2_FUNCTION_DUMP:
+            return asFP<HWC2_PFN_DUMP>(dumpHook);
+        case HWC2_FUNCTION_REGISTER_CALLBACK:
+            return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook);
+
+        // virtual display functions
+        case HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT:
+            return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(getMaxVirtualDisplayCountHook);
+        case HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY:
+            return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(createVirtualDisplayHook);
+        case HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY:
+            return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(destroyVirtualDisplayHook);
+        case HWC2_FUNCTION_SET_OUTPUT_BUFFER:
+            return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(setOutputBufferHook);
+
+        // display functions
+        case HWC2_FUNCTION_GET_DISPLAY_NAME:
+            return asFP<HWC2_PFN_GET_DISPLAY_NAME>(getDisplayNameHook);
+        case HWC2_FUNCTION_GET_DISPLAY_TYPE:
+            return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(getDisplayTypeHook);
+        case HWC2_FUNCTION_GET_DOZE_SUPPORT:
+            return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(getDozeSupportHook);
+        case HWC2_FUNCTION_GET_HDR_CAPABILITIES:
+            return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(getHdrCapabilitiesHook);
+        case HWC2_FUNCTION_SET_POWER_MODE:
+            return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook);
+        case HWC2_FUNCTION_SET_VSYNC_ENABLED:
+            return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook);
+        case HWC2_FUNCTION_GET_COLOR_MODES:
+            return asFP<HWC2_PFN_GET_COLOR_MODES>(getColorModesHook);
+        case HWC2_FUNCTION_SET_COLOR_MODE:
+            return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook);
+        case HWC2_FUNCTION_SET_COLOR_TRANSFORM:
+            return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook);
+        case HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT:
+            return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(getClientTargetSupportHook);
+        case HWC2_FUNCTION_SET_CLIENT_TARGET:
+            return asFP<HWC2_PFN_SET_CLIENT_TARGET>(setClientTargetHook);
+
+        // config functions
+        case HWC2_FUNCTION_GET_DISPLAY_CONFIGS:
+            return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(getDisplayConfigsHook);
+        case HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE:
+            return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(getDisplayAttributeHook);
+        case HWC2_FUNCTION_GET_ACTIVE_CONFIG:
+            return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(getActiveConfigHook);
+        case HWC2_FUNCTION_SET_ACTIVE_CONFIG:
+            return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(setActiveConfigHook);
+
+        // validate/present functions
+        case HWC2_FUNCTION_VALIDATE_DISPLAY:
+            return asFP<HWC2_PFN_VALIDATE_DISPLAY>(validateDisplayHook);
+        case HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES:
+            return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(getChangedCompositionTypesHook);
+        case HWC2_FUNCTION_GET_DISPLAY_REQUESTS:
+            return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(getDisplayRequestsHook);
+        case HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES:
+            return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(acceptDisplayChangesHook);
+        case HWC2_FUNCTION_PRESENT_DISPLAY:
+            return asFP<HWC2_PFN_PRESENT_DISPLAY>(presentDisplayHook);
+        case HWC2_FUNCTION_GET_RELEASE_FENCES:
+            return asFP<HWC2_PFN_GET_RELEASE_FENCES>(getReleaseFencesHook);
+
+        // layer create/destroy
+        case HWC2_FUNCTION_CREATE_LAYER:
+            return asFP<HWC2_PFN_CREATE_LAYER>(createLayerHook);
+        case HWC2_FUNCTION_DESTROY_LAYER:
+            return asFP<HWC2_PFN_DESTROY_LAYER>(destroyLayerHook);
+
+        // layer functions; validateDisplay not required
+        case HWC2_FUNCTION_SET_CURSOR_POSITION:
+            return asFP<HWC2_PFN_SET_CURSOR_POSITION>(setCursorPositionHook);
+        case HWC2_FUNCTION_SET_LAYER_BUFFER:
+            return asFP<HWC2_PFN_SET_LAYER_BUFFER>(setLayerBufferHook);
+        case HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE:
+            return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(setLayerSurfaceDamageHook);
+
+        // layer state functions; validateDisplay required
+        case HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE:
+            return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(setLayerCompositionTypeHook);
+        case HWC2_FUNCTION_SET_LAYER_BLEND_MODE:
+            return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(setLayerStateHook<int32_t>);
+        case HWC2_FUNCTION_SET_LAYER_COLOR:
+            return asFP<HWC2_PFN_SET_LAYER_COLOR>(setLayerStateHook<hwc_color_t>);
+        case HWC2_FUNCTION_SET_LAYER_DATASPACE:
+            return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerStateHook<int32_t>);
+        case HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME:
+            return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(setLayerStateHook<hwc_rect_t>);
+        case HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA:
+            return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(setLayerStateHook<float>);
+        case HWC2_FUNCTION_SET_LAYER_SIDEBAND_STREAM:
+            return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(setLayerStateHook<buffer_handle_t>);
+        case HWC2_FUNCTION_SET_LAYER_SOURCE_CROP:
+            return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(setLayerStateHook<hwc_frect_t>);
+        case HWC2_FUNCTION_SET_LAYER_TRANSFORM:
+            return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerStateHook<int32_t>);
+        case HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION:
+            return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(setLayerStateHook<hwc_region_t>);
+        case HWC2_FUNCTION_SET_LAYER_Z_ORDER:
+            return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerStateHook<uint32_t>);
+
+        default:
+            ALOGE("unknown function descriptor %d", descriptor);
+            return nullptr;
+    }
+}
+
+void getCapabilitiesHook(hwc2_device_t* /*device*/, uint32_t* outCount,
+                         int32_t* /*outCapabilities*/) {
+    *outCount = 0;
+}
+
+int closeHook(hw_device_t* device) {
+    auto& adapter = HWC2OnFbAdapter::cast(device);
+    adapter.close();
+    return 0;
+}
+
+} // anonymous namespace
+
+HWC2OnFbAdapter::HWC2OnFbAdapter(framebuffer_device_t* fbDevice)
+      : hwc2_device_t(), mFbDevice(fbDevice) {
+    common.close = closeHook;
+    hwc2_device::getCapabilities = getCapabilitiesHook;
+    hwc2_device::getFunction = getFunctionHook;
+
+    mFbInfo.name = "fbdev";
+    mFbInfo.width = mFbDevice->width;
+    mFbInfo.height = mFbDevice->height;
+    mFbInfo.format = mFbDevice->format;
+    mFbInfo.vsync_period_ns = int(1e9 / mFbDevice->fps);
+    mFbInfo.xdpi_scaled = int(mFbDevice->xdpi * 1000.0f);
+    mFbInfo.ydpi_scaled = int(mFbDevice->ydpi * 1000.0f);
+
+    mVsyncThread.start(0, mFbInfo.vsync_period_ns);
+}
+
+HWC2OnFbAdapter& HWC2OnFbAdapter::cast(hw_device_t* device) {
+    return *reinterpret_cast<HWC2OnFbAdapter*>(device);
+}
+
+HWC2OnFbAdapter& HWC2OnFbAdapter::cast(hwc2_device_t* device) {
+    return *reinterpret_cast<HWC2OnFbAdapter*>(device);
+}
+
+hwc2_display_t HWC2OnFbAdapter::getDisplayId() {
+    return 0;
+}
+
+hwc2_config_t HWC2OnFbAdapter::getConfigId() {
+    return 0;
+}
+
+void HWC2OnFbAdapter::close() {
+    mVsyncThread.stop();
+    framebuffer_close(mFbDevice);
+}
+
+const HWC2OnFbAdapter::Info& HWC2OnFbAdapter::getInfo() const {
+    return mFbInfo;
+}
+
+void HWC2OnFbAdapter::updateDebugString() {
+    if (mFbDevice->common.version >= 1 && mFbDevice->dump) {
+        char buffer[4096];
+        mFbDevice->dump(mFbDevice, buffer, sizeof(buffer));
+        buffer[sizeof(buffer) - 1] = '\0';
+
+        mDebugString = buffer;
+    }
+}
+
+const std::string& HWC2OnFbAdapter::getDebugString() const {
+    return mDebugString;
+}
+
+void HWC2OnFbAdapter::setState(State state) {
+    mState = state;
+}
+
+HWC2OnFbAdapter::State HWC2OnFbAdapter::getState() const {
+    return mState;
+}
+
+hwc2_layer_t HWC2OnFbAdapter::addLayer() {
+    hwc2_layer_t id = ++mNextLayerId;
+
+    mLayers.insert(id);
+    mDirtyLayers.insert(id);
+
+    return id;
+}
+
+bool HWC2OnFbAdapter::removeLayer(hwc2_layer_t layer) {
+    mDirtyLayers.erase(layer);
+    return mLayers.erase(layer);
+}
+
+bool HWC2OnFbAdapter::hasLayer(hwc2_layer_t layer) const {
+    return mLayers.count(layer) > 0;
+}
+
+bool HWC2OnFbAdapter::markLayerDirty(hwc2_layer_t layer, bool dirty) {
+    if (mLayers.count(layer) == 0) {
+        return false;
+    }
+
+    if (dirty) {
+        mDirtyLayers.insert(layer);
+    } else {
+        mDirtyLayers.erase(layer);
+    }
+
+    return true;
+}
+
+const std::unordered_set<hwc2_layer_t>& HWC2OnFbAdapter::getDirtyLayers() const {
+    return mDirtyLayers;
+}
+
+void HWC2OnFbAdapter::clearDirtyLayers() {
+    mDirtyLayers.clear();
+}
+
+/*
+ * For each frame, SurfaceFlinger
+ *
+ *  - peforms GLES composition
+ *  - calls eglSwapBuffers
+ *  - calls setClientTarget, which maps to setBuffer below
+ *  - calls presentDisplay, which maps to postBuffer below
+ *
+ * setBuffer should be a good place to call compositionComplete.
+ *
+ * As for post, it
+ *
+ *  - schedules the buffer for presentation on the next vsync
+ *  - locks the buffer and blocks all other users trying to lock it
+ *
+ * It does not give us a way to return a present fence, and we need to live
+ * with that.  The implication is that, when we are double-buffered,
+ * SurfaceFlinger assumes the front buffer is available for rendering again
+ * immediately after the back buffer is posted.  The locking semantics
+ * hopefully are strong enough that the rendering will be blocked.
+ */
+void HWC2OnFbAdapter::setBuffer(buffer_handle_t buffer) {
+    if (mFbDevice->compositionComplete) {
+        mFbDevice->compositionComplete(mFbDevice);
+    }
+    mBuffer = buffer;
+}
+
+bool HWC2OnFbAdapter::postBuffer() {
+    int error = 0;
+    if (mBuffer) {
+        error = mFbDevice->post(mFbDevice, mBuffer);
+    }
+
+    return error == 0;
+}
+
+void HWC2OnFbAdapter::setVsyncCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) {
+    mVsyncThread.setCallback(callback, data);
+}
+
+void HWC2OnFbAdapter::enableVsync(bool enable) {
+    mVsyncThread.enableCallback(enable);
+}
+
+int64_t HWC2OnFbAdapter::VsyncThread::now() {
+    struct timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+
+    return int64_t(ts.tv_sec) * 1'000'000'000 + ts.tv_nsec;
+}
+
+bool HWC2OnFbAdapter::VsyncThread::sleepUntil(int64_t t) {
+    struct timespec ts;
+    ts.tv_sec = t / 1'000'000'000;
+    ts.tv_nsec = t % 1'000'000'000;
+
+    while (true) {
+        int error = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr);
+        if (error) {
+            if (error == EINTR) {
+                continue;
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+}
+
+void HWC2OnFbAdapter::VsyncThread::start(int64_t firstVsync, int64_t period) {
+    mNextVsync = firstVsync;
+    mPeriod = period;
+    mStarted = true;
+    mThread = std::thread(&VsyncThread::vsyncLoop, this);
+}
+
+void HWC2OnFbAdapter::VsyncThread::stop() {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mStarted = false;
+    }
+    mCondition.notify_all();
+    mThread.join();
+}
+
+void HWC2OnFbAdapter::VsyncThread::setCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    mCallback = callback;
+    mCallbackData = data;
+}
+
+void HWC2OnFbAdapter::VsyncThread::enableCallback(bool enable) {
+    {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mCallbackEnabled = enable;
+    }
+    mCondition.notify_all();
+}
+
+void HWC2OnFbAdapter::VsyncThread::vsyncLoop() {
+    prctl(PR_SET_NAME, "VsyncThread", 0, 0, 0);
+
+    std::unique_lock<std::mutex> lock(mMutex);
+    if (!mStarted) {
+        return;
+    }
+
+    while (true) {
+        if (!mCallbackEnabled) {
+            mCondition.wait(lock, [this] { return mCallbackEnabled || !mStarted; });
+            if (!mStarted) {
+                break;
+            }
+        }
+
+        lock.unlock();
+
+        // adjust mNextVsync if necessary
+        int64_t t = now();
+        if (mNextVsync < t) {
+            int64_t n = (t - mNextVsync + mPeriod - 1) / mPeriod;
+            mNextVsync += mPeriod * n;
+        }
+        bool fire = sleepUntil(mNextVsync);
+
+        lock.lock();
+
+        if (fire) {
+            ALOGV("VsyncThread(%" PRId64 ")", mNextVsync);
+            if (mCallback) {
+                mCallback(mCallbackData, getDisplayId(), mNextVsync);
+            }
+            mNextVsync += mPeriod;
+        }
+    }
+}
+
+} // namespace android
diff --git a/libs/hwc2onfbadapter/include/hwc2onfbadapter/HWC2OnFbAdapter.h b/libs/hwc2onfbadapter/include/hwc2onfbadapter/HWC2OnFbAdapter.h
new file mode 100644
index 0000000..d6272fd
--- /dev/null
+++ b/libs/hwc2onfbadapter/include/hwc2onfbadapter/HWC2OnFbAdapter.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef ANDROID_SF_HWC2_ON_FB_ADAPTER_H
+#define ANDROID_SF_HWC2_ON_FB_ADAPTER_H
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_set>
+
+#include <hardware/hwcomposer2.h>
+
+struct framebuffer_device_t;
+
+namespace android {
+
+class HWC2OnFbAdapter : public hwc2_device_t {
+public:
+    HWC2OnFbAdapter(framebuffer_device_t* fbDevice);
+
+    static HWC2OnFbAdapter& cast(hw_device_t* device);
+    static HWC2OnFbAdapter& cast(hwc2_device_t* device);
+
+    static hwc2_display_t getDisplayId();
+    static hwc2_config_t getConfigId();
+
+    void close();
+
+    struct Info {
+        std::string name;
+        uint32_t width;
+        uint32_t height;
+        int format;
+        int vsync_period_ns;
+        int xdpi_scaled;
+        int ydpi_scaled;
+    };
+    const Info& getInfo() const;
+
+    void updateDebugString();
+    const std::string& getDebugString() const;
+
+    enum class State {
+        MODIFIED,
+        VALIDATED_WITH_CHANGES,
+        VALIDATED,
+    };
+    void setState(State state);
+    State getState() const;
+
+    hwc2_layer_t addLayer();
+    bool removeLayer(hwc2_layer_t layer);
+    bool hasLayer(hwc2_layer_t layer) const;
+    bool markLayerDirty(hwc2_layer_t layer, bool dirty);
+    const std::unordered_set<hwc2_layer_t>& getDirtyLayers() const;
+    void clearDirtyLayers();
+
+    void setBuffer(buffer_handle_t buffer);
+    bool postBuffer();
+
+    void setVsyncCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data);
+    void enableVsync(bool enable);
+
+private:
+    framebuffer_device_t* mFbDevice{nullptr};
+    Info mFbInfo{};
+
+    std::string mDebugString;
+
+    State mState{State::MODIFIED};
+
+    uint64_t mNextLayerId{0};
+    std::unordered_set<hwc2_layer_t> mLayers;
+    std::unordered_set<hwc2_layer_t> mDirtyLayers;
+
+    buffer_handle_t mBuffer{nullptr};
+
+    class VsyncThread {
+    public:
+        static int64_t now();
+        static bool sleepUntil(int64_t t);
+
+        void start(int64_t first, int64_t period);
+        void stop();
+        void setCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data);
+        void enableCallback(bool enable);
+
+    private:
+        void vsyncLoop();
+        bool waitUntilNextVsync();
+
+        std::thread mThread;
+        int64_t mNextVsync{0};
+        int64_t mPeriod{0};
+
+        std::mutex mMutex;
+        std::condition_variable mCondition;
+        bool mStarted{false};
+        HWC2_PFN_VSYNC mCallback{nullptr};
+        hwc2_callback_data_t mCallbackData{nullptr};
+        bool mCallbackEnabled{false};
+    };
+    VsyncThread mVsyncThread;
+};
+
+} // namespace android
+
+#endif // ANDROID_SF_HWC2_ON_FB_ADAPTER_H
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index 719ed9b..016de64 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -47,6 +47,8 @@
         "-DLOG_TAG=\"libbufferhub\"",
         "-DTRACE=0",
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+        "-Wall",
+        "-Werror",
     ],
     export_include_dirs: localIncludeFiles,
     static_libs: staticLibraries,
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 93ccd0f..b279875 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -49,6 +49,11 @@
         "-DLOG_TAG=\"libbufferhubqueue\"",
         "-DTRACE=0",
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+        "-Wall",
+        "-Werror",
+        "-Wno-format",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
     ],
     srcs: sourceFiles,
     export_include_dirs: includeFiles,
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index 865573c..5a3c3a6 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -28,6 +28,9 @@
         "-DTRACE=0",
         "-O0",
         "-g",
+        "-Wall",
+        "-Werror",
+        "-Wno-error=sign-compare", // to fix later
     ],
     name: "buffer_hub_queue-test",
     tags: ["optional"],
@@ -42,6 +45,8 @@
         "-DTRACE=0",
         "-O0",
         "-g",
+        "-Wall",
+        "-Werror",
     ],
     name: "buffer_hub_queue_producer-test",
     tags: ["optional"],
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index e3ab7fa..8213ca2 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -57,6 +57,8 @@
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
         "-DGL_GLEXT_PROTOTYPES",
         "-DEGL_EGLEXT_PROTOTYPES",
+        "-Wall",
+        "-Werror",
     ],  // + [ "-UNDEBUG", "-DDEBUG", "-O0", "-g" ],
     export_include_dirs: localIncludeFiles,
     shared_libs: sharedLibraries,
diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp
index 7fe9825..0d23b62 100644
--- a/libs/vr/libdvr/Android.bp
+++ b/libs/vr/libdvr/Android.bp
@@ -22,6 +22,8 @@
 cflags = [
     "-DLOG_TAG=\"libdvr\"",
     "-DTRACE=0",
+    "-Wall",
+    "-Werror",
 ]
 
 srcs = [
diff --git a/libs/vr/libdvr/include/dvr/dvr_display_types.h b/libs/vr/libdvr/include/dvr/dvr_display_types.h
index 25364d8..fd69843 100644
--- a/libs/vr/libdvr/include/dvr/dvr_display_types.h
+++ b/libs/vr/libdvr/include/dvr/dvr_display_types.h
@@ -13,7 +13,7 @@
 #ifndef __FLOAT32X4T_86
 #define __FLOAT32X4T_86
 typedef float float32x4_t __attribute__((__vector_size__(16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; };
+typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
 #endif
 #endif
 
diff --git a/libs/vr/libdvrcommon/Android.bp b/libs/vr/libdvrcommon/Android.bp
index 62aeb79..c7d808b 100644
--- a/libs/vr/libdvrcommon/Android.bp
+++ b/libs/vr/libdvrcommon/Android.bp
@@ -40,6 +40,8 @@
     cflags: [
         "-DLOG_TAG=\"libdvrcommon\"",
         "-DTRACE=0",
+        "-Wall",
+        "-Werror",
     ],
     export_include_dirs: localIncludeFiles,
 
@@ -59,6 +61,11 @@
     tags: ["optional"],
 
     srcs: testFiles,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
 
     shared_libs: sharedLibraries,
 
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
index 364873d..6a46876 100644
--- a/libs/vr/libperformance/Android.bp
+++ b/libs/vr/libperformance/Android.bp
@@ -32,7 +32,9 @@
     srcs: sourceFiles,
     cflags: [
         "-DLOG_TAG=\"libperformance\"",
-	"-DTRACE=0"
+        "-DTRACE=0",
+        "-Wall",
+        "-Werror",
     ],
     export_include_dirs: includeFiles,
     static_libs: staticLibraries,
diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp
index 0fb2d84..4d80e91 100644
--- a/libs/vr/libvrflinger/Android.bp
+++ b/libs/vr/libvrflinger/Android.bp
@@ -77,6 +77,10 @@
         "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
         "-DGL_GLEXT_PROTOTYPES",
         "-DEGL_EGLEXT_PROTOTYPES",
+        "-Wall",
+        "-Werror",
+        "-Wno-error=sign-compare", // to fix later
+        "-Wno-unused-variable",
     ],
     shared_libs: sharedLibraries,
     whole_static_libs: staticLibraries,
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
index d022adf..e92178a 100644
--- a/libs/vr/libvrsensor/Android.bp
+++ b/libs/vr/libvrsensor/Android.bp
@@ -42,6 +42,11 @@
 
 cc_library {
     srcs: sourceFiles,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-macro-redefined",
+    ],
     export_include_dirs: includeFiles,
     static_libs: staticLibraries,
     shared_libs: sharedLibraries,
diff --git a/libs/vr/libvrsensor/include/dvr/pose_client.h b/libs/vr/libvrsensor/include/dvr/pose_client.h
index d684ddc..6a9437c 100644
--- a/libs/vr/libvrsensor/include/dvr/pose_client.h
+++ b/libs/vr/libvrsensor/include/dvr/pose_client.h
@@ -7,7 +7,7 @@
 #ifndef __FLOAT32X4T_86
 #define __FLOAT32X4T_86
 typedef float float32x4_t __attribute__ ((__vector_size__ (16)));
-typedef struct float32x4x4_t { float32x4_t val[4]; };
+typedef struct float32x4x4_t { float32x4_t val[4]; } float32x4x4_t;
 #endif
 #endif
 
diff --git a/opengl/tests/angeles/demo.c b/opengl/tests/angeles/demo.c
index 39d871e..30c3202 100644
--- a/opengl/tests/angeles/demo.c
+++ b/opengl/tests/angeles/demo.c
@@ -462,7 +462,7 @@
 // Called from the app framework.
 void appInit()
 {
-    int a;
+    unsigned int a;
 
     glEnable(GL_NORMALIZE);
     glEnable(GL_DEPTH_TEST);
@@ -492,7 +492,7 @@
 // Called from the app framework.
 void appDeinit()
 {
-    int a;
+    unsigned int a;
     for (a = 0; a < SUPERSHAPE_COUNT; ++a)
         freeGLObject(sSuperShapeObjects[a]);
     freeGLObject(sGroundPlane);
diff --git a/opengl/tests/configdump/configdump.cpp b/opengl/tests/configdump/configdump.cpp
index c423105..a4bb879 100644
--- a/opengl/tests/configdump/configdump.cpp
+++ b/opengl/tests/configdump/configdump.cpp
@@ -78,7 +78,7 @@
 
     for (EGLint i=0 ; i<n ; i++) {
         printf("EGLConfig[%d]\n", i);
-        for (int attr = 0 ; attr<sizeof(attributes)/sizeof(Attribute) ; attr++) {
+        for (unsigned attr = 0 ; attr<sizeof(attributes)/sizeof(Attribute) ; attr++) {
             EGLint value;
             eglGetConfigAttrib(dpy, configs[i], attributes[attr].attribute, &value);
             printf("\t%-32s: %10d (0x%08x)\n", attributes[attr].name, value, value);
diff --git a/opengl/tests/fillrate/fillrate.cpp b/opengl/tests/fillrate/fillrate.cpp
index 2db63d7..a42f3f2 100644
--- a/opengl/tests/fillrate/fillrate.cpp
+++ b/opengl/tests/fillrate/fillrate.cpp
@@ -30,7 +30,7 @@
 
 using namespace android;
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     EGLint configAttribs[] = {
          EGL_DEPTH_SIZE, 0,
@@ -153,7 +153,7 @@
 
      for (int c=1, j=0 ; c<32 ; c++, j++) {
          nsecs_t t = times[j];
-         printf("%lld\t%d\t%f\n", t, c, (double(t)/c)/1000000.0);
+         printf("%ld\t%d\t%f\n", t, c, (double(t)/c)/1000000.0);
      }
 
 
diff --git a/opengl/tests/finish/finish.cpp b/opengl/tests/finish/finish.cpp
index ea3a60f..ab955fe 100644
--- a/opengl/tests/finish/finish.cpp
+++ b/opengl/tests/finish/finish.cpp
@@ -31,7 +31,7 @@
 
 using namespace android;
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     EGLint configAttribs[] = {
          EGL_DEPTH_SIZE, 0,
diff --git a/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp b/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
index 405a3f0..e0aafa2 100644
--- a/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
+++ b/opengl/tests/gl2_copyTexImage/gl2_copyTexImage.cpp
@@ -362,7 +362,7 @@
     return true;
 }
 
-int main(int argc, char** argv) {
+int main(int /*argc*/, char** /*argv*/) {
     EGLBoolean returnValue;
     EGLConfig myConfig = {0};
 
diff --git a/opengl/tests/gl2_jni/jni/gl_code.cpp b/opengl/tests/gl2_jni/jni/gl_code.cpp
index c4180d4..5af4f6b 100644
--- a/opengl/tests/gl2_jni/jni/gl_code.cpp
+++ b/opengl/tests/gl2_jni/jni/gl_code.cpp
@@ -153,12 +153,12 @@
     JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj);
 };
 
-JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * env, jobject obj,  jint width, jint height)
+JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_init(JNIEnv * /*env*/, jobject /*obj*/,  jint width, jint height)
 {
     setupGraphics(width, height);
 }
 
-JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * env, jobject obj)
+JNIEXPORT void JNICALL Java_com_android_gl2jni_GL2JNILib_step(JNIEnv * /*env*/, jobject /*obj*/)
 {
     renderFrame();
 }
diff --git a/opengl/tests/gl_basic/gl_basic.cpp b/opengl/tests/gl_basic/gl_basic.cpp
index e50d88f..0b663f7 100644
--- a/opengl/tests/gl_basic/gl_basic.cpp
+++ b/opengl/tests/gl_basic/gl_basic.cpp
@@ -189,7 +189,7 @@
     return true;
 }
 
-int main(int argc, char **argv)
+int main(int /*argc*/, char **/*argv*/)
 {
     int q;
     int start, end;
diff --git a/opengl/tests/gl_jni/jni/gl_code.cpp b/opengl/tests/gl_jni/jni/gl_code.cpp
index 48944e0..3aa8adb 100644
--- a/opengl/tests/gl_jni/jni/gl_code.cpp
+++ b/opengl/tests/gl_jni/jni/gl_code.cpp
@@ -140,13 +140,13 @@
     JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * env, jobject obj);
 };
 
-JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_init(JNIEnv * env, jobject obj,  jint width, jint height)
+JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_init(JNIEnv * /*env*/, jobject /*obj*/,  jint width, jint height)
 {
     init_scene(width, height);
     create_texture();
 }
 
-JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_step(JNIEnv * env, jobject obj)
+JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_step(JNIEnv * /*env*/, jobject /*obj*/)
 {
     const GLfloat vertices[] = {
             -1,  -1,  0,
@@ -177,7 +177,7 @@
     glDrawElements(GL_TRIANGLES, nelem, GL_UNSIGNED_SHORT, quadIndices);
 }
 
-JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * env, jobject obj)
+JNIEXPORT void JNICALL Java_com_android_gljni_GLJNILib_changeBackground(JNIEnv * /*env*/, jobject /*obj*/)
 {
     background = 1.0f - background;
 }
diff --git a/opengl/tests/gl_perf/gl2_perf.cpp b/opengl/tests/gl_perf/gl2_perf.cpp
index 35df84f..2998864 100644
--- a/opengl/tests/gl_perf/gl2_perf.cpp
+++ b/opengl/tests/gl_perf/gl2_perf.cpp
@@ -56,7 +56,7 @@
 static EGLDisplay dpy;
 static EGLSurface surface;
 
-int main(int argc, char** argv) {
+int main(int /*argc*/, char** /*argv*/) {
     EGLBoolean returnValue;
     EGLConfig myConfig = {0};
 
diff --git a/opengl/tests/gl_perfapp/jni/gl_code.cpp b/opengl/tests/gl_perfapp/jni/gl_code.cpp
index 946ee07..0cb594a 100644
--- a/opengl/tests/gl_perfapp/jni/gl_code.cpp
+++ b/opengl/tests/gl_perfapp/jni/gl_code.cpp
@@ -61,7 +61,7 @@
     JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * env, jobject obj);
 };
 
-JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_init(JNIEnv * env, jobject obj,  jint width, jint height)
+JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_init(JNIEnv * /*env*/, jobject /*obj*/,  jint width, jint height)
 {
     gWidth = width;
     gHeight = height;
@@ -87,7 +87,7 @@
     }
 }
 
-JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * env, jobject obj)
+JNIEXPORT void JNICALL Java_com_android_glperf_GLPerfLib_step(JNIEnv * /*env*/, jobject /*obj*/)
 {
     if (! done) {
         if (stateClock > 0 && ((stateClock & 1) == 0)) {
diff --git a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
index fad26a6..7ef3f4e 100644
--- a/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
+++ b/opengl/tests/gl_yuvtex/gl_yuvtex.cpp
@@ -90,7 +90,7 @@
 static sp<GraphicBuffer> yuvTexBuffer;
 static GLuint yuvTex;
 
-bool setupYuvTexSurface(EGLDisplay dpy, EGLContext context) {
+static bool setupYuvTexSurface(EGLDisplay dpy) {
     int blockWidth = yuvTexWidth > 16 ? yuvTexWidth / 16 : 1;
     int blockHeight = yuvTexHeight > 16 ? yuvTexHeight / 16 : 1;
     yuvTexBuffer = new GraphicBuffer(yuvTexWidth, yuvTexHeight, yuvTexFormat,
@@ -298,7 +298,7 @@
     printGLString("Renderer", GL_RENDERER);
     printGLString("Extensions", GL_EXTENSIONS);
 
-    if(!setupYuvTexSurface(dpy, context)) {
+    if(!setupYuvTexSurface(dpy)) {
         fprintf(stderr, "Could not set up texture surface.\n");
         return 1;
     }
diff --git a/opengl/tests/gldual/jni/gl_code.cpp b/opengl/tests/gldual/jni/gl_code.cpp
index 3b11400..90d150b 100644
--- a/opengl/tests/gldual/jni/gl_code.cpp
+++ b/opengl/tests/gldual/jni/gl_code.cpp
@@ -153,12 +153,12 @@
     JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * env, jobject obj);
 };
 
-JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_init(JNIEnv * env, jobject obj, jint width, jint height)

+JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_init(JNIEnv * /*env*/, jobject /*obj*/, jint width, jint height)

 {
     setupGraphics(width, height);
 }
 
-JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * env, jobject obj)
+JNIEXPORT void JNICALL Java_com_android_gldual_GLDualLib_step(JNIEnv * /*env*/, jobject /*obj*/)
 {
     renderFrame();
 }
diff --git a/opengl/tests/gralloc/gralloc.cpp b/opengl/tests/gralloc/gralloc.cpp
index 8987040..29e0882 100644
--- a/opengl/tests/gralloc/gralloc.cpp
+++ b/opengl/tests/gralloc/gralloc.cpp
@@ -37,7 +37,7 @@
     return d;
 }
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     size_t size = 128*256*4;
     void* temp = malloc(size);
diff --git a/opengl/tests/linetex/linetex.cpp b/opengl/tests/linetex/linetex.cpp
index 5ad695b..cdc051b 100644
--- a/opengl/tests/linetex/linetex.cpp
+++ b/opengl/tests/linetex/linetex.cpp
@@ -29,7 +29,7 @@
 
 using namespace android;
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     EGLint configAttribs[] = {
          EGL_DEPTH_SIZE, 0,
diff --git a/opengl/tests/swapinterval/swapinterval.cpp b/opengl/tests/swapinterval/swapinterval.cpp
index 3a8a8a1..c4261e6 100644
--- a/opengl/tests/swapinterval/swapinterval.cpp
+++ b/opengl/tests/swapinterval/swapinterval.cpp
@@ -28,7 +28,7 @@
 
 using namespace android;
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     EGLint configAttribs[] = {
             EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
diff --git a/opengl/tests/textures/textures.cpp b/opengl/tests/textures/textures.cpp
index 1e55db0..7be942a 100644
--- a/opengl/tests/textures/textures.cpp
+++ b/opengl/tests/textures/textures.cpp
@@ -27,7 +27,7 @@
 
 using namespace android;
 
-int main(int argc, char** argv)
+int main(int /*argc*/, char** /*argv*/)
 {
     EGLint configAttribs[] = {
          EGL_DEPTH_SIZE, 0,
diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
index f6813fd..f90e3ec 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp
@@ -17,11 +17,10 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
index 4df61d3..12b96f4 100644
--- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp
@@ -17,11 +17,10 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/android_view_Surface.h>
 #include <android_runtime/android_graphics_SurfaceTexture.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
index 1fa9275..dd17ca4 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES/gl.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
index 4004a7d..b2bbdf6 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES2/gl2.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp
index c5bdf32..b039bc9 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp
@@ -18,7 +18,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES3/gl3.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp
index 2260a80..dd00e92 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <GLES3/gl31.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp
index 130612d..88e00be 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp
@@ -17,7 +17,6 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <stdint.h>
diff --git a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp
index e9c5fc7..3e7ec8b 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp
+++ b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp
@@ -17,8 +17,7 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include <stdint.h>
-#include <GLES3/gl32.h>
\ No newline at end of file
+#include <GLES3/gl32.h>
diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp
index 7062c57..2163d76 100644
--- a/opengl/tools/glgen/stubs/gles11/common.cpp
+++ b/opengl/tools/glgen/stubs/gles11/common.cpp
@@ -1,5 +1,5 @@
 #include <jni.h>
-#include <JNIHelp.h>
+#include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 #include <assert.h>
diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
index 026cb37..29296ff 100644
--- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
+++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp
@@ -17,11 +17,10 @@
 // This source file is automatically generated
 
 #pragma GCC diagnostic ignored "-Wunused-variable"
-#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
 #pragma GCC diagnostic ignored "-Wunused-function"
 
 #include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/misc.h>
 
diff --git a/services/sensorservice/tests/Android.mk b/services/sensorservice/tests/Android.mk
index e894655..8a9c36b 100644
--- a/services/sensorservice/tests/Android.mk
+++ b/services/sensorservice/tests/Android.mk
@@ -4,6 +4,8 @@
 LOCAL_SRC_FILES:= \
 	sensorservicetest.cpp
 
+LOCAL_CFLAGS := -Wall -Werror
+
 LOCAL_SHARED_LIBRARIES := \
 	libutils libsensor libandroid
 
diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp
index 9201520..e92b8d8 100644
--- a/services/vr/hardware_composer/Android.bp
+++ b/services/vr/hardware_composer/Android.bp
@@ -44,6 +44,13 @@
 
   cflags: [
     "-DLOG_TAG=\"vr_hwc\"",
+    "-Wall",
+    "-Werror",
+    // mVrClient unused in vr_composer_client.cpp
+    "-Wno-error=unused-private-field",
+    // Warnings in vr_hwc.cpp to be fixed after sync of goog/master.
+    "-Wno-sign-compare",
+    "-Wno-unused-parameter",
   ],
 
 }
@@ -63,6 +70,11 @@
   },
   export_include_dirs: ["aidl"],
 
+  cflags: [
+    "-Wall",
+    "-Werror",
+  ],
+
   shared_libs: [
     "libbinder",
     "libui",
@@ -92,6 +104,8 @@
   ],
   cflags: [
     "-DLOG_TAG=\"vr_hwc\"",
+    "-Wall",
+    "-Werror",
   ],
 }
 
@@ -120,6 +134,8 @@
   ],
   cflags: [
     "-DLOG_TAG=\"vr_hwc\"",
+    "-Wall",
+    "-Werror",
   ],
   init_rc: [
     "vr_hwc.rc",
@@ -137,6 +153,13 @@
     // symbols in the *-binder library get optimized out.
     "libvr_hwc-binder",
   ],
+  cflags: [
+    "-Wall",
+    "-Werror",
+    // warnings in vr_composer_test.cpp to be fixed after merge of goog/master
+    "-Wno-sign-compare",
+    "-Wno-unused-parameter",
+  ],
   shared_libs: [
     "libbase",
     "libbinder",
diff --git a/services/vr/performanced/Android.mk b/services/vr/performanced/Android.mk
index dbc66f1..1470234 100644
--- a/services/vr/performanced/Android.mk
+++ b/services/vr/performanced/Android.mk
@@ -36,6 +36,7 @@
 LOCAL_SRC_FILES := $(sourceFiles)
 LOCAL_CFLAGS := -DLOG_TAG=\"performanced\"
 LOCAL_CFLAGS += -DTRACE=0
+LOCAL_CFLAGS += -Wall -Werror
 LOCAL_STATIC_LIBRARIES := $(staticLibraries)
 LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
 LOCAL_MODULE := performanced
diff --git a/services/vr/performanced/directory_reader.h b/services/vr/performanced/directory_reader.h
index 7d7ecc5..b9d27c3 100644
--- a/services/vr/performanced/directory_reader.h
+++ b/services/vr/performanced/directory_reader.h
@@ -19,7 +19,7 @@
     directory_ = fdopendir(directory_fd.get());
     error_ = errno;
     if (directory_ != nullptr)
-      directory_fd.release();
+      (void) directory_fd.release(); // ignore return result?
   }
 
   ~DirectoryReader() {
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
index 6b11ce3..7196b2b 100644
--- a/services/vr/virtual_touchpad/Android.bp
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -23,7 +23,11 @@
     shared_libs: shared_libs,
     header_libs: header_libraries,
     cppflags: ["-std=c++11"],
-    cflags: ["-DLOG_TAG=\"VrVirtualTouchpad\""],
+    cflags: [
+        "-DLOG_TAG=\"VrVirtualTouchpad\"",
+        "-Wall",
+        "-Werror",
+    ],
     name: "libvirtualtouchpad",
     tags: ["optional"],
 }
@@ -44,6 +48,10 @@
     srcs: test_src_files,
     static_libs: test_static_libs,
     header_libs: header_libraries,
+    cflags = [
+        "-Wall",
+        "-Werror",
+    ],
     cppflags = [
         "-std=c++11",
     ],
@@ -83,6 +91,8 @@
     cppflags: ["-std=c++11"],
     cflags: [
         "-DLOG_TAG=\"VrVirtualTouchpad\"",
+        "-Wall",
+        "-Werror",
     ],
     host_ldlibs: ["-llog"],
     name: "virtual_touchpad",
@@ -112,7 +122,11 @@
     shared_libs: client_shared_libs,
     header_libs: header_libraries,
     cppflags: ["-std=c++11"],
-    cflags: ["-DLOG_TAG=\"VirtualTouchpadClient\""],
+    cflags: [
+        "-DLOG_TAG=\"VirtualTouchpadClient\"",
+        "-Wall",
+        "-Werror",
+    ],
     host_ldlibs: ["-llog"],
     name: "libvirtualtouchpadclient",
     tags: ["optional"],
diff --git a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
index b19b018..198ed06 100644
--- a/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
+++ b/services/vr/virtual_touchpad/tests/VirtualTouchpad_test.cpp
@@ -110,17 +110,6 @@
   EvdevInjectorForTesting injector[kTouchpads];
 };
 
-void DumpDifference(const char* expect, const char* actual) {
-  printf("  common: ");
-  while (*expect && *expect == *actual) {
-    putchar(*expect);
-    ++expect;
-    ++actual;
-  }
-  printf("\n  expect: %s\n", expect);
-  printf("  actual: %s\n", actual);
-}
-
 }  // anonymous namespace
 
 class VirtualTouchpadTest : public testing::Test {};
@@ -153,7 +142,6 @@
   }
   const int32_t width = 1 + uidev->absmax[ABS_MT_POSITION_X];
   const int32_t height = 1 + uidev->absmax[ABS_MT_POSITION_Y];
-  const int32_t slots = uidev->absmax[ABS_MT_SLOT];
 
   for (int t = 0; t < touchpad->GetTouchpadCount(); ++t) {
     SCOPED_TRACE(t);