diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-05-12 20:56:14 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-05-12 20:56:14 +0000 |
commit | 3931dd2013685bcc7f2dd14fbc41e36866d791fc (patch) | |
tree | 866e3015c9ac98a27ebf7bfe812f9ce45d12c05d /services/usage/java | |
parent | 0cd6d1cb70e9babc6a5a65940ee5e1f615e46395 (diff) | |
parent | a7f9c762cb386fe97db642b397f3d808fbd89c3d (diff) |
Merge "Delay updating of usage stats package mappings." into rvc-dev
Diffstat (limited to 'services/usage/java')
3 files changed, 98 insertions, 16 deletions
diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java index 4468871ee0fb..316382028677 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java @@ -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; } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 75e3df199c08..b59556f0c17a 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -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()); } /** @@ -980,6 +986,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. + */ List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime, boolean obfuscateInstantApps) { synchronized (mLock) { @@ -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 { diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 4e75b7354baa..26de11af6f4e 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -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() { |