summaryrefslogtreecommitdiff
path: root/services/usage/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/usage/java')
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsIdleService.java53
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java38
-rw-r--r--services/usage/java/com/android/server/usage/UserUsageStatsService.java23
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 5b5d57bf2f51..8c9b77e5cb9a 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() {