release-request-1f2fcfef-9736-44dc-8628-3ba96dac60db-for-git_oc-mr1-release-4343541 snap-temp-L73700000103533431

Change-Id: I4609490094cea2cc4d7e3c2670863e66f90d5802
diff --git a/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java b/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java
index 5da7068..932eae6 100644
--- a/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java
+++ b/distro/installer/src/main/com/android/timezone/distro/installer/TimeZoneDistroInstaller.java
@@ -36,15 +36,43 @@
 public class TimeZoneDistroInstaller {
     /** {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Success. */
     public final static int INSTALL_SUCCESS = 0;
+
     /** {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro corrupt. */
     public final static int INSTALL_FAIL_BAD_DISTRO_STRUCTURE = 1;
-    /** {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro version incompatible. */
+
+    /**
+     * {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro version incompatible.
+     */
     public final static int INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION = 2;
-    /** {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro rules too old for device. */
+
+    /**
+     * {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro rules too old for
+     * device.
+     */
     public final static int INSTALL_FAIL_RULES_TOO_OLD = 3;
-    /** {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro content failed validation. */
+
+    /**
+     * {@link #stageInstallWithErrorCode(TimeZoneDistro)} result code: Distro content failed
+     * validation.
+     */
     public final static int INSTALL_FAIL_VALIDATION_ERROR = 4;
 
+    /**
+     * {@link #stageUninstall()} result code: An uninstall has been successfully staged.
+     */
+    public final static int UNINSTALL_SUCCESS = 0;
+
+    /**
+     * {@link #stageUninstall()} result code: Nothing was installed that required an uninstall to be
+     * staged.
+     */
+    public final static int UNINSTALL_NOTHING_INSTALLED = 1;
+
+    /**
+     * {@link #stageUninstall()} result code: The uninstall could not be staged.
+     */
+    public final static int UNINSTALL_FAIL = 2;
+
     // This constant must match one in system/timezone/tzdatacheck/tzdatacheck.cpp.
     private static final String STAGED_TZ_DATA_DIR_NAME = "staged";
     // This constant must match one in system/timezone/tzdatacheck/tzdatacheck.cpp.
@@ -111,7 +139,7 @@
      * Stage an install of the supplied content, to be installed the next time the device boots.
      *
      * <p>Errors during unpacking or staging will throw an {@link IOException}.
-     * Returns {@link #INSTALL_SUCCESS} or an error code.
+     * Returns {@link #INSTALL_SUCCESS} on success, or one of the failure codes.
      */
     public int stageInstallWithErrorCode(TimeZoneDistro distro) throws IOException {
         if (oldStagedDataDir.exists()) {
@@ -212,13 +240,17 @@
 
     /**
      * Stage an uninstall of the current timezone update in /data which, on reboot, will return the
-     * device to using data from /system. Returns {@code true} if staging the uninstallation was
-     * successful, {@code false} if there was nothing installed in /data to uninstall. If there was
-     * something else staged it will be replaced by this call.
+     * device to using data from /system. If there was something else already staged it will be
+     * removed by this call.
+     *
+     * Returns {@link #UNINSTALL_SUCCESS} if staging the uninstallation was
+     * successful and reboot will be required. Returns {@link #UNINSTALL_NOTHING_INSTALLED} if
+     * there was nothing installed in /data that required an uninstall to be staged, anything that
+     * was staged will have been removed and therefore no reboot is required.
      *
      * <p>Errors encountered during uninstallation will throw an {@link IOException}.
      */
-    public boolean stageUninstall() throws IOException {
+    public int stageUninstall() throws IOException {
         Slog.i(logTag, "Uninstalling time zone update");
 
         if (oldStagedDataDir.exists()) {
@@ -244,7 +276,7 @@
             // stage anything.
             if (!currentTzDataDir.exists()) {
                 Slog.i(logTag, "Nothing to uninstall at " + currentTzDataDir);
-                return false;
+                return UNINSTALL_NOTHING_INSTALLED;
             }
 
             // Stage an uninstall in workingDir.
@@ -256,7 +288,7 @@
             FileUtils.rename(workingDir, stagedTzDataDir);
             Slog.i(logTag, "Uninstall staged: " + stagedTzDataDir + " successfully created");
 
-            return true;
+            return UNINSTALL_SUCCESS;
         } finally {
             deleteBestEffort(oldStagedDataDir);
             deleteBestEffort(workingDir);
diff --git a/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java b/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java
index 596b6ff..123f087 100644
--- a/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java
+++ b/distro/installer/src/test/com/android/timezone/distro/installer/TimeZoneDistroInstallerTest.java
@@ -197,6 +197,28 @@
         assertNoInstalledDistro();
     }
 
+    /**
+     * Tests staging an update when there's already an uninstall staged still results in a staged
+     * install.
+     */
+    public void testStageInstallWithErrorCode_existingStagedUninstall()
+            throws Exception {
+        byte[] distro1Bytes = createValidTimeZoneDistroBytes(NEW_RULES_VERSION, 1);
+        simulateInstalledDistro(distro1Bytes);
+        assertInstalledDistro(distro1Bytes);
+
+        assertEquals(TimeZoneDistroInstaller.UNINSTALL_SUCCESS, installer.stageUninstall());
+        assertDistroUninstallStaged();
+        assertInstalledDistro(distro1Bytes);
+
+        byte[] distro2Bytes = createValidTimeZoneDistroBytes(NEWER_RULES_VERSION, 1);
+        assertEquals(
+                TimeZoneDistroInstaller.INSTALL_SUCCESS,
+                installer.stageInstallWithErrorCode(new TimeZoneDistro(distro2Bytes)));
+        assertInstalledDistro(distro1Bytes);
+        assertInstallDistroStaged(distro2Bytes);
+    }
+
     /** Tests that a distro with a missing tzdata file will not update the content. */
     public void testStageInstallWithErrorCode_missingTzDataFile() throws Exception {
         byte[] stagedDistroBytes = createValidTimeZoneDistroBytes(NEW_RULES_VERSION, 1);
@@ -371,15 +393,19 @@
         assertNoInstalledDistro();
     }
 
+    /** Tests what happens if a stageUninstall() is attempted when there's nothing installed. */
     public void testStageUninstall_noExistingDistro() throws Exception {
         // To stage an uninstall, there would need to be installed rules.
-        assertFalse(installer.stageUninstall());
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED,
+                installer.stageUninstall());
 
         assertNoDistroOperationStaged();
         assertNoInstalledDistro();
     }
 
-    public void testStageUninstall_existingStagedDataDistro() throws Exception {
+    /** Tests what happens if a stageUninstall() is attempted when there's something installed. */
+    public void testStageUninstall_existingInstalledDataDistro() throws Exception {
         // To stage an uninstall, we need to have some installed rules.
         byte[] installedDistroBytes = createValidTimeZoneDistroBytes(NEW_RULES_VERSION, 1);
         simulateInstalledDistro(installedDistroBytes);
@@ -387,11 +413,63 @@
         File stagedDataDir = installer.getStagedTzDataDir();
         assertTrue(stagedDataDir.mkdir());
 
-        assertTrue(installer.stageUninstall());
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_SUCCESS,
+                installer.stageUninstall());
         assertDistroUninstallStaged();
         assertInstalledDistro(installedDistroBytes);
     }
 
+    /**
+     * Tests what happens if a stageUninstall() is attempted when there's something installed
+     * and there's a staged install.
+     */
+    public void testStageUninstall_existingStagedInstall() throws Exception {
+        File stagedDataDir = installer.getStagedTzDataDir();
+        assertTrue(stagedDataDir.mkdir());
+
+        // Stage an install.
+        byte[] distroBytes = createValidTimeZoneDistroBytes(NEW_RULES_VERSION, 1);
+        assertEquals(TimeZoneDistroInstaller.INSTALL_SUCCESS,
+                installer.stageInstallWithErrorCode(new TimeZoneDistro(distroBytes)));
+
+        // Now uninstall. It should just remove the staged install.
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED,
+                installer.stageUninstall());
+        assertNoDistroOperationStaged();
+    }
+
+    /**
+     * Tests what happens if a stageUninstall() is attempted when there's something installed
+     * and there's a staged uninstall.
+     */
+    public void testStageUninstall_existingStagedUninstall() throws Exception {
+        // To stage an uninstall, we need to have some installed rules.
+        byte[] installedDistroBytes = createValidTimeZoneDistroBytes(NEW_RULES_VERSION, 1);
+        simulateInstalledDistro(installedDistroBytes);
+
+        File stagedDataDir = installer.getStagedTzDataDir();
+        assertTrue(stagedDataDir.mkdir());
+
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_SUCCESS,
+                installer.stageUninstall());
+        assertDistroUninstallStaged();
+        assertInstalledDistro(installedDistroBytes);
+
+        // Now stage a second uninstall.
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_SUCCESS,
+                installer.stageUninstall());
+        assertDistroUninstallStaged();
+        assertInstalledDistro(installedDistroBytes);
+    }
+
+    /**
+     * Tests what happens if a stageUninstall() is attempted when there are unexpected working
+     * directories present.
+     */
     public void testStageUninstall_oldDirsAlreadyExists() throws Exception {
         // To stage an uninstall, we need to have some installed rules.
         byte[] installedDistroBytes = createValidTimeZoneDistroBytes(NEW_RULES_VERSION, 1);
@@ -403,7 +481,9 @@
         File workingDir = installer.getWorkingDir();
         assertTrue(workingDir.mkdir());
 
-        assertTrue(installer.stageUninstall());
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_SUCCESS,
+                installer.stageUninstall());
 
         assertDistroUninstallStaged();
         assertFalse(workingDir.exists());
@@ -444,7 +524,9 @@
 
         // Check result after unsuccessfully staging an uninstall.
         // Can't stage an uninstall without an installed distro.
-        assertFalse(installer.stageUninstall());
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED,
+                installer.stageUninstall());
         assertNull(installer.getStagedDistroOperation());
         assertNoDistroOperationStaged();
         assertNoInstalledDistro();
@@ -461,7 +543,9 @@
 
         // Check result after unsuccessfully staging an uninstall (but after removing a staged
         // install). Can't stage an uninstall without an installed distro.
-        assertFalse(installer.stageUninstall());
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED,
+                installer.stageUninstall());
         assertNull(installer.getStagedDistroOperation());
         assertNoDistroOperationStaged();
         assertNoInstalledDistro();
@@ -471,7 +555,9 @@
         assertInstalledDistro(distro1Bytes);
 
         // Check state after successfully staging an uninstall.
-        assertTrue(installer.stageUninstall());
+        assertEquals(
+                TimeZoneDistroInstaller.UNINSTALL_SUCCESS,
+                installer.stageUninstall());
         StagedDistroOperation expectedStagedUninstall = StagedDistroOperation.uninstall();
         assertEquals(expectedStagedUninstall, installer.getStagedDistroOperation());
         assertDistroUninstallStaged();