Loading services/core/java/com/android/server/appop/HistoricalRegistry.java +161 −51 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ import android.app.AppOpsManager.UidState; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Environment; import android.os.Message; import android.os.Process; Loading @@ -38,6 +40,7 @@ import android.util.ArraySet; import android.util.Slog; import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.AtomicDirectory; import com.android.internal.os.BackgroundThread; Loading @@ -45,6 +48,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; Loading @@ -59,10 +63,12 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; /** Loading Loading @@ -105,6 +111,7 @@ import java.util.concurrent.TimeUnit; // TODO (bug:122218838): Validate changed time is handled correctly final class HistoricalRegistry { private static final boolean DEBUG = false; private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE; private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName(); Loading @@ -123,7 +130,7 @@ final class HistoricalRegistry { private static final int MSG_WRITE_PENDING_HISTORY = 1; // See mMode private static final int DEFAULT_MODE = AppOpsManager.HISTORICAL_MODE_DISABLED; private static final int DEFAULT_MODE = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE; // See mBaseSnapshotInterval private static final long DEFAULT_SNAPSHOT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(15); Loading @@ -131,11 +138,13 @@ final class HistoricalRegistry { // See mIntervalCompressionMultiplier private static final long DEFAULT_COMPRESSION_STEP = 10; private static final String HISTORY_FILE_SUFFIX = ".xml"; /** * Whether history is enabled. */ @GuardedBy("mInMemoryLock") private int mMode = AppOpsManager.HISTORICAL_MODE_DISABLED; private int mMode = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE; /** * This granularity has been chosen to allow clean delineation for intervals Loading Loading @@ -172,10 +181,16 @@ final class HistoricalRegistry { HistoricalRegistry(@NonNull Object lock) { mInMemoryLock = lock; if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { // When starting always adjust history to now. mPendingHistoryOffsetMillis = System.currentTimeMillis() - mPersistence.getLastPersistTimeMillisDLocked(); final long lastPersistTimeMills = mPersistence.getLastPersistTimeMillisDLocked(); if (lastPersistTimeMills > 0) { mPendingHistoryOffsetMillis = System.currentTimeMillis() - lastPersistTimeMills; } } } } } Loading Loading @@ -522,6 +537,7 @@ final class HistoricalRegistry { persistPendingHistory(pendingWrites); } } private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) { synchronized (mOnDiskLock) { BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY); Loading Loading @@ -574,8 +590,6 @@ final class HistoricalRegistry { private static final String LOG_TAG = Persistence.class.getSimpleName(); private static final String HISTORY_FILE_SUFFIX = ".xml"; private static final String TAG_HISTORY = "history"; private static final String TAG_OPS = "ops"; private static final String TAG_UID = "uid"; Loading Loading @@ -603,7 +617,7 @@ final class HistoricalRegistry { } private final AtomicDirectory mHistoricalAppOpsDir = new AtomicDirectory( new File(new File(Environment.getDataSystemDeDirectory(), "appops"), "history")); new File(new File(Environment.getDataSystemDirectory(), "appops"), "history")); private File generateFile(@NonNull File baseDir, int depth) { final long globalBeginMillis = computeGlobalIntervalBeginMillis(depth); Loading @@ -622,10 +636,20 @@ final class HistoricalRegistry { try { final File newBaseDir = mHistoricalAppOpsDir.startWrite(); final File oldBaseDir = mHistoricalAppOpsDir.getBackupDirectory(); handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, ops, 0); final HistoricalFilesInvariant filesInvariant; if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant.startTracking(oldBaseDir); } final Set<String> oldFileNames = getHistoricalFileNames(oldBaseDir); handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, ops, oldFileNames, 0); if (DEBUG) { filesInvariant.stopTracking(newBaseDir); } mHistoricalAppOpsDir.finishWrite(); } catch (Throwable t) { Slog.wtf(LOG_TAG, "Failed to write historical app ops, restoring backup", t); wtf("Failed to write historical app ops, restoring backup", t, null); mHistoricalAppOpsDir.failWrite(); } } Loading @@ -649,15 +673,23 @@ final class HistoricalRegistry { } long getLastPersistTimeMillisDLocked() { File baseDir = null; try { final File baseDir = mHistoricalAppOpsDir.startRead(); final File file = generateFile(baseDir, 0); if (file.exists()) { baseDir = mHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); if (files != null && files.length > 0) { final Set<File> historyFiles = new ArraySet<>(); Collections.addAll(historyFiles, files); for (int i = 0;; i++) { final File file = generateFile(baseDir, i); if (historyFiles.contains(file)) { return file.lastModified(); } } } mHistoricalAppOpsDir.finishRead(); } catch (IOException e) { Slog.wtf("Error reading historical app ops. Deleting history.", e); } catch (Throwable e) { wtf("Error reading historical app ops. Deleting history.", e, baseDir); mHistoricalAppOpsDir.delete(); } return 0; Loading @@ -680,27 +712,27 @@ final class HistoricalRegistry { private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked( int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames, long filterBeginTimeMillis, long filterEndTimeMillis) { File baseDir = null; try { final File baseDir = mHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); if (files == null) { return null; } final ArraySet<File> historyFiles = new ArraySet<>(files.length); for (File file : files) { if (file.isFile() && file.getName().endsWith(HISTORY_FILE_SUFFIX)) { historyFiles.add(file); } baseDir = mHistoricalAppOpsDir.startRead(); final HistoricalFilesInvariant filesInvariant; if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant.startTracking(baseDir); } final Set<String> historyFiles = getHistoricalFileNames(baseDir); final long[] globalContentOffsetMillis = {0}; final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked( baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis, globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles); if (DEBUG) { filesInvariant.stopTracking(baseDir); } mHistoricalAppOpsDir.finishRead(); return ops; } catch (IOException | XmlPullParserException e) { Slog.wtf("Error reading historical app ops. Deleting history.", e); } catch (Throwable t) { wtf("Error reading historical app ops. Deleting history.", t, baseDir); mHistoricalAppOpsDir.delete(); } return null; Loading @@ -711,7 +743,7 @@ final class HistoricalRegistry { @Nullable String[] filterOpNames, long filterBeginTimeMillis, long filterEndTimeMillis, @NonNull long[] globalContentOffsetMillis, @Nullable LinkedList<HistoricalOps> outOps, int depth, @NonNull ArraySet<File> historyFiles) @NonNull Set<String> historyFiles) throws IOException, XmlPullParserException { final long previousIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier, depth) * mBaseSnapshotInterval; Loading Loading @@ -761,17 +793,9 @@ final class HistoricalRegistry { return outOps; } private boolean createHardLinkToExistingFile(@NonNull File fromFile, @NonNull File toFile) throws IOException { if (!fromFile.exists()) { return false; } Files.createLink(toFile.toPath(), fromFile.toPath()); return true; } private void handlePersistHistoricalOpsRecursiveDLocked(@NonNull File newBaseDir, @NonNull File oldBaseDir, @Nullable List<HistoricalOps> passedOps, int depth) @NonNull File oldBaseDir, @Nullable List<HistoricalOps> passedOps, @NonNull Set<String> oldFileNames, int depth) throws IOException, XmlPullParserException { final long previousIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier, depth) * mBaseSnapshotInterval; Loading @@ -779,12 +803,15 @@ final class HistoricalRegistry { depth + 1) * mBaseSnapshotInterval; if (passedOps == null || passedOps.isEmpty()) { if (!oldFileNames.isEmpty()) { // If there is an old file we need to copy it over to the new state. final File oldFile = generateFile(oldBaseDir, depth); if (oldFileNames.remove(oldFile.getName())) { final File newFile = generateFile(newBaseDir, depth); if (createHardLinkToExistingFile(oldFile, newFile)) { Files.createLink(newFile.toPath(), oldFile.toPath()); } handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, passedOps, depth + 1); passedOps, oldFileNames, depth + 1); } return; } Loading Loading @@ -900,9 +927,11 @@ final class HistoricalRegistry { enforceOpsWellFormed(overflowedOps); } final File newFile = generateFile(newBaseDir, depth); oldFileNames.remove(newFile.getName()); if (persistedOps != null) { normalizeSnapshotForSlotDuration(persistedOps, slotDurationMillis); final File newFile = generateFile(newBaseDir, depth); writeHistoricalOpsDLocked(persistedOps, intervalOverflowMillis, newFile); if (DEBUG) { Slog.i(LOG_TAG, "Persisted at depth: " + depth Loading @@ -912,7 +941,7 @@ final class HistoricalRegistry { } handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, overflowedOps, depth + 1); overflowedOps, oldFileNames, depth + 1); } private @NonNull List<HistoricalOps> readHistoricalOpsLocked(File baseDir, Loading @@ -920,11 +949,11 @@ final class HistoricalRegistry { @Nullable String filterPackageName, @Nullable String[] filterOpNames, long filterBeginTimeMillis, long filterEndTimeMillis, @Nullable long[] cumulativeOverflowMillis, int depth, @NonNull ArraySet<File> historyFiles) @NonNull Set<String> historyFiles) throws IOException, XmlPullParserException { final File file = generateFile(baseDir, depth); if (historyFiles != null) { historyFiles.remove(file); historyFiles.remove(file.getName()); } if (filterBeginTimeMillis >= filterEndTimeMillis || filterEndTimeMillis < intervalBeginMillis) { Loading Loading @@ -1364,6 +1393,61 @@ final class HistoricalRegistry { } return builder.toString(); } private static Set<String> getHistoricalFileNames(@NonNull File historyDir) { final File[] files = historyDir.listFiles(); if (files == null) { return Collections.emptySet(); } final ArraySet<String> fileNames = new ArraySet<>(files.length); for (File file : files) { fileNames.add(file.getName()); } return fileNames; } } private static class HistoricalFilesInvariant { private final @NonNull List<File> mBeginFiles = new ArrayList<>(); public void startTracking(@NonNull File folder) { final File[] files = folder.listFiles(); if (files != null) { Collections.addAll(mBeginFiles, files); } } public void stopTracking(@NonNull File folder) { final List<File> endFiles = new ArrayList<>(); final File[] files = folder.listFiles(); if (files != null) { Collections.addAll(endFiles, files); } final long beginOldestFileOffsetMillis = getOldestFileOffsetMillis(mBeginFiles); final long endOldestFileOffsetMillis = getOldestFileOffsetMillis(endFiles); if (endOldestFileOffsetMillis < beginOldestFileOffsetMillis) { final String message = "History loss detected!" + "\nold files: " + mBeginFiles; wtf(message, null, folder); throw new IllegalStateException(message); } } private static long getOldestFileOffsetMillis(@NonNull List<File> files) { if (files.isEmpty()) { return 0; } String longestName = files.get(0).getName(); final int fileCount = files.size(); for (int i = 1; i < fileCount; i++) { final File file = files.get(i); if (file.getName().length() > longestName.length()) { longestName = file.getName(); } } return Long.parseLong(longestName.replace(HISTORY_FILE_SUFFIX, "")); } } private final class StringDumpVisitor implements AppOpsManager.HistoricalOpsVisitor { Loading Loading @@ -1493,4 +1577,30 @@ final class HistoricalRegistry { } } } private static void wtf(@Nullable String message, @Nullable Throwable t, @Nullable File storage) { Slog.wtf(LOG_TAG, message, t); if (KEEP_WTF_LOG) { try { final File file = new File(new File(Environment.getDataSystemDirectory(), "appops"), "wtf" + TimeUtils.formatForLogging(System.currentTimeMillis())); if (file.createNewFile()) { try (PrintWriter writer = new PrintWriter(file)) { if (t != null) { writer.append('\n').append(t.toString()); } writer.append('\n').append(Debug.getCallers(10)); if (storage != null) { writer.append("\nfiles: " + Arrays.toString(storage.listFiles())); } else { writer.append("\nfiles: none"); } } } } catch (IOException e) { /* ignore */ } } } } Loading
services/core/java/com/android/server/appop/HistoricalRegistry.java +161 −51 Original line number Diff line number Diff line Loading @@ -27,7 +27,9 @@ import android.app.AppOpsManager.UidState; import android.content.ContentResolver; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Debug; import android.os.Environment; import android.os.Message; import android.os.Process; Loading @@ -38,6 +40,7 @@ import android.util.ArraySet; import android.util.Slog; import android.util.TimeUtils; import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.AtomicDirectory; import com.android.internal.os.BackgroundThread; Loading @@ -45,6 +48,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; Loading @@ -59,10 +63,12 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; /** Loading Loading @@ -105,6 +111,7 @@ import java.util.concurrent.TimeUnit; // TODO (bug:122218838): Validate changed time is handled correctly final class HistoricalRegistry { private static final boolean DEBUG = false; private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE; private static final String LOG_TAG = HistoricalRegistry.class.getSimpleName(); Loading @@ -123,7 +130,7 @@ final class HistoricalRegistry { private static final int MSG_WRITE_PENDING_HISTORY = 1; // See mMode private static final int DEFAULT_MODE = AppOpsManager.HISTORICAL_MODE_DISABLED; private static final int DEFAULT_MODE = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE; // See mBaseSnapshotInterval private static final long DEFAULT_SNAPSHOT_INTERVAL_MILLIS = TimeUnit.MINUTES.toMillis(15); Loading @@ -131,11 +138,13 @@ final class HistoricalRegistry { // See mIntervalCompressionMultiplier private static final long DEFAULT_COMPRESSION_STEP = 10; private static final String HISTORY_FILE_SUFFIX = ".xml"; /** * Whether history is enabled. */ @GuardedBy("mInMemoryLock") private int mMode = AppOpsManager.HISTORICAL_MODE_DISABLED; private int mMode = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE; /** * This granularity has been chosen to allow clean delineation for intervals Loading Loading @@ -172,10 +181,16 @@ final class HistoricalRegistry { HistoricalRegistry(@NonNull Object lock) { mInMemoryLock = lock; if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { // When starting always adjust history to now. mPendingHistoryOffsetMillis = System.currentTimeMillis() - mPersistence.getLastPersistTimeMillisDLocked(); final long lastPersistTimeMills = mPersistence.getLastPersistTimeMillisDLocked(); if (lastPersistTimeMills > 0) { mPendingHistoryOffsetMillis = System.currentTimeMillis() - lastPersistTimeMills; } } } } } Loading Loading @@ -522,6 +537,7 @@ final class HistoricalRegistry { persistPendingHistory(pendingWrites); } } private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) { synchronized (mOnDiskLock) { BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY); Loading Loading @@ -574,8 +590,6 @@ final class HistoricalRegistry { private static final String LOG_TAG = Persistence.class.getSimpleName(); private static final String HISTORY_FILE_SUFFIX = ".xml"; private static final String TAG_HISTORY = "history"; private static final String TAG_OPS = "ops"; private static final String TAG_UID = "uid"; Loading Loading @@ -603,7 +617,7 @@ final class HistoricalRegistry { } private final AtomicDirectory mHistoricalAppOpsDir = new AtomicDirectory( new File(new File(Environment.getDataSystemDeDirectory(), "appops"), "history")); new File(new File(Environment.getDataSystemDirectory(), "appops"), "history")); private File generateFile(@NonNull File baseDir, int depth) { final long globalBeginMillis = computeGlobalIntervalBeginMillis(depth); Loading @@ -622,10 +636,20 @@ final class HistoricalRegistry { try { final File newBaseDir = mHistoricalAppOpsDir.startWrite(); final File oldBaseDir = mHistoricalAppOpsDir.getBackupDirectory(); handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, ops, 0); final HistoricalFilesInvariant filesInvariant; if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant.startTracking(oldBaseDir); } final Set<String> oldFileNames = getHistoricalFileNames(oldBaseDir); handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, ops, oldFileNames, 0); if (DEBUG) { filesInvariant.stopTracking(newBaseDir); } mHistoricalAppOpsDir.finishWrite(); } catch (Throwable t) { Slog.wtf(LOG_TAG, "Failed to write historical app ops, restoring backup", t); wtf("Failed to write historical app ops, restoring backup", t, null); mHistoricalAppOpsDir.failWrite(); } } Loading @@ -649,15 +673,23 @@ final class HistoricalRegistry { } long getLastPersistTimeMillisDLocked() { File baseDir = null; try { final File baseDir = mHistoricalAppOpsDir.startRead(); final File file = generateFile(baseDir, 0); if (file.exists()) { baseDir = mHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); if (files != null && files.length > 0) { final Set<File> historyFiles = new ArraySet<>(); Collections.addAll(historyFiles, files); for (int i = 0;; i++) { final File file = generateFile(baseDir, i); if (historyFiles.contains(file)) { return file.lastModified(); } } } mHistoricalAppOpsDir.finishRead(); } catch (IOException e) { Slog.wtf("Error reading historical app ops. Deleting history.", e); } catch (Throwable e) { wtf("Error reading historical app ops. Deleting history.", e, baseDir); mHistoricalAppOpsDir.delete(); } return 0; Loading @@ -680,27 +712,27 @@ final class HistoricalRegistry { private @Nullable LinkedList<HistoricalOps> collectHistoricalOpsBaseDLocked( int filterUid, @NonNull String filterPackageName, @Nullable String[] filterOpNames, long filterBeginTimeMillis, long filterEndTimeMillis) { File baseDir = null; try { final File baseDir = mHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); if (files == null) { return null; } final ArraySet<File> historyFiles = new ArraySet<>(files.length); for (File file : files) { if (file.isFile() && file.getName().endsWith(HISTORY_FILE_SUFFIX)) { historyFiles.add(file); } baseDir = mHistoricalAppOpsDir.startRead(); final HistoricalFilesInvariant filesInvariant; if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant.startTracking(baseDir); } final Set<String> historyFiles = getHistoricalFileNames(baseDir); final long[] globalContentOffsetMillis = {0}; final LinkedList<HistoricalOps> ops = collectHistoricalOpsRecursiveDLocked( baseDir, filterUid, filterPackageName, filterOpNames, filterBeginTimeMillis, filterEndTimeMillis, globalContentOffsetMillis, null /*outOps*/, 0 /*depth*/, historyFiles); if (DEBUG) { filesInvariant.stopTracking(baseDir); } mHistoricalAppOpsDir.finishRead(); return ops; } catch (IOException | XmlPullParserException e) { Slog.wtf("Error reading historical app ops. Deleting history.", e); } catch (Throwable t) { wtf("Error reading historical app ops. Deleting history.", t, baseDir); mHistoricalAppOpsDir.delete(); } return null; Loading @@ -711,7 +743,7 @@ final class HistoricalRegistry { @Nullable String[] filterOpNames, long filterBeginTimeMillis, long filterEndTimeMillis, @NonNull long[] globalContentOffsetMillis, @Nullable LinkedList<HistoricalOps> outOps, int depth, @NonNull ArraySet<File> historyFiles) @NonNull Set<String> historyFiles) throws IOException, XmlPullParserException { final long previousIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier, depth) * mBaseSnapshotInterval; Loading Loading @@ -761,17 +793,9 @@ final class HistoricalRegistry { return outOps; } private boolean createHardLinkToExistingFile(@NonNull File fromFile, @NonNull File toFile) throws IOException { if (!fromFile.exists()) { return false; } Files.createLink(toFile.toPath(), fromFile.toPath()); return true; } private void handlePersistHistoricalOpsRecursiveDLocked(@NonNull File newBaseDir, @NonNull File oldBaseDir, @Nullable List<HistoricalOps> passedOps, int depth) @NonNull File oldBaseDir, @Nullable List<HistoricalOps> passedOps, @NonNull Set<String> oldFileNames, int depth) throws IOException, XmlPullParserException { final long previousIntervalEndMillis = (long) Math.pow(mIntervalCompressionMultiplier, depth) * mBaseSnapshotInterval; Loading @@ -779,12 +803,15 @@ final class HistoricalRegistry { depth + 1) * mBaseSnapshotInterval; if (passedOps == null || passedOps.isEmpty()) { if (!oldFileNames.isEmpty()) { // If there is an old file we need to copy it over to the new state. final File oldFile = generateFile(oldBaseDir, depth); if (oldFileNames.remove(oldFile.getName())) { final File newFile = generateFile(newBaseDir, depth); if (createHardLinkToExistingFile(oldFile, newFile)) { Files.createLink(newFile.toPath(), oldFile.toPath()); } handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, passedOps, depth + 1); passedOps, oldFileNames, depth + 1); } return; } Loading Loading @@ -900,9 +927,11 @@ final class HistoricalRegistry { enforceOpsWellFormed(overflowedOps); } final File newFile = generateFile(newBaseDir, depth); oldFileNames.remove(newFile.getName()); if (persistedOps != null) { normalizeSnapshotForSlotDuration(persistedOps, slotDurationMillis); final File newFile = generateFile(newBaseDir, depth); writeHistoricalOpsDLocked(persistedOps, intervalOverflowMillis, newFile); if (DEBUG) { Slog.i(LOG_TAG, "Persisted at depth: " + depth Loading @@ -912,7 +941,7 @@ final class HistoricalRegistry { } handlePersistHistoricalOpsRecursiveDLocked(newBaseDir, oldBaseDir, overflowedOps, depth + 1); overflowedOps, oldFileNames, depth + 1); } private @NonNull List<HistoricalOps> readHistoricalOpsLocked(File baseDir, Loading @@ -920,11 +949,11 @@ final class HistoricalRegistry { @Nullable String filterPackageName, @Nullable String[] filterOpNames, long filterBeginTimeMillis, long filterEndTimeMillis, @Nullable long[] cumulativeOverflowMillis, int depth, @NonNull ArraySet<File> historyFiles) @NonNull Set<String> historyFiles) throws IOException, XmlPullParserException { final File file = generateFile(baseDir, depth); if (historyFiles != null) { historyFiles.remove(file); historyFiles.remove(file.getName()); } if (filterBeginTimeMillis >= filterEndTimeMillis || filterEndTimeMillis < intervalBeginMillis) { Loading Loading @@ -1364,6 +1393,61 @@ final class HistoricalRegistry { } return builder.toString(); } private static Set<String> getHistoricalFileNames(@NonNull File historyDir) { final File[] files = historyDir.listFiles(); if (files == null) { return Collections.emptySet(); } final ArraySet<String> fileNames = new ArraySet<>(files.length); for (File file : files) { fileNames.add(file.getName()); } return fileNames; } } private static class HistoricalFilesInvariant { private final @NonNull List<File> mBeginFiles = new ArrayList<>(); public void startTracking(@NonNull File folder) { final File[] files = folder.listFiles(); if (files != null) { Collections.addAll(mBeginFiles, files); } } public void stopTracking(@NonNull File folder) { final List<File> endFiles = new ArrayList<>(); final File[] files = folder.listFiles(); if (files != null) { Collections.addAll(endFiles, files); } final long beginOldestFileOffsetMillis = getOldestFileOffsetMillis(mBeginFiles); final long endOldestFileOffsetMillis = getOldestFileOffsetMillis(endFiles); if (endOldestFileOffsetMillis < beginOldestFileOffsetMillis) { final String message = "History loss detected!" + "\nold files: " + mBeginFiles; wtf(message, null, folder); throw new IllegalStateException(message); } } private static long getOldestFileOffsetMillis(@NonNull List<File> files) { if (files.isEmpty()) { return 0; } String longestName = files.get(0).getName(); final int fileCount = files.size(); for (int i = 1; i < fileCount; i++) { final File file = files.get(i); if (file.getName().length() > longestName.length()) { longestName = file.getName(); } } return Long.parseLong(longestName.replace(HISTORY_FILE_SUFFIX, "")); } } private final class StringDumpVisitor implements AppOpsManager.HistoricalOpsVisitor { Loading Loading @@ -1493,4 +1577,30 @@ final class HistoricalRegistry { } } } private static void wtf(@Nullable String message, @Nullable Throwable t, @Nullable File storage) { Slog.wtf(LOG_TAG, message, t); if (KEEP_WTF_LOG) { try { final File file = new File(new File(Environment.getDataSystemDirectory(), "appops"), "wtf" + TimeUtils.formatForLogging(System.currentTimeMillis())); if (file.createNewFile()) { try (PrintWriter writer = new PrintWriter(file)) { if (t != null) { writer.append('\n').append(t.toString()); } writer.append('\n').append(Debug.getCallers(10)); if (storage != null) { writer.append("\nfiles: " + Arrays.toString(storage.listFiles())); } else { writer.append("\nfiles: none"); } } } } catch (IOException e) { /* ignore */ } } } }