Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d9f6f55 authored by Manjeet Rulhania's avatar Manjeet Rulhania Committed by Android (Google) Code Review
Browse files

Merge "Create interface for historical registry" into main

parents a6bf8fdd 8832165c
Loading
Loading
Loading
Loading
+19 −8
Original line number Diff line number Diff line
@@ -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.
@@ -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());
@@ -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);
@@ -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
@@ -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());
@@ -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)
@@ -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();
    }
+1 −1
Original line number Diff line number Diff line
@@ -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);
    }

+41 −26
Original line number Diff line number Diff line
@@ -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;

@@ -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(
@@ -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) {
@@ -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,
@@ -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,
@@ -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;
@@ -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,
@@ -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) {
@@ -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) {
@@ -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) {
@@ -585,7 +595,8 @@ final class HistoricalRegistry {
        }
    }

    void offsetHistory(long offsetMillis) {
    @Override
    public void offsetHistory(long offsetMillis) {
        synchronized (mOnDiskLock) {
            synchronized (mInMemoryLock) {
                if (!isPersistenceInitializedMLocked()) {
@@ -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()) {
@@ -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;
@@ -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()) {
@@ -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();
    }
@@ -741,7 +754,8 @@ final class HistoricalRegistry {
        return mCurrentHistoricalOps;
    }

    void shutdown() {
    @Override
    public void shutdown() {
        synchronized (mInMemoryLock) {
            if (mMode == AppOpsManager.HISTORICAL_MODE_DISABLED) {
                return;
@@ -752,7 +766,8 @@ final class HistoricalRegistry {
        mDiscreteRegistry.shutdown();
    }

    void persistPendingHistory() {
    @Override
    public void persistPendingHistory() {
        final List<HistoricalOps> pendingWrites;
        synchronized (mOnDiskLock) {
            synchronized (mInMemoryLock) {
+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);
}
+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) {

    }
}