UsageStatsService: don't create stuff directly in /data/system_de
/data/system_de is only for per-user data; it *must* only contain per-user encrypted directories. Only vold should ever create anything directly in this directory. In preparation for removing system_server's write access to this directory (https://r.android.com/2078213), make UsageStatsService store its globalcomponentusage file at /data/system/usagestats/globalcomponentusage instead of /data/system_de/usagestats/globalcomponentusage. Migration happens lazily, except that the old file and directory aren't ever deleted since the SELinux policy will no longer allow system_server to do that; the old file just stops being used. vold will need to handle the cleanup instead, or we could just leave the file around. Note that before Android 11, UsageStatsService stored per-user stats in /data/system/usagestats/$userId. These per-user stats are *not* the same thing as globalcomponentusage, which was added in Android 12. UsageStatsService contains code to migrate the per-user stats to /data/system_ce/$userId/usagestats. This is fine, and is the right thing to do, since the per-user stats are potentially sensitive per-user data. This does mean that UsageStatsService now implements two types of migrations, and that the source directory for one migration is the target directory for the other, which is a bit unfortunate. However, since different files are involved in each one, it all works out. Bug: 156305599 Change-Id: I53c16640e8ed8b7eac111990f1cdb3f59579e051
This commit is contained in:
@@ -76,7 +76,6 @@ import android.os.SystemProperties;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AtomicFile;
|
||||
@@ -140,13 +139,26 @@ public class UsageStatsService extends SystemService implements
|
||||
private static final boolean ENABLE_KERNEL_UPDATES = true;
|
||||
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
|
||||
|
||||
private static final File USAGE_STATS_LEGACY_DIR = new File(
|
||||
Environment.getDataSystemDirectory(), "usagestats");
|
||||
// For migration purposes, indicates whether to keep the legacy usage stats directory or not
|
||||
private static final boolean KEEP_LEGACY_DIR = false;
|
||||
// /data/system/usagestats. Now only used for globalcomponentusage. Previously per-user stats
|
||||
// were stored here too, but they've been moved to /data/system_ce/$userId/usagestats.
|
||||
private static final File COMMON_USAGE_STATS_DIR =
|
||||
new File(Environment.getDataSystemDirectory(), "usagestats");
|
||||
private static final File LEGACY_USER_USAGE_STATS_DIR = COMMON_USAGE_STATS_DIR;
|
||||
|
||||
private static final File COMMON_USAGE_STATS_DE_DIR =
|
||||
// /data/system_de/usagestats. When the globalcomponentusage file was added, it was incorrectly
|
||||
// added here instead of in /data/system/usagestats where it should be. We lazily migrate this
|
||||
// file by reading it from here if needed, and always writing it to the new path. We don't
|
||||
// delete the old directory, as system_server no longer has permission to do so.
|
||||
//
|
||||
// Note, this migration is *not* related to the migration of the per-user stats from
|
||||
// /data/system/usagestats/$userId to /data/system_ce/$userId/usagestats mentioned above. Both
|
||||
// of these just happen to involve /data/system/usagestats. /data/system is the right place for
|
||||
// system data not tied to a user, but the wrong place for per-user data. So due to two
|
||||
// separate mistakes, we've unfortunately ended up with one case where we need to move files out
|
||||
// of /data/system, and one case where we need to move a different file *into* /data/system.
|
||||
private static final File LEGACY_COMMON_USAGE_STATS_DIR =
|
||||
new File(Environment.getDataSystemDeDirectory(), "usagestats");
|
||||
|
||||
private static final String GLOBAL_COMPONENT_USAGE_FILE_NAME = "globalcomponentusage";
|
||||
|
||||
private static final char TOKEN_DELIMITER = '/';
|
||||
@@ -630,7 +642,7 @@ public class UsageStatsService extends SystemService implements
|
||||
final int previousVersion = Integer.parseInt(reader.readLine());
|
||||
// UsageStatsDatabase.BACKUP_VERSION was 4 when usage stats were migrated to CE.
|
||||
if (previousVersion >= 4) {
|
||||
deleteLegacyDir(userId);
|
||||
deleteLegacyUserDir(userId);
|
||||
return;
|
||||
}
|
||||
// If migration logic needs to be changed in a future version, do it here.
|
||||
@@ -651,7 +663,7 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
Slog.i(TAG, "Starting migration to system CE for user " + userId);
|
||||
final File legacyUserDir = new File(USAGE_STATS_LEGACY_DIR, Integer.toString(userId));
|
||||
final File legacyUserDir = new File(LEGACY_USER_USAGE_STATS_DIR, Integer.toString(userId));
|
||||
if (legacyUserDir.exists()) {
|
||||
copyRecursively(usageStatsDir, legacyUserDir);
|
||||
}
|
||||
@@ -666,8 +678,8 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
Slog.i(TAG, "Finished migration to system CE for user " + userId);
|
||||
|
||||
// Migration was successful - delete the legacy directory
|
||||
deleteLegacyDir(userId);
|
||||
// Migration was successful - delete the legacy user directory
|
||||
deleteLegacyUserDir(userId);
|
||||
}
|
||||
|
||||
private static void copyRecursively(final File parent, File f) {
|
||||
@@ -698,21 +710,14 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteLegacyDir(int userId) {
|
||||
final File legacyUserDir = new File(USAGE_STATS_LEGACY_DIR, Integer.toString(userId));
|
||||
if (!KEEP_LEGACY_DIR && legacyUserDir.exists()) {
|
||||
private void deleteLegacyUserDir(int userId) {
|
||||
final File legacyUserDir = new File(LEGACY_USER_USAGE_STATS_DIR, Integer.toString(userId));
|
||||
if (legacyUserDir.exists()) {
|
||||
deleteRecursively(legacyUserDir);
|
||||
if (legacyUserDir.exists()) {
|
||||
Slog.w(TAG, "Error occurred while attempting to delete legacy usage stats "
|
||||
+ "dir for user " + userId);
|
||||
}
|
||||
// If all users have been migrated, delete the parent legacy usage stats directory
|
||||
if (USAGE_STATS_LEGACY_DIR.list() != null
|
||||
&& USAGE_STATS_LEGACY_DIR.list().length == 0) {
|
||||
if (!USAGE_STATS_LEGACY_DIR.delete()) {
|
||||
Slog.w(TAG, "Error occurred while attempting to delete legacy usage stats dir");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -807,13 +812,16 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
private void loadGlobalComponentUsageLocked() {
|
||||
final File[] packageUsageFile = COMMON_USAGE_STATS_DE_DIR.listFiles(
|
||||
(dir, name) -> TextUtils.equals(name, GLOBAL_COMPONENT_USAGE_FILE_NAME));
|
||||
if (packageUsageFile == null || packageUsageFile.length == 0) {
|
||||
return;
|
||||
AtomicFile af = new AtomicFile(new File(COMMON_USAGE_STATS_DIR,
|
||||
GLOBAL_COMPONENT_USAGE_FILE_NAME));
|
||||
if (!af.exists()) {
|
||||
af = new AtomicFile(new File(LEGACY_COMMON_USAGE_STATS_DIR,
|
||||
GLOBAL_COMPONENT_USAGE_FILE_NAME));
|
||||
if (!af.exists()) {
|
||||
return;
|
||||
}
|
||||
Slog.i(TAG, "Reading " + GLOBAL_COMPONENT_USAGE_FILE_NAME + " file from old location");
|
||||
}
|
||||
|
||||
final AtomicFile af = new AtomicFile(packageUsageFile[0]);
|
||||
final Map<String, Long> tmpUsage = new ArrayMap<>();
|
||||
try {
|
||||
try (FileInputStream in = af.openRead()) {
|
||||
@@ -831,7 +839,7 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Most likely trying to read a corrupted file - log the failure
|
||||
Slog.e(TAG, "Could not read " + packageUsageFile[0]);
|
||||
Slog.e(TAG, "Could not read " + af.getBaseFile());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,11 +848,11 @@ public class UsageStatsService extends SystemService implements
|
||||
return;
|
||||
}
|
||||
|
||||
if (!COMMON_USAGE_STATS_DE_DIR.mkdirs() && !COMMON_USAGE_STATS_DE_DIR.exists()) {
|
||||
throw new IllegalStateException("Common usage stats DE directory does not exist: "
|
||||
+ COMMON_USAGE_STATS_DE_DIR.getAbsolutePath());
|
||||
if (!COMMON_USAGE_STATS_DIR.mkdirs() && !COMMON_USAGE_STATS_DIR.exists()) {
|
||||
throw new IllegalStateException("Common usage stats directory does not exist: "
|
||||
+ COMMON_USAGE_STATS_DIR.getAbsolutePath());
|
||||
}
|
||||
final File lastTimePackageFile = new File(COMMON_USAGE_STATS_DE_DIR,
|
||||
final File lastTimePackageFile = new File(COMMON_USAGE_STATS_DIR,
|
||||
GLOBAL_COMPONENT_USAGE_FILE_NAME);
|
||||
final AtomicFile af = new AtomicFile(lastTimePackageFile);
|
||||
FileOutputStream fos = null;
|
||||
|
||||
Reference in New Issue
Block a user