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

Commit e27c8a16 authored by Stanislav Zholnin's avatar Stanislav Zholnin Committed by Android (Google) Code Review
Browse files

Merge "Remove data from DiscreteRegistry for uninstalled packages." into sc-dev

parents 8c7e28c9 3836d735
Loading
Loading
Loading
Loading
+133 −84
Original line number Original line Diff line number Diff line
@@ -44,6 +44,7 @@ import android.os.FileUtils;
import android.os.Process;
import android.os.Process;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.TypedXmlSerializer;
@@ -83,7 +84,7 @@ import java.util.List;
 */
 */


final class DiscreteRegistry {
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 TAG = DiscreteRegistry.class.getSimpleName();


    private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
    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,
    void recordDiscreteAccess(int uid, String packageName, int op, @Nullable String attributionTag,
            @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
            @AppOpsManager.OpFlags int flags, @AppOpsManager.UidState int uidState, long accessTime,
            long accessDuration) {
            long accessDuration) {
@@ -220,80 +210,34 @@ final class DiscreteRegistry {
    void writeAndClearAccessHistory() {
    void writeAndClearAccessHistory() {
        synchronized (mOnDiskLock) {
        synchronized (mOnDiskLock) {
            if (mDiscreteAccessDir == null) {
            if (mDiscreteAccessDir == null) {
                Slog.e(TAG, "State not saved - persistence not initialized.");
                Slog.d(TAG, "State not saved - persistence not initialized.");
                return;
                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;
            DiscreteOps discreteOps;
            synchronized (mInMemoryLock) {
            synchronized (mInMemoryLock) {
                discreteOps = mDiscreteOps;
                discreteOps = mDiscreteOps;
                mDiscreteOps = new DiscreteOps();
                mDiscreteOps = new DiscreteOps();
                mCachedOps = null;
                mCachedOps = null;
            }
            }
            if (discreteOps.isEmpty()) {
            deleteOldDiscreteHistoryFilesLocked();
                return;
            if (!discreteOps.isEmpty()) {
            }
                persistDiscreteOpsLocked(discreteOps);
            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()));
            }
            }
        }
        }
    }
    }


    void getHistoricalDiscreteOps(AppOpsManager.HistoricalOps result, long beginTimeMillis,
    void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
            long endTimeMillis, @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
            long beginTimeMillis, 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) {
        DiscreteOps discreteOps = getAndCacheDiscreteOps();
        DiscreteOps discreteOps = getAllDiscreteOps();
        discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
        discreteOps.filter(beginTimeMillis, endTimeMillis, filter, uidFilter, packageNameFilter,
                opNamesFilter, attributionTagFilter, flagsFilter);
                opNamesFilter, attributionTagFilter, flagsFilter);
        discreteOps.applyToHistoricalOps(result);
        discreteOps.applyToHistoricalOps(result);
        return;
        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) {
    private void readDiscreteOpsFromDisk(DiscreteOps discreteOps) {
        synchronized (mOnDiskLock) {
        synchronized (mOnDiskLock) {
            long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
            long beginTimeMillis = Instant.now().minus(sDiscreteHistoryCutoff,
@@ -303,11 +247,11 @@ final class DiscreteRegistry {
            if (files != null && files.length > 0) {
            if (files != null && files.length > 0) {
                for (File f : files) {
                for (File f : files) {
                    final String fileName = f.getName();
                    final String fileName = f.getName();
                    if (!fileName.endsWith(TIMELINE_FILE_SUFFIX)) {
                    if (!fileName.endsWith(DISCRETE_HISTORY_FILE_SUFFIX)) {
                        continue;
                        continue;
                    }
                    }
                    long timestamp = Long.valueOf(fileName.substring(0,
                    long timestamp = Long.valueOf(fileName.substring(0,
                            fileName.length() - TIMELINE_FILE_SUFFIX.length()));
                            fileName.length() - DISCRETE_HISTORY_FILE_SUFFIX.length()));
                    if (timestamp < beginTimeMillis) {
                    if (timestamp < beginTimeMillis) {
                        continue;
                        continue;
                    }
                    }
@@ -322,8 +266,19 @@ final class DiscreteRegistry {
            synchronized (mInMemoryLock) {
            synchronized (mInMemoryLock) {
                mDiscreteOps = new DiscreteOps();
                mDiscreteOps = new DiscreteOps();
            }
            }
            FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
            clearOnDiskHistoryLocked();
            createDiscreteAccessDir();
        }
    }

    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,
            @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 = getAndCacheDiscreteOps();
        DiscreteOps discreteOps = getAllDiscreteOps();
        String[] opNamesFilter = dumpOp == OP_NONE ? null
        String[] opNamesFilter = dumpOp == OP_NONE ? null
                : new String[]{AppOpsManager.opToPublicName(dumpOp)};
                : new String[]{AppOpsManager.opToPublicName(dumpOp)};
        discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
        discreteOps.filter(0, Instant.now().toEpochMilli(), filter, uidFilter, packageNameFilter,
@@ -340,17 +295,26 @@ final class DiscreteRegistry {
        discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
        discreteOps.dump(pw, sdf, date, prefix, nDiscreteOps);
    }
    }


    public static boolean isDiscreteOp(int op, int uid, @AppOpsManager.OpFlags int flags) {
    private void clearOnDiskHistoryLocked() {
        if (!ArrayUtils.contains(sDiscreteOps, op)) {
        mCachedOps = null;
            return false;
        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) {
            if (mCachedOps == null) {
            return false;
                mCachedOps = new DiscreteOps();
                readDiscreteOpsFromDisk(mCachedOps);
            }
            discreteOps.merge(mCachedOps);
            return discreteOps;
        }
        }
        return true;
    }
    }


    private final class DiscreteOps {
    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) {
        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++) {
@@ -406,8 +379,7 @@ final class DiscreteRegistry {
            }
            }
        }
        }


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


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


        private void dump(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
        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() {
    private void createDiscreteAccessDirLocked() {
        if (!mDiscreteAccessDir.exists()) {
        if (!mDiscreteAccessDir.exists()) {
            if (!mDiscreteAccessDir.mkdirs()) {
            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,
        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) {
@@ -934,5 +963,25 @@ final class DiscreteRegistry {
        }
        }
        return result;
        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 Original line Diff line number Diff line
@@ -394,8 +394,8 @@ final class HistoricalRegistry {
        }
        }


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


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


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


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


    void writeAndClearDiscreteHistory() {
    void writeAndClearDiscreteHistory() {