diff options
7 files changed, 94 insertions, 79 deletions
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index dca722e649bc..4ce9184f0b96 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -123,6 +123,9 @@ public final class UserHandle implements Parcelable { @TestApi public static final int MIN_SECONDARY_USER_ID = 10; + /** @hide */ + public static final int MAX_SECONDARY_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE; + /** * (Arbitrary) user handle cache size. * {@link #CACHED_USER_HANDLES} caches user handles in the range of diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index a35aa7c74ee5..70eeb7fecc8f 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -329,11 +329,11 @@ public abstract class UsageStatsManagerInternal { * 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. * + * @param userId The user to update * @return {@code true} if the updating was successful, {@code false} otherwise */ - public abstract boolean updatePackageMappingsData(); + public abstract boolean updatePackageMappingsData(@UserIdInt int userId); /** * Listener interface for usage events. diff --git a/services/core/java/com/android/server/backup/SystemBackupAgent.java b/services/core/java/com/android/server/backup/SystemBackupAgent.java index b18be3cc094e..08dcccfe405b 100644 --- a/services/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/services/core/java/com/android/server/backup/SystemBackupAgent.java @@ -86,7 +86,7 @@ public class SystemBackupAgent extends BackupAgentHelper { private static final Set<String> sEligibleForMultiUser = Sets.newArraySet( PERMISSION_HELPER, NOTIFICATION_HELPER, SYNC_SETTINGS_HELPER, APP_LOCALES_HELPER, - ACCOUNT_MANAGER_HELPER); + ACCOUNT_MANAGER_HELPER, USAGE_STATS_HELPER); private int mUserId = UserHandle.USER_SYSTEM; @@ -100,7 +100,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper(mUserId)); addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(mUserId)); addHelper(PERMISSION_HELPER, new PermissionBackupHelper(mUserId)); - addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this)); + addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(mUserId)); addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper()); addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper(mUserId)); addHelper(SLICES_HELPER, new SliceBackupHelper(this)); diff --git a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java index d6a70d369cdc..4098c1a9cff9 100644 --- a/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java +++ b/services/core/java/com/android/server/backup/UsageStatsBackupHelper.java @@ -1,9 +1,9 @@ package com.android.server.backup; +import android.annotation.UserIdInt; import android.app.backup.BlobBackupHelper; import android.app.usage.UsageStatsManagerInternal; -import android.content.Context; import android.os.UserHandle; import android.util.Log; @@ -26,8 +26,16 @@ public class UsageStatsBackupHelper extends BlobBackupHelper { // same as UsageStatsDatabase.KEY_USAGE_STATS static final String KEY_USAGE_STATS = "usage_stats"; - public UsageStatsBackupHelper(Context context) { + private final @UserIdInt int mUserId; + + /** + * Marshall/unmarshall the usagestats data for the given user + * + * @param userId The userId to backup/restore + */ + public UsageStatsBackupHelper(@UserIdInt int userId) { super(BLOB_VERSION, KEY_USAGE_STATS); + mUserId = userId; } @Override @@ -38,8 +46,11 @@ public class UsageStatsBackupHelper extends BlobBackupHelper { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); try { + // Note: Write 0 here deliberately so that a backup from a secondary user + // can still be restored to an older OS where the restore was always to user 0 + // Writing the actual userId here would result in restores not working on pre-U. out.writeInt(UserHandle.USER_SYSTEM); - out.write(localUsageStatsManager.getBackupPayload(UserHandle.USER_SYSTEM, key)); + out.write(localUsageStatsManager.getBackupPayload(mUserId, key)); } catch (IOException ioe) { if (DEBUG) Log.e(TAG, "Failed to backup Usage Stats", ioe); baos.reset(); @@ -49,7 +60,6 @@ public class UsageStatsBackupHelper extends BlobBackupHelper { return null; } - @Override protected void applyRestoredPayload(String key, byte[] payload) { if (KEY_USAGE_STATS.equals(key)) { @@ -57,10 +67,10 @@ public class UsageStatsBackupHelper extends BlobBackupHelper { LocalServices.getService(UsageStatsManagerInternal.class); DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload)); try { - int user = in.readInt(); + in.readInt(); // Legacy userId parameter, read and ignore byte[] restoreData = new byte[payload.length - 4]; in.read(restoreData, 0, restoreData.length); - localUsageStatsManager.applyRestoredPayload(user, key, restoreData); + localUsageStatsManager.applyRestoredPayload(mUserId, key, restoreData); } catch (IOException ioe) { if (DEBUG) Log.e(TAG, "Failed to restore Usage Stats", ioe); } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 1c838803fe21..762d1f665653 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -262,7 +262,7 @@ public class UserManagerService extends IUserManager.Stub { // We need to keep process uid within Integer.MAX_VALUE. @VisibleForTesting - static final int MAX_USER_ID = Integer.MAX_VALUE / UserHandle.PER_USER_RANGE; + static final int MAX_USER_ID = UserHandle.MAX_SECONDARY_USER_ID; // Max size of the queue of recently removed users @VisibleForTesting diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java index 316382028677..20f03d8c1b2a 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java @@ -15,6 +15,7 @@ */ package com.android.server.usage; +import android.annotation.UserIdInt; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; @@ -35,65 +36,71 @@ import java.util.concurrent.TimeUnit; public class UsageStatsIdleService extends JobService { /** - * Base job ID for the pruning job - must be unique within the system server uid. + * Namespace for prune job */ - private static final int PRUNE_JOB_ID = 546357475; + private static final String PRUNE_JOB_NS = "usagestats_prune"; + /** - * 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. + * Namespace for update mappings job */ - private static final int UPDATE_MAPPINGS_JOB_ID = 546378950; + private static final String UPDATE_MAPPINGS_JOB_NS = "usagestats_mapping"; private static final String USER_ID_KEY = "user_id"; - static void scheduleJob(Context context, int userId) { - final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user + /** Schedule a prune job */ + static void schedulePruneJob(Context context, @UserIdInt int userId) { final ComponentName component = new ComponentName(context.getPackageName(), UsageStatsIdleService.class.getName()); final PersistableBundle bundle = new PersistableBundle(); bundle.putInt(USER_ID_KEY, userId); - final JobInfo pruneJob = new JobInfo.Builder(userJobId, component) + final JobInfo pruneJob = new JobInfo.Builder(userId, component) .setRequiresDeviceIdle(true) .setExtras(bundle) .setPersisted(true) .build(); - scheduleJobInternal(context, pruneJob, userJobId); + scheduleJobInternal(context, pruneJob, PRUNE_JOB_NS, userId); } - static void scheduleUpdateMappingsJob(Context context) { + static void scheduleUpdateMappingsJob(Context context, @UserIdInt int userId) { final ComponentName component = new ComponentName(context.getPackageName(), UsageStatsIdleService.class.getName()); - final JobInfo updateMappingsJob = new JobInfo.Builder(UPDATE_MAPPINGS_JOB_ID, component) + final PersistableBundle bundle = new PersistableBundle(); + bundle.putInt(USER_ID_KEY, userId); + final JobInfo updateMappingsJob = new JobInfo.Builder(userId, component) .setPersisted(true) .setMinimumLatency(TimeUnit.DAYS.toMillis(1)) .setOverrideDeadline(TimeUnit.DAYS.toMillis(2)) + .setExtras(bundle) .build(); - scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_ID); + scheduleJobInternal(context, updateMappingsJob, UPDATE_MAPPINGS_JOB_NS, userId); } - private static void scheduleJobInternal(Context context, JobInfo pruneJob, int jobId) { - final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); - 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(jobId); // cancel any previously scheduled prune job - jobScheduler.schedule(pruneJob); + private static void scheduleJobInternal(Context context, JobInfo jobInfo, + String namespace, int jobId) { + JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); + jobScheduler = jobScheduler.forNamespace(namespace); + final JobInfo pendingJob = jobScheduler.getPendingJob(jobId); + // only schedule a new job if one doesn't exist already for this user + if (!jobInfo.equals(pendingJob)) { + jobScheduler.cancel(jobId); // cancel any previously scheduled job + jobScheduler.schedule(jobInfo); } } - static void cancelJob(Context context, int userId) { - cancelJobInternal(context, PRUNE_JOB_ID + userId); + static void cancelPruneJob(Context context, @UserIdInt int userId) { + cancelJobInternal(context, PRUNE_JOB_NS, userId); } - static void cancelUpdateMappingsJob(Context context) { - cancelJobInternal(context, UPDATE_MAPPINGS_JOB_ID); + static void cancelUpdateMappingsJob(Context context, @UserIdInt int userId) { + cancelJobInternal(context, UPDATE_MAPPINGS_JOB_NS, userId); } - private static void cancelJobInternal(Context context, int jobId) { - final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); + private static void cancelJobInternal(Context context, String namespace, int jobId) { + JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); if (jobScheduler != null) { + jobScheduler = jobScheduler.forNamespace(namespace); jobScheduler.cancel(jobId); } } @@ -102,15 +109,19 @@ public class UsageStatsIdleService extends JobService { public boolean onStartJob(JobParameters params) { final PersistableBundle bundle = params.getExtras(); final int userId = bundle.getInt(USER_ID_KEY, -1); - if (userId == -1 && params.getJobId() != UPDATE_MAPPINGS_JOB_ID) { + + if (userId == -1) { // legacy job return false; } + // Do async AsyncTask.execute(() -> { final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService( UsageStatsManagerInternal.class); - if (params.getJobId() == UPDATE_MAPPINGS_JOB_ID) { - final boolean jobFinished = usageStatsManagerInternal.updatePackageMappingsData(); + final String jobNs = params.getJobNamespace(); + if (UPDATE_MAPPINGS_JOB_NS.equals(jobNs)) { + final boolean jobFinished = + usageStatsManagerInternal.updatePackageMappingsData(userId); jobFinished(params, !jobFinished); // reschedule if data was not updated } else { final boolean jobFinished = @@ -118,6 +129,8 @@ public class UsageStatsIdleService extends JobService { jobFinished(params, !jobFinished); // reschedule if data was not pruned } }); + + // Job is running asynchronously 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 b3a1f2b85f63..7ff5b4a28f1b 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -433,11 +433,9 @@ 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()); - } + + UsageStatsIdleService.scheduleUpdateMappingsJob(getContext(), userId); + final boolean deleteObsoleteData = shouldDeleteObsoleteData(UserHandle.of(userId)); synchronized (mLock) { // This should be safe to add this early. Other than reportEventOrAddToQueue and @@ -1261,8 +1259,8 @@ public class UsageStatsService extends SystemService implements } mAppStandby.onUserRemoved(userId); // Cancel any scheduled jobs for this user since the user is being removed. - UsageStatsIdleService.cancelJob(getContext(), userId); - UsageStatsIdleService.cancelUpdateMappingsJob(getContext()); + UsageStatsIdleService.cancelPruneJob(getContext(), userId); + UsageStatsIdleService.cancelUpdateMappingsJob(getContext(), userId); } /** @@ -1300,7 +1298,7 @@ public class UsageStatsService extends SystemService implements // Schedule a job to prune any data related to this package. if (tokenRemoved != PackagesTokenData.UNASSIGNED_TOKEN) { - UsageStatsIdleService.scheduleJob(getContext(), userId); + UsageStatsIdleService.schedulePruneJob(getContext(), userId); } } @@ -1325,19 +1323,19 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - private boolean updatePackageMappingsData() { + private boolean updatePackageMappingsData(@UserIdInt int userId) { // don't update the mappings if a profile user is defined - if (!shouldDeleteObsoleteData(UserHandle.SYSTEM)) { + if (!shouldDeleteObsoleteData(UserHandle.of(userId))) { return true; // return true so job scheduler doesn't reschedule the job } // fetch the installed packages outside the lock so it doesn't block package manager. - final HashMap<String, Long> installedPkgs = getInstalledPackages(UserHandle.USER_SYSTEM); + final HashMap<String, Long> installedPkgs = getInstalledPackages(userId); synchronized (mLock) { - if (!mUserUnlockedStates.contains(UserHandle.USER_SYSTEM)) { + if (!mUserUnlockedStates.contains(userId)) { return false; // user is no longer unlocked } - final UserUsageStatsService userService = mUserState.get(UserHandle.USER_SYSTEM); + final UserUsageStatsService userService = mUserState.get(userId); if (userService == null) { return false; // user was stopped or removed } @@ -3055,44 +3053,35 @@ public class UsageStatsService extends SystemService implements } @Override - public byte[] getBackupPayload(int user, String key) { - if (!mUserUnlockedStates.contains(user)) { - Slog.w(TAG, "Failed to get backup payload for locked user " + user); + public byte[] getBackupPayload(@UserIdInt int userId, String key) { + if (!mUserUnlockedStates.contains(userId)) { + Slog.w(TAG, "Failed to get backup payload for locked user " + userId); return null; } synchronized (mLock) { - // 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) { - return null; // user was stopped or removed - } - return userStats.getBackupPayload(key); - } else { - return null; + final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId); + if (userStats == null) { + return null; // user was stopped or removed } + Slog.i(TAG, "Returning backup payload for u=" + userId); + return userStats.getBackupPayload(key); } } @Override - public void applyRestoredPayload(int user, String key, byte[] payload) { + public void applyRestoredPayload(@UserIdInt int userId, String key, byte[] payload) { synchronized (mLock) { - if (!mUserUnlockedStates.contains(user)) { - Slog.w(TAG, "Failed to apply restored payload for locked user " + user); + if (!mUserUnlockedStates.contains(userId)) { + Slog.w(TAG, "Failed to apply restored payload for locked user " + userId); return; } - if (user == UserHandle.USER_SYSTEM) { - final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user); - if (userStats == null) { - return; // user was stopped or removed - } - final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload); - mAppStandby.restoreAppsToRare(restoredApps, user); + final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(userId); + if (userStats == null) { + return; // user was stopped or removed } + final Set<String> restoredApps = userStats.applyRestoredPayload(key, payload); + mAppStandby.restoreAppsToRare(restoredApps, userId); } } @@ -3165,8 +3154,8 @@ public class UsageStatsService extends SystemService implements } @Override - public boolean updatePackageMappingsData() { - return UsageStatsService.this.updatePackageMappingsData(); + public boolean updatePackageMappingsData(@UserIdInt int userId) { + return UsageStatsService.this.updatePackageMappingsData(userId); } /** |