Merge "Delay updating of usage stats package mappings." into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3931dd2013
@@ -315,4 +315,15 @@ public abstract class UsageStatsManagerInternal {
|
||||
* @return {@code true} if the pruning was successful, {@code false} otherwise
|
||||
*/
|
||||
public abstract boolean pruneUninstalledPackagesData(@UserIdInt int userId);
|
||||
|
||||
/**
|
||||
* Called by {@link com.android.server.usage.UsageStatsIdleService} between 24 to 48 hours of
|
||||
* when the user is first unlocked to update the usage stats package mappings data that might
|
||||
* be stale or have existed from a restore and belongs to packages that are not installed for
|
||||
* this user anymore.
|
||||
* Note: this is only executed for the system user.
|
||||
*
|
||||
* @return {@code true} if the updating was successful, {@code false} otherwise
|
||||
*/
|
||||
public abstract boolean updatePackageMappingsData();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.os.PersistableBundle;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* JobService used to do any work for UsageStats while the device is idle.
|
||||
*/
|
||||
@@ -36,6 +38,11 @@ public class UsageStatsIdleService extends JobService {
|
||||
* Base job ID for the pruning job - must be unique within the system server uid.
|
||||
*/
|
||||
private static final int PRUNE_JOB_ID = 546357475;
|
||||
/**
|
||||
* Job ID for the update mappings job - must be unique within the system server uid.
|
||||
* Incrementing PRUNE_JOB_ID by 21475 (MAX_USER_ID) to ensure there is no overlap in job ids.
|
||||
*/
|
||||
private static final int UPDATE_MAPPINGS_JOB_ID = 546378950;
|
||||
|
||||
private static final String USER_ID_KEY = "user_id";
|
||||
|
||||
@@ -51,35 +58,65 @@ public class UsageStatsIdleService extends JobService {
|
||||
.setPersisted(true)
|
||||
.build();
|
||||
|
||||
scheduleJobInternal(context, pruneJob, userJobId);
|
||||
}
|
||||
|
||||
static void scheduleUpdateMappingsJob(Context context) {
|
||||
final ComponentName component = new ComponentName(context.getPackageName(),
|
||||
UsageStatsIdleService.class.getName());
|
||||
final JobInfo updateMappingsJob = new JobInfo.Builder(UPDATE_MAPPINGS_JOB_ID, component)
|
||||
.setPersisted(true)
|
||||
.setMinimumLatency(TimeUnit.DAYS.toMillis(1))
|
||||
.setOverrideDeadline(TimeUnit.DAYS.toMillis(2))
|
||||
.build();
|
||||
|
||||
scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_ID);
|
||||
}
|
||||
|
||||
private static void scheduleJobInternal(Context context, JobInfo pruneJob, int jobId) {
|
||||
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
|
||||
final JobInfo pendingPruneJob = jobScheduler.getPendingJob(userJobId);
|
||||
final JobInfo pendingPruneJob = jobScheduler.getPendingJob(jobId);
|
||||
// only schedule a new prune job if one doesn't exist already for this user
|
||||
if (!pruneJob.equals(pendingPruneJob)) {
|
||||
jobScheduler.cancel(userJobId); // cancel any previously scheduled prune job
|
||||
jobScheduler.cancel(jobId); // cancel any previously scheduled prune job
|
||||
jobScheduler.schedule(pruneJob);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void cancelJob(Context context, int userId) {
|
||||
final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
|
||||
cancelJobInternal(context, PRUNE_JOB_ID + userId);
|
||||
}
|
||||
|
||||
static void cancelUpdateMappingsJob(Context context) {
|
||||
cancelJobInternal(context, UPDATE_MAPPINGS_JOB_ID);
|
||||
}
|
||||
|
||||
private static void cancelJobInternal(Context context, int jobId) {
|
||||
final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
|
||||
jobScheduler.cancel(userJobId);
|
||||
if (jobScheduler != null) {
|
||||
jobScheduler.cancel(jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
final PersistableBundle bundle = params.getExtras();
|
||||
final int userId = bundle.getInt(USER_ID_KEY, -1);
|
||||
if (userId == -1) {
|
||||
if (userId == -1 && params.getJobId() != UPDATE_MAPPINGS_JOB_ID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AsyncTask.execute(() -> {
|
||||
final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
|
||||
UsageStatsManagerInternal.class);
|
||||
final boolean pruned = usageStatsManagerInternal.pruneUninstalledPackagesData(userId);
|
||||
jobFinished(params, !pruned); // reschedule if data was not pruned
|
||||
if (params.getJobId() == UPDATE_MAPPINGS_JOB_ID) {
|
||||
final boolean jobFinished = usageStatsManagerInternal.updatePackageMappingsData();
|
||||
jobFinished(params, !jobFinished); // reschedule if data was not updated
|
||||
} else {
|
||||
final boolean jobFinished =
|
||||
usageStatsManagerInternal.pruneUninstalledPackagesData(userId);
|
||||
jobFinished(params, !jobFinished); // reschedule if data was not pruned
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -332,6 +332,11 @@ public class UsageStatsService extends SystemService implements
|
||||
private void onUserUnlocked(int userId) {
|
||||
// fetch the installed packages outside the lock so it doesn't block package manager.
|
||||
final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
|
||||
// delay updating of package mappings for user 0 since their data is not likely to be stale.
|
||||
// this also makes it less likely for restored data to be erased on unexpected reboots.
|
||||
if (userId == UserHandle.USER_SYSTEM) {
|
||||
UsageStatsIdleService.scheduleUpdateMappingsJob(getContext());
|
||||
}
|
||||
synchronized (mLock) {
|
||||
// Create a user unlocked event to report
|
||||
final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime());
|
||||
@@ -543,8 +548,8 @@ public class UsageStatsService extends SystemService implements
|
||||
* Initializes the given user's usage stats service - this should ideally only be called once,
|
||||
* when the user is initially unlocked.
|
||||
*/
|
||||
private void initializeUserUsageStatsServiceLocked(int userId,
|
||||
long currentTimeMillis, HashMap<String, Long> installedPackages) {
|
||||
private void initializeUserUsageStatsServiceLocked(int userId, long currentTimeMillis,
|
||||
HashMap<String, Long> installedPackages) {
|
||||
final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
|
||||
"usagestats");
|
||||
final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId,
|
||||
@@ -931,6 +936,7 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
// Cancel any scheduled jobs for this user since the user is being removed.
|
||||
UsageStatsIdleService.cancelJob(getContext(), userId);
|
||||
UsageStatsIdleService.cancelUpdateMappingsJob(getContext());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -977,6 +983,26 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
private boolean updatePackageMappingsData() {
|
||||
// fetch the installed packages outside the lock so it doesn't block package manager.
|
||||
final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM);
|
||||
synchronized (mLock) {
|
||||
if (!mUserUnlockedStates.get(UserHandle.USER_SYSTEM)) {
|
||||
return false; // user is no longer unlocked
|
||||
}
|
||||
|
||||
final UserUsageStatsService userService = mUserState.get(UserHandle.USER_SYSTEM);
|
||||
if (userService == null) {
|
||||
return false; // user was stopped or removed
|
||||
}
|
||||
|
||||
return userService.updatePackageMappingsLocked(installedPkgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the Binder stub.
|
||||
*/
|
||||
@@ -2137,6 +2163,9 @@ public class UsageStatsService extends SystemService implements
|
||||
}
|
||||
|
||||
// Check to ensure that only user 0's data is b/r for now
|
||||
// Note: if backup and restore is enabled for users other than the system user, the
|
||||
// #onUserUnlocked logic, specifically when the update mappings job is scheduled via
|
||||
// UsageStatsIdleService.scheduleUpdateMappingsJob, will have to be updated.
|
||||
if (user == UserHandle.USER_SYSTEM) {
|
||||
final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
|
||||
if (userStats == null) {
|
||||
@@ -2229,6 +2258,11 @@ public class UsageStatsService extends SystemService implements
|
||||
public boolean pruneUninstalledPackagesData(int userId) {
|
||||
return UsageStatsService.this.pruneUninstalledPackagesData(userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updatePackageMappingsData() {
|
||||
return UsageStatsService.this.updatePackageMappingsData();
|
||||
}
|
||||
}
|
||||
|
||||
private class MyPackageMonitor extends PackageMonitor {
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.app.usage.UsageStatsManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -181,19 +182,27 @@ class UserUsageStatsService {
|
||||
|
||||
private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
|
||||
mDatabase.readMappingsLocked();
|
||||
updatePackageMappingsLocked(installedPackages);
|
||||
// Package mappings for the system user are updated after 24 hours via a job scheduled by
|
||||
// UsageStatsIdleService to ensure restored data is not lost on first boot. Additionally,
|
||||
// this makes user service initialization a little quicker on subsequent boots.
|
||||
if (mUserId != UserHandle.USER_SYSTEM) {
|
||||
updatePackageMappingsLocked(installedPackages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries Job Scheduler for any pending data prune jobs and if any exist, it updates the
|
||||
* package mappings in memory by removing those tokens.
|
||||
* Compares the package mappings on disk with the ones currently installed and removes the
|
||||
* mappings for those packages that have been uninstalled.
|
||||
* This will only happen once per device boot, when the user is unlocked for the first time.
|
||||
* If the user is the system user (user 0), this is delayed to ensure data for packages
|
||||
* that were restored isn't removed before the restore is complete.
|
||||
*
|
||||
* @param installedPackages map of installed packages (package_name:package_install_time)
|
||||
* @return {@code true} on a successful mappings update, {@code false} otherwise.
|
||||
*/
|
||||
private void updatePackageMappingsLocked(HashMap<String, Long> installedPackages) {
|
||||
boolean updatePackageMappingsLocked(HashMap<String, Long> installedPackages) {
|
||||
if (ArrayUtils.isEmpty(installedPackages)) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
final long timeNow = System.currentTimeMillis();
|
||||
@@ -206,7 +215,7 @@ class UserUsageStatsService {
|
||||
}
|
||||
}
|
||||
if (removedPackages.isEmpty()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove packages in the mappings that are no longer installed and persist to disk
|
||||
@@ -217,7 +226,9 @@ class UserUsageStatsService {
|
||||
mDatabase.writeMappingsLocked();
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "Unable to write updated package mappings file on service initialization.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean pruneUninstalledPackagesData() {
|
||||
|
||||
Reference in New Issue
Block a user