Loading core/java/com/android/internal/os/MonotonicClock.java +26 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; } } Loading @@ -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()); Loading @@ -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); Loading core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java +38 −10 Original line number Diff line number Diff line Loading @@ -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); } } Loading
core/java/com/android/internal/os/MonotonicClock.java +26 −16 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 Loading @@ -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; } } Loading @@ -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()); Loading @@ -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); Loading
core/tests/coretests/src/com/android/internal/os/MonotonicClockTest.java +38 −10 Original line number Diff line number Diff line Loading @@ -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); } }