summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java43
-rw-r--r--services/usage/java/com/android/server/usage/IntervalStats.java24
-rw-r--r--services/usage/java/com/android/server/usage/PackagesTokenData.java62
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsDatabase.java44
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java39
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java55
-rw-r--r--tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java1
7 files changed, 249 insertions, 19 deletions
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index 3a0ad4d76ef3..df0c37a9856c 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -74,6 +74,7 @@ public class UsageStatsDatabaseTest {
mContext = InstrumentationRegistry.getTargetContext();
mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest");
mUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ mUsageStatsDatabase.readMappingsLocked();
mUsageStatsDatabase.init(1);
populateIntervalStats();
clearUsageStatsFiles();
@@ -388,6 +389,7 @@ public class UsageStatsDatabaseTest {
void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException {
// Write IntervalStats to disk in old version format
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion);
+ prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(interval, mIntervalStats);
if (oldVersion >= 5) {
@@ -396,6 +398,7 @@ public class UsageStatsDatabaseTest {
// Simulate an upgrade to a new version and read from the disk
UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion);
+ newDB.readMappingsLocked();
newDB.init(mEndTime);
List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
mIntervalStatsVerifier);
@@ -415,6 +418,7 @@ public class UsageStatsDatabaseTest {
*/
void runBackupRestoreTest(int version) throws IOException {
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir);
+ prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
// Create a backup with a specific version
@@ -423,6 +427,7 @@ public class UsageStatsDatabaseTest {
clearUsageStatsFiles();
UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
+ newDB.readMappingsLocked();
newDB.init(1);
// Attempt to restore the usage stats from the backup
newDB.applyRestoredPayload(KEY_USAGE_STATS, blob);
@@ -539,12 +544,14 @@ public class UsageStatsDatabaseTest {
private void compareObfuscatedData(int interval) throws IOException {
// Write IntervalStats to disk
UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, 5);
+ prevDB.readMappingsLocked();
prevDB.init(1);
prevDB.putUsageStats(interval, mIntervalStats);
prevDB.writeMappingsLocked();
// Read IntervalStats from disk into a new db
UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, 5);
+ newDB.readMappingsLocked();
newDB.init(mEndTime);
List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime,
mIntervalStatsVerifier);
@@ -561,4 +568,40 @@ public class UsageStatsDatabaseTest {
compareObfuscatedData(UsageStatsManager.INTERVAL_MONTHLY);
compareObfuscatedData(UsageStatsManager.INTERVAL_YEARLY);
}
+
+ private void verifyPackageNotRetained(int interval) throws IOException {
+ UsageStatsDatabase db = new UsageStatsDatabase(mTestDir, 5);
+ db.readMappingsLocked();
+ db.init(1);
+ db.putUsageStats(interval, mIntervalStats);
+
+ final String removedPackage = "fake.package.name0";
+ // invoke handler call directly from test to remove package
+ db.onPackageRemoved(removedPackage, System.currentTimeMillis());
+
+ List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+ mIntervalStatsVerifier);
+ for (int i = 0; i < stats.size(); i++) {
+ final IntervalStats stat = stats.get(i);
+ if (stat.packageStats.containsKey(removedPackage)) {
+ fail("Found removed package " + removedPackage + " in package stats.");
+ return;
+ }
+ for (int j = 0; j < stat.events.size(); j++) {
+ final Event event = stat.events.get(j);
+ if (removedPackage.equals(event.mPackage)) {
+ fail("Found an event from removed package " + removedPackage);
+ return;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testPackageRetention() throws IOException {
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_DAILY);
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_WEEKLY);
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
+ verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
+ }
}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 7ea669d1f0a2..46b261b64192 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -454,8 +454,7 @@ public class IntervalStats {
for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
final UsageStats usageStats = packageStatsObfuscated.valueAt(statsIndex);
- usageStats.mPackageName = packagesTokenData.getString(packageToken,
- PackagesTokenData.PACKAGE_NAME_INDEX);
+ usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
if (usageStats.mPackageName == null) {
Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
continue;
@@ -501,8 +500,7 @@ public class IntervalStats {
for (int i = this.events.size() - 1; i >= 0; i--) {
final Event event = this.events.get(i);
final int packageToken = event.mPackageToken;
- event.mPackage = packagesTokenData.getString(packageToken,
- PackagesTokenData.PACKAGE_NAME_INDEX);
+ event.mPackage = packagesTokenData.getPackageString(packageToken);
if (event.mPackage == null) {
Slog.e(TAG, "Unable to parse event package " + packageToken);
this.events.remove(i);
@@ -586,7 +584,12 @@ public class IntervalStats {
continue;
}
- final int packageToken = packagesTokenData.getPackageTokenOrAdd(packageName);
+ final int packageToken = packagesTokenData.getPackageTokenOrAdd(
+ packageName, usageStats.mEndTimeStamp);
+ // don't obfuscate stats whose packages have been removed
+ if (packageToken == PackagesTokenData.UNASSIGNED_TOKEN) {
+ continue;
+ }
usageStats.mPackageToken = packageToken;
// Update chooser counts.
final int chooserActionsSize = usageStats.mChooserCounts.size();
@@ -619,14 +622,19 @@ public class IntervalStats {
* task root package and class names, and shortcut and notification channel ids.
*/
private void obfuscateEventsData(PackagesTokenData packagesTokenData) {
- final int eventSize = events.size();
- for (int i = 0; i < eventSize; i++) {
+ for (int i = events.size() - 1; i >= 0; i--) {
final Event event = events.get(i);
if (event == null) {
continue;
}
- final int packageToken = packagesTokenData.getPackageTokenOrAdd(event.mPackage);
+ final int packageToken = packagesTokenData.getPackageTokenOrAdd(
+ event.mPackage, event.mTimeStamp);
+ // don't obfuscate events from packages that have been removed
+ if (packageToken == PackagesTokenData.UNASSIGNED_TOKEN) {
+ events.remove(i);
+ continue;
+ }
event.mPackageToken = packageToken;
if (!TextUtils.isEmpty(event.mClass)) {
event.mClassToken = packagesTokenData.getTokenOrAdd(packageToken,
diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java
index 3beee678d7ff..4bf08a49af0f 100644
--- a/services/usage/java/com/android/server/usage/PackagesTokenData.java
+++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java
@@ -29,14 +29,14 @@ import java.util.ArrayList;
*/
public final class PackagesTokenData {
/**
- * The default token for any string that hasn't been tokenized yet.
+ * The package name is always stored at index 0 in {@code tokensToPackagesMap}.
*/
- public static final int UNASSIGNED_TOKEN = -1;
+ private static final int PACKAGE_NAME_INDEX = 0;
/**
- * The package name is always stored at index 0 in {@code tokensToPackagesMap}.
+ * The default token for any string that hasn't been tokenized yet.
*/
- public static final int PACKAGE_NAME_INDEX = 0;
+ public static final int UNASSIGNED_TOKEN = -1;
/**
* The main token counter for each package.
@@ -52,6 +52,10 @@ public final class PackagesTokenData {
* map of the {@code tokenToPackagesMap} in this class, mainly for an O(1) access to the tokens.
*/
public final ArrayMap<String, ArrayMap<String, Integer>> packagesToTokensMap = new ArrayMap<>();
+ /**
+ * Stores a map of packages that were removed and when they were removed.
+ */
+ public final ArrayMap<String, Long> removedPackagesMap = new ArrayMap<>();
public PackagesTokenData() {
}
@@ -61,9 +65,26 @@ public final class PackagesTokenData {
* created and the relevant mappings are updated.
*
* @param packageName the package name whose token is being fetched
+ * @param timeStamp the time stamp of the event or end time of the usage stats; used to verify
+ * the package hasn't been removed
* @return the mapped token
*/
- public int getPackageTokenOrAdd(String packageName) {
+ public int getPackageTokenOrAdd(String packageName, long timeStamp) {
+ final Long timeRemoved = removedPackagesMap.get(packageName);
+ if (timeRemoved != null && timeRemoved > timeStamp) {
+ return UNASSIGNED_TOKEN; // package was removed
+ /*
+ Note: instead of querying Package Manager each time for a list of packages to verify
+ if this package is still installed, it's more efficient to check the internal list of
+ removed packages and verify with the incoming time stamp. Although rare, it is possible
+ that some asynchronous function is triggered after a package is removed and the
+ time stamp passed into this function is not accurate. We'll have to keep the respective
+ event/usage stat until the next time the device reboots and the mappings are cleaned.
+ Additionally, this is a data class with some helper methods - it doesn't make sense to
+ overload it with references to other services.
+ */
+ }
+
ArrayMap<String, Integer> packageTokensMap = packagesToTokensMap.get(packageName);
if (packageTokensMap == null) {
packageTokensMap = new ArrayMap<>();
@@ -104,6 +125,20 @@ public final class PackagesTokenData {
}
/**
+ * Fetches the package name for the given token.
+ *
+ * @param packageToken the package token representing the package name
+ * @return the string representing the given token or {@code null} if not found
+ */
+ public String getPackageString(int packageToken) {
+ final ArrayList<String> packageStrings = tokensToPackagesMap.get(packageToken);
+ if (packageStrings == null) {
+ return null;
+ }
+ return packageStrings.get(PACKAGE_NAME_INDEX);
+ }
+
+ /**
* Fetches the string represented by the given token.
*
* @param packageToken the package token for which this token belongs to
@@ -121,4 +156,21 @@ public final class PackagesTokenData {
return null;
}
}
+
+ /**
+ * Removes the package from all known mappings.
+ *
+ * @param packageName the package to be removed
+ * @param timeRemoved the time stamp of when the package was removed
+ */
+ public void removePackage(String packageName, long timeRemoved) {
+ removedPackagesMap.put(packageName, timeRemoved);
+
+ if (!packagesToTokensMap.containsKey(packageName)) {
+ return;
+ }
+ final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
+ packagesToTokensMap.remove(packageName);
+ tokensToPackagesMap.delete(packageToken);
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 5c785f76d4db..db7ed1f58c1a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -17,6 +17,7 @@
package com.android.server.usage;
import android.app.usage.TimeSparseArray;
+import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
@@ -125,7 +126,7 @@ public class UsageStatsDatabase {
// The obfuscated packages to tokens mappings file
private final File mPackageMappingsFile;
// Holds all of the data related to the obfuscated packages and their token mappings.
- private final PackagesTokenData mPackagesTokenData = new PackagesTokenData();
+ final PackagesTokenData mPackagesTokenData = new PackagesTokenData();
/**
* UsageStatsDatabase constructor that allows setting the version number.
@@ -159,8 +160,6 @@ public class UsageStatsDatabase {
*/
public void init(long currentTimeMillis) {
synchronized (mLock) {
- readMappingsLocked();
-
for (File f : mIntervalDirs) {
f.mkdirs();
if (!f.exists()) {
@@ -538,6 +537,12 @@ public class UsageStatsDatabase {
}
}
+ void onPackageRemoved(String packageName, long timeRemoved) {
+ synchronized (mLock) {
+ mPackagesTokenData.removePackage(packageName, timeRemoved);
+ }
+ }
+
public void onTimeChanged(long timeDiffMillis) {
synchronized (mLock) {
StringBuilder logBuilder = new StringBuilder();
@@ -612,6 +617,37 @@ public class UsageStatsDatabase {
}
/**
+ * Filter out those stats from the given stats that belong to removed packages. Filtering out
+ * all of the stats at once has an amortized cost for future calls.
+ */
+ void filterStats(IntervalStats stats) {
+ if (mPackagesTokenData.removedPackagesMap.isEmpty()) {
+ return;
+ }
+ final ArrayMap<String, Long> removedPackagesMap = mPackagesTokenData.removedPackagesMap;
+
+ // filter out package usage stats
+ final int removedPackagesSize = removedPackagesMap.size();
+ for (int i = 0; i < removedPackagesSize; i++) {
+ final String removedPackage = removedPackagesMap.keyAt(i);
+ final UsageStats usageStats = stats.packageStats.get(removedPackage);
+ if (usageStats != null && usageStats.mEndTimeStamp < removedPackagesMap.valueAt(i)) {
+ stats.packageStats.remove(removedPackage);
+ }
+ }
+
+ // filter out events
+ final int eventsSize = stats.events.size();
+ for (int i = stats.events.size() - 1; i >= 0; i--) {
+ final UsageEvents.Event event = stats.events.get(i);
+ final Long timeRemoved = removedPackagesMap.get(event.mPackage);
+ if (timeRemoved != null && timeRemoved > event.mTimeStamp) {
+ stats.events.remove(i);
+ }
+ }
+ }
+
+ /**
* Figures out what to extract from the given IntervalStats object.
*/
public interface StatCombiner<T> {
@@ -954,7 +990,7 @@ public class UsageStatsDatabase {
* Reads the obfuscated data file from disk containing the tokens to packages mappings and
* rebuilds the packages to tokens mappings based on that data.
*/
- private void readMappingsLocked() {
+ public void readMappingsLocked() {
if (!mPackageMappingsFile.exists()) {
return; // package mappings file is missing - recreate mappings on next write.
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b12d9008689a..f007bd379713 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -141,6 +141,7 @@ public class UsageStatsService extends SystemService implements
static final int MSG_UID_STATE_CHANGED = 3;
static final int MSG_REPORT_EVENT_TO_ALL_USERID = 4;
static final int MSG_UNLOCKED_USER = 5;
+ static final int MSG_PACKAGE_REMOVED = 6;
private final Object mLock = new Object();
Handler mHandler;
@@ -148,7 +149,6 @@ public class UsageStatsService extends SystemService implements
UserManager mUserManager;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
- PackageMonitor mPackageMonitor;
IDeviceIdleController mDeviceIdleController;
// Do not use directly. Call getDpmInternal() instead
DevicePolicyManagerInternal mDpmInternal;
@@ -164,6 +164,8 @@ public class UsageStatsService extends SystemService implements
/** Manages app time limit observers */
AppTimeLimitController mAppTimeLimit;
+ private final PackageMonitor mPackageMonitor = new MyPackageMonitor();
+
// A map maintaining a queue of events to be reported per user.
private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>();
final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray();
@@ -246,6 +248,8 @@ public class UsageStatsService extends SystemService implements
mAppStandby.addListener(mStandbyChangeListener);
+ mPackageMonitor.register(getContext(), null, UserHandle.ALL, true);
+
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_STARTED);
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
@@ -846,6 +850,26 @@ public class UsageStatsService extends SystemService implements
}
/**
+ * Called by the Handler for message MSG_PACKAGE_REMOVED.
+ */
+ private void onPackageRemoved(int userId, String packageName) {
+ synchronized (mLock) {
+ final long timeRemoved = System.currentTimeMillis();
+ if (!mUserUnlockedStates.get(userId, false)) {
+ // If user is not unlocked and a package is removed for them, we will handle it
+ // when the user service is initialized and package manager is queried.
+ return;
+ }
+ final UserUsageStatsService userService = mUserState.get(userId);
+ if (userService == null) {
+ return;
+ }
+
+ userService.onPackageRemoved(packageName, timeRemoved);
+ }
+ }
+
+ /**
* Called by the Binder stub.
*/
List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime,
@@ -1162,7 +1186,9 @@ public class UsageStatsService extends SystemService implements
case MSG_REMOVE_USER:
onUserRemoved(msg.arg1);
break;
-
+ case MSG_PACKAGE_REMOVED:
+ onPackageRemoved(msg.arg1, (String) msg.obj);
+ break;
case MSG_UID_STATE_CHANGED: {
final int uid = msg.arg1;
final int procState = msg.arg2;
@@ -2112,4 +2138,13 @@ public class UsageStatsService extends SystemService implements
return mAppTimeLimit.getAppUsageLimit(packageName, user);
}
}
+
+ private class MyPackageMonitor extends PackageMonitor {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ mHandler.obtainMessage(MSG_PACKAGE_REMOVED, getChangingUserId(), 0, packageName)
+ .sendToTarget();
+ super.onPackageRemoved(packageName, uid);
+ }
+ }
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index ec6caded01a7..23df1c553de2 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -34,7 +34,10 @@ import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
+import android.os.Process;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.ArrayMap;
@@ -44,6 +47,7 @@ import android.util.Slog;
import android.util.SparseIntArray;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
import com.android.server.usage.UsageStatsDatabase.StatCombiner;
import java.io.File;
@@ -51,6 +55,7 @@ import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
/**
@@ -108,6 +113,7 @@ class UserUsageStatsService {
}
void init(final long currentTimeMillis) {
+ readPackageMappingsLocked();
mDatabase.init(currentTimeMillis);
int nullCount = 0;
@@ -169,6 +175,54 @@ class UserUsageStatsService {
persistActiveStats();
}
+ void onPackageRemoved(String packageName, long timeRemoved) {
+ mDatabase.onPackageRemoved(packageName, timeRemoved);
+ }
+
+ private void readPackageMappingsLocked() {
+ mDatabase.readMappingsLocked();
+ cleanUpPackageMappingsLocked();
+ }
+
+ /**
+ * Queries Package Manager for a list of installed packages and removes those packages from
+ * mPackagesTokenData which are not installed any more.
+ * This will only happen once per device boot, when the user is unlocked for the first time.
+ */
+ private void cleanUpPackageMappingsLocked() {
+ final long timeNow = System.currentTimeMillis();
+ /*
+ Note (b/142501248): PackageManagerInternal#getInstalledApplications is not lightweight.
+ Once its implementation is updated, or it's replaced with a better alternative, update
+ the call here to use it. For now, using the heavy #getInstalledApplications is okay since
+ this clean-up is only performed once every boot.
+ */
+ final PackageManagerInternal packageManagerInternal =
+ LocalServices.getService(PackageManagerInternal.class);
+ if (packageManagerInternal == null) {
+ return;
+ }
+ final List<ApplicationInfo> installedPackages =
+ packageManagerInternal.getInstalledApplications(0, mUserId, Process.SYSTEM_UID);
+ // convert the package list to a set for easy look-ups
+ final HashSet<String> packagesSet = new HashSet<>(installedPackages.size());
+ for (int i = installedPackages.size() - 1; i >= 0; i--) {
+ packagesSet.add(installedPackages.get(i).packageName);
+ }
+ final List<String> removedPackages = new ArrayList<>();
+ // populate list of packages that are found in the mappings but not in the installed list
+ for (int i = mDatabase.mPackagesTokenData.packagesToTokensMap.size() - 1; i >= 0; i--) {
+ if (!packagesSet.contains(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i))) {
+ removedPackages.add(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i));
+ }
+ }
+
+ // remove packages in the mappings that are no longer installed
+ for (int i = removedPackages.size() - 1; i >= 0; i--) {
+ mDatabase.mPackagesTokenData.removePackage(removedPackages.get(i), timeNow);
+ }
+ }
+
private void onTimeChanged(long oldTime, long newTime) {
persistActiveStats();
mDatabase.onTimeChanged(newTime - oldTime);
@@ -400,6 +454,7 @@ class UserUsageStatsService {
if (results == null) {
results = new ArrayList<>();
}
+ mDatabase.filterStats(currentStats);
combiner.combine(currentStats, true, results);
}
diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
index 62aef876a2eb..7e8a13470c35 100644
--- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
+++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java
@@ -80,6 +80,7 @@ public class UsageStatsDatabasePerfTest {
sContext = InstrumentationRegistry.getTargetContext();
mTestDir = new File(sContext.getFilesDir(), "UsageStatsDatabasePerfTest");
sUsageStatsDatabase = new UsageStatsDatabase(mTestDir);
+ sUsageStatsDatabase.readMappingsLocked();
sUsageStatsDatabase.init(1);
}