Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53abe607 authored by Billy Lau's avatar Billy Lau
Browse files

Reuse a same buffer per session instead of reallocating each time.

Previously, a fixed size buffer was allocated each time a call to
`computeSha256DigestForLargeFile` was made. This causes
undesirable performance issues. This change allocates a buffer
per apex/module-update session instead.

To fix this, that method is amended to take in a buffer passed
in by the caller instead.

To aid in the usage of this API, a new method called
`createLargeFileBuffer` is introduced to help callers allocate
a pre-determined buffer size to be passed into the SHA
computation method based on whether the device is low RAM
device or not.

Subsequent RSS benchmarking shows that this helps to
significantly lower the RSS high watermark (HWM) for
the `system_server` process into just noise.

Bug: 229137737
Test: Manual
Change-Id: I6d2d17353f999bd6cfb937cd5ae6e6925f1353ac
parent 06ac74c5
Loading
Loading
Loading
Loading
+27 −13
Original line number Diff line number Diff line
@@ -171,39 +171,53 @@ public final class PackageUtils {
    }

    /**
     * @see #computeSha256DigestForLargeFile(String, String)
     * Creates a fixed size buffer based on whether the device is low ram or not. This is to be used
     * with the {@link #computeSha256DigestForLargeFile(String, byte[])} and
     * {@link #computeSha256DigestForLargeFile(String, byte[], String)} methods.
     * @return a byte array of size {@link #LOW_RAM_BUFFER_SIZE_BYTES} if the device is a low RAM
     *          device, otherwise a byte array of size {@link #HIGH_RAM_BUFFER_SIZE_BYTES}
     */
    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath) {
        return computeSha256DigestForLargeFile(filePath, null);
    public static @NonNull byte[] createLargeFileBuffer() {
        int bufferSize = ActivityManager.isLowRamDeviceStatic()
                ? LOW_RAM_BUFFER_SIZE_BYTES : HIGH_RAM_BUFFER_SIZE_BYTES;
        return new byte[bufferSize];
    }

    /**
     * Computes the SHA256 digest of large files.
     * @see #computeSha256DigestForLargeFile(String, byte[], String)
     */
    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
            @NonNull byte[] fileBuffer) {
        return computeSha256DigestForLargeFile(filePath, fileBuffer, null);
    }

    /**
     * Computes the SHA256 digest of large files. This is typically useful for large APEXs.
     * @param filePath The path to which the file's content is to be hashed.
     * @param fileBuffer A buffer to read file's content into memory. It is strongly recommended to
     *                   make use of the {@link #createLargeFileBuffer()} method to create this
     *                   buffer.
     * @param separator Separator between each pair of characters, such as colon, or null to omit.
     * @return The digest or null if an error occurs.
     * @return The SHA256 digest or null if an error occurs.
     */
    public static @Nullable String computeSha256DigestForLargeFile(@NonNull String filePath,
            @Nullable String separator) {
            @NonNull byte[] fileBuffer, @Nullable String separator) {
        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("SHA256");
            messageDigest.reset();
        } catch (NoSuchAlgorithmException e) {
            // this shouldn't happen!
            // this really 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),
            DigestInputStream digestInputStream = new DigestInputStream(new FileInputStream(f),
                    messageDigest);
            byte[] buffer = new byte[bufferSize];
            while (digestStream.read(buffer) != -1);
            while (digestInputStream.read(fileBuffer) != -1);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }

+6 −3
Original line number Diff line number Diff line
@@ -500,6 +500,7 @@ public class BinaryTransparencyService extends SystemService {
        // ones appearing out of the blue. Thus, we're going to only go through our cache to check
        // for changes, rather than freshly invoking `getInstalledPackages()` and
        // `getInstalledModules()`
        byte[] largeFileBuffer = PackageUtils.createLargeFileBuffer();
        for (Map.Entry<String, Long> entry : mBinaryLastUpdateTimes.entrySet()) {
            String packageName = entry.getKey();
            try {
@@ -513,7 +514,7 @@ public class BinaryTransparencyService extends SystemService {

                    // compute the digest for the updated package
                    String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
                            packageInfo.applicationInfo.sourceDir);
                            packageInfo.applicationInfo.sourceDir, largeFileBuffer);
                    if (sha256digest == null) {
                        Slog.e(TAG, "Failed to compute SHA256sum for file at "
                                + packageInfo.applicationInfo.sourceDir);
@@ -545,11 +546,13 @@ public class BinaryTransparencyService extends SystemService {
        // In general, we care about all APEXs, *and* all Modules, which may include some APKs.

        // First, we deal with all installed APEXs.
        byte[] largeFileBuffer = PackageUtils.createLargeFileBuffer();
        for (PackageInfo packageInfo : getInstalledApexs()) {
            ApplicationInfo appInfo = packageInfo.applicationInfo;

            // compute SHA256 for these APEXs
            String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir);
            String sha256digest = PackageUtils.computeSha256DigestForLargeFile(appInfo.sourceDir,
                    largeFileBuffer);
            if (sha256digest == null) {
                Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
                        packageInfo.packageName));
@@ -585,7 +588,7 @@ public class BinaryTransparencyService extends SystemService {

                // compute SHA256 digest for these modules
                String sha256digest = PackageUtils.computeSha256DigestForLargeFile(
                        appInfo.sourceDir);
                        appInfo.sourceDir, largeFileBuffer);
                if (sha256digest == null) {
                    Slog.e(TAG, String.format("Failed to compute SHA256 digest for %s",
                            packageName));