merge in oc-release history after reset to oc-dev
diff --git a/ojluni/src/main/java/java/util/Hashtable.java b/ojluni/src/main/java/java/util/Hashtable.java
index a07569c..6061290 100644
--- a/ojluni/src/main/java/java/util/Hashtable.java
+++ b/ojluni/src/main/java/java/util/Hashtable.java
@@ -861,6 +861,12 @@
return h;
}
+ @Override
+ public synchronized V getOrDefault(Object key, V defaultValue) {
+ V result = get(key);
+ return (null == result) ? defaultValue : result;
+ }
+
@SuppressWarnings("unchecked")
@Override
public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
@@ -902,57 +908,216 @@
}
}
- // BEGIN Android-changed: Just wrap synchronization around Map.super implementations.
- // Upstream uses different overridden implementations.
- @Override
- public synchronized V getOrDefault(Object key, V defaultValue) {
- return Map.super.getOrDefault(key, defaultValue);
- }
-
@Override
public synchronized V putIfAbsent(K key, V value) {
- return Map.super.putIfAbsent(key, value);
+ Objects.requireNonNull(value);
+
+ // Makes sure the key is not already in the hashtable.
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> entry = (HashtableEntry<K,V>)tab[index];
+ for (; entry != null; entry = entry.next) {
+ if ((entry.hash == hash) && entry.key.equals(key)) {
+ V old = entry.value;
+ if (old == null) {
+ entry.value = value;
+ }
+ return old;
+ }
+ }
+
+ addEntry(hash, key, value, index);
+ return null;
}
@Override
public synchronized boolean remove(Object key, Object value) {
- return Map.super.remove(key, value);
+ Objects.requireNonNull(value);
+
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key) && e.value.equals(value)) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ e.value = null;
+ return true;
+ }
+ }
+ return false;
}
@Override
public synchronized boolean replace(K key, V oldValue, V newValue) {
- return Map.super.replace(key, oldValue, newValue);
+ Objects.requireNonNull(oldValue);
+ Objects.requireNonNull(newValue);
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (; e != null; e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key)) {
+ if (e.value.equals(oldValue)) {
+ e.value = newValue;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return false;
}
@Override
public synchronized V replace(K key, V value) {
- return Map.super.replace(key, value);
+ Objects.requireNonNull(value);
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (; e != null; e = e.next) {
+ if ((e.hash == hash) && e.key.equals(key)) {
+ V oldValue = e.value;
+ e.value = value;
+ return oldValue;
+ }
+ }
+ return null;
}
@Override
- public synchronized V computeIfAbsent(K key, Function<? super K,
- ? extends V> mappingFunction) {
- return Map.super.computeIfAbsent(key, mappingFunction);
+ public synchronized V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
+ Objects.requireNonNull(mappingFunction);
+
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (; e != null; e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ // Hashtable not accept null value
+ return e.value;
+ }
+ }
+
+ V newValue = mappingFunction.apply(key);
+ if (newValue != null) {
+ addEntry(hash, key, newValue, index);
+ }
+
+ return newValue;
}
@Override
- public synchronized V computeIfPresent(K key, BiFunction<? super K,
- ? super V, ? extends V> remappingFunction) {
- return Map.super.computeIfPresent(key, remappingFunction);
+ public synchronized V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ V newValue = remappingFunction.apply(key, e.value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+ return null;
}
@Override
- public synchronized V compute(K key, BiFunction<? super K, ? super V,
- ? extends V> remappingFunction) {
- return Map.super.compute(key, remappingFunction);
+ public synchronized V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && Objects.equals(e.key, key)) {
+ V newValue = remappingFunction.apply(key, e.value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+
+ V newValue = remappingFunction.apply(key, null);
+ if (newValue != null) {
+ addEntry(hash, key, newValue, index);
+ }
+
+ return newValue;
}
@Override
- public synchronized V merge(K key, V value, BiFunction<? super V, ? super V,
- ? extends V> remappingFunction) {
- return Map.super.merge(key, value, remappingFunction);
+ public synchronized V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
+ Objects.requireNonNull(remappingFunction);
+
+ HashtableEntry<?,?> tab[] = table;
+ int hash = key.hashCode();
+ int index = (hash & 0x7FFFFFFF) % tab.length;
+ @SuppressWarnings("unchecked")
+ HashtableEntry<K,V> e = (HashtableEntry<K,V>)tab[index];
+ for (HashtableEntry<K,V> prev = null; e != null; prev = e, e = e.next) {
+ if (e.hash == hash && e.key.equals(key)) {
+ V newValue = remappingFunction.apply(e.value, value);
+ if (newValue == null) {
+ modCount++;
+ if (prev != null) {
+ prev.next = e.next;
+ } else {
+ tab[index] = e.next;
+ }
+ count--;
+ } else {
+ e.value = newValue;
+ }
+ return newValue;
+ }
+ }
+
+ if (value != null) {
+ addEntry(hash, key, value, index);
+ }
+
+ return value;
}
- // END Android-changed: Just add synchronization around Map's default implementations.
/**
* Save the state of the Hashtable to a stream (i.e., serialize it).
diff --git a/tzdata/shared2/src/main/libcore/tzdata/shared2/DistroVersion.java b/tzdata/shared2/src/main/libcore/tzdata/shared2/DistroVersion.java
index 3902d36..80f37bc 100644
--- a/tzdata/shared2/src/main/libcore/tzdata/shared2/DistroVersion.java
+++ b/tzdata/shared2/src/main/libcore/tzdata/shared2/DistroVersion.java
@@ -152,6 +152,15 @@
}
@Override
+ public int hashCode() {
+ int result = formatMajorVersion;
+ result = 31 * result + formatMinorVersion;
+ result = 31 * result + rulesVersion.hashCode();
+ result = 31 * result + revision;
+ return result;
+ }
+
+ @Override
public String toString() {
return "DistroVersion{" +
"formatMajorVersion=" + formatMajorVersion +
diff --git a/tzdata/shared2/src/main/libcore/tzdata/shared2/FileUtils.java b/tzdata/shared2/src/main/libcore/tzdata/shared2/FileUtils.java
index a170a9e..39626ac 100644
--- a/tzdata/shared2/src/main/libcore/tzdata/shared2/FileUtils.java
+++ b/tzdata/shared2/src/main/libcore/tzdata/shared2/FileUtils.java
@@ -17,6 +17,8 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
@@ -173,4 +175,14 @@
return toReturn;
}
}
+
+ /**
+ * Creates an empty file.
+ *
+ * @param file the file to create
+ * @throws IOException if the file cannot be created
+ */
+ public static void createEmptyFile(File file) throws IOException {
+ new FileOutputStream(file, false /* append */).close();
+ }
}
diff --git a/tzdata/shared2/src/main/libcore/tzdata/shared2/StagedDistroOperation.java b/tzdata/shared2/src/main/libcore/tzdata/shared2/StagedDistroOperation.java
new file mode 100644
index 0000000..0492e12
--- /dev/null
+++ b/tzdata/shared2/src/main/libcore/tzdata/shared2/StagedDistroOperation.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package libcore.tzdata.shared2;
+
+/**
+ * Information about a staged time zone distro operation.
+ */
+public class StagedDistroOperation {
+
+ private static final StagedDistroOperation UNINSTALL_STAGED =
+ new StagedDistroOperation(true /* isUninstall */, null /* stagedVersion */);
+
+ public final boolean isUninstall;
+ public final DistroVersion distroVersion;
+
+ private StagedDistroOperation(boolean isUninstall, DistroVersion distroVersion) {
+ this.isUninstall = isUninstall;
+ this.distroVersion = distroVersion;
+ }
+
+ public static StagedDistroOperation install(DistroVersion distroVersion) {
+ if (distroVersion == null) {
+ throw new NullPointerException("distroVersion==null");
+ }
+ return new StagedDistroOperation(false /* isUninstall */, distroVersion);
+ }
+
+ public static StagedDistroOperation uninstall() {
+ return UNINSTALL_STAGED;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ StagedDistroOperation that = (StagedDistroOperation) o;
+
+ if (isUninstall != that.isUninstall) {
+ return false;
+ }
+ return distroVersion != null ? distroVersion.equals(that.distroVersion)
+ : that.distroVersion == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (isUninstall ? 1 : 0);
+ result = 31 * result + (distroVersion != null ? distroVersion.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "StagedDistroOperation{" +
+ "isUninstall=" + isUninstall +
+ ", distroVersion=" + distroVersion +
+ '}';
+ }
+}
diff --git a/tzdata/shared2/src/test/libcore/tzdata/shared2/FileUtilsTest.java b/tzdata/shared2/src/test/libcore/tzdata/shared2/FileUtilsTest.java
index 3b9ec5b..87e6e6e 100644
--- a/tzdata/shared2/src/test/libcore/tzdata/shared2/FileUtilsTest.java
+++ b/tzdata/shared2/src/test/libcore/tzdata/shared2/FileUtilsTest.java
@@ -253,6 +253,43 @@
assertTrue(Arrays.equals(contents, exhaustedFileRead));
}
+ public void testCreateEmptyFile() throws Exception {
+ File dir = createTempDir();
+ File file = new File(dir, "one");
+ assertFalse(file.exists());
+ FileUtils.createEmptyFile(file);
+ assertTrue(file.exists());
+ assertEquals(0, file.length());
+ }
+
+ public void testCreateEmptyFile_isDir() throws Exception {
+ File dir = createTempDir();
+ assertTrue(dir.exists());
+ assertTrue(dir.isDirectory());
+
+ try {
+ FileUtils.createEmptyFile(dir);
+ } catch (FileNotFoundException expected) {
+ }
+ assertTrue(dir.exists());
+ assertTrue(dir.isDirectory());
+ }
+
+ public void testCreateEmptyFile_truncatesExisting() throws Exception {
+ File dir = createTempDir();
+ File file = new File(dir, "one");
+
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ fos.write(new byte[1000]);
+ }
+ assertTrue(file.exists());
+ assertEquals(1000, file.length());
+
+ FileUtils.createEmptyFile(file);
+ assertTrue(file.exists());
+ assertEquals(0, file.length());
+ }
+
private File createFile(byte[] contents) throws IOException {
File file = File.createTempFile(getClass().getSimpleName(), ".txt");
try (FileOutputStream fos = new FileOutputStream(file)) {
diff --git a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
index bb49fe0..e1ed794 100644
--- a/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
+++ b/tzdata/update2/src/main/libcore/tzdata/update2/TimeZoneDistroInstaller.java
@@ -23,6 +23,7 @@
import libcore.tzdata.shared2.DistroException;
import libcore.tzdata.shared2.DistroVersion;
import libcore.tzdata.shared2.FileUtils;
+import libcore.tzdata.shared2.StagedDistroOperation;
import libcore.tzdata.shared2.TimeZoneDistro;
import libcore.util.ZoneInfoDB;
@@ -31,38 +32,55 @@
* testing. This class is not thread-safe: callers are expected to handle mutual exclusion.
*/
public class TimeZoneDistroInstaller {
- /** {@link #installWithErrorCode(byte[])} result code: Success. */
+ /** {@link #stageInstallWithErrorCode(byte[])} result code: Success. */
public final static int INSTALL_SUCCESS = 0;
- /** {@link #installWithErrorCode(byte[])} result code: Distro corrupt. */
+ /** {@link #stageInstallWithErrorCode(byte[])} result code: Distro corrupt. */
public final static int INSTALL_FAIL_BAD_DISTRO_STRUCTURE = 1;
- /** {@link #installWithErrorCode(byte[])} result code: Distro version incompatible. */
+ /** {@link #stageInstallWithErrorCode(byte[])} result code: Distro version incompatible. */
public final static int INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION = 2;
- /** {@link #installWithErrorCode(byte[])} result code: Distro rules too old for device. */
+ /** {@link #stageInstallWithErrorCode(byte[])} result code: Distro rules too old for device. */
public final static int INSTALL_FAIL_RULES_TOO_OLD = 3;
- /** {@link #installWithErrorCode(byte[])} result code: Distro content failed validation. */
+ /** {@link #stageInstallWithErrorCode(byte[])} result code: Distro content failed validation. */
public final static int INSTALL_FAIL_VALIDATION_ERROR = 4;
+ // This constant must match one in system/core/tzdatacheck.cpp.
+ private static final String STAGED_TZ_DATA_DIR_NAME = "staged";
+ // This constant must match one in system/core/tzdatacheck.cpp.
private static final String CURRENT_TZ_DATA_DIR_NAME = "current";
private static final String WORKING_DIR_NAME = "working";
private static final String OLD_TZ_DATA_DIR_NAME = "old";
+ /**
+ * The name of the file in the staged directory used to indicate a staged uninstallation.
+ */
+ // This constant must match one in system/core/tzdatacheck.cpp.
+ // VisibleForTesting.
+ public static final String UNINSTALL_TOMBSTONE_FILE_NAME = "STAGED_UNINSTALL_TOMBSTONE";
+
private final String logTag;
private final File systemTzDataFile;
- private final File oldTzDataDir;
+ private final File oldStagedDataDir;
+ private final File stagedTzDataDir;
private final File currentTzDataDir;
private final File workingDir;
public TimeZoneDistroInstaller(String logTag, File systemTzDataFile, File installDir) {
this.logTag = logTag;
this.systemTzDataFile = systemTzDataFile;
- oldTzDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
+ oldStagedDataDir = new File(installDir, OLD_TZ_DATA_DIR_NAME);
+ stagedTzDataDir = new File(installDir, STAGED_TZ_DATA_DIR_NAME);
currentTzDataDir = new File(installDir, CURRENT_TZ_DATA_DIR_NAME);
workingDir = new File(installDir, WORKING_DIR_NAME);
}
// VisibleForTesting
- File getOldTzDataDir() {
- return oldTzDataDir;
+ File getOldStagedDataDir() {
+ return oldStagedDataDir;
+ }
+
+ // VisibleForTesting
+ File getStagedTzDataDir() {
+ return stagedTzDataDir;
}
// VisibleForTesting
@@ -76,34 +94,35 @@
}
/**
- * Install the supplied content.
+ * Stage an install of the supplied content, to be installed the next time the device boots.
*
- * <p>Errors during unpacking or installation will throw an {@link IOException}.
+ * <p>Errors during unpacking or staging will throw an {@link IOException}.
* If the distro content is invalid this method returns {@code false}.
* If the installation completed successfully this method returns {@code true}.
*/
public boolean install(byte[] content) throws IOException {
- int result = installWithErrorCode(content);
+ int result = stageInstallWithErrorCode(content);
return result == INSTALL_SUCCESS;
}
/**
- * Install the supplied time zone distro.
+ * Stage an install of the supplied content, to be installed the next time the device boots.
*
- * <p>Errors during unpacking or installation will throw an {@link IOException}.
+ * <p>Errors during unpacking or staging will throw an {@link IOException}.
* Returns {@link #INSTALL_SUCCESS} or an error code.
*/
- public int installWithErrorCode(byte[] content) throws IOException {
- if (oldTzDataDir.exists()) {
- FileUtils.deleteRecursive(oldTzDataDir);
+ public int stageInstallWithErrorCode(byte[] content) throws IOException {
+ if (oldStagedDataDir.exists()) {
+ FileUtils.deleteRecursive(oldStagedDataDir);
}
if (workingDir.exists()) {
FileUtils.deleteRecursive(workingDir);
}
Slog.i(logTag, "Unpacking / verifying time zone update");
- unpackDistro(content, workingDir);
try {
+ unpackDistro(content, workingDir);
+
DistroVersion distroVersion;
try {
distroVersion = readDistroVersion(workingDir);
@@ -151,52 +170,78 @@
Slog.i(logTag, "Applying time zone update");
FileUtils.makeDirectoryWorldAccessible(workingDir);
- if (currentTzDataDir.exists()) {
- Slog.i(logTag, "Moving " + currentTzDataDir + " to " + oldTzDataDir);
- FileUtils.rename(currentTzDataDir, oldTzDataDir);
+ // Check if there is already a staged install or uninstall and remove it if there is.
+ if (!stagedTzDataDir.exists()) {
+ Slog.i(logTag, "Nothing to unstage at " + stagedTzDataDir);
+ } else {
+ Slog.i(logTag, "Moving " + stagedTzDataDir + " to " + oldStagedDataDir);
+ // Move stagedTzDataDir out of the way in one operation so we can't partially delete
+ // the contents.
+ FileUtils.rename(stagedTzDataDir, oldStagedDataDir);
}
- Slog.i(logTag, "Moving " + workingDir + " to " + currentTzDataDir);
- FileUtils.rename(workingDir, currentTzDataDir);
- Slog.i(logTag, "Update applied: " + currentTzDataDir + " successfully created");
+
+ // Move the workingDir to be the new staged directory.
+ Slog.i(logTag, "Moving " + workingDir + " to " + stagedTzDataDir);
+ FileUtils.rename(workingDir, stagedTzDataDir);
+ Slog.i(logTag, "Install staged: " + stagedTzDataDir + " successfully created");
return INSTALL_SUCCESS;
} finally {
- deleteBestEffort(oldTzDataDir);
+ deleteBestEffort(oldStagedDataDir);
deleteBestEffort(workingDir);
}
}
/**
- * Uninstall the current timezone update in /data, returning the device to using data from
- * /system. Returns {@code true} if uninstallation was successful, {@code false} if there was
- * nothing installed in /data to uninstall.
+ * 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.
*
* <p>Errors encountered during uninstallation will throw an {@link IOException}.
*/
- public boolean uninstall() throws IOException {
+ public boolean stageUninstall() throws IOException {
Slog.i(logTag, "Uninstalling time zone update");
- // Make sure we don't have a dir where we're going to move the currently installed data to.
- if (oldTzDataDir.exists()) {
+ if (oldStagedDataDir.exists()) {
// If we can't remove this, an exception is thrown and we don't continue.
- FileUtils.deleteRecursive(oldTzDataDir);
+ FileUtils.deleteRecursive(oldStagedDataDir);
+ }
+ if (workingDir.exists()) {
+ FileUtils.deleteRecursive(workingDir);
}
- if (!currentTzDataDir.exists()) {
- Slog.i(logTag, "Nothing to uninstall at " + currentTzDataDir);
- return false;
+ try {
+ // Check if there is already an install or uninstall staged and remove it.
+ if (!stagedTzDataDir.exists()) {
+ Slog.i(logTag, "Nothing to unstage at " + stagedTzDataDir);
+ } else {
+ Slog.i(logTag, "Moving " + stagedTzDataDir + " to " + oldStagedDataDir);
+ // Move stagedTzDataDir out of the way in one operation so we can't partially delete
+ // the contents.
+ FileUtils.rename(stagedTzDataDir, oldStagedDataDir);
+ }
+
+ // If there's nothing actually installed, there's nothing to uninstall so no need to
+ // stage anything.
+ if (!currentTzDataDir.exists()) {
+ Slog.i(logTag, "Nothing to uninstall at " + currentTzDataDir);
+ return false;
+ }
+
+ // Stage an uninstall in workingDir.
+ FileUtils.ensureDirectoriesExist(workingDir, true /* makeWorldReadable */);
+ FileUtils.createEmptyFile(new File(workingDir, UNINSTALL_TOMBSTONE_FILE_NAME));
+
+ // Move the workingDir to be the new staged directory.
+ Slog.i(logTag, "Moving " + workingDir + " to " + stagedTzDataDir);
+ FileUtils.rename(workingDir, stagedTzDataDir);
+ Slog.i(logTag, "Uninstall staged: " + stagedTzDataDir + " successfully created");
+
+ return true;
+ } finally {
+ deleteBestEffort(oldStagedDataDir);
+ deleteBestEffort(workingDir);
}
-
- Slog.i(logTag, "Moving " + currentTzDataDir + " to " + oldTzDataDir);
- // Move currentTzDataDir out of the way in one operation so we can't partially delete
- // the contents, which would leave a partial install.
- FileUtils.rename(currentTzDataDir, oldTzDataDir);
-
- // Do our best to delete the now uninstalled timezone data.
- deleteBestEffort(oldTzDataDir);
-
- Slog.i(logTag, "Time zone update uninstalled.");
-
- return true;
}
/**
@@ -214,6 +259,24 @@
}
/**
+ * Reads information about any currently staged distro operation. Returns {@code null} if there
+ * is no distro operation staged.
+ *
+ * @throws IOException if there was a problem reading data from /data
+ * @throws DistroException if there was a problem with the staged distro format/structure
+ */
+ public StagedDistroOperation getStagedDistroOperation() throws DistroException, IOException {
+ if (!stagedTzDataDir.exists()) {
+ return null;
+ }
+ if (new File(stagedTzDataDir, UNINSTALL_TOMBSTONE_FILE_NAME).exists()) {
+ return StagedDistroOperation.uninstall();
+ } else {
+ return StagedDistroOperation.install(readDistroVersion(stagedTzDataDir));
+ }
+ }
+
+ /**
* Reads the timezone rules version present in /system. i.e. the version that would be present
* after a factory reset.
*
diff --git a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
index 325b605..cf40bf6 100644
--- a/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
+++ b/tzdata/update2/src/test/libcore/tzdata/update2/TimeZoneDistroInstallerTest.java
@@ -32,10 +32,13 @@
import libcore.io.Streams;
import libcore.tzdata.shared2.DistroVersion;
import libcore.tzdata.shared2.FileUtils;
+import libcore.tzdata.shared2.StagedDistroOperation;
import libcore.tzdata.shared2.TimeZoneDistro;
import libcore.tzdata.testing.ZoneInfoTestHelper;
import libcore.tzdata.update2.tools.TimeZoneDistroBuilder;
+import static org.junit.Assert.assertArrayEquals;
+
/**
* Tests for {@link TimeZoneDistroInstaller}.
*/
@@ -90,100 +93,110 @@
}
/** Tests the an update on a device will fail if the /system tzdata file cannot be found. */
- public void testInstall_badSystemFile() throws Exception {
+ public void testStageInstallWithErrorCode_badSystemFile() throws Exception {
File doesNotExist = new File(testSystemTzDataDir, "doesNotExist");
TimeZoneDistroInstaller brokenSystemInstaller = new TimeZoneDistroInstaller(
"TimeZoneDistroInstallerTest", doesNotExist, testInstallDir);
TimeZoneDistro tzData = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
try {
- brokenSystemInstaller.installWithErrorCode(tzData.getBytes());
+ brokenSystemInstaller.stageInstallWithErrorCode(tzData.getBytes());
fail();
} catch (IOException expected) {}
- assertNoContentInstalled();
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
/** Tests the first successful update on a device */
- public void testInstall_successfulFirstUpdate() throws Exception {
+ public void testStageInstallWithErrorCode_successfulFirstUpdate() throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro.getBytes()));
- assertDistroInstalled(distro);
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertInstallDistroStaged(distro);
+ assertNoInstalledDistro();
}
/**
* Tests we can install an update the same version as is in /system.
*/
- public void testInstall_successfulFirstUpdate_sameVersionAsSystem() throws Exception {
+ public void testStageInstallWithErrorCode_successfulFirstUpdate_sameVersionAsSystem()
+ throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(SYSTEM_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro.getBytes()));
- assertDistroInstalled(distro);
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertInstallDistroStaged(distro);
+ assertNoInstalledDistro();
}
/**
* Tests we cannot install an update older than the version in /system.
*/
- public void testInstall_unsuccessfulFirstUpdate_olderVersionThanSystem() throws Exception {
+ public void testStageInstallWithErrorCode_unsuccessfulFirstUpdate_olderVersionThanSystem()
+ throws Exception {
TimeZoneDistro distro = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
- installer.installWithErrorCode(distro.getBytes()));
- assertNoContentInstalled();
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
/**
- * Tests an update on a device when there is a prior update already applied.
+ * Tests an update on a device when there is a prior update already staged.
*/
- public void testInstall_successfulFollowOnUpdate_newerVersion() throws Exception {
+ public void testStageInstallWithErrorCode_successfulFollowOnUpdate_newerVersion()
+ throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro1.getBytes()));
- assertDistroInstalled(distro1);
+ installer.stageInstallWithErrorCode(distro1.getBytes()));
+ assertInstallDistroStaged(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro2.getBytes()));
- assertDistroInstalled(distro2);
+ installer.stageInstallWithErrorCode(distro2.getBytes()));
+ assertInstallDistroStaged(distro2);
TimeZoneDistro distro3 = createValidTimeZoneDistro(NEWER_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro3.getBytes()));
- assertDistroInstalled(distro3);
+ installer.stageInstallWithErrorCode(distro3.getBytes()));
+ assertInstallDistroStaged(distro3);
+ assertNoInstalledDistro();
}
/**
* Tests an update on a device when there is a prior update already applied, but the follow
* on update is older than in /system.
*/
- public void testInstall_unsuccessfulFollowOnUpdate_olderVersion() throws Exception {
+ public void testStageInstallWithErrorCode_unsuccessfulFollowOnUpdate_olderVersion()
+ throws Exception {
TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 2);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro1.getBytes()));
- assertDistroInstalled(distro1);
+ installer.stageInstallWithErrorCode(distro1.getBytes()));
+ assertInstallDistroStaged(distro1);
TimeZoneDistro distro2 = createValidTimeZoneDistro(OLDER_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_RULES_TOO_OLD,
- installer.installWithErrorCode(distro2.getBytes()));
- assertDistroInstalled(distro1);
+ installer.stageInstallWithErrorCode(distro2.getBytes()));
+ assertInstallDistroStaged(distro1);
+ assertNoInstalledDistro();
}
/** Tests that a distro with a missing file will not update the content. */
- public void testInstall_missingTzDataFile() throws Exception {
- TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+ public void testStageInstallWithErrorCode_missingTzDataFile() throws Exception {
+ TimeZoneDistro stagedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(installedDistro.getBytes()));
- assertDistroInstalled(installedDistro);
+ installer.stageInstallWithErrorCode(stagedDistro.getBytes()));
+ assertInstallDistroStaged(stagedDistro);
TimeZoneDistro incompleteDistro =
createValidTimeZoneDistroBuilder(NEWER_RULES_VERSION, 1)
@@ -191,17 +204,18 @@
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
- installer.installWithErrorCode(incompleteDistro.getBytes()));
- assertDistroInstalled(installedDistro);
+ installer.stageInstallWithErrorCode(incompleteDistro.getBytes()));
+ assertInstallDistroStaged(stagedDistro);
+ assertNoInstalledDistro();
}
/** Tests that a distro with a missing file will not update the content. */
- public void testInstall_missingIcuFile() throws Exception {
- TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+ public void testStageInstallWithErrorCode_missingIcuFile() throws Exception {
+ TimeZoneDistro stagedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(installedDistro.getBytes()));
- assertDistroInstalled(installedDistro);
+ installer.stageInstallWithErrorCode(stagedDistro.getBytes()));
+ assertInstallDistroStaged(stagedDistro);
TimeZoneDistro incompleteDistro =
createValidTimeZoneDistroBuilder(NEWER_RULES_VERSION, 1)
@@ -209,14 +223,15 @@
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
- installer.installWithErrorCode(incompleteDistro.getBytes()));
- assertDistroInstalled(installedDistro);
+ installer.stageInstallWithErrorCode(incompleteDistro.getBytes()));
+ assertInstallDistroStaged(stagedDistro);
+ assertNoInstalledDistro();
}
/**
* Tests that an update will be unpacked even if there is a partial update from a previous run.
*/
- public void testInstall_withWorkingDir() throws Exception {
+ public void testStageInstallWithErrorCode_withWorkingDir() throws Exception {
File workingDir = installer.getWorkingDir();
assertTrue(workingDir.mkdir());
createFile(new File(workingDir, "myFile"), new byte[] { 'a' });
@@ -224,42 +239,45 @@
TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
assertEquals(
TimeZoneDistroInstaller.INSTALL_SUCCESS,
- installer.installWithErrorCode(distro.getBytes()));
- assertDistroInstalled(distro);
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertInstallDistroStaged(distro);
+ assertNoInstalledDistro();
}
/**
* Tests that a distro without a distro version file will be rejected.
*/
- public void testInstall_withMissingDistroVersionFile() throws Exception {
+ public void testStageInstallWithErrorCode_withMissingDistroVersionFile() throws Exception {
// Create a distro without a version file.
TimeZoneDistro distro = createValidTimeZoneDistroBuilder(NEW_RULES_VERSION, 1)
.clearVersionForTests()
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
- installer.installWithErrorCode(distro.getBytes()));
- assertNoContentInstalled();
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
/**
* Tests that a distro with an newer distro version will be rejected.
*/
- public void testInstall_withNewerDistroVersion() throws Exception {
+ public void testStageInstallWithErrorCode_withNewerDistroVersion() throws Exception {
// Create a distro that will appear to be newer than the one currently supported.
TimeZoneDistro distro = createValidTimeZoneDistroBuilder(NEW_RULES_VERSION, 1)
.replaceFormatVersionForTests(2, 1)
.buildUnvalidated();
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_FORMAT_VERSION,
- installer.installWithErrorCode(distro.getBytes()));
- assertNoContentInstalled();
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
/**
* Tests that a distro with a badly formed distro version will be rejected.
*/
- public void testInstall_withBadlyFormedDistroVersion() throws Exception {
+ public void testStageInstallWithErrorCode_withBadlyFormedDistroVersion() throws Exception {
// Create a distro that has an invalid major distro version. It should be 3 numeric
// characters, "." and 3 more numeric characters.
DistroVersion validDistroVersion = new DistroVersion(1, 1, NEW_RULES_VERSION, 1);
@@ -269,14 +287,15 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidFormatVersionBytes);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
- installer.installWithErrorCode(distro.getBytes()));
- assertNoContentInstalled();
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
/**
* Tests that a distro with a badly formed revision will be rejected.
*/
- public void testInstall_withBadlyFormedRevision() throws Exception {
+ public void testStageInstallWithErrorCode_withBadlyFormedRevision() throws Exception {
// Create a distro that has an invalid revision. It should be 3 numeric characters.
DistroVersion validDistroVersion = new DistroVersion(1, 1, NEW_RULES_VERSION, 1);
byte[] invalidRevisionBytes = validDistroVersion.toBytes();
@@ -285,14 +304,15 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRevisionBytes);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
- installer.installWithErrorCode(distro.getBytes()));
- assertNoContentInstalled();
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
/**
* Tests that a distro with a badly formed rules version will be rejected.
*/
- public void testInstall_withBadlyFormedRulesVersion() throws Exception {
+ public void testStageInstallWithErrorCode_withBadlyFormedRulesVersion() throws Exception {
// Create a distro that has an invalid rules version. It should be in the form "2016c".
DistroVersion validDistroVersion = new DistroVersion(1, 1, NEW_RULES_VERSION, 1);
byte[] invalidRulesVersionBytes = validDistroVersion.toBytes();
@@ -301,38 +321,124 @@
TimeZoneDistro distro = createTimeZoneDistroWithVersionBytes(invalidRulesVersionBytes);
assertEquals(
TimeZoneDistroInstaller.INSTALL_FAIL_BAD_DISTRO_STRUCTURE,
- installer.installWithErrorCode(distro.getBytes()));
- assertNoContentInstalled();
+ installer.stageInstallWithErrorCode(distro.getBytes()));
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
- public void testUninstall_noExistingDataDistro() throws Exception {
- assertFalse(installer.uninstall());
- assertNoContentInstalled();
+ public void testStageUninstall_noExistingDistro() throws Exception {
+ // To stage an uninstall, there would need to be installed rules.
+ assertFalse(installer.stageUninstall());
+
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
}
- public void testUninstall_existingDataDistro() throws Exception {
- File currentDataDir = installer.getCurrentTzDataDir();
- assertTrue(currentDataDir.mkdir());
+ public void testStageUninstall_existingStagedDataDistro() throws Exception {
+ // To stage an uninstall, we need to have some installed rules.
+ TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+ simulateInstalledDistro(installedDistro);
- assertTrue(installer.uninstall());
- assertNoContentInstalled();
+ File stagedDataDir = installer.getStagedTzDataDir();
+ assertTrue(stagedDataDir.mkdir());
+
+ assertTrue(installer.stageUninstall());
+ assertDistroUninstallStaged();
+ assertInstalledDistro(installedDistro);
}
- public void testUninstall_oldDirsAlreadyExists() throws Exception {
- File oldTzDataDir = installer.getOldTzDataDir();
- assertTrue(oldTzDataDir.mkdir());
+ public void testStageUninstall_oldDirsAlreadyExists() throws Exception {
+ // To stage an uninstall, we need to have some installed rules.
+ TimeZoneDistro installedDistro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+ simulateInstalledDistro(installedDistro);
- File currentDataDir = installer.getCurrentTzDataDir();
- assertTrue(currentDataDir.mkdir());
+ File oldStagedDataDir = installer.getOldStagedDataDir();
+ assertTrue(oldStagedDataDir.mkdir());
- assertTrue(installer.uninstall());
- assertNoContentInstalled();
+ File workingDir = installer.getWorkingDir();
+ assertTrue(workingDir.mkdir());
+
+ assertTrue(installer.stageUninstall());
+
+ assertDistroUninstallStaged();
+ assertFalse(workingDir.exists());
+ assertFalse(oldStagedDataDir.exists());
+ assertInstalledDistro(installedDistro);
}
public void testGetSystemRulesVersion() throws Exception {
assertEquals(SYSTEM_RULES_VERSION, installer.getSystemRulesVersion());
}
+ public void testGetInstalledDistroVersion() throws Exception {
+ // Check result when nothing installed.
+ assertNull(installer.getInstalledDistroVersion());
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
+
+ // Now simulate there being an existing install active.
+ TimeZoneDistro distro = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+ simulateInstalledDistro(distro);
+ assertInstalledDistro(distro);
+
+ // Check result when something installed.
+ assertEquals(distro.getDistroVersion(), installer.getInstalledDistroVersion());
+ assertNoDistroOperationStaged();
+ assertInstalledDistro(distro);
+ }
+
+ public void testGetStagedDistroOperation() throws Exception {
+ TimeZoneDistro distro1 = createValidTimeZoneDistro(NEW_RULES_VERSION, 1);
+ TimeZoneDistro distro2 = createValidTimeZoneDistro(NEWER_RULES_VERSION, 1);
+
+ // Check result when nothing staged.
+ assertNull(installer.getStagedDistroOperation());
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
+
+ // Check result after unsuccessfully staging an uninstall.
+ // Can't stage an uninstall without an installed distro.
+ assertFalse(installer.stageUninstall());
+ assertNull(installer.getStagedDistroOperation());
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
+
+ // Check result after staging an install.
+ assertTrue(installer.install(distro1.getBytes()));
+ StagedDistroOperation expectedStagedInstall =
+ StagedDistroOperation.install(distro1.getDistroVersion());
+ assertEquals(expectedStagedInstall, installer.getStagedDistroOperation());
+ assertInstallDistroStaged(distro1);
+ assertNoInstalledDistro();
+
+ // 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());
+ assertNull(installer.getStagedDistroOperation());
+ assertNoDistroOperationStaged();
+ assertNoInstalledDistro();
+
+ // Now simulate there being an existing install active.
+ simulateInstalledDistro(distro1);
+ assertInstalledDistro(distro1);
+
+ // Check state after successfully staging an uninstall.
+ assertTrue(installer.stageUninstall());
+ StagedDistroOperation expectedStagedUninstall = StagedDistroOperation.uninstall();
+ assertEquals(expectedStagedUninstall, installer.getStagedDistroOperation());
+ assertDistroUninstallStaged();
+ assertInstalledDistro(distro1);
+
+ // Check state after successfully staging an install.
+ assertEquals(TimeZoneDistroInstaller.INSTALL_SUCCESS,
+ installer.stageInstallWithErrorCode(distro2.getBytes()));
+ StagedDistroOperation expectedStagedInstall2 =
+ StagedDistroOperation.install(distro2.getDistroVersion());
+ assertEquals(expectedStagedInstall2, installer.getStagedDistroOperation());
+ assertInstallDistroStaged(distro2);
+ assertInstalledDistro(distro1);
+ }
+
private static TimeZoneDistro createValidTimeZoneDistro(
String rulesVersion, int revision) throws Exception {
return createValidTimeZoneDistroBuilder(rulesVersion, revision).build();
@@ -354,24 +460,27 @@
.setIcuData(icuData);
}
- private void assertDistroInstalled(TimeZoneDistro expectedDistro) throws Exception {
+ private void assertInstallDistroStaged(TimeZoneDistro expectedDistro) throws Exception {
assertTrue(testInstallDir.exists());
- File currentTzDataDir = installer.getCurrentTzDataDir();
- assertTrue(currentTzDataDir.exists());
+ File stagedTzDataDir = installer.getStagedTzDataDir();
+ assertTrue(stagedTzDataDir.exists());
File distroVersionFile =
- new File(currentTzDataDir, TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
+ new File(stagedTzDataDir, TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
assertTrue(distroVersionFile.exists());
- File bionicFile = new File(currentTzDataDir, TimeZoneDistro.TZDATA_FILE_NAME);
+ File bionicFile = new File(stagedTzDataDir, TimeZoneDistro.TZDATA_FILE_NAME);
assertTrue(bionicFile.exists());
- File icuFile = new File(currentTzDataDir, TimeZoneDistro.ICU_DATA_FILE_NAME);
+ File icuFile = new File(stagedTzDataDir, TimeZoneDistro.ICU_DATA_FILE_NAME);
assertTrue(icuFile.exists());
- // Assert getInstalledDistroVersion() is reporting correctly.
- assertEquals(expectedDistro.getDistroVersion(), installer.getInstalledDistroVersion());
+ // Assert getStagedDistroState() is reporting correctly.
+ StagedDistroOperation stagedDistroOperation = installer.getStagedDistroOperation();
+ assertNotNull(stagedDistroOperation);
+ assertFalse(stagedDistroOperation.isUninstall);
+ assertEquals(expectedDistro.getDistroVersion(), stagedDistroOperation.distroVersion);
try (ZipInputStream zis = new ZipInputStream(
new ByteArrayInputStream(expectedDistro.getBytes()))) {
@@ -401,23 +510,64 @@
throws Exception {
byte[] actualBytes = IoUtils.readFileAsByteArray(actual.getPath());
byte[] expectedBytes = Streams.readFullyNoClose(expected);
- assertTrue(Arrays.equals(expectedBytes, actualBytes));
+ assertArrayEquals(expectedBytes, actualBytes);
}
- private void assertNoContentInstalled() throws Exception {
- assertNull(installer.getInstalledDistroVersion());
+ private void assertNoDistroOperationStaged() throws Exception {
+ assertNull(installer.getStagedDistroOperation());
- File currentTzDataDir = installer.getCurrentTzDataDir();
- assertFalse(currentTzDataDir.exists());
+ File stagedTzDataDir = installer.getStagedTzDataDir();
+ assertFalse(stagedTzDataDir.exists());
// Also check no working directories are left lying around.
File workingDir = installer.getWorkingDir();
assertFalse(workingDir.exists());
- File oldDataDir = installer.getOldTzDataDir();
+ File oldDataDir = installer.getOldStagedDataDir();
assertFalse(oldDataDir.exists());
}
+ private void assertDistroUninstallStaged() throws Exception {
+ assertEquals(StagedDistroOperation.uninstall(), installer.getStagedDistroOperation());
+
+ File stagedTzDataDir = installer.getStagedTzDataDir();
+ assertTrue(stagedTzDataDir.exists());
+ assertTrue(stagedTzDataDir.isDirectory());
+
+ File uninstallTombstone =
+ new File(stagedTzDataDir, TimeZoneDistroInstaller.UNINSTALL_TOMBSTONE_FILE_NAME);
+ assertTrue(uninstallTombstone.exists());
+ assertTrue(uninstallTombstone.isFile());
+
+ // Also check no working directories are left lying around.
+ File workingDir = installer.getWorkingDir();
+ assertFalse(workingDir.exists());
+
+ File oldDataDir = installer.getOldStagedDataDir();
+ assertFalse(oldDataDir.exists());
+ }
+
+ private void simulateInstalledDistro(TimeZoneDistro timeZoneDistro) throws Exception {
+ File currentTzDataDir = installer.getCurrentTzDataDir();
+ assertFalse(currentTzDataDir.exists());
+ assertTrue(currentTzDataDir.mkdir());
+ timeZoneDistro.extractTo(currentTzDataDir);
+ }
+
+ private void assertNoInstalledDistro() {
+ assertFalse(installer.getCurrentTzDataDir().exists());
+ }
+
+ private void assertInstalledDistro(TimeZoneDistro timeZoneDistro) throws Exception {
+ File currentTzDataDir = installer.getCurrentTzDataDir();
+ assertTrue(currentTzDataDir.exists());
+ File versionFile = new File(currentTzDataDir, TimeZoneDistro.DISTRO_VERSION_FILE_NAME);
+ assertTrue(versionFile.exists());
+ byte[] expectedVersionBytes = timeZoneDistro.getDistroVersion().toBytes();
+ byte[] actualVersionBytes = FileUtils.readBytes(versionFile, expectedVersionBytes.length);
+ assertArrayEquals(expectedVersionBytes, actualVersionBytes);
+ }
+
private static byte[] createTzData(String rulesVersion) {
return new ZoneInfoTestHelper.TzDataBuilder()
.initializeToValid()