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

Commit ff698de2 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Drop persisted ledgers for uninstalled packages."

parents 82a2109a 5bb19537
Loading
Loading
Loading
Loading
+47 −16
Original line number Diff line number Diff line
@@ -22,13 +22,16 @@ import static com.android.server.tare.TareUtils.appToString;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.biometrics.face.V1_0.UserHandle;
import android.content.pm.PackageInfo;
import android.os.Environment;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArrayMap;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
@@ -36,8 +39,6 @@ import android.util.Xml;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -47,7 +48,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
@@ -161,10 +161,20 @@ public class Scribe {
            return;
        }

        UserManagerInternal userManagerInternal =
                LocalServices.getService(UserManagerInternal.class);
        final int[] userIds = userManagerInternal.getUserIds();
        Arrays.sort(userIds);
        final SparseArray<ArraySet<String>> installedPackagesPerUser = new SparseArray<>();
        final List<PackageInfo> installedPackages = mIrs.getInstalledPackages();
        for (int i = 0; i < installedPackages.size(); ++i) {
            final PackageInfo packageInfo = installedPackages.get(i);
            if (packageInfo.applicationInfo != null) {
                final int userId = UserHandle.getUserId(packageInfo.applicationInfo.uid);
                ArraySet<String> pkgsForUser = installedPackagesPerUser.get(userId);
                if (pkgsForUser == null) {
                    pkgsForUser = new ArraySet<>();
                    installedPackagesPerUser.put(userId, pkgsForUser);
                }
                pkgsForUser.add(packageInfo.packageName);
            }
        }

        try (FileInputStream fis = mStateFile.openRead()) {
            TypedXmlPullParser parser = Xml.resolvePullParser(fis);
@@ -209,7 +219,8 @@ public class Scribe {
                        break;
                    case XML_TAG_USER:
                        earliestEndTime = Math.min(earliestEndTime,
                                readUserFromXmlLocked(parser, userIds, endTimeCutoff));
                                readUserFromXmlLocked(
                                        parser, installedPackagesPerUser, endTimeCutoff));
                        break;
                    default:
                        Slog.e(TAG, "Unexpected tag: " + tagName);
@@ -280,7 +291,8 @@ public class Scribe {
     */
    @Nullable
    private static Pair<String, Ledger> readLedgerFromXml(TypedXmlPullParser parser,
            long endTimeCutoff) throws XmlPullParserException, IOException {
            ArraySet<String> validPackages, long endTimeCutoff)
            throws XmlPullParserException, IOException {
        final String pkgName;
        final long curBalance;
        final List<Ledger.Transaction> transactions = new ArrayList<>();
@@ -288,6 +300,13 @@ public class Scribe {
        pkgName = parser.getAttributeValue(null, XML_ATTR_PACKAGE_NAME);
        curBalance = parser.getAttributeLong(null, XML_ATTR_CURRENT_BALANCE);

        final boolean isInstalled = validPackages.contains(pkgName);
        if (!isInstalled) {
            // Don't return early since we need to go through all the transaction tags and get
            // to the end of the ledger tag.
            Slog.w(TAG, "Invalid pkg " + pkgName + " is saved to disk");
        }

        for (int eventType = parser.next(); eventType != XmlPullParser.END_DOCUMENT;
                eventType = parser.next()) {
            final String tagName = parser.getName();
@@ -298,11 +317,14 @@ public class Scribe {
                }
                continue;
            }
            if (eventType != XmlPullParser.START_TAG || !"transaction".equals(tagName)) {
            if (eventType != XmlPullParser.START_TAG || !XML_TAG_TRANSACTION.equals(tagName)) {
                // Expecting only "transaction" tags.
                Slog.e(TAG, "Unexpected event: (" + eventType + ") " + tagName);
                return null;
            }
            if (!isInstalled) {
                continue;
            }
            if (DEBUG) {
                Slog.d(TAG, "Starting ledger tag: " + tagName);
            }
@@ -320,6 +342,9 @@ public class Scribe {
            transactions.add(new Ledger.Transaction(startTime, endTime, eventId, tag, delta));
        }

        if (!isInstalled) {
            return null;
        }
        return Pair.create(pkgName, new Ledger(curBalance, transactions));
    }

@@ -329,12 +354,14 @@ public class Scribe {
     * @return The earliest valid transaction end time found for the user.
     */
    @GuardedBy("mIrs.getLock()")
    private long readUserFromXmlLocked(TypedXmlPullParser parser, int[] validUserIds,
    private long readUserFromXmlLocked(TypedXmlPullParser parser,
            SparseArray<ArraySet<String>> installedPackagesPerUser,
            long endTimeCutoff) throws XmlPullParserException, IOException {
        int curUser = parser.getAttributeInt(null, XML_ATTR_USER_ID);
        if (Arrays.binarySearch(validUserIds, curUser) < 0) {
        final ArraySet<String> installedPackages = installedPackagesPerUser.get(curUser);
        if (installedPackages == null) {
            Slog.w(TAG, "Invalid user " + curUser + " is saved to disk");
            curUser = UserHandle.NONE;
            curUser = UserHandle.USER_NULL;
            // Don't return early since we need to go through all the ledger tags and get to the end
            // of the user tag.
        }
@@ -351,10 +378,14 @@ public class Scribe {
                continue;
            }
            if (XML_TAG_LEDGER.equals(tagName)) {
                if (curUser == UserHandle.NONE) {
                if (curUser == UserHandle.USER_NULL) {
                    continue;
                }
                final Pair<String, Ledger> ledgerData =
                        readLedgerFromXml(parser, installedPackages, endTimeCutoff);
                if (ledgerData == null) {
                    continue;
                }
                final Pair<String, Ledger> ledgerData = readLedgerFromXml(parser, endTimeCutoff);
                final Ledger ledger = ledgerData.second;
                if (ledger != null) {
                    mLedgers.add(curUser, ledgerData.first, ledger);
+46 −10
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.server.tare;


import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;

import static org.junit.Assert.assertEquals;
@@ -26,6 +25,9 @@ import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.UserHandle;
import android.util.Log;
import android.util.SparseArrayMap;

@@ -34,7 +36,6 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;

import org.junit.After;
import org.junit.Before;
@@ -45,6 +46,7 @@ import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
@@ -63,11 +65,10 @@ public class ScribeTest {
    private MockitoSession mMockingSession;
    private Scribe mScribeUnderTest;
    private File mTestFileDir;
    private final List<PackageInfo> mInstalledPackages = new ArrayList<>();

    @Mock
    private InternalResourceService mIrs;
    @Mock
    private UserManagerInternal mUserManagerInternal;

    private Context getContext() {
        return InstrumentationRegistry.getContext();
@@ -80,16 +81,16 @@ public class ScribeTest {
                .strictness(Strictness.LENIENT)
                .mockStatic(LocalServices.class)
                .startMocking();
        doReturn(mUserManagerInternal)
                .when(() -> LocalServices.getService(UserManagerInternal.class));
        when(mIrs.getLock()).thenReturn(new Object());
        when(mIrs.isEnabled()).thenReturn(true);
        when(mUserManagerInternal.getUserIds()).thenReturn(new int[]{TEST_USER_ID});
        when(mIrs.getInstalledPackages()).thenReturn(mInstalledPackages);
        mTestFileDir = new File(getContext().getFilesDir(), "scribe_test");
        //noinspection ResultOfMethodCallIgnored
        mTestFileDir.mkdirs();
        Log.d(TAG, "Saving data to '" + mTestFileDir + "'");
        mScribeUnderTest = new Scribe(mIrs, mTestFileDir);

        addInstalledPackage(TEST_USER_ID, TEST_PACKAGE);
    }

    @After
@@ -148,13 +149,11 @@ public class ScribeTest {
        final SparseArrayMap<String, Ledger> ledgers = new SparseArrayMap<>();
        final int numUsers = 3;
        final int numLedgers = 5;
        final int[] userIds = new int[numUsers];
        when(mUserManagerInternal.getUserIds()).thenReturn(userIds);
        for (int u = 0; u < numUsers; ++u) {
            final int userId = TEST_USER_ID + u;
            userIds[u] = userId;
            for (int l = 0; l < numLedgers; ++l) {
                final String pkgName = TEST_PACKAGE + l;
                addInstalledPackage(userId, pkgName);
                final Ledger ledger = mScribeUnderTest.getLedgerLocked(userId, pkgName);
                ledger.recordTransaction(new Ledger.Transaction(
                        0, 1000L * u + l, 1, null, 51L * u + l));
@@ -192,6 +191,34 @@ public class ScribeTest {
                mScribeUnderTest.getLedgerLocked(TEST_USER_ID, TEST_PACKAGE));
    }

    @Test
    public void testLoadingMissingPackageFromDisk() {
        final String pkgName = TEST_PACKAGE + ".uninstalled";
        final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName);
        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
        mScribeUnderTest.writeImmediatelyForTesting();

        // Package isn't installed, so make sure it's not saved to memory after loading.
        mScribeUnderTest.loadFromDiskLocked();
        assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(TEST_USER_ID, pkgName));
    }

    @Test
    public void testLoadingMissingUserFromDisk() {
        final int userId = TEST_USER_ID + 1;
        final Ledger ogLedger = mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE);
        ogLedger.recordTransaction(new Ledger.Transaction(0, 1000, 1, null, 51));
        ogLedger.recordTransaction(new Ledger.Transaction(1500, 2000, 2, "green", 52));
        ogLedger.recordTransaction(new Ledger.Transaction(2500, 3000, 3, "blue", 3));
        mScribeUnderTest.writeImmediatelyForTesting();

        // User doesn't show up with any packages, so make sure nothing is saved after loading.
        mScribeUnderTest.loadFromDiskLocked();
        assertLedgersEqual(new Ledger(), mScribeUnderTest.getLedgerLocked(userId, TEST_PACKAGE));
    }

    private void assertLedgersEqual(Ledger expected, Ledger actual) {
        if (expected == null) {
            assertNull(actual);
@@ -219,4 +246,13 @@ public class ScribeTest {
        assertEquals(expected.tag, actual.tag);
        assertEquals(expected.delta, actual.delta);
    }

    private void addInstalledPackage(int userId, String pkgName) {
        PackageInfo pkgInfo = new PackageInfo();
        pkgInfo.packageName = pkgName;
        ApplicationInfo applicationInfo = new ApplicationInfo();
        applicationInfo.uid = UserHandle.getUid(userId, Math.abs(pkgName.hashCode()));
        pkgInfo.applicationInfo = applicationInfo;
        mInstalledPackages.add(pkgInfo);
    }
}