Merge "Drop persisted ledgers for uninstalled packages."

This commit is contained in:
Kweku Adams
2021-10-14 22:33:27 +00:00
committed by Android (Google) Code Review
2 changed files with 93 additions and 26 deletions

View File

@@ -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);

View File

@@ -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);
}
}