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

Commit a2892ba0 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Android (Google) Code Review
Browse files

Merge "Revert "Add crash detection and recovery."" into sc-dev

parents 569bfb35 4ba65f0a
Loading
Loading
Loading
Loading
+0 −92
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

package com.android.server.graphics.fonts;

import android.annotation.NonNull;
import android.util.Slog;

import java.io.File;
import java.io.IOException;

/**
 * A class to detect font-related native crash.
 *
 * <p>If a fs-verity protected file is accessed through mmap and corrupted file block is detected,
 * SIGBUG signal is generated and the process will crash. To find corrupted files and remove them,
 * we use a marker file to detect crash.
 * <ol>
 *     <li>Create a marker file before reading fs-verity protected font files.
 *     <li>Delete the marker file after reading font files successfully.
 *     <li>If the marker file is found in the next process startup, it means that the process
 *         crashed before. We will delete font files to prevent crash loop.
 * </ol>
 *
 * <p>Example usage:
 * <pre>
 *     FontCrashDetector detector = new FontCrashDetector(new File("/path/to/marker_file"));
 *     if (detector.hasCrashed()) {
 *         // Do cleanup
 *     }
 *     try (FontCrashDetector.MonitoredBlock b = detector.start()) {
 *         // Read files
 *     }
 * </pre>
 *
 * <p>This class DOES NOT detect Java exceptions. If a Java exception is thrown while monitoring
 * crash, the marker file will be deleted. Creating and deleting marker files are not lightweight.
 * Please use this class sparingly with caution.
 */
/* package */ final class FontCrashDetector {

    private static final String TAG = "FontCrashDetector";

    @NonNull
    private final File mMarkerFile;

    /* package */ FontCrashDetector(@NonNull File markerFile) {
        mMarkerFile = markerFile;
    }

    /* package */ boolean hasCrashed() {
        return mMarkerFile.exists();
    }

    /* package */ void clear() {
        if (!mMarkerFile.delete()) {
            Slog.e(TAG, "Could not delete marker file: " + mMarkerFile);
        }
    }

    /** Starts crash monitoring. */
    /* package */ MonitoredBlock start() {
        try {
            mMarkerFile.createNewFile();
        } catch (IOException e) {
            Slog.e(TAG, "Could not create marker file: " + mMarkerFile, e);
        }
        return new MonitoredBlock();
    }

    /** A helper class to monitor crash with try-with-resources syntax. */
    /* package */ class MonitoredBlock implements AutoCloseable {
        /** Ends crash monitoring. */
        @Override
        public void close() {
            clear();
        }
    }
}
+6 −27
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ public final class FontManagerService extends IFontManager.Stub {
    private static final String TAG = "FontManagerService";

    private static final String FONT_FILES_DIR = "/data/fonts/files";
    private static final String CRASH_MARKER_FILE = "/data/fonts/config/crash.txt";

    @Override
    public FontConfig getFontConfig() {
@@ -199,10 +198,6 @@ public final class FontManagerService extends IFontManager.Stub {

    private final Object mUpdatableFontDirLock = new Object();

    @GuardedBy("mUpdatableFontDirLock")
    @NonNull
    private final FontCrashDetector mFontCrashDetector;

    @GuardedBy("mUpdatableFontDirLock")
    @Nullable
    private final UpdatableFontDir mUpdatableFontDir;
@@ -217,7 +212,6 @@ public final class FontManagerService extends IFontManager.Stub {

    private FontManagerService(Context context) {
        mContext = context;
        mFontCrashDetector = new FontCrashDetector(new File(CRASH_MARKER_FILE));
        mUpdatableFontDir = createUpdatableFontDir();
        initialize();
    }
@@ -244,21 +238,10 @@ public final class FontManagerService extends IFontManager.Stub {
                }
                return;
            }
            if (mFontCrashDetector.hasCrashed()) {
                Slog.i(TAG, "Crash detected. Clearing font updates.");
                try {
                    mUpdatableFontDir.clearUpdates();
                } catch (SystemFontException e) {
                    Slog.e(TAG, "Failed to clear updates.", e);
                }
                mFontCrashDetector.clear();
            }
            try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
            mUpdatableFontDir.loadFontFileMap();
            updateSerializedFontMap();
        }
    }
    }

    @NonNull
    public Context getContext() {
@@ -286,12 +269,10 @@ public final class FontManagerService extends IFontManager.Stub {
                        FontManager.RESULT_ERROR_VERSION_MISMATCH,
                        "The base config version is older than current.");
            }
            try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
            mUpdatableFontDir.update(requests);
            updateSerializedFontMap();
        }
    }
    }

    /* package */ void clearUpdates() throws SystemFontException {
        if (mUpdatableFontDir == null) {
@@ -300,12 +281,10 @@ public final class FontManagerService extends IFontManager.Stub {
                    "The font updater is disabled.");
        }
        synchronized (mUpdatableFontDirLock) {
            try (FontCrashDetector.MonitoredBlock ignored = mFontCrashDetector.start()) {
            mUpdatableFontDir.clearUpdates();
            updateSerializedFontMap();
        }
    }
    }

    /* package */ Map<String, File> getFontFileMap() {
        if (mUpdatableFontDir == null) {
+0 −77
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

package com.android.server.graphics.fonts;

import static com.google.common.truth.Truth.assertThat;

import android.content.Context;
import android.os.FileUtils;
import android.platform.test.annotations.Presubmit;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;

@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class FontCrashDetectorTest {

    private File mCacheDir;

    @SuppressWarnings("ResultOfMethodCallIgnored")
    @Before
    public void setUp() {
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        mCacheDir = new File(context.getCacheDir(), "UpdatableFontDirTest");
        FileUtils.deleteContentsAndDir(mCacheDir);
        mCacheDir.mkdirs();
    }

    @Test
    public void detectCrash() throws Exception {
        // Prepare a marker file.
        File file = new File(mCacheDir, "detectCrash");
        assertThat(file.createNewFile()).isTrue();

        FontCrashDetector detector = new FontCrashDetector(file);
        assertThat(detector.hasCrashed()).isTrue();

        detector.clear();
        assertThat(detector.hasCrashed()).isFalse();
        assertThat(file.exists()).isFalse();
    }

    @Test
    public void monitorCrash() {
        File file = new File(mCacheDir, "monitorCrash");
        FontCrashDetector detector = new FontCrashDetector(file);
        assertThat(detector.hasCrashed()).isFalse();

        FontCrashDetector.MonitoredBlock block = detector.start();
        assertThat(file.exists()).isTrue();

        block.close();
        assertThat(file.exists()).isFalse();
    }
}
+0 −4
Original line number Diff line number Diff line
@@ -26,13 +26,9 @@ java_test_host {
    srcs: ["src/**/*.java"],
    libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
    static_libs: [
        "block_device_writer_jar",
        "frameworks-base-hostutils",
    ],
    test_suites: ["general-tests", "vts"],
    target_required: [
        "block_device_writer_module",
    ],
    data: [
        ":NotoColorEmojiTtf",
        ":UpdatableSystemFontTestCertDer",
+0 −1
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@

    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
        <option name="cleanup" value="true" />
        <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
        <option name="push" value="UpdatableSystemFontTestCert.der->/data/local/tmp/UpdatableSystemFontTestCert.der" />
        <option name="push" value="NotoColorEmoji.ttf->/data/local/tmp/NotoColorEmoji.ttf" />
        <option name="push" value="UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig->/data/local/tmp/UpdatableSystemFontTestNotoColorEmoji.ttf.fsv_sig" />
Loading