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

Commit 3836d735 authored by Stanislav Zholnin's avatar Stanislav Zholnin
Browse files

Remove data from DiscreteRegistry for uninstalled packages.

Refactoring and bug fixes.

Bug: 176965672
Test: in development
Change-Id: I6d3956df87e7a924cedb545d6f9eae3cfb41062b
parent 008e3b80
Loading
Loading
Loading
Loading
+133 −84
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.os.FileUtils;
import android.os.Process;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -83,7 +84,7 @@ import java.util.List;
 */

final class DiscreteRegistry {
    static final String TIMELINE_FILE_SUFFIX = "tl";
    static final String DISCRETE_HISTORY_FILE_SUFFIX = "tl";
    private static final String TAG = DiscreteRegistry.class.getSimpleName();

    private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
@@ -194,17 +195,6 @@ final class DiscreteRegistry {
        }
    }

    private void createDiscreteAccessDir() {
        if (!mDiscreteAccessDir.exists()) {
            if (!mDiscreteAccessDir.mkdirs()) {
                Slog.e(TAG, "Failed to create DiscreteRegistry directory");
            }
            FileUtils.setPermissions(mDiscreteAccessDir.getPath(),
                    FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
        }
    }

    /* can be called only after HistoricalRegistry.isPersistenceInitialized() check */
    void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
            @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
            long accessDuration) {
@@ -220,80 +210,34 @@ final class DiscreteRegistry {
    void writeAndClearAccessHistory() {
        synchronized (mOnDiskLock) {
            if (mDiscreteAccessDir == null) {
                Slog.e(TAG, "State not saved - persistence not initialized.");
                Slog.d(TAG, "State not saved - persistence not initialized.");
                return;
            }
            final File[] files = mDiscreteAccessDir.listFiles();
            if (files != null && files.length > 0) {
                for (File f : files) {
                    final String fileName = f.getName();
                    if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) {
                        continue;
                    }
                    try {
                        long timestamp = Long.valueOf(fileName.substring(0,
                                fileName.length() - TIMELINE_FILE_SUFFIX.length()));
                        if (Instant.now().minus(sDiscreteHistoryCutoff,
                                ChronoUnit.MILLIS).toEpochMilli() > timestamp) {
                            f.delete();
                            Slog.e(TAG, "Deleting file " + fileName);

                        }
                    } catch (Throwable t) {
                        Slog.e(TAG, "Error while cleaning timeline files: " + t.getMessage() + " "
                                + t.getStackTrace());
                    }
                }
            }
            DiscreteOps discreteOps;
            synchronized (mInMemoryLock) {
                discreteOps = mDiscreteOps;
                mDiscreteOps = new DiscreteOps();
                mCachedOps = null;
            }
            if (discreteOps.isEmpty()) {
                return;
            }
            long currentTimeStamp = Instant.now().toEpochMilli();
            try {
                final File file = new File(mDiscreteAccessDir,
                        currentTimeStamp + TIMELINE_FILE_SUFFIX);
                discreteOps.writeToFile(file);
            } catch (Throwable t) {
                Slog.e(TAG,
                        "Error writing timeline state: " + t.getMessage() + " "
                                + Arrays.toString(t.getStackTrace()));
            deleteOldDiscreteHistoryFilesLocked();
            if (!discreteOps.isEmpty()) {
                persistDiscreteOpsLocked(discreteOps);
            }
        }
    }

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

    private DiscreteOps getAndCacheDiscreteOps() {
        DiscreteOps discreteOps = new DiscreteOps();

        synchronized (mOnDiskLock) {
            synchronized (mInMemoryLock) {
                discreteOps.merge(mDiscreteOps);
            }
            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(sDiscreteHistoryCutoff,
@@ -303,11 +247,11 @@ final class DiscreteRegistry {
            if (files != null && files.length > 0) {
                for (File f : files) {
                    final String fileName = f.getName();
                    if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) {
                    if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
                        continue;
                    }
                    long timestamp = Long.valueOf(fileName.substring(0,
                            fileName.length() - TIMELINE_FILE_SUFFIX.length()));
                            fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
                    if (timestamp < beginTimeMillis) {
                        continue;
                    }
@@ -322,8 +266,19 @@ final class DiscreteRegistry {
            synchronized (mInMemoryLock) {
                mDiscreteOps = new DiscreteOps();
            }
            FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
            createDiscreteAccessDir();
            clearOnDiskHistoryLocked();
        }
    }

    void clearHistory(int uid, String packageName) {
        synchronized (mOnDiskLock) {
            DiscreteOps discreteOps;
            synchronized (mInMemoryLock) {
                discreteOps = getAllDiscreteOps();
                clearHistory();
            }
            discreteOps.clearHistory(uid, packageName);
            persistDiscreteOpsLocked(discreteOps);
        }
    }

@@ -332,7 +287,7 @@ final class DiscreteRegistry {
            @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
            int nDiscreteOps) {
        DiscreteOps discreteOps = getAndCacheDiscreteOps();
        DiscreteOps discreteOps = getAllDiscreteOps();
        String[] opNamesFilter = dumpOp == OP_NONE ? null
                : new String[]{AppOpsManager.opToPublicName(dumpOp)};
        discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
@@ -340,17 +295,26 @@ final class DiscreteRegistry {
        discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
    }

    public static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) {
        if (!ArrayUtils.contains(sDiscreteOps, op)) {
            return false;
    private void clearOnDiskHistoryLocked() {
        mCachedOps = null;
        FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
        createDiscreteAccessDir();
    }
        if (uid < Process.FIRST_APPLICATION_UID) {
            return false;

    private DiscreteOps getAllDiscreteOps() {
        DiscreteOps discreteOps = new DiscreteOps();

        synchronized (mOnDiskLock) {
            synchronized (mInMemoryLock) {
                discreteOps.merge(mDiscreteOps);
            }
        if ((flags & (sDiscreteFlags)) == 0) {
            return false;
            if (mCachedOps == null) {
                mCachedOps = new DiscreteOps();
                readDiscreteOpsFromDisk(mCachedOps);
            }
            discreteOps.merge(mCachedOps);
            return discreteOps;
        }
        return true;
    }

    private final class DiscreteOps {
@@ -399,6 +363,15 @@ final class DiscreteRegistry {
            }
        }

        private void clearHistory(int uid, String packageName) {
            if (mUids.containsKey(uid)) {
                mUids.get(uid).clearPackage(packageName);
                if (mUids.get(uid).isEmpty()) {
                    mUids.remove(uid);
                }
            }
        }

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

        private void writeToFile(File f) throws Exception {
            FileOutputStream stream = new FileOutputStream(f);
        private void writeToStream(FileOutputStream stream) throws Exception {
            TypedXmlSerializer out = Xml.resolveSerializer(stream);

            out.startDocument(null, true);
@@ -423,7 +395,6 @@ final class DiscreteRegistry {
            }
            out.endTag(null, TAG_HISTORY);
            out.endDocument();
            stream.close();
        }

        private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
@@ -475,6 +446,60 @@ final class DiscreteRegistry {
        }
    }

    private void createDiscreteAccessDir() {
        if (!mDiscreteAccessDir.exists()) {
            if (!mDiscreteAccessDir.mkdirs()) {
                Slog.e(TAG, "Failed to create DiscreteRegistry directory");
            }
            FileUtils.setPermissions(mDiscreteAccessDir.getPath(),
                    FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH, -1, -1);
        }
    }

    private void persistDiscreteOpsLocked(DiscreteOps discreteOps) {
        long currentTimeStamp = Instant.now().toEpochMilli();
        final AtomicFile file = new AtomicFile(new File(mDiscreteAccessDir,
                currentTimeStamp + DISCRETE_HISTORY_FILE_SUFFIX));
        FileOutputStream stream = null;
        try {
            stream = file.startWrite();
            discreteOps.writeToStream(stream);
            file.finishWrite(stream);
        } catch (Throwable t) {
            Slog.e(TAG,
                    "Error writing timeline state: " + t.getMessage() + " "
                            + Arrays.toString(t.getStackTrace()));
            if (stream != null) {
                file.failWrite(stream);
            }
        }
    }

    private void deleteOldDiscreteHistoryFilesLocked() {
        final File[] files = mDiscreteAccessDir.listFiles();
        if (files != null && files.length > 0) {
            for (File f : files) {
                final String fileName = f.getName();
                if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
                    continue;
                }
                try {
                    long timestamp = Long.valueOf(fileName.substring(0,
                            fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
                    if (Instant.now().minus(sDiscreteHistoryCutoff,
                            ChronoUnit.MILLIS).toEpochMilli() > timestamp) {
                        f.delete();
                        Slog.e(TAG, "Deleting file " + fileName);

                    }
                } catch (Throwable t) {
                    Slog.e(TAG, "Error while cleaning timeline files: " + t.getMessage() + " "
                            + t.getStackTrace());
                }
            }
        }
    }

    private void createDiscreteAccessDirLocked() {
        if (!mDiscreteAccessDir.exists()) {
            if (!mDiscreteAccessDir.mkdirs()) {
@@ -524,6 +549,10 @@ final class DiscreteRegistry {
            }
        }

        private void clearPackage(String packageName) {
            mPackages.remove(packageName);
        }

        void addDiscreteAccess(int op, @NonNull String packageName, @Nullable String attributionTag,
                @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState,
                long accessTime, long accessDuration) {
@@ -934,5 +963,25 @@ final class DiscreteRegistry {
        }
        return result;
    }

    private static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) {
        if (!ArrayUtils.contains(sDiscreteOps, op)) {
            return false;
        }
        if (!isDiscreteUid(uid)) {
            return false;
        }
        if ((flags & (sDiscreteFlags)) == 0) {
            return false;
        }
        return true;
    }

    private static boolean isDiscreteUid(int uid) {
        if (uid < Process.FIRST_APPLICATION_UID) {
            return false;
        }
        return true;
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -394,8 +394,8 @@ final class HistoricalRegistry {
        }

        if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
            mDiscreteRegistry.getHistoricalDiscreteOps(result, beginTimeMillis, endTimeMillis,
                    filter, uid, packageName, opNames, attributionTag,
            mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
                    endTimeMillis, filter, uid, packageName, opNames, attributionTag,
                    flags);
        }

@@ -428,8 +428,8 @@ final class HistoricalRegistry {
                inMemoryAdjEndTimeMillis);

        if ((historyFlags & HISTORY_FLAG_DISCRETE) != 0) {
            mDiscreteRegistry.getHistoricalDiscreteOps(result, beginTimeMillis, endTimeMillis,
                    filter, uid, packageName, opNames, attributionTag, flags);
            mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis,
                    endTimeMillis, filter, uid, packageName, opNames, attributionTag, flags);
        }

        if ((historyFlags & HISTORY_FLAG_AGGREGATE) != 0) {
@@ -645,6 +645,7 @@ final class HistoricalRegistry {
                mPersistence.clearHistoryDLocked(uid, packageName);
            }
        }
        mDiscreteRegistry.clearHistory(uid, packageName);
    }

    void writeAndClearDiscreteHistory() {