diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java index ff04825f788f4..c5ab82dbf0c51 100644 --- a/core/java/android/util/PackageUtils.java +++ b/core/java/android/util/PackageUtils.java @@ -18,13 +18,17 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.content.pm.Signature; import android.text.TextUtils; import libcore.util.HexEncoding; import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; @@ -35,6 +39,9 @@ import java.util.Arrays; */ public final class PackageUtils { + private static final int LOW_RAM_BUFFER_SIZE_BYTES = 1 * 1000; // 1 kB + private static final int HIGH_RAM_BUFFER_SIZE_BYTES = 1 * 1000 * 1000; // 1 MB + private PackageUtils() { /* hide constructor */ } @@ -162,4 +169,55 @@ public final class PackageUtils { return TextUtils.join(separator, pieces); } + + /** + * @see #computeSha256DigestForLargeFile(String, String) + */ + public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath) { + return computeSha256DigestForLargeFile(filePath, null); + } + + /** + * Computes the SHA256 digest of large files. + * @param filePath The path to which the file's content is to be hashed. + * @param separator Separator between each pair of characters, such as colon, or null to omit. + * @return The digest or null if an error occurs. + */ + public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath, + @Nullable String separator) { + MessageDigest messageDigest; + try { + messageDigest = MessageDigest.getInstance("SHA256"); + messageDigest.reset(); + } catch (NoSuchAlgorithmException e) { + // this shouldn't happen! + return null; + } + + boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic(); + int bufferSize = isLowRamDevice ? LOW_RAM_BUFFER_SIZE_BYTES : HIGH_RAM_BUFFER_SIZE_BYTES; + + File f = new File(filePath); + try { + DigestInputStream digestStream = new DigestInputStream(new FileInputStream(f), + messageDigest); + byte[] buffer = new byte[bufferSize]; + while (digestStream.read(buffer) != -1); + } catch (IOException e) { + return null; + } + + byte[] resultBytes = messageDigest.digest(); + + if (separator == null) { + return HexEncoding.encodeToString(resultBytes, true); + } + + int length = resultBytes.length; + String[] pieces = new String[length]; + for (int index = 0; index < length; index++) { + pieces[index] = HexEncoding.encodeToString(resultBytes[index], true); + } + return TextUtils.join(separator, pieces); + } } diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java index d218af39405dc..6986d3bbe5852 100644 --- a/services/core/java/com/android/server/BinaryTransparencyService.java +++ b/services/core/java/com/android/server/BinaryTransparencyService.java @@ -36,11 +36,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IBinaryTransparencyService; import com.android.internal.util.FrameworkStatsLog; -import java.io.File; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; -import java.nio.file.Files; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -434,7 +431,7 @@ public class BinaryTransparencyService extends SystemService { entry.setValue(packageInfo.lastUpdateTime); // compute the digest for the updated package - String sha256digest = computeSha256DigestOfFile( + String sha256digest = PackageUtils.computeSha256DigestForLargeFile( packageInfo.applicationInfo.sourceDir); if (sha256digest == null) { Slog.e(TAG, "Failed to compute SHA256sum for file at " @@ -471,7 +468,7 @@ public class BinaryTransparencyService extends SystemService { ApplicationInfo appInfo = packageInfo.applicationInfo; // compute SHA256 for these APEXs - String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir); + String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir); if (sha256digest == null) { Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s", packageInfo.packageName)); @@ -506,7 +503,8 @@ public class BinaryTransparencyService extends SystemService { ApplicationInfo appInfo = packageInfo.applicationInfo; // compute SHA256 digest for these modules - String sha256digest = computeSha256DigestOfFile(appInfo.sourceDir); + String sha256digest = PackageUtils.computeSha256DigestForLargeFile( + appInfo.sourceDir); if (sha256digest == null) { Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s", packageName)); @@ -525,16 +523,4 @@ public class BinaryTransparencyService extends SystemService { } } - @Nullable - private String computeSha256DigestOfFile(@NonNull String pathToFile) { - File apexFile = new File(pathToFile); - - try { - byte[] apexFileBytes = Files.readAllBytes(apexFile.toPath()); - return PackageUtils.computeSha256Digest(apexFileBytes); - } catch (IOException e) { - Slog.e(TAG, String.format("I/O error occurs when reading from %s", pathToFile)); - return null; - } - } }