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

Commit c5a608ae authored by Kweku Adams's avatar Kweku Adams
Browse files

Make persisted job loading asynchronous.

Instead of loading all persisted jobs inside of JobSchedulerService's
constructor, start loading them asynchronously and make sure they're
fully loaded before we enter PHASE_SYSTEM_SERVICES_READY.

Bug: 259342534
Test: atest frameworks/base/services/tests/mockingservicestests/src/com/android/server/job
Test: atest frameworks/base/services/tests/servicestests/src/com/android/server/job
Test: atest CtsJobSchedulerTestCases
Change-Id: Ibccfb4de420c348492cf8240137a10e644d30e72
parent b3fb718c
Loading
Loading
Loading
Loading
+15 −3
Original line number Diff line number Diff line
@@ -152,6 +152,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Predicate;

@@ -241,6 +242,7 @@ public class JobSchedulerService extends com.android.server.SystemService
    final Object mLock = new Object();
    /** Master list of jobs. */
    final JobStore mJobs;
    private final CountDownLatch mJobStoreLoadedLatch;
    /** Tracking the standby bucket state of each app */
    final StandbyTracker mStandbyTracker;
    /** Tracking amount of time each package runs for. */
@@ -2032,7 +2034,9 @@ public class JobSchedulerService extends com.android.server.SystemService
        publishLocalService(JobSchedulerInternal.class, new LocalService());

        // Initialize the job store and set up any persisted jobs
        mJobs = JobStore.initAndGet(this);
        mJobStoreLoadedLatch = new CountDownLatch(1);
        mJobs = JobStore.get(this);
        mJobs.initAsync(mJobStoreLoadedLatch);

        mBatteryStateTracker = new BatteryStateTracker();
        mBatteryStateTracker.startTracking();
@@ -2100,7 +2104,7 @@ public class JobSchedulerService extends com.android.server.SystemService

                    // And kick off the work to update the affected jobs, using a secondary
                    // thread instead of chugging away here on the main looper thread.
                    new Thread(mJobTimeUpdater, "JobSchedulerTimeSetReceiver").start();
                    mJobs.runWorkAsync(mJobTimeUpdater);
                }
            }
        }
@@ -2138,7 +2142,15 @@ public class JobSchedulerService extends com.android.server.SystemService

    @Override
    public void onBootPhase(int phase) {
        if (PHASE_SYSTEM_SERVICES_READY == phase) {
        if (PHASE_LOCK_SETTINGS_READY == phase) {
            // This is the last phase before PHASE_SYSTEM_SERVICES_READY. We need to ensure that
            // persisted jobs are loaded before we can proceed to PHASE_SYSTEM_SERVICES_READY.
            try {
                mJobStoreLoadedLatch.await();
            } catch (InterruptedException e) {
                Slog.e(TAG, "Couldn't wait on job store loading latch");
            }
        } else if (PHASE_SYSTEM_SERVICES_READY == phase) {
            mConstantsObserver.start();
            for (StateController controller : mControllers) {
                controller.onSystemServicesReady();
+28 −1
Original line number Diff line number Diff line
@@ -70,6 +70,7 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Predicate;

@@ -131,7 +132,7 @@ public final class JobStore {
    private JobStorePersistStats mPersistInfo = new JobStorePersistStats();

    /** Used by the {@link JobSchedulerService} to instantiate the JobStore. */
    static JobStore initAndGet(JobSchedulerService jobManagerService) {
    static JobStore get(JobSchedulerService jobManagerService) {
        synchronized (sSingletonLock) {
            if (sSingleton == null) {
                sSingleton = new JobStore(jobManagerService.getContext(),
@@ -147,6 +148,7 @@ public final class JobStore {
    @VisibleForTesting
    public static JobStore initAndGetForTesting(Context context, File dataDir) {
        JobStore jobStoreUnderTest = new JobStore(context, new Object(), dataDir);
        jobStoreUnderTest.init();
        jobStoreUnderTest.clearForTesting();
        return jobStoreUnderTest;
    }
@@ -181,10 +183,16 @@ public final class JobStore {
        mXmlTimestamp = mJobsFile.exists()
                ? mJobsFile.getLastModifiedTime() : mJobFileDirectory.lastModified();
        mRtcGood = (sSystemClock.millis() > mXmlTimestamp);
    }

    private void init() {
        readJobMapFromDisk(mJobSet, mRtcGood);
    }

    void initAsync(CountDownLatch completionLatch) {
        mIoHandler.post(new ReadJobMapFromDiskRunnable(mJobSet, mRtcGood, completionLatch));
    }

    private AtomicFile createJobFile(String baseName) {
        return createJobFile(new File(mJobFileDirectory, baseName + ".xml"));
    }
@@ -201,6 +209,15 @@ public final class JobStore {
        return now >= mXmlTimestamp;
    }

    /**
     * Runs any necessary work asynchronously. If this is called after
     * {@link #initAsync(CountDownLatch)}, this ensures the given work runs after
     * the JobStore is initialized.
     */
    void runWorkAsync(@NonNull Runnable r) {
        mIoHandler.post(r);
    }

    /**
     * Find all the jobs that were affected by RTC clock uncertainty at boot time.  Returns
     * parallel lists of the existing JobStatus objects and of new, equivalent JobStatus instances
@@ -998,14 +1015,21 @@ public final class JobStore {
    private final class ReadJobMapFromDiskRunnable implements Runnable {
        private final JobSet jobSet;
        private final boolean rtcGood;
        private final CountDownLatch mCompletionLatch;

        /**
         * @param jobSet Reference to the (empty) set of JobStatus objects that back the JobStore,
         *               so that after disk read we can populate it directly.
         */
        ReadJobMapFromDiskRunnable(JobSet jobSet, boolean rtcIsGood) {
            this(jobSet, rtcIsGood, null);
        }

        ReadJobMapFromDiskRunnable(JobSet jobSet, boolean rtcIsGood,
                @Nullable CountDownLatch completionLatch) {
            this.jobSet = jobSet;
            this.rtcGood = rtcIsGood;
            this.mCompletionLatch = completionLatch;
        }

        @Override
@@ -1088,6 +1112,9 @@ public final class JobStore {
            if (needFileMigration) {
                migrateJobFilesAsync();
            }
            if (mCompletionLatch != null) {
                mCompletionLatch.countDown();
            }
        }

        private List<JobStatus> readJobMapImpl(InputStream fis, boolean rtcIsGood, long nowElapsed)