Loading services/core/java/com/android/server/appop/AppOpsService.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -201,7 +201,7 @@ public class AppOpsService extends IAppOpsService.Stub { @VisibleForTesting @VisibleForTesting final SparseArray<UidState> mUidStates = new SparseArray<>(); final SparseArray<UidState> mUidStates = new SparseArray<>(); private final HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this); final HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this); long mLastRealtime; long mLastRealtime; Loading services/core/java/com/android/server/appop/HistoricalRegistry.java +134 −47 Original line number Original line Diff line number Diff line Loading @@ -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 mInMemoryLock, xxxDMLocked suffix means the method * must be called with the mOnDiskLock and mInMemoryLock locks acquired in that * must be called with the mOnDiskLock and mInMemoryLock locks acquired in that * exact order. * exact order. * <p> * 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): Make sure we handle start of epoch time // TODO (bug:122218838): Validate changed time is handled correctly // TODO (bug:122218838): Validate changed time is handled correctly Loading Loading @@ -177,14 +183,33 @@ final class HistoricalRegistry { // Object managing persistence (read/write) // Object managing persistence (read/write) @GuardedBy("mOnDiskLock") @GuardedBy("mOnDiskLock") private Persistence mPersistence = new Persistence(mBaseSnapshotInterval, private Persistence mPersistence; mIntervalCompressionMultiplier); HistoricalRegistry(@NonNull Object lock) { HistoricalRegistry(@NonNull Object lock) { mInMemoryLock = lock; mInMemoryLock = lock; if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) { } 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 (mOnDiskLock) { synchronized (mInMemoryLock) { 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. // When starting always adjust history to now. final long lastPersistTimeMills = final long lastPersistTimeMills = mPersistence.getLastPersistTimeMillisDLocked(); mPersistence.getLastPersistTimeMillisDLocked(); Loading @@ -197,16 +222,8 @@ final class HistoricalRegistry { } } } } void systemReady(@NonNull ContentResolver resolver) { private boolean isPersistenceInitializedMLocked() { updateParametersFromSetting(resolver); return mPersistence != null; 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 void updateParametersFromSetting(@NonNull ContentResolver resolver) { private void updateParametersFromSetting(@NonNull ContentResolver resolver) { Loading Loading @@ -274,6 +291,11 @@ final class HistoricalRegistry { makeRelativeToEpochStart(currentOps, nowMillis); makeRelativeToEpochStart(currentOps, nowMillis); currentOps.accept(visitor); currentOps.accept(visitor); if(isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } final List<HistoricalOps> ops = mPersistence.readHistoryDLocked(); final List<HistoricalOps> ops = mPersistence.readHistoryDLocked(); if (ops != null) { if (ops != null) { // TODO (bug:122218838): Make sure this is properly dumped // TODO (bug:122218838): Make sure this is properly dumped Loading Loading @@ -302,6 +324,13 @@ final class HistoricalRegistry { void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @NonNull RemoteCallback callback) { @OpFlags int flags, @NonNull RemoteCallback callback) { 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); final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis); mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, beginTimeMillis, endTimeMillis, flags); beginTimeMillis, endTimeMillis, flags); Loading @@ -309,6 +338,8 @@ final class HistoricalRegistry { payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); callback.sendResult(payload); callback.sendResult(payload); } } } } void getHistoricalOps(int uid, @NonNull String packageName, void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, Loading @@ -331,6 +362,12 @@ final class HistoricalRegistry { boolean collectOpsFromDisk; boolean collectOpsFromDisk; synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); callback.sendResult(new Bundle()); return; } currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis); currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis); if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis() if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis() || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) { || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) { Loading Loading @@ -374,6 +411,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags) { @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseAccessCount(op, uid, packageName, uidState, flags, 1); .increaseAccessCount(op, uid, packageName, uidState, flags, 1); } } Loading @@ -384,6 +425,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags) { @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseRejectCount(op, uid, packageName, uidState, flags, 1); .increaseRejectCount(op, uid, packageName, uidState, flags, 1); } } Loading @@ -394,6 +439,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags, long increment) { @UidState int uidState, @OpFlags int flags, long increment) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseAccessDuration(op, uid, packageName, uidState, flags, increment); .increaseAccessDuration(op, uid, packageName, uidState, flags, increment); } } Loading @@ -404,6 +453,8 @@ final class HistoricalRegistry { long baseSnapshotInterval, long intervalCompressionMultiplier) { long baseSnapshotInterval, long intervalCompressionMultiplier) { synchronized (mOnDiskLock) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { 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; boolean resampleHistory = false; Slog.i(LOG_TAG, "New history parameters: mode:" Slog.i(LOG_TAG, "New history parameters: mode:" + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:" + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:" Loading @@ -412,7 +463,7 @@ final class HistoricalRegistry { if (mMode != mode) { if (mMode != mode) { mMode = mode; mMode = mode; if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { clearHistoryOnDiskLocked(); clearHistoryOnDiskDLocked(); } } } } if (mBaseSnapshotInterval != baseSnapshotInterval) { if (mBaseSnapshotInterval != baseSnapshotInterval) { Loading @@ -433,6 +484,10 @@ final class HistoricalRegistry { void offsetHistory(long offsetMillis) { void offsetHistory(long offsetMillis) { synchronized (mOnDiskLock) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } final List<HistoricalOps> history = mPersistence.readHistoryDLocked(); final List<HistoricalOps> history = mPersistence.readHistoryDLocked(); clearHistory(); clearHistory(); if (history != null) { if (history != null) { Loading @@ -453,6 +508,10 @@ final class HistoricalRegistry { void addHistoricalOps(HistoricalOps ops) { void addHistoricalOps(HistoricalOps ops) { final List<HistoricalOps> pendingWrites; final List<HistoricalOps> pendingWrites; synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } // The history files start from mBaseSnapshotInterval - take this into account. // The history files start from mBaseSnapshotInterval - take this into account. ops.offsetBeginAndEndTime(mBaseSnapshotInterval); ops.offsetBeginAndEndTime(mBaseSnapshotInterval); mPendingWrites.offerFirst(ops); mPendingWrites.offerFirst(ops); Loading @@ -468,6 +527,10 @@ final class HistoricalRegistry { } } void resetHistoryParameters() { void resetHistoryParameters() { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS, setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS, DEFAULT_COMPRESSION_STEP); DEFAULT_COMPRESSION_STEP); } } Loading @@ -475,6 +538,10 @@ final class HistoricalRegistry { void clearHistory(int uid, String packageName) { void clearHistory(int uid, String packageName) { synchronized (mOnDiskLock) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { return; return; } } Loading @@ -493,18 +560,24 @@ final class HistoricalRegistry { void clearHistory() { void clearHistory() { synchronized (mOnDiskLock) { 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); BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY); synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { mCurrentHistoricalOps = null; mCurrentHistoricalOps = null; mNextPersistDueTimeMillis = System.currentTimeMillis(); mNextPersistDueTimeMillis = System.currentTimeMillis(); mPendingWrites.clear(); mPendingWrites.clear(); } } mPersistence.clearHistoryDLocked(); Persistence.clearHistoryDLocked(); } } private @NonNull HistoricalOps getUpdatedPendingHistoricalOpsMLocked(long now) { private @NonNull HistoricalOps getUpdatedPendingHistoricalOpsMLocked(long now) { Loading Loading @@ -639,7 +712,7 @@ final class HistoricalRegistry { mIntervalCompressionMultiplier = intervalCompressionMultiplier; mIntervalCompressionMultiplier = intervalCompressionMultiplier; } } private final AtomicDirectory mHistoricalAppOpsDir = new AtomicDirectory( private static final AtomicDirectory sHistoricalAppOpsDir = new AtomicDirectory( new File(new File(Environment.getDataSystemDirectory(), "appops"), "history")); new File(new File(Environment.getDataSystemDirectory(), "appops"), "history")); private File generateFile(@NonNull File baseDir, int depth) { private File generateFile(@NonNull File baseDir, int depth) { Loading @@ -663,8 +736,8 @@ final class HistoricalRegistry { persistHistoricalOpsDLocked(historicalOps); persistHistoricalOpsDLocked(historicalOps); } } void clearHistoryDLocked() { static void clearHistoryDLocked() { mHistoricalAppOpsDir.delete(); sHistoricalAppOpsDir.delete(); } } void persistHistoricalOpsDLocked(@NonNull List<HistoricalOps> ops) { void persistHistoricalOpsDLocked(@NonNull List<HistoricalOps> ops) { Loading @@ -673,8 +746,8 @@ final class HistoricalRegistry { enforceOpsWellFormed(ops); enforceOpsWellFormed(ops); } } try { try { final File newBaseDir = mHistoricalAppOpsDir.startWrite(); final File newBaseDir = sHistoricalAppOpsDir.startWrite(); final File oldBaseDir = mHistoricalAppOpsDir.getBackupDirectory(); final File oldBaseDir = sHistoricalAppOpsDir.getBackupDirectory(); final HistoricalFilesInvariant filesInvariant; final HistoricalFilesInvariant filesInvariant; if (DEBUG) { if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant = new HistoricalFilesInvariant(); Loading @@ -686,10 +759,10 @@ final class HistoricalRegistry { if (DEBUG) { if (DEBUG) { filesInvariant.stopTracking(newBaseDir); filesInvariant.stopTracking(newBaseDir); } } mHistoricalAppOpsDir.finishWrite(); sHistoricalAppOpsDir.finishWrite(); } catch (Throwable t) { } catch (Throwable t) { wtf("Failed to write historical app ops, restoring backup", t, null); wtf("Failed to write historical app ops, restoring backup", t, null); mHistoricalAppOpsDir.failWrite(); sHistoricalAppOpsDir.failWrite(); } } } } Loading @@ -715,22 +788,36 @@ final class HistoricalRegistry { long getLastPersistTimeMillisDLocked() { long getLastPersistTimeMillisDLocked() { File baseDir = null; File baseDir = null; try { try { baseDir = mHistoricalAppOpsDir.startRead(); baseDir = sHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); final File[] files = baseDir.listFiles(); if (files != null && files.length > 0) { if (files != null && files.length > 0) { final Set<File> historyFiles = new ArraySet<>(); File shortestFile = null; Collections.addAll(historyFiles, files); for (File candidate : files) { for (int i = 0;; i++) { final String candidateName = candidate.getName(); final File file = generateFile(baseDir, i); if (!candidateName.endsWith(HISTORY_FILE_SUFFIX)) { if (historyFiles.contains(file)) { continue; return file.lastModified(); } 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(); sHistoricalAppOpsDir.finishRead(); } catch (Throwable e) { } catch (Throwable e) { wtf("Error reading historical app ops. Deleting history.", e, baseDir); wtf("Error reading historical app ops. Deleting history.", e, baseDir); mHistoricalAppOpsDir.delete(); sHistoricalAppOpsDir.delete(); } } return 0; return 0; } } Loading @@ -755,7 +842,7 @@ final class HistoricalRegistry { long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) { long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) { File baseDir = null; File baseDir = null; try { try { baseDir = mHistoricalAppOpsDir.startRead(); baseDir = sHistoricalAppOpsDir.startRead(); final HistoricalFilesInvariant filesInvariant; final HistoricalFilesInvariant filesInvariant; if (DEBUG) { if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant = new HistoricalFilesInvariant(); Loading @@ -770,11 +857,11 @@ final class HistoricalRegistry { if (DEBUG) { if (DEBUG) { filesInvariant.stopTracking(baseDir); filesInvariant.stopTracking(baseDir); } } mHistoricalAppOpsDir.finishRead(); sHistoricalAppOpsDir.finishRead(); return ops; return ops; } catch (Throwable t) { } catch (Throwable t) { wtf("Error reading historical app ops. Deleting history.", t, baseDir); wtf("Error reading historical app ops. Deleting history.", t, baseDir); mHistoricalAppOpsDir.delete(); sHistoricalAppOpsDir.delete(); } } return null; return null; } } Loading Loading @@ -1241,7 +1328,7 @@ final class HistoricalRegistry { private void writeHistoricalOpsDLocked(@Nullable List<HistoricalOps> allOps, private void writeHistoricalOpsDLocked(@Nullable List<HistoricalOps> allOps, long intervalOverflowMillis, @NonNull File file) throws IOException { long intervalOverflowMillis, @NonNull File file) throws IOException { final FileOutputStream output = mHistoricalAppOpsDir.openWrite(file); final FileOutputStream output = sHistoricalAppOpsDir.openWrite(file); try { try { final XmlSerializer serializer = Xml.newSerializer(); final XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(output, StandardCharsets.UTF_8.name()); serializer.setOutput(output, StandardCharsets.UTF_8.name()); Loading @@ -1263,9 +1350,9 @@ final class HistoricalRegistry { } } serializer.endTag(null, TAG_HISTORY); serializer.endTag(null, TAG_HISTORY); serializer.endDocument(); serializer.endDocument(); mHistoricalAppOpsDir.closeWrite(output); sHistoricalAppOpsDir.closeWrite(output); } catch (IOException e) { } catch (IOException e) { mHistoricalAppOpsDir.failWrite(output); sHistoricalAppOpsDir.failWrite(output); throw e; throw e; } } } } Loading services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java +24 −0 Original line number Original line Diff line number Diff line Loading @@ -37,12 +37,15 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.HandlerThread; import android.os.Process; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.provider.Settings; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4; import org.junit.AfterClass; import org.junit.Before; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; Loading @@ -68,11 +71,14 @@ public class AppOpsServiceTest { private File mAppOpsFile; private File mAppOpsFile; private Context mContext; private Context mContext; private Handler mHandler; private Handler mHandler; private AppOpsManager mAppOpsManager; private AppOpsService mAppOpsService; private AppOpsService mAppOpsService; private String mMyPackageName; private String mMyPackageName; private int mMyUid; private int mMyUid; private long mTestStartMillis; private long mTestStartMillis; private static String sDefaultAppopHistoryParameters; @Before @Before public void setUp() { public void setUp() { mContext = InstrumentationRegistry.getTargetContext(); mContext = InstrumentationRegistry.getTargetContext(); Loading @@ -88,11 +94,29 @@ public class AppOpsServiceTest { mMyPackageName = mContext.getOpPackageName(); mMyPackageName = mContext.getOpPackageName(); mMyUid = Process.myUid(); mMyUid = Process.myUid(); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); mAppOpsService.mHistoricalRegistry.systemReady(mContext.getContentResolver()); mAppOpsService.mContext = mContext; mAppOpsService.mContext = mContext; mTestStartMillis = System.currentTimeMillis(); mTestStartMillis = System.currentTimeMillis(); } } @BeforeClass public static void configureDesiredAppopHistoryParameters() { final Context context = InstrumentationRegistry.getTargetContext(); sDefaultAppopHistoryParameters = Settings.Global.getString(context.getContentResolver(), Settings.Global.APPOP_HISTORY_PARAMETERS); Settings.Global.putString(InstrumentationRegistry.getTargetContext().getContentResolver(), Settings.Global.APPOP_HISTORY_PARAMETERS, null); } @AfterClass public static void restoreDefaultAppopHistoryParameters() { Settings.Global.putString(InstrumentationRegistry.getTargetContext().getContentResolver(), Settings.Global.APPOP_HISTORY_PARAMETERS, sDefaultAppopHistoryParameters); } @Test @Test public void testGetOpsForPackage_noOpsLogged() { public void testGetOpsForPackage_noOpsLogged() { assertThat(getLoggedOps()).isNull(); assertThat(getLoggedOps()).isNull(); Loading Loading
services/core/java/com/android/server/appop/AppOpsService.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -201,7 +201,7 @@ public class AppOpsService extends IAppOpsService.Stub { @VisibleForTesting @VisibleForTesting final SparseArray<UidState> mUidStates = new SparseArray<>(); final SparseArray<UidState> mUidStates = new SparseArray<>(); private final HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this); final HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this); long mLastRealtime; long mLastRealtime; Loading
services/core/java/com/android/server/appop/HistoricalRegistry.java +134 −47 Original line number Original line Diff line number Diff line Loading @@ -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 mInMemoryLock, xxxDMLocked suffix means the method * must be called with the mOnDiskLock and mInMemoryLock locks acquired in that * must be called with the mOnDiskLock and mInMemoryLock locks acquired in that * exact order. * exact order. * <p> * 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): Make sure we handle start of epoch time // TODO (bug:122218838): Validate changed time is handled correctly // TODO (bug:122218838): Validate changed time is handled correctly Loading Loading @@ -177,14 +183,33 @@ final class HistoricalRegistry { // Object managing persistence (read/write) // Object managing persistence (read/write) @GuardedBy("mOnDiskLock") @GuardedBy("mOnDiskLock") private Persistence mPersistence = new Persistence(mBaseSnapshotInterval, private Persistence mPersistence; mIntervalCompressionMultiplier); HistoricalRegistry(@NonNull Object lock) { HistoricalRegistry(@NonNull Object lock) { mInMemoryLock = lock; mInMemoryLock = lock; if (mMode != AppOpsManager.HISTORICAL_MODE_DISABLED) { } 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 (mOnDiskLock) { synchronized (mInMemoryLock) { 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. // When starting always adjust history to now. final long lastPersistTimeMills = final long lastPersistTimeMills = mPersistence.getLastPersistTimeMillisDLocked(); mPersistence.getLastPersistTimeMillisDLocked(); Loading @@ -197,16 +222,8 @@ final class HistoricalRegistry { } } } } void systemReady(@NonNull ContentResolver resolver) { private boolean isPersistenceInitializedMLocked() { updateParametersFromSetting(resolver); return mPersistence != null; 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 void updateParametersFromSetting(@NonNull ContentResolver resolver) { private void updateParametersFromSetting(@NonNull ContentResolver resolver) { Loading Loading @@ -274,6 +291,11 @@ final class HistoricalRegistry { makeRelativeToEpochStart(currentOps, nowMillis); makeRelativeToEpochStart(currentOps, nowMillis); currentOps.accept(visitor); currentOps.accept(visitor); if(isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } final List<HistoricalOps> ops = mPersistence.readHistoryDLocked(); final List<HistoricalOps> ops = mPersistence.readHistoryDLocked(); if (ops != null) { if (ops != null) { // TODO (bug:122218838): Make sure this is properly dumped // TODO (bug:122218838): Make sure this is properly dumped Loading Loading @@ -302,6 +324,13 @@ final class HistoricalRegistry { void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, void getHistoricalOpsFromDiskRaw(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @NonNull RemoteCallback callback) { @OpFlags int flags, @NonNull RemoteCallback callback) { 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); final HistoricalOps result = new HistoricalOps(beginTimeMillis, endTimeMillis); mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, mPersistence.collectHistoricalOpsDLocked(result, uid, packageName, opNames, beginTimeMillis, endTimeMillis, flags); beginTimeMillis, endTimeMillis, flags); Loading @@ -309,6 +338,8 @@ final class HistoricalRegistry { payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); payload.putParcelable(AppOpsManager.KEY_HISTORICAL_OPS, result); callback.sendResult(payload); callback.sendResult(payload); } } } } void getHistoricalOps(int uid, @NonNull String packageName, void getHistoricalOps(int uid, @NonNull String packageName, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, @Nullable String[] opNames, long beginTimeMillis, long endTimeMillis, Loading @@ -331,6 +362,12 @@ final class HistoricalRegistry { boolean collectOpsFromDisk; boolean collectOpsFromDisk; synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); callback.sendResult(new Bundle()); return; } currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis); currentOps = getUpdatedPendingHistoricalOpsMLocked(currentTimeMillis); if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis() if (!(inMemoryAdjBeginTimeMillis >= currentOps.getEndTimeMillis() || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) { || inMemoryAdjEndTimeMillis <= currentOps.getBeginTimeMillis())) { Loading Loading @@ -374,6 +411,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags) { @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseAccessCount(op, uid, packageName, uidState, flags, 1); .increaseAccessCount(op, uid, packageName, uidState, flags, 1); } } Loading @@ -384,6 +425,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags) { @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseRejectCount(op, uid, packageName, uidState, flags, 1); .increaseRejectCount(op, uid, packageName, uidState, flags, 1); } } Loading @@ -394,6 +439,10 @@ final class HistoricalRegistry { @UidState int uidState, @OpFlags int flags, long increment) { @UidState int uidState, @OpFlags int flags, long increment) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) getUpdatedPendingHistoricalOpsMLocked(System.currentTimeMillis()) .increaseAccessDuration(op, uid, packageName, uidState, flags, increment); .increaseAccessDuration(op, uid, packageName, uidState, flags, increment); } } Loading @@ -404,6 +453,8 @@ final class HistoricalRegistry { long baseSnapshotInterval, long intervalCompressionMultiplier) { long baseSnapshotInterval, long intervalCompressionMultiplier) { synchronized (mOnDiskLock) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { 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; boolean resampleHistory = false; Slog.i(LOG_TAG, "New history parameters: mode:" Slog.i(LOG_TAG, "New history parameters: mode:" + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:" + AppOpsManager.historicalModeToString(mMode) + " baseSnapshotInterval:" Loading @@ -412,7 +463,7 @@ final class HistoricalRegistry { if (mMode != mode) { if (mMode != mode) { mMode = mode; mMode = mode; if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { clearHistoryOnDiskLocked(); clearHistoryOnDiskDLocked(); } } } } if (mBaseSnapshotInterval != baseSnapshotInterval) { if (mBaseSnapshotInterval != baseSnapshotInterval) { Loading @@ -433,6 +484,10 @@ final class HistoricalRegistry { void offsetHistory(long offsetMillis) { void offsetHistory(long offsetMillis) { synchronized (mOnDiskLock) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } final List<HistoricalOps> history = mPersistence.readHistoryDLocked(); final List<HistoricalOps> history = mPersistence.readHistoryDLocked(); clearHistory(); clearHistory(); if (history != null) { if (history != null) { Loading @@ -453,6 +508,10 @@ final class HistoricalRegistry { void addHistoricalOps(HistoricalOps ops) { void addHistoricalOps(HistoricalOps ops) { final List<HistoricalOps> pendingWrites; final List<HistoricalOps> pendingWrites; synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } // The history files start from mBaseSnapshotInterval - take this into account. // The history files start from mBaseSnapshotInterval - take this into account. ops.offsetBeginAndEndTime(mBaseSnapshotInterval); ops.offsetBeginAndEndTime(mBaseSnapshotInterval); mPendingWrites.offerFirst(ops); mPendingWrites.offerFirst(ops); Loading @@ -468,6 +527,10 @@ final class HistoricalRegistry { } } void resetHistoryParameters() { void resetHistoryParameters() { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS, setHistoryParameters(DEFAULT_MODE, DEFAULT_SNAPSHOT_INTERVAL_MILLIS, DEFAULT_COMPRESSION_STEP); DEFAULT_COMPRESSION_STEP); } } Loading @@ -475,6 +538,10 @@ final class HistoricalRegistry { void clearHistory(int uid, String packageName) { void clearHistory(int uid, String packageName) { synchronized (mOnDiskLock) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Slog.e(LOG_TAG, "Interaction before persistence initialized"); return; } if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { if (mMode != AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { return; return; } } Loading @@ -493,18 +560,24 @@ final class HistoricalRegistry { void clearHistory() { void clearHistory() { synchronized (mOnDiskLock) { 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); BackgroundThread.getHandler().removeMessages(MSG_WRITE_PENDING_HISTORY); synchronized (mInMemoryLock) { synchronized (mInMemoryLock) { mCurrentHistoricalOps = null; mCurrentHistoricalOps = null; mNextPersistDueTimeMillis = System.currentTimeMillis(); mNextPersistDueTimeMillis = System.currentTimeMillis(); mPendingWrites.clear(); mPendingWrites.clear(); } } mPersistence.clearHistoryDLocked(); Persistence.clearHistoryDLocked(); } } private @NonNull HistoricalOps getUpdatedPendingHistoricalOpsMLocked(long now) { private @NonNull HistoricalOps getUpdatedPendingHistoricalOpsMLocked(long now) { Loading Loading @@ -639,7 +712,7 @@ final class HistoricalRegistry { mIntervalCompressionMultiplier = intervalCompressionMultiplier; mIntervalCompressionMultiplier = intervalCompressionMultiplier; } } private final AtomicDirectory mHistoricalAppOpsDir = new AtomicDirectory( private static final AtomicDirectory sHistoricalAppOpsDir = new AtomicDirectory( new File(new File(Environment.getDataSystemDirectory(), "appops"), "history")); new File(new File(Environment.getDataSystemDirectory(), "appops"), "history")); private File generateFile(@NonNull File baseDir, int depth) { private File generateFile(@NonNull File baseDir, int depth) { Loading @@ -663,8 +736,8 @@ final class HistoricalRegistry { persistHistoricalOpsDLocked(historicalOps); persistHistoricalOpsDLocked(historicalOps); } } void clearHistoryDLocked() { static void clearHistoryDLocked() { mHistoricalAppOpsDir.delete(); sHistoricalAppOpsDir.delete(); } } void persistHistoricalOpsDLocked(@NonNull List<HistoricalOps> ops) { void persistHistoricalOpsDLocked(@NonNull List<HistoricalOps> ops) { Loading @@ -673,8 +746,8 @@ final class HistoricalRegistry { enforceOpsWellFormed(ops); enforceOpsWellFormed(ops); } } try { try { final File newBaseDir = mHistoricalAppOpsDir.startWrite(); final File newBaseDir = sHistoricalAppOpsDir.startWrite(); final File oldBaseDir = mHistoricalAppOpsDir.getBackupDirectory(); final File oldBaseDir = sHistoricalAppOpsDir.getBackupDirectory(); final HistoricalFilesInvariant filesInvariant; final HistoricalFilesInvariant filesInvariant; if (DEBUG) { if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant = new HistoricalFilesInvariant(); Loading @@ -686,10 +759,10 @@ final class HistoricalRegistry { if (DEBUG) { if (DEBUG) { filesInvariant.stopTracking(newBaseDir); filesInvariant.stopTracking(newBaseDir); } } mHistoricalAppOpsDir.finishWrite(); sHistoricalAppOpsDir.finishWrite(); } catch (Throwable t) { } catch (Throwable t) { wtf("Failed to write historical app ops, restoring backup", t, null); wtf("Failed to write historical app ops, restoring backup", t, null); mHistoricalAppOpsDir.failWrite(); sHistoricalAppOpsDir.failWrite(); } } } } Loading @@ -715,22 +788,36 @@ final class HistoricalRegistry { long getLastPersistTimeMillisDLocked() { long getLastPersistTimeMillisDLocked() { File baseDir = null; File baseDir = null; try { try { baseDir = mHistoricalAppOpsDir.startRead(); baseDir = sHistoricalAppOpsDir.startRead(); final File[] files = baseDir.listFiles(); final File[] files = baseDir.listFiles(); if (files != null && files.length > 0) { if (files != null && files.length > 0) { final Set<File> historyFiles = new ArraySet<>(); File shortestFile = null; Collections.addAll(historyFiles, files); for (File candidate : files) { for (int i = 0;; i++) { final String candidateName = candidate.getName(); final File file = generateFile(baseDir, i); if (!candidateName.endsWith(HISTORY_FILE_SUFFIX)) { if (historyFiles.contains(file)) { continue; return file.lastModified(); } 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(); sHistoricalAppOpsDir.finishRead(); } catch (Throwable e) { } catch (Throwable e) { wtf("Error reading historical app ops. Deleting history.", e, baseDir); wtf("Error reading historical app ops. Deleting history.", e, baseDir); mHistoricalAppOpsDir.delete(); sHistoricalAppOpsDir.delete(); } } return 0; return 0; } } Loading @@ -755,7 +842,7 @@ final class HistoricalRegistry { long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) { long filterBeginTimeMillis, long filterEndTimeMillis, @OpFlags int filterFlags) { File baseDir = null; File baseDir = null; try { try { baseDir = mHistoricalAppOpsDir.startRead(); baseDir = sHistoricalAppOpsDir.startRead(); final HistoricalFilesInvariant filesInvariant; final HistoricalFilesInvariant filesInvariant; if (DEBUG) { if (DEBUG) { filesInvariant = new HistoricalFilesInvariant(); filesInvariant = new HistoricalFilesInvariant(); Loading @@ -770,11 +857,11 @@ final class HistoricalRegistry { if (DEBUG) { if (DEBUG) { filesInvariant.stopTracking(baseDir); filesInvariant.stopTracking(baseDir); } } mHistoricalAppOpsDir.finishRead(); sHistoricalAppOpsDir.finishRead(); return ops; return ops; } catch (Throwable t) { } catch (Throwable t) { wtf("Error reading historical app ops. Deleting history.", t, baseDir); wtf("Error reading historical app ops. Deleting history.", t, baseDir); mHistoricalAppOpsDir.delete(); sHistoricalAppOpsDir.delete(); } } return null; return null; } } Loading Loading @@ -1241,7 +1328,7 @@ final class HistoricalRegistry { private void writeHistoricalOpsDLocked(@Nullable List<HistoricalOps> allOps, private void writeHistoricalOpsDLocked(@Nullable List<HistoricalOps> allOps, long intervalOverflowMillis, @NonNull File file) throws IOException { long intervalOverflowMillis, @NonNull File file) throws IOException { final FileOutputStream output = mHistoricalAppOpsDir.openWrite(file); final FileOutputStream output = sHistoricalAppOpsDir.openWrite(file); try { try { final XmlSerializer serializer = Xml.newSerializer(); final XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(output, StandardCharsets.UTF_8.name()); serializer.setOutput(output, StandardCharsets.UTF_8.name()); Loading @@ -1263,9 +1350,9 @@ final class HistoricalRegistry { } } serializer.endTag(null, TAG_HISTORY); serializer.endTag(null, TAG_HISTORY); serializer.endDocument(); serializer.endDocument(); mHistoricalAppOpsDir.closeWrite(output); sHistoricalAppOpsDir.closeWrite(output); } catch (IOException e) { } catch (IOException e) { mHistoricalAppOpsDir.failWrite(output); sHistoricalAppOpsDir.failWrite(output); throw e; throw e; } } } } Loading
services/tests/servicestests/src/com/android/server/appop/AppOpsServiceTest.java +24 −0 Original line number Original line Diff line number Diff line Loading @@ -37,12 +37,15 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.HandlerThread; import android.os.Process; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteCallback; import android.provider.Settings; import androidx.test.InstrumentationRegistry; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4; import org.junit.AfterClass; import org.junit.Before; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; Loading @@ -68,11 +71,14 @@ public class AppOpsServiceTest { private File mAppOpsFile; private File mAppOpsFile; private Context mContext; private Context mContext; private Handler mHandler; private Handler mHandler; private AppOpsManager mAppOpsManager; private AppOpsService mAppOpsService; private AppOpsService mAppOpsService; private String mMyPackageName; private String mMyPackageName; private int mMyUid; private int mMyUid; private long mTestStartMillis; private long mTestStartMillis; private static String sDefaultAppopHistoryParameters; @Before @Before public void setUp() { public void setUp() { mContext = InstrumentationRegistry.getTargetContext(); mContext = InstrumentationRegistry.getTargetContext(); Loading @@ -88,11 +94,29 @@ public class AppOpsServiceTest { mMyPackageName = mContext.getOpPackageName(); mMyPackageName = mContext.getOpPackageName(); mMyUid = Process.myUid(); mMyUid = Process.myUid(); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); mAppOpsService = new AppOpsService(mAppOpsFile, mHandler); mAppOpsService.mHistoricalRegistry.systemReady(mContext.getContentResolver()); mAppOpsService.mContext = mContext; mAppOpsService.mContext = mContext; mTestStartMillis = System.currentTimeMillis(); mTestStartMillis = System.currentTimeMillis(); } } @BeforeClass public static void configureDesiredAppopHistoryParameters() { final Context context = InstrumentationRegistry.getTargetContext(); sDefaultAppopHistoryParameters = Settings.Global.getString(context.getContentResolver(), Settings.Global.APPOP_HISTORY_PARAMETERS); Settings.Global.putString(InstrumentationRegistry.getTargetContext().getContentResolver(), Settings.Global.APPOP_HISTORY_PARAMETERS, null); } @AfterClass public static void restoreDefaultAppopHistoryParameters() { Settings.Global.putString(InstrumentationRegistry.getTargetContext().getContentResolver(), Settings.Global.APPOP_HISTORY_PARAMETERS, sDefaultAppopHistoryParameters); } @Test @Test public void testGetOpsForPackage_noOpsLogged() { public void testGetOpsForPackage_noOpsLogged() { assertThat(getLoggedOps()).isNull(); assertThat(getLoggedOps()).isNull(); Loading