libcutils: remove schedgroup fds [DO NOT MERGE] am: bcb6419fc9  -s ours
am: 058188d345

Change-Id: Idea874d7b616b6e183d1e76f891b298a6e0f0668
diff --git a/.clang-format-2 b/.clang-format-2
index aab4665..41591ce 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -1,4 +1,5 @@
 BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/.clang-format-4 b/.clang-format-4
index 1497447..ae4a451 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -1,5 +1,6 @@
 BasedOnStyle: Google
 AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4de5e57..041586c 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -62,15 +62,14 @@
 // Waits for the system property `key` to have the value `expected_value`.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
-bool WaitForProperty(const std::string& key,
-                     const std::string& expected_value,
-                     std::chrono::milliseconds relative_timeout);
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+                     std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
 
 // Waits for the system property `key` to be created.
 // Times out after `relative_timeout`.
 // Returns true on success, false on timeout.
-bool WaitForPropertyCreation(const std::string& key,
-                             std::chrono::milliseconds relative_timeout);
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+                                                         std::chrono::milliseconds::max());
 
 } // namespace base
 } // namespace android
diff --git a/base/properties.cpp b/base/properties.cpp
index 32c0128..816bca0 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -101,22 +101,24 @@
 }
 
 // TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
   auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
   auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
   ts.tv_sec = s.count();
   ts.tv_nsec = ns.count();
 }
 
+// TODO: boot_clock?
 using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
 
-static void UpdateTimeSpec(timespec& ts,
-                           const AbsTime& timeout) {
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+                           const AbsTime& start_time) {
   auto now = std::chrono::steady_clock::now();
-  auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
-  if (remaining_timeout < 0ns) {
+  auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+  if (time_elapsed >= relative_timeout) {
     ts = { 0, 0 };
   } else {
+    auto remaining_timeout = relative_timeout - time_elapsed;
     DurationToTimeSpec(ts, remaining_timeout);
   }
 }
@@ -127,11 +129,7 @@
 // Returns nullptr on timeout.
 static const prop_info* WaitForPropertyCreation(const std::string& key,
                                                 const std::chrono::milliseconds& relative_timeout,
-                                                AbsTime& absolute_timeout) {
-  // TODO: boot_clock?
-  auto now = std::chrono::steady_clock::now();
-  absolute_timeout = now + relative_timeout;
-
+                                                const AbsTime& start_time) {
   // Find the property's prop_info*.
   const prop_info* pi;
   unsigned global_serial = 0;
@@ -139,17 +137,16 @@
     // The property doesn't even exist yet.
     // Wait for a global change and then look again.
     timespec ts;
-    UpdateTimeSpec(ts, absolute_timeout);
+    UpdateTimeSpec(ts, relative_timeout, start_time);
     if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
   }
   return pi;
 }
 
-bool WaitForProperty(const std::string& key,
-                     const std::string& expected_value,
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
                      std::chrono::milliseconds relative_timeout) {
-  AbsTime absolute_timeout;
-  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+  auto start_time = std::chrono::steady_clock::now();
+  const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
   if (pi == nullptr) return false;
 
   WaitForPropertyData data;
@@ -162,7 +159,7 @@
     if (data.done) return true;
 
     // It didn't, so wait for the property to change before checking again.
-    UpdateTimeSpec(ts, absolute_timeout);
+    UpdateTimeSpec(ts, relative_timeout, start_time);
     uint32_t unused;
     if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
   }
@@ -170,8 +167,8 @@
 
 bool WaitForPropertyCreation(const std::string& key,
                              std::chrono::milliseconds relative_timeout) {
-  AbsTime absolute_timeout;
-  return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+  auto start_time = std::chrono::steady_clock::now();
+  return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
 }
 
 }  // namespace base
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index 1bbe482..de5f3dc 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -151,6 +151,38 @@
   ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
 }
 
+TEST(properties, WaitForProperty_MaxTimeout) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    std::this_thread::sleep_for(500ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  // Test that this does not immediately return false due to overflow issues with the timeout.
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+  thread.join();
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+  std::atomic<bool> flag{false};
+  std::thread thread([&]() {
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+    while (!flag) std::this_thread::yield();
+    std::this_thread::sleep_for(500ms);
+    android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+  });
+
+  ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+  flag = true;
+  // Assert that this immediately returns with a negative timeout
+  ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+  thread.join();
+}
+
 TEST(properties, WaitForPropertyCreation) {
   std::thread thread([&]() {
     std::this_thread::sleep_for(100ms);
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
index be4d2dd..66e5e58 100644
--- a/demangle/demangle.cpp
+++ b/demangle/demangle.cpp
@@ -20,22 +20,25 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <cctype>
 #include <string>
 
 #include <demangle.h>
 
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
 
-void usage(const char* prog_name) {
-  printf("Usage: %s [-c] <NAME_TO_DEMANGLE>\n", prog_name);
-  printf("  -c\n");
-  printf("    Compare the results of __cxa_demangle against the current\n");
-  printf("    demangler.\n");
+static void Usage(const char* prog_name) {
+  printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+  printf("\n");
+  printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+  printf("reading from stdin otherwise.\n");
+  printf("\n");
+  printf("-c\tCompare against __cxa_demangle\n");
+  printf("\n");
 }
 
-std::string DemangleWithCxa(const char* name) {
+static std::string DemangleWithCxa(const char* name) {
   const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-
   if (cxa_demangle == nullptr) {
     return name;
   }
@@ -54,6 +57,49 @@
   return demangled_str;
 }
 
+static void Compare(const char* name, const std::string& demangled_name) {
+  std::string cxa_demangled_name(DemangleWithCxa(name));
+  if (cxa_demangled_name != demangled_name) {
+    printf("\nMismatch!\n");
+    printf("\tmangled name: %s\n", name);
+    printf("\tour demangle: %s\n", demangled_name.c_str());
+    printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+    exit(1);
+  }
+}
+
+static int Filter(bool compare) {
+  char* line = nullptr;
+  size_t line_length = 0;
+
+  while ((getline(&line, &line_length, stdin)) != -1) {
+    char* p = line;
+    char* name;
+    while ((name = strstr(p, "_Z")) != nullptr) {
+      // Output anything before the identifier.
+      *name = 0;
+      printf("%s", p);
+      *name = '_';
+
+      // Extract the identifier.
+      p = name;
+      while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+      // Demangle and output.
+      std::string identifier(name, p);
+      std::string demangled_name = demangle(identifier.c_str());
+      printf("%s", demangled_name.c_str());
+
+      if (compare) Compare(identifier.c_str(), demangled_name);
+    }
+    // Output anything after the last identifier.
+    printf("%s", p);
+  }
+
+  free(line);
+  return 0;
+}
+
 int main(int argc, char** argv) {
 #ifdef __BIONIC__
   const char* prog_name = getprogname();
@@ -67,31 +113,21 @@
     if (opt_char == 'c') {
       compare = true;
     } else {
-      usage(prog_name);
+      Usage(prog_name);
       return 1;
     }
   }
-  if (optind >= argc || argc - optind != 1) {
-    printf("Must supply a single argument.\n\n");
-    usage(prog_name);
-    return 1;
-  }
-  const char* name = argv[optind];
 
-  std::string demangled_name = demangle(name);
+  // With no arguments, act as a filter.
+  if (optind == argc) return Filter(compare);
 
-  printf("%s\n", demangled_name.c_str());
+  // Otherwise demangle each argument.
+  while (optind < argc) {
+    const char* name = argv[optind++];
+    std::string demangled_name = demangle(name);
+    printf("%s\n", demangled_name.c_str());
 
-  if (compare) {
-    std::string cxa_demangle_str(DemangleWithCxa(name));
-
-    if (cxa_demangle_str != demangled_name) {
-      printf("Mismatch\n");
-      printf("cxa demangle: %s\n", cxa_demangle_str.c_str());
-      return 1;
-    } else {
-      printf("Match\n");
-    }
+    if (compare) Compare(name, demangled_name);
   }
   return 0;
 }
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 5b2f218..cffa6ce 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -56,7 +56,7 @@
             return true;
         }
 
-        LERROR << "Error finding '" << key << "' in device tree";
+        LINFO << "Error finding '" << key << "' in device tree";
     }
 
     return false;
diff --git a/init/Android.mk b/init/Android.mk
index b52c949..1ca88d7 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -16,6 +16,14 @@
     -DREBOOT_BOOTLOADER_ON_PANIC=0
 endif
 
+ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
+init_options += \
+    -DSHUTDOWN_ZERO_TIMEOUT=1
+else
+init_options += \
+    -DSHUTDOWN_ZERO_TIMEOUT=0
+endif
+
 init_options += -DLOG_UEVENTS=0
 
 init_cflags += \
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 2844d78..e34abdb 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -341,7 +341,14 @@
     }
 
     /* TODO update default waiting time based on usage data */
-    unsigned int shutdownTimeout = android::base::GetUintProperty("ro.build.shutdown_timeout", 10u);
+    constexpr unsigned int shutdownTimeoutDefault = 10;
+    unsigned int shutdownTimeout = shutdownTimeoutDefault;
+    if (SHUTDOWN_ZERO_TIMEOUT) {  // eng build
+        shutdownTimeout = 0;
+    } else {
+        shutdownTimeout =
+            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+    }
     LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
 
     static const constexpr char* shutdown_critical_services[] = {"vold", "watchdogd"};
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..96ca566 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -41,7 +42,7 @@
 }
 
 int load_canned_fs_config(const char* fn) {
-    char line[PATH_MAX + 200];
+    char buf[PATH_MAX + 200];
     FILE* f;
 
     f = fopen(fn, "r");
@@ -50,17 +51,21 @@
         return -1;
     }
 
-    while (fgets(line, sizeof(line), f)) {
+    while (fgets(buf, sizeof(buf), f)) {
         Path* p;
         char* token;
+        char* line = buf;
+        bool rootdir;
 
         while (canned_used >= canned_alloc) {
             canned_alloc = (canned_alloc+1) * 2;
             canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
         }
         p = canned_data + canned_used;
-        p->path = strdup(strtok(line, " "));
-        p->uid = atoi(strtok(NULL, " "));
+        if (line[0] == '/') line++;
+        rootdir = line[0] == ' ';
+        p->path = strdup(rootdir ? "" : strtok(line, " "));
+        p->uid = atoi(strtok(rootdir ? line : NULL, " "));
         p->gid = atoi(strtok(NULL, " "));
         p->mode = strtol(strtok(NULL, " "), NULL, 8);   // mode is in octal
         p->capabilities = 0;
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 81356fe..04a620c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -235,7 +235,9 @@
     }
     iovec[1].iov_len = entry.len;
 
-    log_time retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mRealTime;
+    log_time retval = reader->sendDatav(iovec, 1 + (entry.len != 0))
+                          ? FLUSH_ERROR
+                          : mRealTime;
 
     if (buffer) free(buffer);
 
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index 381df5a..8fcd17f 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -30,6 +30,19 @@
 
 #include "android-base/logging.h"
 
+// The name of the directory that holds a staged time zone update distro. If this exists it should
+// replace the one in CURRENT_DIR_NAME.
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* STAGED_DIR_NAME = "/staged";
+
+// The name of the directory that holds the (optional) installed time zone update distro.
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* CURRENT_DIR_NAME = "/current";
+
+// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
+
 // The name of the file containing the distro version information.
 // See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
 static const char* DISTRO_VERSION_FILENAME = "/distro_version";
@@ -75,7 +88,6 @@
 static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
 static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
 
-
 static void usage() {
     std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
             "\n"
@@ -184,7 +196,7 @@
     return 0;
 }
 
-enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
+enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
 
 static PathStatus checkPath(const std::string& path) {
     struct stat buf;
@@ -195,7 +207,31 @@
         }
         return NONE;
     }
-    return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
+    return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
+}
+
+/*
+ * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
+ * cannot be accessed this method returns false.
+ */
+static bool deleteFile(const std::string& fileToDelete) {
+    // Check whether the file exists.
+    PathStatus pathStatus = checkPath(fileToDelete);
+    if (pathStatus == NONE) {
+        LOG(INFO) << "Path " << fileToDelete << " does not exist";
+        return true;
+    }
+    if (pathStatus != IS_REG) {
+        LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
+        return false;
+    }
+
+    // Attempt the deletion.
+    int rc = unlink(fileToDelete.c_str());
+    if (rc != 0) {
+        PLOG(WARNING) << "unlink() failed for " << fileToDelete;
+    }
+    return rc == 0;
 }
 
 /*
@@ -260,8 +296,7 @@
     std::string dataUpdatesDirName(dataZoneInfoDir);
     dataUpdatesDirName += "/updates";
     LOG(INFO) << "Removing: " << dataUpdatesDirName;
-    bool deleted = deleteDir(dataUpdatesDirName);
-    if (!deleted) {
+    if (!deleteDir(dataUpdatesDirName)) {
         LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
                 << " was not successful";
     }
@@ -270,14 +305,151 @@
 /*
  * Deletes the timezone update distro directory.
  */
-static void deleteUpdateDistroDir(std::string& distroDirName) {
+static void deleteUpdateDistroDir(const std::string& distroDirName) {
     LOG(INFO) << "Removing: " << distroDirName;
-    bool deleted = deleteDir(distroDirName);
-    if (!deleted) {
+    if (!deleteDir(distroDirName)) {
         LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
     }
 }
 
+static void handleStagedUninstall(const std::string& dataStagedDirName,
+                                  const std::string& dataCurrentDirName,
+                                  const PathStatus dataCurrentDirStatus) {
+    LOG(INFO) << "Staged operation is an uninstall.";
+
+    // Delete the current install directory.
+    switch (dataCurrentDirStatus) {
+        case NONE:
+            // This is unexpected: No uninstall should be staged if there is nothing to
+            // uninstall. Carry on anyway.
+            LOG(WARNING) << "No current install to delete.";
+            break;
+        case IS_DIR:
+            // This is normal. Delete the current install dir.
+            if (!deleteDir(dataCurrentDirName)) {
+                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+                             << " was not successful";
+                // If this happens we don't know whether we were able to delete or not. We don't
+                // delete the staged operation so it will be retried next boot unless overridden.
+                return;
+            }
+            break;
+        case IS_REG:
+        default:
+            // This is unexpected: We can try to delete the unexpected file and carry on.
+            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+                         << " is not actually a directory. Attempting deletion.";
+            if (!deleteFile(dataCurrentDirName)) {
+                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
+                return;
+            }
+            break;
+    }
+
+    // Delete the staged uninstall dir.
+    if (!deleteDir(dataStagedDirName)) {
+        LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+                     << " was not successful";
+        // If this happens we don't know whether we were able to delete the staged operation
+        // or not.
+        return;
+    }
+    LOG(INFO) << "Staged uninstall complete.";
+}
+
+static void handleStagedInstall(const std::string& dataStagedDirName,
+                                const std::string& dataCurrentDirName,
+                                const PathStatus dataCurrentDirStatus) {
+    LOG(INFO) << "Staged operation is an install.";
+
+    switch (dataCurrentDirStatus) {
+        case NONE:
+            // This is expected: This is the first install.
+            LOG(INFO) << "No current install to replace.";
+            break;
+        case IS_DIR:
+            // This is expected: We are replacing an existing install.
+            // Delete the current dir so we can replace it.
+            if (!deleteDir(dataCurrentDirName)) {
+                LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+                             << " was not successful";
+                // If this happens, we cannot proceed.
+                return;
+            }
+            break;
+        case IS_REG:
+        default:
+            // This is unexpected: We can try to delete the unexpected file and carry on.
+            LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+                         << " is not actually a directory. Attempting deletion.";
+            if (!deleteFile(dataCurrentDirName)) {
+                LOG(WARNING) << "Could not delete " << dataCurrentDirName;
+                return;
+            }
+            break;
+    }
+
+    // Move the staged dir so it is the new current dir, completing the install.
+    LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
+    int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
+    if (rc == -1) {
+        PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
+                      << &dataCurrentDirName[0];
+        return;
+    }
+
+    LOG(INFO) << "Staged install complete.";
+}
+/*
+ * Process a staged operation if there is one.
+ */
+static void processStagedOperation(const std::string& dataStagedDirName,
+                                   const std::string& dataCurrentDirName) {
+    PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
+
+    // Exit early for the common case.
+    if (dataStagedDirStatus == NONE) {
+        LOG(DEBUG) << "No staged time zone operation.";
+        return;
+    }
+
+    // Check known directory names are in a good starting state.
+    if (dataStagedDirStatus != IS_DIR) {
+        LOG(WARNING) << "Staged distro dir " << dataStagedDirName
+                     << " could not be accessed or is not a directory."
+                     << " stagedDirStatus=" << dataStagedDirStatus;
+        return;
+    }
+
+    // dataStagedDirStatus == IS_DIR.
+
+    // Work out whether there is anything currently installed.
+    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
+    if (dataCurrentDirStatus == ERR) {
+        LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
+                     << " dataCurrentDirStatus=" << dataCurrentDirStatus;
+        return;
+    }
+
+    // We must perform the staged operation.
+
+    // Check to see if the staged directory contains an uninstall or an install operation.
+    std::string uninstallTombStoneFile(dataStagedDirName);
+    uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
+    int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
+    if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
+        // Error case.
+        LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
+        return;
+    }
+    if (uninstallTombStoneFileStatus == IS_REG) {
+        handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
+    } else {
+        // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
+        handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
+    }
+}
+
 /*
  * After a platform update it is likely that timezone data found on the system partition will be
  * newer than the version found in the data partition. This tool detects this case and removes the
@@ -300,15 +472,25 @@
     const char* systemZoneInfoDir = argv[1];
     const char* dataZoneInfoDir = argv[2];
 
-    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
+    std::string dataStagedDirName(dataZoneInfoDir);
+    dataStagedDirName += STAGED_DIR_NAME;
+
     std::string dataCurrentDirName(dataZoneInfoDir);
-    dataCurrentDirName += "/current";
-    int dataCurrentDirStatus = checkPath(dataCurrentDirName);
+    dataCurrentDirName += CURRENT_DIR_NAME;
+
+    // Check for an process any staged operation.
+    // If the staged operation could not be handled we still have to validate the current installed
+    // directory so we do not check for errors and do not quit early.
+    processStagedOperation(dataStagedDirName, dataCurrentDirName);
+
+    // Check the distro directory exists. If it does not, exit quickly: nothing to do.
+    PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
     if (dataCurrentDirStatus == NONE) {
         LOG(INFO) << "timezone distro dir " << dataCurrentDirName
                 << " does not exist. No action required.";
         return 0;
     }
+
     // If the distro directory path is not a directory or we can't stat() the path, exit with a
     // warning: either there's a problem accessing storage or the world is not as it should be;
     // nothing to do.