Loading services/core/java/android/app/usage/UsageStatsManagerInternal.java +11 −0 Original line number Diff line number Diff line Loading @@ -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(); } services/usage/java/com/android/server/usage/UsageStatsIdleService.java +45 −8 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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"; Loading @@ -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; } Loading services/usage/java/com/android/server/usage/UsageStatsService.java +36 −2 Original line number Diff line number Diff line Loading @@ -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()); Loading Loading @@ -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, Loading Loading @@ -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()); } /** Loading Loading @@ -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. */ Loading Loading @@ -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) { Loading Loading @@ -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 { Loading services/usage/java/com/android/server/usage/UserUsageStatsService.java +17 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -181,19 +182,27 @@ class UserUsageStatsService { private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) { mDatabase.readMappingsLocked(); // 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(); Loading @@ -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 Loading @@ -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() { Loading Loading
services/core/java/android/app/usage/UsageStatsManagerInternal.java +11 −0 Original line number Diff line number Diff line Loading @@ -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(); }
services/usage/java/com/android/server/usage/UsageStatsIdleService.java +45 −8 Original line number Diff line number Diff line Loading @@ -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. */ Loading @@ -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"; Loading @@ -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; } Loading
services/usage/java/com/android/server/usage/UsageStatsService.java +36 −2 Original line number Diff line number Diff line Loading @@ -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()); Loading Loading @@ -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, Loading Loading @@ -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()); } /** Loading Loading @@ -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. */ Loading Loading @@ -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) { Loading Loading @@ -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 { Loading
services/usage/java/com/android/server/usage/UserUsageStatsService.java +17 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -181,19 +182,27 @@ class UserUsageStatsService { private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) { mDatabase.readMappingsLocked(); // 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(); Loading @@ -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 Loading @@ -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() { Loading