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

Commit b9f98fac authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge "Fix MonotonicClock persistence" into main

parents f18b4fa9 0c0996b5
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);
    }
}