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

Commit 0c0996b5 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Fix MonotonicClock persistence

Bug: 315498731
Test: atest FrameworksCoreTests:MonotonicClockTest
Change-Id: I83cb0c48d304e13dedcbc9851f80015c76b98aa5
parent 4881667c
Loading
Loading
Loading
Loading
+26 −16
Original line number Diff line number Diff line
@@ -17,11 +17,11 @@
package com.android.internal.os;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Xml;

import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;

@@ -49,21 +49,28 @@ public class MonotonicClock {

    private final AtomicFile mFile;
    private final Clock mClock;
    private long mTimeshift;
    private final long mTimeshift;

    public static final long UNDEFINED = -1;

    public MonotonicClock(File file) {
        mFile = new AtomicFile(file);
        mClock = Clock.SYSTEM_CLOCK;
        read();
        this (file, Clock.SYSTEM_CLOCK.elapsedRealtime(), Clock.SYSTEM_CLOCK);
    }

    public MonotonicClock(long monotonicTime, @NonNull Clock clock) {
        this(null, monotonicTime, clock);
    }

    public MonotonicClock(@Nullable File file, long monotonicTime, @NonNull Clock clock) {
        mClock = clock;
        if (file != null) {
            mFile = new AtomicFile(file);
            mTimeshift = read(monotonicTime - mClock.elapsedRealtime());
        } else {
            mFile = null;
            mTimeshift = monotonicTime - mClock.elapsedRealtime();
        }
    }

    /**
     * Returns time in milliseconds, based on SystemClock.elapsedTime, adjusted so that
@@ -81,15 +88,16 @@ public class MonotonicClock {
        return mTimeshift + elapsedRealtimeMs;
    }

    private void read() {
    private long read(long defaultTimeshift) {
        if (!mFile.exists()) {
            return;
            return defaultTimeshift;
        }

        try {
            readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser());
            return readXml(new ByteArrayInputStream(mFile.readFully()), Xml.newBinaryPullParser());
        } catch (IOException e) {
            Log.e(TAG, "Cannot load monotonic clock from " + mFile.getBaseFile(), e);
            return defaultTimeshift;
        }
    }

@@ -102,18 +110,21 @@ public class MonotonicClock {
            return;
        }

        try (FileOutputStream out = mFile.startWrite()) {
        FileOutputStream out = null;
        try  {
            out = mFile.startWrite();
            writeXml(out, Xml.newBinarySerializer());
            mFile.finishWrite(out);
        } catch (IOException e) {
            Log.e(TAG, "Cannot write monotonic clock to " + mFile.getBaseFile(), e);
            mFile.failWrite(out);
        }
    }

    /**
     * Parses an XML file containing the persistent state of the monotonic clock.
     */
    @VisibleForTesting
    public void readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException {
    private long readXml(InputStream inputStream, TypedXmlPullParser parser) throws IOException {
        long savedTimeshift = 0;
        try {
            parser.setInput(inputStream, StandardCharsets.UTF_8.name());
@@ -128,14 +139,13 @@ public class MonotonicClock {
        } catch (XmlPullParserException e) {
            throw new IOException(e);
        }
        mTimeshift = savedTimeshift - mClock.elapsedRealtime();
        return savedTimeshift - mClock.elapsedRealtime();
    }

    /**
     * Creates an XML file containing the persistent state of the monotonic clock.
     */
    @VisibleForTesting
    public void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException {
    private void writeXml(OutputStream out, TypedXmlSerializer serializer) throws IOException {
        serializer.setOutput(out, StandardCharsets.UTF_8.name());
        serializer.startDocument(null, true);
        serializer.startTag(null, XML_TAG_MONOTONIC_TIME);
+38 −10
Original line number Diff line number Diff line
@@ -18,39 +18,67 @@ package com.android.internal.os;

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

import android.util.Xml;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class MonotonicClockTest {
    private final MockClock mClock = new MockClock();
    private File mFile;

    @Before
    public void setup() throws IOException {
        File systemDir = Files.createTempDirectory("MonotonicClockTest").toFile();
        mFile = new File(systemDir, "test_monotonic_clock.xml");
        if (mFile.exists()) {
            assertThat(mFile.delete()).isTrue();
        }
    }

    @Test
    public void persistence() throws IOException {
        MonotonicClock monotonicClock = new MonotonicClock(1000, mClock);
        MonotonicClock monotonicClock = new MonotonicClock(mFile, 1000, mClock);
        mClock.realtime = 234;

        assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        monotonicClock.writeXml(out, Xml.newBinarySerializer());
        monotonicClock.write();

        mClock.realtime = 42;
        MonotonicClock newMonotonicClock = new MonotonicClock(0, mClock);
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        newMonotonicClock.readXml(in, Xml.newBinaryPullParser());
        MonotonicClock newMonotonicClock = new MonotonicClock(mFile, 0, mClock);

        mClock.realtime = 2000;
        assertThat(newMonotonicClock.monotonicTime()).isEqualTo(1234 - 42 + 2000);
    }

    @Test
    public void constructor() {
        MonotonicClock monotonicClock = new MonotonicClock(null, 1000, mClock);
        mClock.realtime = 234;

        assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
    }

    @Test
    public void corruptedFile() throws IOException {
        // Create an invalid binary XML file to cause IOException: "Unexpected magic number"
        try (FileWriter w = new FileWriter(mFile)) {
            w.write("garbage");
        }

        MonotonicClock monotonicClock = new MonotonicClock(mFile, 1000, mClock);
        mClock.realtime = 234;

        assertThat(monotonicClock.monotonicTime()).isEqualTo(1234);
    }
}