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

Commit b1d71f88 authored by Stanislav Zholnin's avatar Stanislav Zholnin
Browse files

Introduce caching to DiscreteRegistry.

Cached representation of state on disk is kept in the memory
after API query until next time data is dump to disk. Second and
following API queries will not hit the disk.
Other improvements and bug fixes.

Bug: 176965672
Test: in development
Change-Id: I583cba4c74487c79f023efd4b3ce2ee844351719
parent 884614ff
Loading
Loading
Loading
Loading
+242 −105
Original line number Original line Diff line number Diff line
@@ -49,8 +49,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.XmlUtils;


import libcore.util.EmptyArray;

import java.io.File;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileOutputStream;
@@ -85,6 +83,8 @@ final class DiscreteRegistry {
    private static final String TAG = DiscreteRegistry.class.getSimpleName();
    private static final String TAG = DiscreteRegistry.class.getSimpleName();


    private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
    private static final long TIMELINE_HISTORY_CUTOFF = Duration.ofHours(24).toMillis();
    private static final long TIMELINE_QUANTIZATION = Duration.ofMinutes(1).toMillis();

    private static final String TAG_HISTORY = "h";
    private static final String TAG_HISTORY = "h";
    private static final String ATTR_VERSION = "v";
    private static final String ATTR_VERSION = "v";
    private static final int CURRENT_VERSION = 1;
    private static final int CURRENT_VERSION = 1;
@@ -107,6 +107,8 @@ final class DiscreteRegistry {
    private static final String ATTR_UID_STATE = "us";
    private static final String ATTR_UID_STATE = "us";
    private static final String ATTR_FLAGS = "f";
    private static final String ATTR_FLAGS = "f";


    private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED;

    // Lock for read/write access to on disk state
    // Lock for read/write access to on disk state
    private final Object mOnDiskLock = new Object();
    private final Object mOnDiskLock = new Object();


@@ -119,6 +121,9 @@ final class DiscreteRegistry {
    @GuardedBy("mInMemoryLock")
    @GuardedBy("mInMemoryLock")
    private DiscreteOps mDiscreteOps;
    private DiscreteOps mDiscreteOps;


    @GuardedBy("mOnDiskLock")
    private DiscreteOps mCachedOps = null;

    DiscreteRegistry(Object inMemoryLock) {
    DiscreteRegistry(Object inMemoryLock) {
        mInMemoryLock = inMemoryLock;
        mInMemoryLock = inMemoryLock;
        mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
        mDiscreteAccessDir = new File(new File(Environment.getDataSystemDirectory(), "appops"),
@@ -173,18 +178,19 @@ final class DiscreteRegistry {
                    }
                    }
                }
                }
            }
            }
        }
            DiscreteOps discreteOps;
            DiscreteOps discreteOps;
            synchronized (mInMemoryLock) {
            synchronized (mInMemoryLock) {
                discreteOps = mDiscreteOps;
                discreteOps = mDiscreteOps;
                mDiscreteOps = new DiscreteOps();
                mDiscreteOps = new DiscreteOps();
                mCachedOps = null;
            }
            }
            if (discreteOps.isEmpty()) {
            if (discreteOps.isEmpty()) {
                return;
                return;
            }
            }
            long currentTimeStamp = Instant.now().toEpochMilli();
            long currentTimeStamp = Instant.now().toEpochMilli();
            try {
            try {
            final File file = new File(mDiscreteAccessDir, currentTimeStamp + TIMELINE_FILE_SUFFIX);
                final File file = new File(mDiscreteAccessDir,
                        currentTimeStamp + TIMELINE_FILE_SUFFIX);
                discreteOps.writeToFile(file);
                discreteOps.writeToFile(file);
            } catch (Throwable t) {
            } catch (Throwable t) {
                Slog.e(TAG,
                Slog.e(TAG,
@@ -192,30 +198,39 @@ final class DiscreteRegistry {
                                + Arrays.toString(t.getStackTrace()));
                                + Arrays.toString(t.getStackTrace()));
            }
            }
        }
        }
    }


    void getHistoricalDiscreteOps(AppOpsManager.HistoricalOps result, long beginTimeMillis,
    void getHistoricalDiscreteOps(AppOpsManager.HistoricalOps result, long beginTimeMillis,
            long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
            long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
            @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
            @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
        writeAndClearAccessHistory();
        DiscreteOps discreteOps = getAndCacheDiscreteOps();
        DiscreteOps discreteOps = new DiscreteOps();
        discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
        readDiscreteOpsFromDisk(discreteOps, beginTimeMillis, endTimeMillis, filter, uidFilter,
                opNamesFilter, attributionTagFilter, flagsFilter);
                packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
        discreteOps.applyToHistoricalOps(result);
        discreteOps.applyToHistoricalOps(result);
        return;
        return;
    }
    }


    private void readDiscreteOpsFromDisk(DiscreteOps discreteOps, long beginTimeMillis,
    private DiscreteOps getAndCacheDiscreteOps() {
            long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
        DiscreteOps discreteOps = new DiscreteOps();
            @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,

            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
        synchronized (mOnDiskLock) {
        synchronized (mOnDiskLock) {
            long historyBeginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
            synchronized (mInMemoryLock) {
                    ChronoUnit.MILLIS).toEpochMilli();
                discreteOps.merge(mDiscreteOps);
            if (historyBeginTimeMillis > endTimeMillis) {
                return;
            }
            }
            beginTimeMillis = max(beginTimeMillis, historyBeginTimeMillis);
            if (mCachedOps == null) {
                mCachedOps = new DiscreteOps();
                readDiscreteOpsFromDisk(mCachedOps);
            }
            discreteOps.merge(mCachedOps);
        }
        return discreteOps;
    }

    private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
        synchronized (mOnDiskLock) {
            long beginTimeMillis = Instant.now().minus(TIMELINE_HISTORY_CUTOFF,
                    ChronoUnit.MILLIS).toEpochMilli();


            final File[] files = mDiscreteAccessDir.listFiles();
            final File[] files = mDiscreteAccessDir.listFiles();
            if (files != null && files.length > 0) {
            if (files != null && files.length > 0) {
@@ -229,8 +244,7 @@ final class DiscreteRegistry {
                    if (timestamp < beginTimeMillis) {
                    if (timestamp < beginTimeMillis) {
                        continue;
                        continue;
                    }
                    }
                    discreteOps.readFromFile(f, beginTimeMillis, endTimeMillis, filter, uidFilter,
                    discreteOps.readFromFile(f, beginTimeMillis);
                            packageNameFilter, opNamesFilter, attributionTagFilter, flagsFilter);
                }
                }
            }
            }
        }
        }
@@ -251,15 +265,11 @@ final class DiscreteRegistry {
            @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
            @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
            int nDiscreteOps) {
            int nDiscreteOps) {
        DiscreteOps discreteOps = new DiscreteOps();
        DiscreteOps discreteOps = getAndCacheDiscreteOps();
        synchronized (mOnDiskLock) {
        String[] opNamesFilter = dumpOp == OP_NONE ? null
            writeAndClearAccessHistory();
            String[] opNamesFilter = dumpOp == OP_NONE ? EmptyArray.STRING
                : new String[]{AppOpsManager.opToPublicName(dumpOp)};
                : new String[]{AppOpsManager.opToPublicName(dumpOp)};
            readDiscreteOpsFromDisk(discreteOps, 0, Instant.now().toEpochMilli(), filter,
        discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
                    uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
                opNamesFilter, attributionTagFilter, OP_FLAGS_ALL);
                    OP_FLAGS_ALL);
        }
        discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
        discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
    }
    }


@@ -270,7 +280,7 @@ final class DiscreteRegistry {
        if (!isDiscreteUid(uid)) {
        if (!isDiscreteUid(uid)) {
            return false;
            return false;
        }
        }
        if ((flags & (OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED)) == 0) {
        if ((flags & (OP_FLAGS_DISCRETE)) == 0) {
            return false;
            return false;
        }
        }
        return true;
        return true;
@@ -298,6 +308,19 @@ final class DiscreteRegistry {
            mUids = new ArrayMap<>();
            mUids = new ArrayMap<>();
        }
        }


        boolean isEmpty() {
            return mUids.isEmpty();
        }

        void merge(DiscreteOps other) {
            int nUids = other.mUids.size();
            for (int i = 0; i < nUids; i++) {
                int uid = other.mUids.keyAt(i);
                DiscreteUidOps uidOps = other.mUids.valueAt(i);
                getOrCreateDiscreteUidOps(uid).merge(uidOps);
            }
        }

        void addDiscreteAccess(int op, int uid, @NonNull String packageName,
        void addDiscreteAccess(int op, int uid, @NonNull String packageName,
                @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
                @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
                @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
                @AppOpsManager.UidState int uidState, long accessTime, long accessDuration) {
@@ -305,6 +328,25 @@ final class DiscreteRegistry {
                    uidState, accessTime, accessDuration);
                    uidState, accessTime, accessDuration);
        }
        }


        private void filter(long beginTimeMillis, long endTimeMillis,
                @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
                @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
            if ((filter & FILTER_BY_UID) != 0) {
                ArrayMap<Integer, DiscreteUidOps> uids = new ArrayMap<>();
                uids.put(uidFilter, getOrCreateDiscreteUidOps(uidFilter));
                mUids = uids;
            }
            int nUids = mUids.size();
            for (int i = nUids - 1; i >= 0; i--) {
                mUids.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, packageNameFilter,
                        opNamesFilter, attributionTagFilter, flagsFilter);
                if (mUids.valueAt(i).isEmpty()) {
                    mUids.removeAt(i);
                }
            }
        }

        private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
        private void applyToHistoricalOps(AppOpsManager.HistoricalOps result) {
            int nUids = mUids.size();
            int nUids = mUids.size();
            for (int i = 0; i < nUids; i++) {
            for (int i = 0; i < nUids; i++) {
@@ -353,14 +395,7 @@ final class DiscreteRegistry {
            return result;
            return result;
        }
        }


        boolean isEmpty() {
        private void readFromFile(File f, long beginTimeMillis) {
            return mUids.isEmpty();
        }

        private void readFromFile(File f, long beginTimeMillis, long endTimeMillis,
                @AppOpsManager.HistoricalOpsRequestFilter int filter,
                int uidFilter, @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
            try {
            try {
                FileInputStream stream = new FileInputStream(f);
                FileInputStream stream = new FileInputStream(f);
                TypedXmlPullParser parser = Xml.resolvePullParser(stream);
                TypedXmlPullParser parser = Xml.resolvePullParser(stream);
@@ -377,12 +412,7 @@ final class DiscreteRegistry {
                while (XmlUtils.nextElementWithin(parser, depth)) {
                while (XmlUtils.nextElementWithin(parser, depth)) {
                    if (TAG_UID.equals(parser.getName())) {
                    if (TAG_UID.equals(parser.getName())) {
                        int uid = parser.getAttributeInt(null, ATTR_UID, -1);
                        int uid = parser.getAttributeInt(null, ATTR_UID, -1);
                        if ((filter & FILTER_BY_UID) != 0 && uid != uidFilter) {
                        getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis);
                            continue;
                        }
                        getOrCreateDiscreteUidOps(uid).deserialize(parser, beginTimeMillis,
                                endTimeMillis, filter, packageNameFilter, opNamesFilter,
                                attributionTagFilter, flagsFilter);
                    }
                    }
                }
                }
            } catch (Throwable t) {
            } catch (Throwable t) {
@@ -400,6 +430,38 @@ final class DiscreteRegistry {
            mPackages = new ArrayMap<>();
            mPackages = new ArrayMap<>();
        }
        }


        boolean isEmpty() {
            return mPackages.isEmpty();
        }

        void merge(DiscreteUidOps other) {
            int nPackages = other.mPackages.size();
            for (int i = 0; i < nPackages; i++) {
                String packageName = other.mPackages.keyAt(i);
                DiscretePackageOps p = other.mPackages.valueAt(i);
                getOrCreateDiscretePackageOps(packageName).merge(p);
            }
        }

        private void filter(long beginTimeMillis, long endTimeMillis,
                @AppOpsManager.HistoricalOpsRequestFilter int filter,
                @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
            if ((filter & FILTER_BY_PACKAGE_NAME) != 0) {
                ArrayMap<String, DiscretePackageOps> packages = new ArrayMap<>();
                packages.put(packageNameFilter, getOrCreateDiscretePackageOps(packageNameFilter));
                mPackages = packages;
            }
            int nPackages = mPackages.size();
            for (int i = nPackages - 1; i >= 0; i--) {
                mPackages.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter, opNamesFilter,
                        attributionTagFilter, flagsFilter);
                if (mPackages.valueAt(i).isEmpty()) {
                    mPackages.removeAt(i);
                }
            }
        }

        void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
        void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                long accessTime, long accessDuration) {
                long accessTime, long accessDuration) {
@@ -445,22 +507,12 @@ final class DiscreteRegistry {
            }
            }
        }
        }


        void deserialize(TypedXmlPullParser parser, long beginTimeMillis,
        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
                long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter,
                @Nullable String packageNameFilter,
                @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
                @AppOpsManager.OpFlags int flagsFilter) throws Exception {
            int depth = parser.getDepth();
            int depth = parser.getDepth();
            while (XmlUtils.nextElementWithin(parser, depth)) {
            while (XmlUtils.nextElementWithin(parser, depth)) {
                if (TAG_PACKAGE.equals(parser.getName())) {
                if (TAG_PACKAGE.equals(parser.getName())) {
                    String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
                    String packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
                    if ((filter & FILTER_BY_PACKAGE_NAME) != 0
                    getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis);
                            && !packageName.equals(packageNameFilter)) {
                        continue;
                    }
                    getOrCreateDiscretePackageOps(packageName).deserialize(parser, beginTimeMillis,
                            endTimeMillis, filter, opNamesFilter, attributionTagFilter,
                            flagsFilter);
                }
                }
            }
            }
        }
        }
@@ -473,6 +525,10 @@ final class DiscreteRegistry {
            mPackageOps = new ArrayMap<>();
            mPackageOps = new ArrayMap<>();
        }
        }


        boolean isEmpty() {
            return mPackageOps.isEmpty();
        }

        void addDiscreteAccess(int op, @Nullable String attributionTag,
        void addDiscreteAccess(int op, @Nullable String attributionTag,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                long accessTime, long accessDuration) {
                long accessTime, long accessDuration) {
@@ -480,6 +536,35 @@ final class DiscreteRegistry {
                    accessDuration);
                    accessDuration);
        }
        }


        void merge(DiscretePackageOps other) {
            int nOps = other.mPackageOps.size();
            for (int i = 0; i < nOps; i++) {
                int opId = other.mPackageOps.keyAt(i);
                DiscreteOp op = other.mPackageOps.valueAt(i);
                getOrCreateDiscreteOp(opId).merge(op);
            }
        }

        private void filter(long beginTimeMillis, long endTimeMillis,
                @AppOpsManager.HistoricalOpsRequestFilter int filter,
                @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
                @AppOpsManager.OpFlags int flagsFilter) {
            int nOps = mPackageOps.size();
            for (int i = nOps - 1; i >= 0; i--) {
                int opId = mPackageOps.keyAt(i);
                if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
                        AppOpsManager.opToPublicName(opId))) {
                    mPackageOps.removeAt(i);
                    continue;
                }
                mPackageOps.valueAt(i).filter(beginTimeMillis, endTimeMillis, filter,
                        attributionTagFilter, flagsFilter);
                if (mPackageOps.valueAt(i).isEmpty()) {
                    mPackageOps.removeAt(i);
                }
            }
        }

        private DiscreteOp getOrCreateDiscreteOp(int op) {
        private DiscreteOp getOrCreateDiscreteOp(int op) {
            DiscreteOp result = mPackageOps.get(op);
            DiscreteOp result = mPackageOps.get(op);
            if (result == null) {
            if (result == null) {
@@ -519,20 +604,12 @@ final class DiscreteRegistry {
            }
            }
        }
        }


        void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
                @AppOpsManager.HistoricalOpsRequestFilter int filter,
                @Nullable String[] opNamesFilter, @Nullable String attributionTagFilter,
                @AppOpsManager.OpFlags int flagsFilter) throws Exception {
            int depth = parser.getDepth();
            int depth = parser.getDepth();
            while (XmlUtils.nextElementWithin(parser, depth)) {
            while (XmlUtils.nextElementWithin(parser, depth)) {
                if (TAG_OP.equals(parser.getName())) {
                if (TAG_OP.equals(parser.getName())) {
                    int op = parser.getAttributeInt(null, ATTR_OP_ID);
                    int op = parser.getAttributeInt(null, ATTR_OP_ID);
                    if ((filter & FILTER_BY_OP_NAMES) != 0 && !ArrayUtils.contains(opNamesFilter,
                    getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis);
                            AppOpsManager.opToPublicName(op))) {
                        continue;
                    }
                    getOrCreateDiscreteOp(op).deserialize(parser, beginTimeMillis, endTimeMillis,
                            filter, attributionTagFilter, flagsFilter);
                }
                }
            }
            }
        }
        }
@@ -545,31 +622,66 @@ final class DiscreteRegistry {
            mAttributedOps = new ArrayMap<>();
            mAttributedOps = new ArrayMap<>();
        }
        }


        boolean isEmpty() {
            return mAttributedOps.isEmpty();
        }

        void merge(DiscreteOp other) {
            int nTags = other.mAttributedOps.size();
            for (int i = 0; i < nTags; i++) {
                String tag = other.mAttributedOps.keyAt(i);
                List<DiscreteOpEvent> otherEvents = other.mAttributedOps.valueAt(i);
                List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(tag);
                mAttributedOps.put(tag, stableListMerge(events, otherEvents));
            }
        }

        private void filter(long beginTimeMillis, long endTimeMillis,
                @AppOpsManager.HistoricalOpsRequestFilter int filter,
                @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter) {
            if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0) {
                ArrayMap<String, List<DiscreteOpEvent>> attributedOps = new ArrayMap<>();
                attributedOps.put(attributionTagFilter,
                        getOrCreateDiscreteOpEventsList(attributionTagFilter));
                mAttributedOps = attributedOps;
            }

            int nTags = mAttributedOps.size();
            for (int i = nTags - 1; i >= 0; i--) {
                String tag = mAttributedOps.keyAt(i);
                List<DiscreteOpEvent> list = mAttributedOps.valueAt(i);
                list = filterEventsList(list, beginTimeMillis, endTimeMillis, flagsFilter);
                mAttributedOps.put(tag, list);
                if (list.size() == 0) {
                    mAttributedOps.removeAt(i);
                }
            }
        }

        void addDiscreteAccess(@Nullable String attributionTag,
        void addDiscreteAccess(@Nullable String attributionTag,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                long accessTime, long accessDuration) {
                long accessTime, long accessDuration) {
            List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
            List<DiscreteOpEvent> attributedOps = getOrCreateDiscreteOpEventsList(
                    attributionTag);
                    attributionTag);
            accessTime = Instant.ofEpochMilli(accessTime).truncatedTo(
            accessTime = accessTime / TIMELINE_QUANTIZATION * TIMELINE_QUANTIZATION;
                    ChronoUnit.MINUTES).toEpochMilli();


            int nAttributedOps = attributedOps.size();
            int nAttributedOps = attributedOps.size();
            for (int i = nAttributedOps - 1; i >= 0; i--) {
            int i = nAttributedOps;
                DiscreteOpEvent previousOp = attributedOps.get(i);
            for (; i > 0; i--) {
                if (i == nAttributedOps - 1 && previousOp.mNoteTime == accessTime
                DiscreteOpEvent previousOp = attributedOps.get(i - 1);
                        && accessDuration > -1) {
                    // existing event with updated duration
                    attributedOps.remove(i);
                    break;
                }
                if (previousOp.mNoteTime < accessTime) {
                if (previousOp.mNoteTime < accessTime) {
                    break;
                    break;
                }
                }
                if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
                if (previousOp.mOpFlag == flags && previousOp.mUidState == uidState) {
                    if (accessDuration != previousOp.mNoteDuration
                            && accessDuration > TIMELINE_QUANTIZATION) {
                        break;
                    } else {
                        return;
                        return;
                    }
                    }
                }
                }
            attributedOps.add(new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
            }
            attributedOps.add(i, new DiscreteOpEvent(accessTime, accessDuration, uidState, flags));
        }
        }


        private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
        private List<DiscreteOpEvent> getOrCreateDiscreteOpEventsList(String attributionTag) {
@@ -633,18 +745,11 @@ final class DiscreteRegistry {
            }
            }
        }
        }


        void deserialize(TypedXmlPullParser parser, long beginTimeMillis, long endTimeMillis,
        void deserialize(TypedXmlPullParser parser, long beginTimeMillis) throws Exception {
                @AppOpsManager.HistoricalOpsRequestFilter int filter,
                @Nullable String attributionTagFilter,
                @AppOpsManager.OpFlags int flagsFilter) throws Exception {
            int outerDepth = parser.getDepth();
            int outerDepth = parser.getDepth();
            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
                if (TAG_TAG.equals(parser.getName())) {
                if (TAG_TAG.equals(parser.getName())) {
                    String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
                    String attributionTag = parser.getAttributeValue(null, ATTR_TAG);
                    if ((filter & FILTER_BY_ATTRIBUTION_TAG) != 0 && !attributionTag.equals(
                            attributionTagFilter)) {
                        continue;
                    }
                    List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
                    List<DiscreteOpEvent> events = getOrCreateDiscreteOpEventsList(
                            attributionTag);
                            attributionTag);
                    int innerDepth = parser.getDepth();
                    int innerDepth = parser.getDepth();
@@ -655,11 +760,7 @@ final class DiscreteRegistry {
                                    -1);
                                    -1);
                            int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
                            int uidState = parser.getAttributeInt(null, ATTR_UID_STATE);
                            int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
                            int opFlags = parser.getAttributeInt(null, ATTR_FLAGS);
                            if ((flagsFilter & opFlags) == 0) {
                            if (noteTime + noteDuration < beginTimeMillis) {
                                continue;
                            }
                            if ((noteTime + noteDuration < beginTimeMillis
                                    && noteTime > endTimeMillis)) {
                                continue;
                                continue;
                            }
                            }
                            DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
                            DiscreteOpEvent event = new DiscreteOpEvent(noteTime, noteDuration,
@@ -715,5 +816,41 @@ final class DiscreteRegistry {
            out.attributeInt(null, ATTR_FLAGS, mOpFlag);
            out.attributeInt(null, ATTR_FLAGS, mOpFlag);
        }
        }
    }
    }

    private static List<DiscreteOpEvent> stableListMerge(List<DiscreteOpEvent> a,
            List<DiscreteOpEvent> b) {
        int nA = a.size();
        int nB = b.size();
        int i = 0;
        int k = 0;
        List<DiscreteOpEvent> result = new ArrayList<>(nA + nB);
        while (i < nA || k < nB) {
            if (i == nA) {
                result.add(b.get(k++));
            } else if (k == nB) {
                result.add(a.get(i++));
            } else if (a.get(i).mNoteTime < b.get(k).mNoteTime) {
                result.add(a.get(i++));
            } else {
                result.add(b.get(k++));
            }
        }
        return result;
    }

    private static List<DiscreteOpEvent> filterEventsList(List<DiscreteOpEvent> list,
            long beginTimeMillis, long endTimeMillis, @AppOpsManager.OpFlags int flagsFilter) {
        int n = list.size();
        List<DiscreteOpEvent> result = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            DiscreteOpEvent event = list.get(i);
            if ((event.mOpFlag & flagsFilter) != 0
                    && event.mNoteTime + event.mNoteDuration > beginTimeMillis
                    && event.mNoteTime < endTimeMillis) {
                result.add(event);
            }
        }
        return result;
    }
}
}
+1 −1
Original line number Original line Diff line number Diff line
@@ -532,7 +532,7 @@ final class HistoricalRegistry {
                        System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
                        System.currentTimeMillis()).increaseAccessDuration(op, uid, packageName,
                        attributionTag, uidState, flags, increment);
                        attributionTag, uidState, flags, increment);
                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
                mDiscreteRegistry.recordDiscreteAccess(uid, packageName, op, attributionTag,
                        flags, uidState, increment, eventStartTime);
                        flags, uidState, eventStartTime, increment);
            }
            }
        }
        }
    }
    }