Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf82ced3 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Delay updating of usage stats package mappings." into rvc-dev am: 3931dd20

Change-Id: Idb4744e1dbfc0cc473625e102909f6c51e49f432
parents 877f630d 3931dd20
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -315,4 +315,15 @@ public abstract class UsageStatsManagerInternal {
     * @return {@code true} if the pruning was successful, {@code false} otherwise
     * @return {@code true} if the pruning was successful, {@code false} otherwise
     */
     */
    public abstract boolean pruneUninstalledPackagesData(@UserIdInt int userId);
    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();
}
}
+45 −8
Original line number Original line Diff line number Diff line
@@ -27,6 +27,8 @@ import android.os.PersistableBundle;


import com.android.server.LocalServices;
import com.android.server.LocalServices;


import java.util.concurrent.TimeUnit;

/**
/**
 * JobService used to do any work for UsageStats while the device is idle.
 * 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.
     * Base job ID for the pruning job - must be unique within the system server uid.
     */
     */
    private static final int PRUNE_JOB_ID = 546357475;
    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";
    private static final String USER_ID_KEY = "user_id";


@@ -51,35 +58,65 @@ public class UsageStatsIdleService extends JobService {
                .setPersisted(true)
                .setPersisted(true)
                .build();
                .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 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
        // only schedule a new prune job if one doesn't exist already for this user
        if (!pruneJob.equals(pendingPruneJob)) {
        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);
            jobScheduler.schedule(pruneJob);
        }
        }

    }
    }


    static void cancelJob(Context context, int userId) {
    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);
        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
        jobScheduler.cancel(userJobId);
        if (jobScheduler != null) {
            jobScheduler.cancel(jobId);
        }
    }
    }


    @Override
    @Override
    public boolean onStartJob(JobParameters params) {
    public boolean onStartJob(JobParameters params) {
        final PersistableBundle bundle = params.getExtras();
        final PersistableBundle bundle = params.getExtras();
        final int userId = bundle.getInt(USER_ID_KEY, -1);
        final int userId = bundle.getInt(USER_ID_KEY, -1);
        if (userId == -1) {
        if (userId == -1 && params.getJobId() != UPDATE_MAPPINGS_JOB_ID) {
            return false;
            return false;
        }
        }


        AsyncTask.execute(() -> {
        AsyncTask.execute(() -> {
            final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
            final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
                    UsageStatsManagerInternal.class);
                    UsageStatsManagerInternal.class);
            final boolean pruned = usageStatsManagerInternal.pruneUninstalledPackagesData(userId);
            if (params.getJobId() == UPDATE_MAPPINGS_JOB_ID) {
            jobFinished(params, !pruned); // reschedule if data was not pruned
                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;
        return true;
    }
    }
+36 −2
Original line number Original line Diff line number Diff line
@@ -332,6 +332,11 @@ public class UsageStatsService extends SystemService implements
    private void onUserUnlocked(int userId) {
    private void onUserUnlocked(int userId) {
        // fetch the installed packages outside the lock so it doesn't block package manager.
        // fetch the installed packages outside the lock so it doesn't block package manager.
        final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
        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) {
        synchronized (mLock) {
            // Create a user unlocked event to report
            // Create a user unlocked event to report
            final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime());
            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,
     * Initializes the given user's usage stats service - this should ideally only be called once,
     * when the user is initially unlocked.
     * when the user is initially unlocked.
     */
     */
    private void initializeUserUsageStatsServiceLocked(int userId,
    private void initializeUserUsageStatsServiceLocked(int userId, long currentTimeMillis,
            long currentTimeMillis, HashMap<String, Long> installedPackages) {
            HashMap<String, Long> installedPackages) {
        final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
        final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
                "usagestats");
                "usagestats");
        final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId,
        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.
        // Cancel any scheduled jobs for this user since the user is being removed.
        UsageStatsIdleService.cancelJob(getContext(), userId);
        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.
     * 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
                // 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) {
                if (user == UserHandle.USER_SYSTEM) {
                    final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
                    final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
                    if (userStats == null) {
                    if (userStats == null) {
@@ -2229,6 +2258,11 @@ public class UsageStatsService extends SystemService implements
        public boolean pruneUninstalledPackagesData(int userId) {
        public boolean pruneUninstalledPackagesData(int userId) {
            return UsageStatsService.this.pruneUninstalledPackagesData(userId);
            return UsageStatsService.this.pruneUninstalledPackagesData(userId);
        }
        }

        @Override
        public boolean updatePackageMappingsData() {
            return UsageStatsService.this.updatePackageMappingsData();
        }
    }
    }


    private class MyPackageMonitor extends PackageMonitor {
    private class MyPackageMonitor extends PackageMonitor {
+17 −6
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.UserHandle;
import android.text.format.DateUtils;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.ArraySet;
@@ -181,19 +182,27 @@ class UserUsageStatsService {


    private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
    private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
        mDatabase.readMappingsLocked();
        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);
            updatePackageMappingsLocked(installedPackages);
        }
        }
    }


    /**
    /**
     * Queries Job Scheduler for any pending data prune jobs and if any exist, it updates the
     * Compares the package mappings on disk with the ones currently installed and removes the
     * package mappings in memory by removing those tokens.
     * 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.
     * 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)
     * @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)) {
        if (ArrayUtils.isEmpty(installedPackages)) {
            return;
            return true;
        }
        }


        final long timeNow = System.currentTimeMillis();
        final long timeNow = System.currentTimeMillis();
@@ -206,7 +215,7 @@ class UserUsageStatsService {
            }
            }
        }
        }
        if (removedPackages.isEmpty()) {
        if (removedPackages.isEmpty()) {
            return;
            return true;
        }
        }


        // remove packages in the mappings that are no longer installed and persist to disk
        // remove packages in the mappings that are no longer installed and persist to disk
@@ -217,7 +226,9 @@ class UserUsageStatsService {
            mDatabase.writeMappingsLocked();
            mDatabase.writeMappingsLocked();
        } catch (Exception e) {
        } catch (Exception e) {
            Slog.w(TAG, "Unable to write updated package mappings file on service initialization.");
            Slog.w(TAG, "Unable to write updated package mappings file on service initialization.");
            return false;
        }
        }
        return true;
    }
    }


    boolean pruneUninstalledPackagesData() {
    boolean pruneUninstalledPackagesData() {