Loading services/core/java/com/android/server/appop/AppOpsService.java +19 −8 Original line number Diff line number Diff line Loading @@ -367,7 +367,7 @@ public class AppOpsService extends IAppOpsService.Stub { private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10); private final RateLimiter mRateLimiter = new RateLimiter(RATE_LIMITER_WINDOW); volatile @NonNull HistoricalRegistry mHistoricalRegistry; volatile @NonNull HistoricalRegistryInterface mHistoricalRegistry; /* * These are app op restrictions imposed per user from various parties. Loading Loading @@ -1056,8 +1056,12 @@ public class AppOpsService extends IAppOpsService.Stub { AppOpsManager.invalidateAppOpModeCache(); AppOpsManager.disableAppOpModeCache(); if (Flags.enableAllSqliteAppopsAccesses()) { mHistoricalRegistry = new HistoricalRegistrySql(context); } else { mHistoricalRegistry = new HistoricalRegistry(this, context); } } public void publish() { ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder()); Loading Loading @@ -1424,7 +1428,7 @@ public class AppOpsService extends IAppOpsService.Stub { @GuardedBy("this") private void packageRemovedLocked(int uid, String packageName) { getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory, getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistryInterface::clearHistory, mHistoricalRegistry, uid, packageName)); UidState uidState = mUidStates.get(uid); Loading Loading @@ -1992,10 +1996,12 @@ public class AppOpsService extends IAppOpsService.Stub { new String[attributionChainExemptPackages.size()]) : null; // Must not hold the appops lock getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps, getIoHandler().post(PooledLambda.obtainRunnable( HistoricalRegistryInterface::getHistoricalOps, mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType, filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray, callback).recycleOnUse()); callback ).recycleOnUse()); } @Override Loading Loading @@ -2024,7 +2030,7 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock getIoHandler().post(PooledLambda.obtainRunnable( HistoricalRegistry::getHistoricalOpsFromDiskRaw, HistoricalRegistryInterface::getHistoricalOpsFromDiskRaw, mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType, filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray, callback).recycleOnUse()); Loading Loading @@ -6961,7 +6967,6 @@ public class AppOpsService extends IAppOpsService.Stub { offsetHistory_enforcePermission(); // Must not hold the appops lock mHistoricalRegistry.offsetHistory(offsetMillis); mHistoricalRegistry.offsetDiscreteHistory(offsetMillis); } @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_APPOPS) Loading Loading @@ -7002,7 +7007,13 @@ public class AppOpsService extends IAppOpsService.Stub { SystemClock.sleep(offlineDurationMillis); } mHistoricalRegistry = new HistoricalRegistry(mHistoricalRegistry); if (Flags.enableAllSqliteAppopsAccesses()) { mHistoricalRegistry = new HistoricalRegistrySql( (HistoricalRegistrySql) mHistoricalRegistry); } else { mHistoricalRegistry = new HistoricalRegistry((HistoricalRegistry) mHistoricalRegistry); } mHistoricalRegistry.systemReady(mContext.getContentResolver()); mHistoricalRegistry.persistPendingHistory(); } Loading services/core/java/com/android/server/appop/AttributedOp.java +1 −1 Original line number Diff line number Diff line Loading @@ -163,7 +163,7 @@ final class AttributedOp { public void rejected(@AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) { rejected(System.currentTimeMillis(), uidState, flags); mAppOpsService.mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, mAppOpsService.mHistoricalRegistry.incrementOpRejectedCount(parent.op, parent.uid, parent.packageName, tag, uidState, flags); } Loading services/core/java/com/android/server/appop/HistoricalRegistry.java +41 −26 Original line number Diff line number Diff line Loading @@ -128,7 +128,7 @@ import java.util.concurrent.TimeUnit; */ // TODO (bug:122218838): Make sure we handle start of epoch time // TODO (bug:122218838): Validate changed time is handled correctly final class HistoricalRegistry { final class HistoricalRegistry implements HistoricalRegistryInterface { private static final boolean DEBUG = false; private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE; Loading Loading @@ -218,7 +218,8 @@ final class HistoricalRegistry { mDiscreteRegistry = other.mDiscreteRegistry; } void systemReady(@NonNull ContentResolver resolver) { @Override public void systemReady(@NonNull ContentResolver resolver) { mDiscreteRegistry.systemReady(); final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS); resolver.registerContentObserver(uri, false, new ContentObserver( Loading Loading @@ -320,8 +321,10 @@ final class HistoricalRegistry { + "=" + setting + " resetting!"); } void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @Override public void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @HistoricalOpsRequestFilter int filter) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { Loading Loading @@ -366,7 +369,8 @@ final class HistoricalRegistry { } } void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Override public void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, @Nullable String attributionTagFilter, @HistoricalOpsRequestFilter int filter, int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, Loading @@ -381,7 +385,8 @@ final class HistoricalRegistry { } } void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Override public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, Loading Loading @@ -414,11 +419,12 @@ final class HistoricalRegistry { callback.sendResult(payload); } void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) { @Override public void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) { final long currentTimeMillis = System.currentTimeMillis(); if (endTimeMillis == Long.MAX_VALUE) { endTimeMillis = currentTimeMillis; Loading Loading @@ -493,7 +499,8 @@ final class HistoricalRegistry { callback.sendResult(payload); } void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @Override public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long accessTime, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, Loading @@ -515,7 +522,8 @@ final class HistoricalRegistry { } } void incrementOpRejected(int op, int uid, @NonNull String packageName, @Override public void incrementOpRejectedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { Loading @@ -530,7 +538,8 @@ final class HistoricalRegistry { } } void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @Override public void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long eventStartTime, long increment, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { Loading @@ -550,7 +559,8 @@ final class HistoricalRegistry { } } void setHistoryParameters(@HistoricalMode int mode, @Override public void setHistoryParameters(@HistoricalMode int mode, long baseSnapshotInterval, long intervalCompressionMultiplier) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { Loading Loading @@ -585,7 +595,8 @@ final class HistoricalRegistry { } } void offsetHistory(long offsetMillis) { @Override public void offsetHistory(long offsetMillis) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Loading @@ -607,13 +618,11 @@ final class HistoricalRegistry { mPersistence.persistHistoricalOpsDLocked(history); } } } void offsetDiscreteHistory(long offsetMillis) { mDiscreteRegistry.offsetHistory(offsetMillis); } void addHistoricalOps(HistoricalOps ops) { @Override public void addHistoricalOps(HistoricalOps ops) { final List<HistoricalOps> pendingWrites; synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Loading @@ -634,7 +643,8 @@ final class HistoricalRegistry { offsetHistory(offsetMillis); } void resetHistoryParameters() { @Override public void resetHistoryParameters() { if (!isPersistenceInitializedMLocked()) { Slog.d(LOG_TAG, "Interaction before persistence initialized"); return; Loading @@ -644,7 +654,8 @@ final class HistoricalRegistry { mDiscreteRegistry.setDebugMode(false); } void clearHistory(int uid, String packageName) { @Override public void clearHistory(int uid, String packageName) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Loading @@ -668,11 +679,13 @@ final class HistoricalRegistry { mDiscreteRegistry.clearHistory(uid, packageName); } void writeAndClearDiscreteHistory() { @Override public void writeAndClearDiscreteHistory() { mDiscreteRegistry.writeAndClearOldAccessHistory(); } void clearAllHistory() { @Override public void clearAllHistory() { clearHistoricalRegistry(); mDiscreteRegistry.clearHistory(); } Loading Loading @@ -741,7 +754,8 @@ final class HistoricalRegistry { return mCurrentHistoricalOps; } void shutdown() { @Override public void shutdown() { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { return; Loading @@ -752,7 +766,8 @@ final class HistoricalRegistry { mDiscreteRegistry.shutdown(); } void persistPendingHistory() { @Override public void persistPendingHistory() { final List<HistoricalOps> pendingWrites; synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { Loading services/core/java/com/android/server/appop/HistoricalRegistryInterface.java 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appop; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.ContentResolver; import android.os.RemoteCallback; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; /** * A registry to record app operation access events, which are generated upon an application's * access to private data or system resources. These events are stored in both aggregated * and individual/discrete formats. */ public interface HistoricalRegistryInterface { /** * A callback to inform system components are ready. */ void systemReady(@NonNull ContentResolver resolver); /** * Callback for system shutdown. */ void shutdown(); /** * Dumps aggregated/historical events to the console based on the filters. */ void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @AppOpsManager.HistoricalOpsRequestFilter int filter); /** * Dumps discrete/individual events to the console based on filters. */ void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, @Nullable String attributionTagFilter, @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, int nDiscreteOps); /** * Record duration for given op. */ void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, long eventStartTime, long increment, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId); /** * Record access counts for given op. */ void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, long accessTime, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, int accessCount); /** * Record rejected counts for given op. */ void incrementOpRejectedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags); /** * Read historical ops from both aggregated and discrete events based on input filter. */ void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @AppOpsManager.OpHistoryFlags int historyFlags, @AppOpsManager.HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback); /** * Remove app op events for a given UID and package. */ void clearHistory(int uid, String packageName); /** * A periodic callback from {@link AppOpsService} to flush the in memory discrete * app op events to disk/database. */ void writeAndClearDiscreteHistory(); /** * A callback flush the in memory app op events to disk/database. */ void persistPendingHistory(); /** * Set history parameters. * * @param mode - Whether historical registry is Active, Passive or Disabled. * @param baseSnapshotInterval - Interval between 2 snapshots, default 15 minutes. * @param intervalCompressionMultiplier - Interval compression multiplier, default is 10. */ void setHistoryParameters(@AppOpsManager.HistoricalMode int mode, long baseSnapshotInterval, long intervalCompressionMultiplier); /** * Reset history parameters to defaults. */ void resetHistoryParameters(); /** * Remove all app op accesses from both aggregated and individual event's storage. */ void clearAllHistory(); /** * Offsets the history by the given duration. */ void offsetHistory(long offsetMillis); /** * Retrieve historical app op stats for a period form disk. */ void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @AppOpsManager.OpHistoryFlags int historyFlags, @AppOpsManager.HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flags, String[] attributionExemptedPackages, @NonNull RemoteCallback callback); /** * Adds ops to the history directly. This could be useful for testing especially * when the historical registry operates in passive mode. */ void addHistoricalOps(AppOpsManager.HistoricalOps ops); } services/core/java/com/android/server/appop/HistoricalRegistrySql.java 0 → 100644 +145 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appop; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; import android.os.RemoteCallback; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; // TODO add more documentation later /** * This historical registry implementation store app events in sqlite. The data is stored in 2 * tables 1) discrete events 2) aggregated events. */ public class HistoricalRegistrySql implements HistoricalRegistryInterface { // TODO impl will be added in a separate CL HistoricalRegistrySql(Context context) { } HistoricalRegistrySql(HistoricalRegistrySql other) { } @Override public void systemReady(@NonNull ContentResolver resolver) { } @Override public void shutdown() { } @Override public void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, int filter) { } @Override public void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, @Nullable String attributionTagFilter, int filter, int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, int nDiscreteOps) { } @Override public void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, int uidState, int flags, long eventStartTime, long increment, int attributionFlags, int attributionChainId) { } @Override public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, int uidState, int flags, long accessTime, int attributionFlags, int attributionChainId, int accessCount) { } @Override public void incrementOpRejectedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, int uidState, int flags) { } @Override public void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, int historyFlags, int filter, long beginTimeMillis, long endTimeMillis, int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) { } @Override public void clearHistory(int uid, String packageName) { } @Override public void writeAndClearDiscreteHistory() { } @Override public void persistPendingHistory() { } @Override public void setHistoryParameters(int mode, long baseSnapshotInterval, long intervalCompressionMultiplier) { } @Override public void resetHistoryParameters() { } @Override public void clearAllHistory() { } @Override public void offsetHistory(long offsetMillis) { } @Override public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, int historyFlags, int filter, long beginTimeMillis, long endTimeMillis, int flags, String[] attributionExemptedPackages, @NonNull RemoteCallback callback) { } @Override public void addHistoricalOps(AppOpsManager.HistoricalOps ops) { } } Loading
services/core/java/com/android/server/appop/AppOpsService.java +19 −8 Original line number Diff line number Diff line Loading @@ -367,7 +367,7 @@ public class AppOpsService extends IAppOpsService.Stub { private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10); private final RateLimiter mRateLimiter = new RateLimiter(RATE_LIMITER_WINDOW); volatile @NonNull HistoricalRegistry mHistoricalRegistry; volatile @NonNull HistoricalRegistryInterface mHistoricalRegistry; /* * These are app op restrictions imposed per user from various parties. Loading Loading @@ -1056,8 +1056,12 @@ public class AppOpsService extends IAppOpsService.Stub { AppOpsManager.invalidateAppOpModeCache(); AppOpsManager.disableAppOpModeCache(); if (Flags.enableAllSqliteAppopsAccesses()) { mHistoricalRegistry = new HistoricalRegistrySql(context); } else { mHistoricalRegistry = new HistoricalRegistry(this, context); } } public void publish() { ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder()); Loading Loading @@ -1424,7 +1428,7 @@ public class AppOpsService extends IAppOpsService.Stub { @GuardedBy("this") private void packageRemovedLocked(int uid, String packageName) { getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistry::clearHistory, getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistryInterface::clearHistory, mHistoricalRegistry, uid, packageName)); UidState uidState = mUidStates.get(uid); Loading Loading @@ -1992,10 +1996,12 @@ public class AppOpsService extends IAppOpsService.Stub { new String[attributionChainExemptPackages.size()]) : null; // Must not hold the appops lock getIoHandler().post(PooledLambda.obtainRunnable(HistoricalRegistry::getHistoricalOps, getIoHandler().post(PooledLambda.obtainRunnable( HistoricalRegistryInterface::getHistoricalOps, mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType, filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray, callback).recycleOnUse()); callback ).recycleOnUse()); } @Override Loading Loading @@ -2024,7 +2030,7 @@ public class AppOpsService extends IAppOpsService.Stub { // Must not hold the appops lock getIoHandler().post(PooledLambda.obtainRunnable( HistoricalRegistry::getHistoricalOpsFromDiskRaw, HistoricalRegistryInterface::getHistoricalOpsFromDiskRaw, mHistoricalRegistry, uid, packageName, attributionTag, opNamesArray, dataType, filter, beginTimeMillis, endTimeMillis, flags, chainExemptPkgArray, callback).recycleOnUse()); Loading Loading @@ -6961,7 +6967,6 @@ public class AppOpsService extends IAppOpsService.Stub { offsetHistory_enforcePermission(); // Must not hold the appops lock mHistoricalRegistry.offsetHistory(offsetMillis); mHistoricalRegistry.offsetDiscreteHistory(offsetMillis); } @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_APPOPS) Loading Loading @@ -7002,7 +7007,13 @@ public class AppOpsService extends IAppOpsService.Stub { SystemClock.sleep(offlineDurationMillis); } mHistoricalRegistry = new HistoricalRegistry(mHistoricalRegistry); if (Flags.enableAllSqliteAppopsAccesses()) { mHistoricalRegistry = new HistoricalRegistrySql( (HistoricalRegistrySql) mHistoricalRegistry); } else { mHistoricalRegistry = new HistoricalRegistry((HistoricalRegistry) mHistoricalRegistry); } mHistoricalRegistry.systemReady(mContext.getContentResolver()); mHistoricalRegistry.persistPendingHistory(); } Loading
services/core/java/com/android/server/appop/AttributedOp.java +1 −1 Original line number Diff line number Diff line Loading @@ -163,7 +163,7 @@ final class AttributedOp { public void rejected(@AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) { rejected(System.currentTimeMillis(), uidState, flags); mAppOpsService.mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, mAppOpsService.mHistoricalRegistry.incrementOpRejectedCount(parent.op, parent.uid, parent.packageName, tag, uidState, flags); } Loading
services/core/java/com/android/server/appop/HistoricalRegistry.java +41 −26 Original line number Diff line number Diff line Loading @@ -128,7 +128,7 @@ import java.util.concurrent.TimeUnit; */ // TODO (bug:122218838): Make sure we handle start of epoch time // TODO (bug:122218838): Validate changed time is handled correctly final class HistoricalRegistry { final class HistoricalRegistry implements HistoricalRegistryInterface { private static final boolean DEBUG = false; private static final boolean KEEP_WTF_LOG = Build.IS_DEBUGGABLE; Loading Loading @@ -218,7 +218,8 @@ final class HistoricalRegistry { mDiscreteRegistry = other.mDiscreteRegistry; } void systemReady(@NonNull ContentResolver resolver) { @Override public void systemReady(@NonNull ContentResolver resolver) { mDiscreteRegistry.systemReady(); final Uri uri = Settings.Global.getUriFor(Settings.Global.APPOP_HISTORY_PARAMETERS); resolver.registerContentObserver(uri, false, new ContentObserver( Loading Loading @@ -320,8 +321,10 @@ final class HistoricalRegistry { + "=" + setting + " resetting!"); } void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @Override public void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @HistoricalOpsRequestFilter int filter) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { Loading Loading @@ -366,7 +369,8 @@ final class HistoricalRegistry { } } void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Override public void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, @Nullable String attributionTagFilter, @HistoricalOpsRequestFilter int filter, int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, Loading @@ -381,7 +385,8 @@ final class HistoricalRegistry { } } void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Override public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, Loading Loading @@ -414,11 +419,12 @@ final class HistoricalRegistry { callback.sendResult(payload); } void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) { @Override public void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @OpHistoryFlags int historyFlags, @HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @OpFlags int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) { final long currentTimeMillis = System.currentTimeMillis(); if (endTimeMillis == Long.MAX_VALUE) { endTimeMillis = currentTimeMillis; Loading Loading @@ -493,7 +499,8 @@ final class HistoricalRegistry { callback.sendResult(payload); } void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @Override public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long accessTime, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, Loading @@ -515,7 +522,8 @@ final class HistoricalRegistry { } } void incrementOpRejected(int op, int uid, @NonNull String packageName, @Override public void incrementOpRejectedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags) { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) { Loading @@ -530,7 +538,8 @@ final class HistoricalRegistry { } } void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @Override public void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState, @OpFlags int flags, long eventStartTime, long increment, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId) { Loading @@ -550,7 +559,8 @@ final class HistoricalRegistry { } } void setHistoryParameters(@HistoricalMode int mode, @Override public void setHistoryParameters(@HistoricalMode int mode, long baseSnapshotInterval, long intervalCompressionMultiplier) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { Loading Loading @@ -585,7 +595,8 @@ final class HistoricalRegistry { } } void offsetHistory(long offsetMillis) { @Override public void offsetHistory(long offsetMillis) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Loading @@ -607,13 +618,11 @@ final class HistoricalRegistry { mPersistence.persistHistoricalOpsDLocked(history); } } } void offsetDiscreteHistory(long offsetMillis) { mDiscreteRegistry.offsetHistory(offsetMillis); } void addHistoricalOps(HistoricalOps ops) { @Override public void addHistoricalOps(HistoricalOps ops) { final List<HistoricalOps> pendingWrites; synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Loading @@ -634,7 +643,8 @@ final class HistoricalRegistry { offsetHistory(offsetMillis); } void resetHistoryParameters() { @Override public void resetHistoryParameters() { if (!isPersistenceInitializedMLocked()) { Slog.d(LOG_TAG, "Interaction before persistence initialized"); return; Loading @@ -644,7 +654,8 @@ final class HistoricalRegistry { mDiscreteRegistry.setDebugMode(false); } void clearHistory(int uid, String packageName) { @Override public void clearHistory(int uid, String packageName) { synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { if (!isPersistenceInitializedMLocked()) { Loading @@ -668,11 +679,13 @@ final class HistoricalRegistry { mDiscreteRegistry.clearHistory(uid, packageName); } void writeAndClearDiscreteHistory() { @Override public void writeAndClearDiscreteHistory() { mDiscreteRegistry.writeAndClearOldAccessHistory(); } void clearAllHistory() { @Override public void clearAllHistory() { clearHistoricalRegistry(); mDiscreteRegistry.clearHistory(); } Loading Loading @@ -741,7 +754,8 @@ final class HistoricalRegistry { return mCurrentHistoricalOps; } void shutdown() { @Override public void shutdown() { synchronized (mInMemoryLock) { if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) { return; Loading @@ -752,7 +766,8 @@ final class HistoricalRegistry { mDiscreteRegistry.shutdown(); } void persistPendingHistory() { @Override public void persistPendingHistory() { final List<HistoricalOps> pendingWrites; synchronized (mOnDiskLock) { synchronized (mInMemoryLock) { Loading
services/core/java/com/android/server/appop/HistoricalRegistryInterface.java 0 → 100644 +153 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appop; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.ContentResolver; import android.os.RemoteCallback; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; /** * A registry to record app operation access events, which are generated upon an application's * access to private data or system resources. These events are stored in both aggregated * and individual/discrete formats. */ public interface HistoricalRegistryInterface { /** * A callback to inform system components are ready. */ void systemReady(@NonNull ContentResolver resolver); /** * Callback for system shutdown. */ void shutdown(); /** * Dumps aggregated/historical events to the console based on the filters. */ void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, @AppOpsManager.HistoricalOpsRequestFilter int filter); /** * Dumps discrete/individual events to the console based on filters. */ void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, @Nullable String attributionTagFilter, @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, int nDiscreteOps); /** * Record duration for given op. */ void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, long eventStartTime, long increment, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId); /** * Record access counts for given op. */ void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags, long accessTime, @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId, int accessCount); /** * Record rejected counts for given op. */ void incrementOpRejectedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags); /** * Read historical ops from both aggregated and discrete events based on input filter. */ void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @AppOpsManager.OpHistoryFlags int historyFlags, @AppOpsManager.HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback); /** * Remove app op events for a given UID and package. */ void clearHistory(int uid, String packageName); /** * A periodic callback from {@link AppOpsService} to flush the in memory discrete * app op events to disk/database. */ void writeAndClearDiscreteHistory(); /** * A callback flush the in memory app op events to disk/database. */ void persistPendingHistory(); /** * Set history parameters. * * @param mode - Whether historical registry is Active, Passive or Disabled. * @param baseSnapshotInterval - Interval between 2 snapshots, default 15 minutes. * @param intervalCompressionMultiplier - Interval compression multiplier, default is 10. */ void setHistoryParameters(@AppOpsManager.HistoricalMode int mode, long baseSnapshotInterval, long intervalCompressionMultiplier); /** * Reset history parameters to defaults. */ void resetHistoryParameters(); /** * Remove all app op accesses from both aggregated and individual event's storage. */ void clearAllHistory(); /** * Offsets the history by the given duration. */ void offsetHistory(long offsetMillis); /** * Retrieve historical app op stats for a period form disk. */ void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, @AppOpsManager.OpHistoryFlags int historyFlags, @AppOpsManager.HistoricalOpsRequestFilter int filter, long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flags, String[] attributionExemptedPackages, @NonNull RemoteCallback callback); /** * Adds ops to the history directly. This could be useful for testing especially * when the historical registry operates in passive mode. */ void addHistoricalOps(AppOpsManager.HistoricalOps ops); }
services/core/java/com/android/server/appop/HistoricalRegistrySql.java 0 → 100644 +145 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.appop; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; import android.os.RemoteCallback; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; // TODO add more documentation later /** * This historical registry implementation store app events in sqlite. The data is stored in 2 * tables 1) discrete events 2) aggregated events. */ public class HistoricalRegistrySql implements HistoricalRegistryInterface { // TODO impl will be added in a separate CL HistoricalRegistrySql(Context context) { } HistoricalRegistrySql(HistoricalRegistrySql other) { } @Override public void systemReady(@NonNull ContentResolver resolver) { } @Override public void shutdown() { } @Override public void dump(String prefix, PrintWriter pw, int filterUid, @Nullable String filterPackage, @Nullable String filterAttributionTag, int filterOp, int filter) { } @Override public void dumpDiscreteData(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter, @Nullable String attributionTagFilter, int filter, int dumpOp, @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix, int nDiscreteOps) { } @Override public void increaseOpAccessDuration(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, int uidState, int flags, long eventStartTime, long increment, int attributionFlags, int attributionChainId) { } @Override public void incrementOpAccessedCount(int op, int uid, @NonNull String packageName, @NonNull String deviceId, @Nullable String attributionTag, int uidState, int flags, long accessTime, int attributionFlags, int attributionChainId, int accessCount) { } @Override public void incrementOpRejectedCount(int op, int uid, @NonNull String packageName, @Nullable String attributionTag, int uidState, int flags) { } @Override public void getHistoricalOps(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, int historyFlags, int filter, long beginTimeMillis, long endTimeMillis, int flags, @Nullable String[] attributionExemptPkgs, @NonNull RemoteCallback callback) { } @Override public void clearHistory(int uid, String packageName) { } @Override public void writeAndClearDiscreteHistory() { } @Override public void persistPendingHistory() { } @Override public void setHistoryParameters(int mode, long baseSnapshotInterval, long intervalCompressionMultiplier) { } @Override public void resetHistoryParameters() { } @Override public void clearAllHistory() { } @Override public void offsetHistory(long offsetMillis) { } @Override public void getHistoricalOpsFromDiskRaw(int uid, @Nullable String packageName, @Nullable String attributionTag, @Nullable String[] opNames, int historyFlags, int filter, long beginTimeMillis, long endTimeMillis, int flags, String[] attributionExemptedPackages, @NonNull RemoteCallback callback) { } @Override public void addHistoricalOps(AppOpsManager.HistoricalOps ops) { } }