diff options
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | core/java/android/os/RecoverySystem.java | 73 |
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); + } } /** |