summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Tao Bao <tbao@google.com> 2017-04-11 00:28:34 -0700
committer Tao Bao <tbao@google.com> 2017-04-17 13:51:02 -0700
commite01b520dd490935694a50f9f0abd7dc0564d95c5 (patch)
treebcea550c096c9a65bf930a4d44601152d34339a2
parent5b06dbd12bcd28fbc051c88c3ea7f31bcf20d1f8 (diff)
RecoverySystem: Verify the package compatibility.
This CL extends the existing API RecoverySystem.verifyPackage() to verify the compatibility entry in an OTA package. It returns true directly if such an entry doesn't exist. Otherwise it reads and passes the contents to VintfObject to perform the verification. This CL also adds a new system API RecoverySystem.verifyPackageCompatibility() to verify a given compatibility zip file. It extracts all the entries and passes them to VintfObject.verify() to verify the package compatibility. This API is for streaming A/B OTAs only, where we don't download the full package but only needed entries. Bug: 36592877 Bug: 36596980 Test: RecoverySystem.verifyPackage() returns the same result for packages w/o a compatibility entry. Change-Id: I038be672868a91820c045d1da100e8e33b23d442
-rw-r--r--api/system-current.txt1
-rw-r--r--core/java/android/os/RecoverySystem.java73
2 files changed, 74 insertions, 0 deletions
diff --git a/api/system-current.txt b/api/system-current.txt
index 303a607b383c..91d0d7abe85b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -34402,6 +34402,7 @@ package android.os {
method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException;
method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
method public static void verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File) throws java.security.GeneralSecurityException, java.io.IOException;
+ method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
}
public static abstract interface RecoverySystem.ProgressListener {
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 5f66abd58d8b..bb0fdbe4e26a 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -16,6 +16,8 @@
package android.os;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.annotation.SystemApi;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -24,9 +26,12 @@ import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
+import libcore.io.Streams;
+
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
@@ -39,6 +44,7 @@ import java.security.Signature;
import java.security.SignatureException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
@@ -46,6 +52,7 @@ import java.util.List;
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
import com.android.internal.logging.MetricsLogger;
@@ -317,6 +324,72 @@ public class RecoverySystem {
} finally {
raf.close();
}
+
+ // Additionally verify the package compatibility.
+ if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
+ throw new SignatureException("package compatibility verification failed");
+ }
+ }
+
+ /**
+ * Verifies the compatibility entry from an {@link InputStream}.
+ *
+ * @return the verification result.
+ */
+ private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException {
+ ArrayList<String> list = new ArrayList<>();
+ ZipInputStream zis = new ZipInputStream(inputStream);
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ long entrySize = entry.getSize();
+ if (entrySize > Integer.MAX_VALUE || entrySize < 0) {
+ throw new IOException(
+ "invalid entry size (" + entrySize + ") in the compatibility file");
+ }
+ byte[] bytes = new byte[(int) entrySize];
+ Streams.readFully(zis, bytes);
+ list.add(new String(bytes, UTF_8));
+ }
+ if (list.isEmpty()) {
+ throw new IOException("no entries found in the compatibility file");
+ }
+ // TODO(b/36814503): Enable the actual verification when VintfObject APIs are ready.
+ // return (VintfObject.verify(list.toArray(new String[list.size()])) == 0);
+ return true;
+ }
+
+ /**
+ * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is
+ * a zip file (inside the OTA package zip).
+ *
+ * @return {@code true} if the entry doesn't exist or verification passes.
+ */
+ private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile)
+ throws IOException {
+ try (ZipFile zip = new ZipFile(packageFile)) {
+ ZipEntry entry = zip.getEntry("compatibility.zip");
+ if (entry == null) {
+ return true;
+ }
+ InputStream inputStream = zip.getInputStream(entry);
+ return verifyPackageCompatibility(inputStream);
+ }
+ }
+
+ /**
+ * Verifies the package compatibility info against the current system.
+ *
+ * @param compatibilityFile the {@link File} that contains the package compatibility info.
+ * @throws IOException if there were any errors reading the compatibility file.
+ * @return the compatibility verification result.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
+ try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
+ return verifyPackageCompatibility(inputStream);
+ }
}
/**