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

Commit 719bee98 authored by mrulhania's avatar mrulhania
Browse files

Handle rollback from unified sqlite to xml

We aren't moving enable_sqlite_appops_accesses flag forward, and
will be using enable_all_sqlite_appops_accesses (unified schema)
for sqlite in nextfood. If we need to rollback, the rollback should
move data from unified schema sqlite to xml directly.

This CL also cleans up historical ops data during migration
or rollback.

Bug: 377584611
Test: presubmit
Test: DiscreteOpsMigrationAndRollbackTest
Test: manual
Flag: android.permission.flags.enable_all_sqlite_appops_accesses
Change-Id: I4a73fe281871b6652a88d239423f1f1197de8ed8
parent fc188a82
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -117,6 +117,35 @@ public class DiscreteOpsMigrationHelper {
        }
    }

    /**
     * rollback discrete ops from unified schema sqlite to xml schema.
     */
    static void rollbackFromUnifiedSchemaSqliteToXml(AppOpHistoryHelper sourceRegistry,
            DiscreteOpsXmlRegistry targetRegistry) {
        try {
            List<AggregatedAppOpAccessEvent> unifiedSchemaSqliteOps =
                    sourceRegistry.getAppOpHistory();
            List<DiscreteOpsSqlRegistry.DiscreteOp> discreteOps = new ArrayList<>();
            for (AggregatedAppOpAccessEvent event : unifiedSchemaSqliteOps) {
                discreteOps.add(new DiscreteOpsSqlRegistry.DiscreteOp(event.uid(),
                        event.packageName(), event.attributionTag(),
                        event.deviceId(), event.opCode(), event.opFlags(),
                        event.attributionFlags(),
                        event.uidState(), event.attributionChainId(),
                        event.accessTimeMillis(),
                        event.durationMillis()));
            }
            DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(discreteOps);
            targetRegistry.migrateDiscreteAppOpHistory(xmlOps);
            if (!sourceRegistry.deleteDatabase()) {
                Slog.w(LOG_TAG, "Couldn't delete appops unified sql database.");
            }
        } catch (Exception ex) {
            Slog.e(LOG_TAG, "rollbackFromUnifiedSchemaSqliteToXml failed.", ex);
            sourceRegistry.deleteDatabase();
        }
    }

    /**
     * rollback discrete ops from sqlite to xml.
     */
+15 −0
Original line number Diff line number Diff line
@@ -236,15 +236,21 @@ public class HistoricalRegistry implements HistoricalRegistryInterface {
                mHistoryRetentionMillis);
        // migrate discrete ops from xml or sqlite to unified-schema sqlite database.
        if (DiscreteOpsXmlRegistry.getDiscreteOpsDir().exists()) {
            Slog.i(TAG, "migrate discrete ops from xml to unified sqlite.");
            DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mContext);
            DiscreteOpsMigrationHelper.migrateFromXmlToUnifiedSchemaSqlite(
                    xmlRegistry, mShortIntervalHistoryHelper);
        } else if (DiscreteOpsDbHelper.getDatabaseFile().exists()) {
            Slog.i(TAG, "migrate discrete ops from sqlite to unified sqlite.");
            DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext);
            DiscreteOpsMigrationHelper.migrateFromSqliteToUnifiedSchemaSqlite(
                    sqlRegistry, mShortIntervalHistoryHelper);
        }

        if (LegacyHistoricalRegistry.historicalOpsDirExist()) {
            LegacyHistoricalRegistry.deleteHistoricalOpsDir();
        }

        mChainIdOffset = mShortIntervalHistoryHelper.getLargestAttributionChainId();

        // Set up listener for quantization, op flags or app ops list config for testing
@@ -680,6 +686,15 @@ public class HistoricalRegistry implements HistoricalRegistryInterface {
        mIsReady = true;
    }


    static boolean historicalOpsDbExist() {
        return getDatabaseFile(LONG_INTERVAL_DATABASE_FILE).exists();
    }

    static void deleteHistoricalOpsDb(Context context) {
        context.deleteDatabase(getDatabaseFile(LONG_INTERVAL_DATABASE_FILE).getAbsolutePath());
    }

    @NonNull
    // This is used during rollback in LegacyHistoricalRegistry, will be removed during flag
    // cleanup
+35 −8
Original line number Diff line number Diff line
@@ -262,32 +262,51 @@ final class LegacyHistoricalRegistry implements HistoricalRegistryInterface {
        if (Flags.enableSqliteAppopsAccesses()) {
            DiscreteOpsSqlRegistry sqlRegistry = (DiscreteOpsSqlRegistry) mDiscreteRegistry;
            if (DiscreteOpsXmlRegistry.getDiscreteOpsDir().exists()) {
                Slog.i(LOG_TAG, "migrate discrete ops from xml to sqlite.");
                DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mContext);
                xmlRegistry.systemReady();
                DiscreteOpsMigrationHelper.migrateFromXmlToSqlite(
                        xmlRegistry, sqlRegistry);
            } else if (HistoricalRegistry.getDiscreteOpsDatabaseFile().exists()) {
                // roll back from unified schema sqlite to discrete ops sqlite.
                AppOpHistoryHelper appOpHistoryHelper = new AppOpHistoryHelper(mContext,
                        HistoricalRegistry.getDiscreteOpsDatabaseFile(),
                        HistoricalRegistry.AggregationTimeWindow.SHORT,
                        HistoricalRegistry.getDiscreteOpsDatabaseVersion());
                appOpHistoryHelper.systemReady(
                        HistoricalRegistry.getDiscreteOpsQuantizationMillis(),
                        HistoricalRegistry.getAppOpsHistoryRetentionMillis());
                Slog.i(LOG_TAG, "rollback discrete ops from unified sqlite to sqlite.");
                AppOpHistoryHelper appOpHistoryHelper = getAppOpHistoryHelper();
                DiscreteOpsMigrationHelper.rollbackFromUnifiedSchemaSqliteToSqlite(
                        appOpHistoryHelper, sqlRegistry);
            }
        } else {
            if (DiscreteOpsDbHelper.getDatabaseFile().exists()) { // roll-back sqlite to xml
            if (HistoricalRegistry.getDiscreteOpsDatabaseFile().exists()) {
                Slog.i(LOG_TAG, "rollback discrete ops from unified sqlite to xml.");
                DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mContext);
                AppOpHistoryHelper appOpHistoryHelper = getAppOpHistoryHelper();
                DiscreteOpsMigrationHelper.rollbackFromUnifiedSchemaSqliteToXml(
                        appOpHistoryHelper, xmlRegistry);
                if (HistoricalRegistry.historicalOpsDbExist()) {
                    HistoricalRegistry.deleteHistoricalOpsDb(mContext);
                }
            } else if (DiscreteOpsDbHelper.getDatabaseFile().exists()) {
                Slog.i(LOG_TAG, "rollback discrete ops from sqlite to xml.");
                DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext);
                sqlRegistry.systemReady();
                DiscreteOpsXmlRegistry xmlRegistry = (DiscreteOpsXmlRegistry) mDiscreteRegistry;
                DiscreteOpsMigrationHelper.rollbackFromSqliteToXml(sqlRegistry, xmlRegistry);
            }


        }
    }

    private @NonNull AppOpHistoryHelper getAppOpHistoryHelper() {
        AppOpHistoryHelper appOpHistoryHelper = new AppOpHistoryHelper(mContext,
                HistoricalRegistry.getDiscreteOpsDatabaseFile(),
                HistoricalRegistry.AggregationTimeWindow.SHORT,
                HistoricalRegistry.getDiscreteOpsDatabaseVersion());
        appOpHistoryHelper.systemReady(
                HistoricalRegistry.getDiscreteOpsQuantizationMillis(),
                HistoricalRegistry.getAppOpsHistoryRetentionMillis());
        return appOpHistoryHelper;
    }

    private boolean isPersistenceInitializedMLocked() {
        return mPersistence != null;
    }
@@ -864,6 +883,14 @@ final class LegacyHistoricalRegistry implements HistoricalRegistryInterface {
        }
    }

    static boolean historicalOpsDirExist() {
        return Persistence.sHistoricalAppOpsDir.exists();
    }

    static void deleteHistoricalOpsDir() {
        Persistence.sHistoricalAppOpsDir.delete();
    }

    private static final class Persistence {
        private static final boolean DEBUG = false;

+32 −0
Original line number Diff line number Diff line
@@ -232,6 +232,38 @@ public class DiscreteOpsMigrationAndRollbackTest {
        assertThat(appOpHistoryHelper.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);
    }

    @Test
    public void rollbackFromUnifiedSchemaSqliteToXml() {
        // write to unified schema sqlite registry
        AppOpHistoryHelper appOpHistoryHelper = new AppOpHistoryHelper(mContext,
                mContext.getDatabasePath(DISCRETE_OPS_UNIFIED_SCHEMA_DB_NAME),
                HistoricalRegistry.AggregationTimeWindow.SHORT, 1);
        appOpHistoryHelper.systemReady(Duration.ofMinutes(1).toMillis(),
                Duration.ofDays(7).toMillis());
        for (int i = 1; i <= RECORD_COUNT; i++) {
            appOpHistoryHelper.incrementOpAccessedCount(AppOpsManager.OP_COARSE_LOCATION,
                    RECORD_COUNT + i, mContext.getPackageName(),
                    VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT,  null,
                    UID_STATE_FOREGROUND, AppOpsManager.OP_FLAG_SELF, System.currentTimeMillis(),
                    ATTRIBUTION_FLAG_ACCESSOR, i, 1, false);
        }
        // flush records from cache to the database.
        appOpHistoryHelper.shutdown();
        assertThat(appOpHistoryHelper.getAppOpHistory().size()).isEqualTo(RECORD_COUNT);
        assertThat(appOpHistoryHelper.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);

        // now rollback to xml registry
        DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
        xmlRegistry.systemReady();
        DiscreteOpsMigrationHelper.rollbackFromUnifiedSchemaSqliteToXml(
                appOpHistoryHelper, xmlRegistry);
        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = xmlRegistry.getAllDiscreteOps();

        assertThat(appOpHistoryHelper.getAppOpHistory()).isEmpty();
        assertThat(xmlOps.mLargestChainId).isEqualTo(RECORD_COUNT);
        assertThat(xmlOps.mUids.size()).isEqualTo(RECORD_COUNT);
    }

    private static class DiscreteOpBuilder {
        private int mUid;
        private String mPackageName;