diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 69a1c9f584cb4..805cac755fd54 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -108,6 +108,12 @@ import java.util.concurrent.TimeUnit; * must be called with the mInMemoryLock, xxxDMLocked suffix means the method * must be called with the mOnDiskLock and mInMemoryLock locks acquired in that * exact order. + *

+ * INITIALIZATION: We can initialize persistence only after the system is ready + * as we need to check the optional configuration override from the settings + * database which is not initialized at the time the app ops service is created. + * This means that all entry points that touch persistence should be short + * circuited via isPersistenceInitialized() check. */ // TODO (bug:122218838): Make sure we handle start of epoch time // TODO (bug:122218838): Validate changed time is handled correctly @@ -177,14 +183,33 @@ final class HistoricalRegistry { // Object managing persistence (read/write) @GuardedBy("mOnDiskLock") - private Persistence mPersistence = new Persistence(mBaseSnapshotInterval, - mIntervalCompressionMultiplier); + private Persistence mPersistence; HistoricalRegistry(@NonNull Object lock) { mInMemoryLock = lock; - if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) { - synchronized (mOnDiskLock) { - synchronized (mInMemoryLock) { + } + + void systemReady(@NonNull ContentResolver resolver) { + final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS); + resolver.registerContentObserver(uri, false, new ContentObserver( + FgThread.getHandler()) { + @Override + public void onChange(boolean selfChange) { + updateParametersFromSetting(resolver); + } + }); + + updateParametersFromSetting(resolver); + + synchronized (mOnDiskLock) { + synchronized (mInMemoryLock) { + if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) { + // Can be uninitialized if there is no config in the settings table. + if (!isPersistenceInitializedMLocked()) { + mPersistence = new Persistence(mBaseSnapshotInterval, + mIntervalCompressionMultiplier); + } + // When starting always adjust history to now. final long lastPersistTimeMills = mPersistence.getLastPersistTimeMillisDLocked(); @@ -197,16 +222,8 @@ final class HistoricalRegistry { } } - void systemReady(@NonNull ContentResolver resolver) { - updateParametersFromSetting(resolver); - final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS); - resolver.registerContentObserver(uri, false, new ContentObserver( - FgThread.getHandler()) { - @Override - public void onChange(boolean selfChange) { - updateParametersFromSetting(resolver); - } - }); + private boolean isPersistenceInitializedMLocked() { + return mPersistence != null; } private void updateParametersFromSetting(@NonNull ContentResolver resolver) { @@ -274,6 +291,11 @@ final class HistoricalRegistry { makeRelativeToEpochStart(currentOps, nowMillis); currentOps.accept(visitor); + if(isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } + final List ops = mPersistence.readHistoryDLocked(); if (ops != null) { // TODO (bug:122218838): Make sure this is properly dumped @@ -302,12 +324,21 @@ final class HistoricalRegistry { void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @NonNull RemoteCallback callback) { - final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis); - mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, - beginTimeMillis, endTimeMillis, flags); - final Bundle payload = new Bundle(); - payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); - callback.sendResult(payload); + synchronized (mOnDiskLock) { + synchronized (mInMemoryLock) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + callback.sendResult(new Bundle()); + return; + } + final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis); + mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, + beginTimeMillis, endTimeMillis, flags); + final Bundle payload = new Bundle(); + payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); + callback.sendResult(payload); + } + } } void getHistoricalOps(int uid, @NonNull String packageName, @@ -331,6 +362,12 @@ final class HistoricalRegistry { boolean collectOpsFromDisk; synchronized (mInMemoryLock) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + callback.sendResult(new Bundle()); + return; + } + currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis); if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis() || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) { @@ -374,6 +411,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseAccessCount(op, uid, packageName, uidState, flags, 1); } @@ -384,6 +425,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseRejectCount(op, uid, packageName, uidState, flags, 1); } @@ -394,6 +439,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags, long increment) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseAccessDuration(op, uid, packageName, uidState, flags, increment); } @@ -404,6 +453,8 @@ final class HistoricalRegistry { long baseSnapshotInterval, long intervalCompressionMultiplier) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { + // NOTE: We allow this call if persistence is not initialized as + // it is a part of the persistence initialization process. boolean resampleHistory = false; Slog.i(LOG_TAG, "New history parameters: mode:" + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:" @@ -412,7 +463,7 @@ final class HistoricalRegistry { if (mMode != mode) { mMode = mode; if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { - clearHistoryOnDiskLocked(); + clearHistoryOnDiskDLocked(); } } if (mBaseSnapshotInterval != baseSnapshotInterval) { @@ -433,6 +484,10 @@ final class HistoricalRegistry { void offsetHistory(long offsetMillis) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } final List history = mPersistence.readHistoryDLocked(); clearHistory(); if (history != null) { @@ -453,6 +508,10 @@ final class HistoricalRegistry { void addHistoricalOps(HistoricalOps ops) { final List pendingWrites; synchronized (mInMemoryLock) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } // The history files start from mBaseSnapshotInterval - take this into account. ops.offsetBeginAndEndTime(mBaseSnapshotInterval); mPendingWrites.offerFirst(ops); @@ -468,6 +527,10 @@ final class HistoricalRegistry { } void resetHistoryParameters() { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS, DEFAULT_COMPRESSION_STEP); } @@ -475,6 +538,10 @@ final class HistoricalRegistry { void clearHistory(int uid, String packageName) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { return; } @@ -493,11 +560,17 @@ final class HistoricalRegistry { void clearHistory() { synchronized (mOnDiskLock) { - clearHistoryOnDiskLocked(); + synchronized (mInMemoryLock) { + if (!isPersistenceInitializedMLocked()) { + Slog.e(LOG_TAG, "Interaction before persistence initialized"); + return; + } + clearHistoryOnDiskDLocked(); + } } } - private void clearHistoryOnDiskLocked() { + private void clearHistoryOnDiskDLocked() { BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY); synchronized (mInMemoryLock) { mCurrentHistoricalOps = null; @@ -718,13 +791,27 @@ final class HistoricalRegistry { baseDir = mHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); if (files != null && files.length > 0) { - final Set 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(); + File shortestFile = null; + for (File candidate : files) { + final String candidateName = candidate.getName(); + if (!candidateName.endsWith(HISTORY_FILE_SUFFIX)) { + continue; } + if (shortestFile == null) { + shortestFile = candidate; + } else if (candidateName.length() < shortestFile.getName().length()) { + shortestFile = candidate; + } + } + if (shortestFile == null) { + return 0; + } + final String shortestNameNoExtension = shortestFile.getName() + .replace(HISTORY_FILE_SUFFIX, ""); + try { + return Long.parseLong(shortestNameNoExtension); + } catch (NumberFormatException e) { + return 0; } } mHistoricalAppOpsDir.finishRead();