Moves first parts of backup crypto code (ChunkHash class) to the framework.

Some changes were needed to the original code:
- Guava's EqualsTester tests are replaced by regular equals tests.
- Guava's primitives.UnsignedBytes.LexicographicalComparator needed to
be copied over, as no corresponding comparator exists in the framework.

Bug: 111386661
Test: atest RunFrameworksServicesRoboTests
Change-Id: I24fef4b47f7777b9be0c2e51f0be48e45b323987
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java
new file mode 100644
index 0000000..1ae598e
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2018 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 com.android.server.backup.encryption.chunk;
+
+import com.android.internal.util.Preconditions;
+import java.util.Arrays;
+import java.util.Base64;
+
+/**
+ * Represents the SHA-256 hash of the plaintext of a chunk, which is frequently used as a key.
+ *
+ * <p>This class is {@link Comparable} and implements {@link #equals(Object)} and {@link
+ * #hashCode()}.
+ */
+public class ChunkHash implements Comparable<ChunkHash> {
+    /** The length of the hash in bytes. The hash is a SHA-256, so this is 256 bits. */
+    public static final int HASH_LENGTH_BYTES = 256 / 8;
+
+    private static final int UNSIGNED_MASK = 0xFF;
+
+    private final byte[] mHash;
+
+    /** Constructs a new instance which wraps the given SHA-256 hash bytes. */
+    public ChunkHash(byte[] hash) {
+        Preconditions.checkArgument(hash.length == HASH_LENGTH_BYTES, "Hash must have 256 bits");
+        mHash = hash;
+    }
+
+    public byte[] getHash() {
+        return mHash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof ChunkHash)) {
+            return false;
+        }
+
+        ChunkHash chunkHash = (ChunkHash) o;
+        return Arrays.equals(mHash, chunkHash.mHash);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(mHash);
+    }
+
+    @Override
+    public int compareTo(ChunkHash other) {
+        return lexicographicalCompareUnsignedBytes(getHash(), other.getHash());
+    }
+
+    @Override
+    public String toString() {
+        return Base64.getEncoder().encodeToString(mHash);
+    }
+
+    private static int lexicographicalCompareUnsignedBytes(byte[] left, byte[] right) {
+        int minLength = Math.min(left.length, right.length);
+        for (int i = 0; i < minLength; i++) {
+            int result = toInt(left[i]) - toInt(right[i]);
+            if (result != 0) {
+                return result;
+            }
+        }
+        return left.length - right.length;
+    }
+
+    private static int toInt(byte value) {
+        return value & UNSIGNED_MASK;
+    }
+}
diff --git a/services/robotests/Android.mk b/services/robotests/Android.mk
index 2691701..8b59771 100644
--- a/services/robotests/Android.mk
+++ b/services/robotests/Android.mk
@@ -75,6 +75,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     platform-robolectric-android-all-stubs \
     android-support-test \
+    guava \
     mockito-robolectric-prebuilt \
     platform-test-annotations \
     truth-prebuilt \
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
new file mode 100644
index 0000000..3b6e038
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 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 com.android.server.backup.encryption.chunk;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.primitives.Bytes;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class ChunkHashTest {
+    private static final int HASH_LENGTH_BYTES = 256 / 8;
+    private static final byte[] TEST_HASH_1 = Arrays.copyOf(new byte[] {1}, HASH_LENGTH_BYTES);
+    private static final byte[] TEST_HASH_2 = Arrays.copyOf(new byte[] {2}, HASH_LENGTH_BYTES);
+
+    @Test
+    public void testGetHash_returnsHash() {
+        ChunkHash chunkHash = new ChunkHash(TEST_HASH_1);
+
+        byte[] hash = chunkHash.getHash();
+
+        assertThat(hash).asList().containsExactlyElementsIn(Bytes.asList(TEST_HASH_1)).inOrder();
+    }
+
+    @Test
+    public void testEquals() {
+        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
+        ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1);
+        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
+
+        assertThat(chunkHash1).isEqualTo(equalChunkHash1);
+        assertThat(chunkHash1).isNotEqualTo(chunkHash2);
+    }
+
+    @Test
+    public void testHashCode() {
+        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
+        ChunkHash equalChunkHash1 = new ChunkHash(TEST_HASH_1);
+        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
+
+        int hash1 = chunkHash1.hashCode();
+        int equalHash1 = equalChunkHash1.hashCode();
+        int hash2 = chunkHash2.hashCode();
+
+        assertThat(hash1).isEqualTo(equalHash1);
+        assertThat(hash1).isNotEqualTo(hash2);
+    }
+
+    @Test
+    public void testCompareTo_whenEqual_returnsZero() {
+        ChunkHash chunkHash = new ChunkHash(TEST_HASH_1);
+        ChunkHash equalChunkHash = new ChunkHash(TEST_HASH_1);
+
+        int result = chunkHash.compareTo(equalChunkHash);
+
+        assertThat(result).isEqualTo(0);
+    }
+
+    @Test
+    public void testCompareTo_whenArgumentGreater_returnsNegative() {
+        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
+        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
+
+        int result = chunkHash1.compareTo(chunkHash2);
+
+        assertThat(result).isLessThan(0);
+    }
+
+    @Test
+    public void testCompareTo_whenArgumentSmaller_returnsPositive() {
+        ChunkHash chunkHash1 = new ChunkHash(TEST_HASH_1);
+        ChunkHash chunkHash2 = new ChunkHash(TEST_HASH_2);
+
+        int result = chunkHash2.compareTo(chunkHash1);
+
+        assertThat(result).isGreaterThan(0);
+    }
+}