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

Commit b1bd0ab5 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Store a compressed copy of packages.xml.

This is step 1, use JNI call to compress+checksum the backup copy.
Split into 2 steps to detect any issues/benchmark regressions etc.

packages.xml: 461903bytes

GZip compression (204498bytes):
write: 12ms, compress: 21ms
write: 16ms, compress: 70ms
write: 146ms, compress: 48ms
write: 11ms, compress: 46ms
write: 13ms, compress: 54ms
write: 16ms, compress: 57ms
write: 21ms, compress: 74ms
write: 17ms, compress: 94ms
write: 13ms, compress: 77ms
write: 10ms, compress: 56ms
write: 8ms, compress: 48ms

Native LZ4 + checksum (239481bytes), compatible with lz4 cli:
write: 45ms, compress: 8ms
write: 106ms, compress: 13ms
write: 33ms, compress: 10ms
write: 11ms, compress: 12ms
write: 11ms, compress: 40ms
write: 121ms, compress: 13ms
write: 14ms, compress: 20ms
write: 13ms, compress: 22ms
write: 19ms, compress: 10ms
write: 10ms, compress: 10ms
write: 112ms, compress: 28ms

Bug: 253568736
Test: presubmit
Change-Id: I09825df7196edf19c67ce936302643d7ca7f1aa9
parent 6b74e744
Loading
Loading
Loading
Loading
+31 −2
Original line number Diff line number Diff line
@@ -369,6 +369,8 @@ public final class Settings implements Watchable, Snappable {

    // Current settings file.
    private final File mSettingsFilename;
    // Compressed current settings file.
    private final File mCompressedSettingsFilename;
    // Previous settings file.
    // Removed when the current settings file successfully stored.
    private final File mPreviousSettingsFilename;
@@ -639,6 +641,7 @@ public final class Settings implements Watchable, Snappable {
        mRuntimePermissionsPersistence = null;
        mPermissionDataProvider = null;
        mSettingsFilename = null;
        mCompressedSettingsFilename = null;
        mPreviousSettingsFilename = null;
        mPackageListFilename = null;
        mStoppedPackagesFilename = null;
@@ -710,6 +713,7 @@ public final class Settings implements Watchable, Snappable {
                |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                -1, -1);
        mSettingsFilename = new File(mSystemDir, "packages.xml");
        mCompressedSettingsFilename = new File(mSystemDir, "packages.compressed");
        mPreviousSettingsFilename = new File(mSystemDir, "packages-backup.xml");
        mPackageListFilename = new File(mSystemDir, "packages.list");
        FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
@@ -751,6 +755,7 @@ public final class Settings implements Watchable, Snappable {
        mLock = null;
        mRuntimePermissionsPersistence = r.mRuntimePermissionsPersistence;
        mSettingsFilename = null;
        mCompressedSettingsFilename = null;
        mPreviousSettingsFilename = null;
        mPackageListFilename = null;
        mStoppedPackagesFilename = null;
@@ -2588,6 +2593,8 @@ public final class Settings implements Watchable, Snappable {
                Slog.w(PackageManagerService.TAG, "Preserving older settings backup");
            }
        }
        // Compressed settings are not valid anymore.
        mCompressedSettingsFilename.delete();

        mPastSignatures.clear();

@@ -2677,10 +2684,30 @@ public final class Settings implements Watchable, Snappable {
            mPreviousSettingsFilename.delete();

            FileUtils.setPermissions(mSettingsFilename.toString(),
                    FileUtils.S_IRUSR|FileUtils.S_IWUSR
                    |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
                    FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP,
                    -1, -1);

            final FileInputStream fis = new FileInputStream(mSettingsFilename);
            final AtomicFile compressed = new AtomicFile(mCompressedSettingsFilename);
            final FileOutputStream fos = compressed.startWrite();

            BackgroundThread.getHandler().post(() -> {
                try {
                    if (!nativeCompressLz4(fis.getFD().getInt$(), fos.getFD().getInt$())) {
                        throw new IOException("Failed to compress");
                    }
                    compressed.finishWrite(fos);
                    FileUtils.setPermissions(mCompressedSettingsFilename.toString(),
                            FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP
                                    | FileUtils.S_IWGRP, -1, -1);
                } catch (IOException e) {
                    Slog.e(PackageManagerService.TAG, "Failed to write compressed settings file: "
                            + mCompressedSettingsFilename, e);
                    compressed.delete();
                }
                IoUtils.closeQuietly(fis);
            });

            writeKernelMappingLPr();
            writePackageListLPr();
            writeAllUsersPackageRestrictionsLPr(sync);
@@ -2703,6 +2730,8 @@ public final class Settings implements Watchable, Snappable {
        //Debug.stopMethodTracing();
    }

    private native boolean nativeCompressLz4(int inputFd, int outputFd);

    private void writeKernelRemoveUserLPr(int userId) {
        if (mKernelMappingFilename == null) return;

+25 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ cc_library_static {
        "com_android_server_PersistentDataBlockService.cpp",
        "com_android_server_am_LowMemDetector.cpp",
        "com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
        "com_android_server_pm_Settings.cpp",
        "com_android_server_sensor_SensorService.cpp",
        "com_android_server_wm_TaskFpsCallbackController.cpp",
        "onload.cpp",
@@ -152,6 +153,7 @@ cc_defaults {
        "libpsi",
        "libdataloader",
        "libincfs",
        "liblz4",
        "android.hardware.audio.common@2.0",
        "android.media.audio.common.types-V1-ndk",
        "android.hardware.broadcastradio@1.0",
@@ -232,3 +234,26 @@ filegroup {
        "com_android_server_app_GameManagerService.cpp",
    ],
}

// Settings JNI library for unit tests.
cc_library_shared {
    name: "libservices.core.settings.testonly",
    defaults: ["libservices.core-libs"],

    cpp_std: "c++2a",
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-unused-parameter",
        "-Wthread-safety",
    ],

    srcs: [
        "com_android_server_pm_Settings.cpp",
        "onload_settings.cpp",
    ],

    header_libs: [
        "bionic_libc_platform_headers",
    ],
}
+161 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

#define ATRACE_TAG ATRACE_TAG_ADB
#define LOG_TAG "Settings-jni"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/no_destructor.h>
#include <core_jni_helpers.h>
#include <lz4frame.h>
#include <nativehelper/JNIHelp.h>

#include <vector>

namespace android {

namespace {

struct LZ4FCContextDeleter {
    void operator()(LZ4F_cctx* cctx) { LZ4F_freeCompressionContext(cctx); }
};

static constexpr int LZ4_BUFFER_SIZE = 64 * 1024;

static bool writeToFile(std::vector<char>& outBuffer, int fdOut) {
    if (!android::base::WriteFully(fdOut, outBuffer.data(), outBuffer.size())) {
        PLOG(ERROR) << "Error to write to output file";
        return false;
    }
    outBuffer.clear();
    return true;
}

static bool compressAndWriteLz4(LZ4F_cctx* context, std::vector<char>& inBuffer,
                                std::vector<char>& outBuffer, int fdOut) {
    auto inSize = inBuffer.size();
    if (inSize > 0) {
        auto prvSize = outBuffer.size();
        auto outSize = LZ4F_compressBound(inSize, nullptr);
        outBuffer.resize(prvSize + outSize);
        auto rc = LZ4F_compressUpdate(context, outBuffer.data() + prvSize, outSize, inBuffer.data(),
                                      inSize, nullptr);
        if (LZ4F_isError(rc)) {
            LOG(ERROR) << "LZ4F_compressUpdate failed: " << LZ4F_getErrorName(rc);
            return false;
        }
        outBuffer.resize(prvSize + rc);
    }

    if (outBuffer.size() > LZ4_BUFFER_SIZE) {
        return writeToFile(outBuffer, fdOut);
    }

    return true;
}

static jboolean nativeCompressLz4(JNIEnv* env, jclass klass, jint fdIn, jint fdOut) {
    LZ4F_cctx* cctx;
    if (LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) != 0) {
        LOG(ERROR) << "Failed to initialize LZ4 compression context.";
        return false;
    }
    std::unique_ptr<LZ4F_cctx, LZ4FCContextDeleter> context(cctx);

    std::vector<char> inBuffer, outBuffer;
    inBuffer.reserve(LZ4_BUFFER_SIZE);
    outBuffer.reserve(2 * LZ4_BUFFER_SIZE);

    LZ4F_preferences_t prefs;

    memset(&prefs, 0, sizeof(prefs));

    // Set compression parameters.
    prefs.autoFlush = 0;
    prefs.compressionLevel = 0;
    prefs.frameInfo.blockMode = LZ4F_blockLinked;
    prefs.frameInfo.blockSizeID = LZ4F_default;
    prefs.frameInfo.blockChecksumFlag = LZ4F_noBlockChecksum;
    prefs.frameInfo.contentChecksumFlag = LZ4F_contentChecksumEnabled;
    prefs.favorDecSpeed = 0;

    struct stat sb;
    if (fstat(fdIn, &sb) == -1) {
        PLOG(ERROR) << "Failed to obtain input file size.";
        return false;
    }
    prefs.frameInfo.contentSize = sb.st_size;

    // Write header first.
    outBuffer.resize(LZ4F_HEADER_SIZE_MAX);
    auto rc = LZ4F_compressBegin(context.get(), outBuffer.data(), outBuffer.size(), &prefs);
    if (LZ4F_isError(rc)) {
        LOG(ERROR) << "LZ4F_compressBegin failed: " << LZ4F_getErrorName(rc);
        return false;
    }
    outBuffer.resize(rc);

    bool eof = false;
    while (!eof) {
        constexpr auto capacity = LZ4_BUFFER_SIZE;
        inBuffer.resize(capacity);
        auto read = TEMP_FAILURE_RETRY(::read(fdIn, inBuffer.data(), inBuffer.size()));
        if (read < 0) {
            PLOG(ERROR) << "Failed to read from input file.";
            return false;
        }

        inBuffer.resize(read);

        if (read == 0) {
            eof = true;
        }

        if (!compressAndWriteLz4(context.get(), inBuffer, outBuffer, fdOut)) {
            return false;
        }
    }

    // Footer.
    auto prvSize = outBuffer.size();
    outBuffer.resize(outBuffer.capacity());
    rc = LZ4F_compressEnd(context.get(), outBuffer.data() + prvSize, outBuffer.size() - prvSize,
                          nullptr);
    if (LZ4F_isError(rc)) {
        LOG(ERROR) << "LZ4F_compressEnd failed: " << LZ4F_getErrorName(rc);
        return false;
    }
    outBuffer.resize(prvSize + rc);

    if (!writeToFile(outBuffer, fdOut)) {
        return false;
    }

    return true;
}

static const JNINativeMethod method_table[] = {
        {"nativeCompressLz4", "(II)Z", (void*)nativeCompressLz4},
};

} // namespace

int register_android_server_com_android_server_pm_Settings(JNIEnv* env) {
    return jniRegisterNativeMethods(env, "com/android/server/pm/Settings", method_table,
                                    NELEM(method_table));
}

} // namespace android
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(JNIEnv* env);
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
int register_android_server_com_android_server_pm_Settings(JNIEnv* env);
int register_android_server_AdbDebuggingManager(JNIEnv* env);
int register_android_server_FaceService(JNIEnv* env);
int register_android_server_GpuService(JNIEnv* env);
@@ -114,6 +115,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
    register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
    register_com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker(env);
    register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
    register_android_server_com_android_server_pm_Settings(env);
    register_android_server_AdbDebuggingManager(env);
    register_android_server_FaceService(env);
    register_android_server_GpuService(env);
+39 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

#include "jni.h"
#include "utils/Log.h"

namespace android {
int register_android_server_com_android_server_pm_Settings(JNIEnv* env);
};

using namespace android;

extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");

    register_android_server_com_android_server_pm_Settings(env);

    return JNI_VERSION_1_4;
}
Loading