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

Commit a2eb48a8 authored by mrulhania's avatar mrulhania
Browse files

Minimize writes when reading app op events

We write the app op events from cache to database
to simplify and support versatile sql queries.

This change will minimize writes to the database
by only writing records for reqeusted app ops. This
helps in optimizing storage as we avoid writing
duplicate/premature app op events to storage.

Bug: 377584611
Flag: EXEMPT bug fix
Test: run all appops test locally via atest
Change-Id: I58467a3072690434e288ca598a552351b6a906ca
parent ef9bbd0d
Loading
Loading
Loading
Loading
+38 −13
Original line number Original line Diff line number Diff line
@@ -118,7 +118,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
    @Override
    @Override
    void shutdown() {
    void shutdown() {
        mSqliteWriteHandler.removeAllPendingMessages();
        mSqliteWriteHandler.removeAllPendingMessages();
        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAllAppOpEvents());
    }
    }


    @Override
    @Override
@@ -172,10 +172,14 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
            @Nullable String[] opNamesFilter,
            @Nullable String[] opNamesFilter,
            @Nullable String attributionTagFilter, int opFlagsFilter,
            @Nullable String attributionTagFilter, int opFlagsFilter,
            Set<String> attributionExemptPkgs) {
            Set<String> attributionExemptPkgs) {
        IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
        // flush the cache into database before read.
        // flush the cache into database before read.
        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
        if (opCodes != null) {
            mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAppOpEvents(opCodes));
        } else {
            mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAllAppOpEvents());
        }
        boolean assembleChains = attributionExemptPkgs != null;
        boolean assembleChains = attributionExemptPkgs != null;
        IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
        beginTimeMillis = Math.max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
        beginTimeMillis = Math.max(beginTimeMillis, Instant.now().minus(sDiscreteHistoryCutoff,
                ChronoUnit.MILLIS).toEpochMilli());
                ChronoUnit.MILLIS).toEpochMilli());
        List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
        List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
@@ -214,7 +218,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
            int nDiscreteOps) {
            int nDiscreteOps) {
        // flush the cache into database before dump.
        // flush the cache into database before dump.
        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.evictAllAppOpEvents());
        IntArray opCodes = new IntArray();
        IntArray opCodes = new IntArray();
        if (dumpOp != AppOpsManager.OP_NONE) {
        if (dumpOp != AppOpsManager.OP_NONE) {
            opCodes.add(dumpOp);
            opCodes.add(dumpOp);
@@ -366,7 +370,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
                    try {
                    try {
                        List<DiscreteOp> evictedEvents;
                        List<DiscreteOp> evictedEvents;
                        synchronized (mDiscreteOpCache) {
                        synchronized (mDiscreteOpCache) {
                            evictedEvents = mDiscreteOpCache.evict();
                            evictedEvents = mDiscreteOpCache.evictOldAppOpEvents();
                        }
                        }
                        mDiscreteOpsDbHelper.insertDiscreteOps(evictedEvents);
                        mDiscreteOpsDbHelper.insertDiscreteOps(evictedEvents);
                    } finally {
                    } finally {
@@ -389,7 +393,7 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
                    try {
                    try {
                        List<DiscreteOp> evictedEvents;
                        List<DiscreteOp> evictedEvents;
                        synchronized (mDiscreteOpCache) {
                        synchronized (mDiscreteOpCache) {
                            evictedEvents = mDiscreteOpCache.evict();
                            evictedEvents = mDiscreteOpCache.evictOldAppOpEvents();
                            // if nothing to evict, just write the whole cache to database.
                            // if nothing to evict, just write the whole cache to database.
                            if (evictedEvents.isEmpty()
                            if (evictedEvents.isEmpty()
                                    && mDiscreteOpCache.size() >= mDiscreteOpCache.capacity()) {
                                    && mDiscreteOpCache.size() >= mDiscreteOpCache.capacity()) {
@@ -451,9 +455,10 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
        }
        }


        /**
        /**
         * Evict entries older than {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization}.
         * Evict entries older than {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization} i.e.
         * app op events older than one minute (default quantization) will be evicted.
         */
         */
        private List<DiscreteOp> evict() {
        private List<DiscreteOp> evictOldAppOpEvents() {
            synchronized (this) {
            synchronized (this) {
                List<DiscreteOp> evictedEvents = new ArrayList<>();
                List<DiscreteOp> evictedEvents = new ArrayList<>();
                Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
                Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
@@ -470,11 +475,9 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
        }
        }


        /**
        /**
         * Remove all the entries from cache.
         * Evict all app op entries from cache, and return the list of removed ops.
         *
         * @return return all removed entries.
         */
         */
        public List<DiscreteOp> getAllEventsAndClear() {
        public List<DiscreteOp> evictAllAppOpEvents() {
            synchronized (this) {
            synchronized (this) {
                List<DiscreteOp> cachedOps = new ArrayList<>(mCache.size());
                List<DiscreteOp> cachedOps = new ArrayList<>(mCache.size());
                if (mCache.isEmpty()) {
                if (mCache.isEmpty()) {
@@ -486,6 +489,25 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
            }
            }
        }
        }


        /**
         * Evict specified app ops from cache, and return the list of evicted ops.
         */
        public List<DiscreteOp> evictAppOpEvents(IntArray ops) {
            synchronized (this) {
                List<DiscreteOp> evictedOps = new ArrayList<>();
                if (mCache.isEmpty()) {
                    return evictedOps;
                }
                for (DiscreteOp discreteOp: mCache) {
                    if (ops.contains(discreteOp.getOpCode())) {
                        evictedOps.add(discreteOp);
                    }
                }
                evictedOps.forEach(mCache::remove);
                return evictedOps;
            }
        }

        int size() {
        int size() {
            return mCache.size();
            return mCache.size();
        }
        }
@@ -646,7 +668,10 @@ public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
                    + ", uidState=" + getUidStateName(mUidState)
                    + ", uidState=" + getUidStateName(mUidState)
                    + ", chainId=" + mChainId
                    + ", chainId=" + mChainId
                    + ", accessTime=" + mAccessTime
                    + ", accessTime=" + mAccessTime
                    + ", duration=" + mDuration + '}';
                    + ", mDiscretizedAccessTime=" + mDiscretizedAccessTime
                    + ", duration=" + mDuration
                    + ", mDiscretizedDuration=" + mDiscretizedDuration
                    + '}';
        }
        }


        public int getUid() {
        public int getUid() {
+2 −1
Original line number Original line Diff line number Diff line
@@ -97,7 +97,8 @@ public class DiscreteAppOpSqlPersistenceTest {
        mDiscreteRegistry.recordDiscreteAccess(opEvent2);
        mDiscreteRegistry.recordDiscreteAccess(opEvent2);
        List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
        List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();


        assertThat(discreteOps.size()).isEqualTo(1);
        assertWithMessage("Expected list size is 1, but the list is: " + discreteOps)
                .that(discreteOps.size()).isEqualTo(1);
        assertThat(discreteOps).contains(opEvent);
        assertThat(discreteOps).contains(opEvent);
    }
    }