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

Commit 7535df42 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Put syncs in a dedicated job namespace."

parents 9810a68c 5ebdf21a
Loading
Loading
Loading
Loading
+0 −8
Original line number Original line Diff line number Diff line
@@ -17,23 +17,15 @@
package com.android.server.job;
package com.android.server.job;


import android.annotation.Nullable;
import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobParameters;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoOutputStream;


import java.util.List;

/**
/**
 * JobScheduler local system service interface.
 * JobScheduler local system service interface.
 * {@hide} Only for use within the system server.
 * {@hide} Only for use within the system server.
 */
 */
public interface JobSchedulerInternal {
public interface JobSchedulerInternal {


    /**
     * Returns a list of pending jobs scheduled by the system service.
     */
    List<JobInfo> getSystemScheduledPendingJobs();

    /**
    /**
     * Cancel the jobs for a given uid (e.g. when app data is cleared)
     * Cancel the jobs for a given uid (e.g. when app data is cleared)
     *
     *
+0 −17
Original line number Original line Diff line number Diff line
@@ -3478,23 +3478,6 @@ public class JobSchedulerService extends com.android.server.SystemService


    final class LocalService implements JobSchedulerInternal {
    final class LocalService implements JobSchedulerInternal {


        /**
         * Returns a list of all pending jobs. A running job is not considered pending. Periodic
         * jobs are always considered pending.
         */
        @Override
        public List<JobInfo> getSystemScheduledPendingJobs() {
            synchronized (mLock) {
                final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
                mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
                    if (job.getJob().isPeriodic() || !mConcurrencyManager.isJobRunningLocked(job)) {
                        pendingJobs.add(job.getJob());
                    }
                });
                return pendingJobs;
            }
        }

        @Override
        @Override
        public void cancelJobsForUid(int uid, boolean includeProxiedJobs,
        public void cancelJobsForUid(int uid, boolean includeProxiedJobs,
                @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
                @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
+2 −0
Original line number Original line Diff line number Diff line
@@ -83,4 +83,6 @@ message SyncStatusProto {
  }
  }


  repeated StatusInfo status = 1;
  repeated StatusInfo status = 1;

  optional bool is_job_namespace_migrated = 2;
}
}
+52 −32
Original line number Original line Diff line number Diff line
@@ -206,13 +206,6 @@ public class SyncManager {
     */
     */
    private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds
    private static final long SYNC_DELAY_ON_CONFLICT = 10*1000; // 10 seconds


    /**
     * Generate job ids in the range [MIN_SYNC_JOB_ID, MAX_SYNC_JOB_ID) to avoid conflicts with
     * other jobs scheduled by the system process.
     */
    private static final int MIN_SYNC_JOB_ID = 100000;
    private static final int MAX_SYNC_JOB_ID = 110000;

    private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
    private static final String SYNC_WAKE_LOCK_PREFIX = "*sync*/";
    private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
    private static final String HANDLE_SYNC_ALARM_WAKE_LOCK = "SyncManagerHandleSyncAlarm";
    private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
    private static final String SYNC_LOOP_WAKE_LOCK = "SyncLoopWakeLock";
@@ -243,12 +236,11 @@ public class SyncManager {


    volatile private PowerManager.WakeLock mSyncManagerWakeLock;
    volatile private PowerManager.WakeLock mSyncManagerWakeLock;
    volatile private boolean mDataConnectionIsConnected = false;
    volatile private boolean mDataConnectionIsConnected = false;
    private volatile int mNextJobIdOffset = 0;
    private volatile int mNextJobId = 0;


    private final NotificationManager mNotificationMgr;
    private final NotificationManager mNotificationMgr;
    private final IBatteryStats mBatteryStats;
    private final IBatteryStats mBatteryStats;
    private JobScheduler mJobScheduler;
    private JobScheduler mJobScheduler;
    private JobSchedulerInternal mJobSchedulerInternal;


    private SyncStorageEngine mSyncStorageEngine;
    private SyncStorageEngine mSyncStorageEngine;


@@ -284,24 +276,19 @@ public class SyncManager {
    }
    }


    private int getUnusedJobIdH() {
    private int getUnusedJobIdH() {
        final int maxNumSyncJobIds = MAX_SYNC_JOB_ID - MIN_SYNC_JOB_ID;
        final List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
        final List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
        while (isJobIdInUseLockedH(mNextJobId, pendingJobs)) {
        for (int i = 0; i < maxNumSyncJobIds; ++i) {
            // SyncManager jobs are placed in their own namespace. Since there's no chance of
            int newJobId = MIN_SYNC_JOB_ID + ((mNextJobIdOffset + i) % maxNumSyncJobIds);
            // conflicting with other parts of the system, we can just keep incrementing until
            if (!isJobIdInUseLockedH(newJobId, pendingJobs)) {
            // we find an unused ID.
                mNextJobIdOffset = (mNextJobIdOffset + i + 1) % maxNumSyncJobIds;
            mNextJobId++;
                return newJobId;
        }
        }
        }
        return mNextJobId;
        // We've used all 10,000 intended job IDs.... We're probably in a world of pain right now :/
        Slog.wtf(TAG, "All " + maxNumSyncJobIds + " possible sync job IDs are taken :/");
        mNextJobIdOffset = (mNextJobIdOffset + 1) % maxNumSyncJobIds;
        return MIN_SYNC_JOB_ID + mNextJobIdOffset;
    }
    }


    private List<SyncOperation> getAllPendingSyncs() {
    private List<SyncOperation> getAllPendingSyncs() {
        verifyJobScheduler();
        verifyJobScheduler();
        List<JobInfo> pendingJobs = mJobSchedulerInternal.getSystemScheduledPendingJobs();
        List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
        final int numJobs = pendingJobs.size();
        final int numJobs = pendingJobs.size();
        final List<SyncOperation> pendingSyncs = new ArrayList<>(numJobs);
        final List<SyncOperation> pendingSyncs = new ArrayList<>(numJobs);
        for (int i = 0; i < numJobs; ++i) {
        for (int i = 0; i < numJobs; ++i) {
@@ -309,6 +296,8 @@ public class SyncManager {
            SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
            SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
            if (op != null) {
            if (op != null) {
                pendingSyncs.add(op);
                pendingSyncs.add(op);
            } else {
                Slog.wtf(TAG, "Non-sync job inside of SyncManager's namespace");
            }
            }
        }
        }
        return pendingSyncs;
        return pendingSyncs;
@@ -494,6 +483,38 @@ public class SyncManager {
        });
        });
    }
    }


    /**
     * Migrate syncs from the default job namespace to SyncManager's namespace if they haven't been
     * migrated already.
     */
    private void migrateSyncJobNamespaceIfNeeded() {
        if (mSyncStorageEngine.isJobNamespaceMigrated()) {
            return;
        }
        final JobScheduler jobSchedulerDefaultNamespace =
                mContext.getSystemService(JobScheduler.class);
        final List<JobInfo> pendingJobs = jobSchedulerDefaultNamespace.getAllPendingJobs();
        // Wait until we've confirmed that all syncs have been migrated to the new namespace
        // before we persist successful migration to our status file. This is done to avoid
        // internal consistency issues if the devices reboots right after SyncManager has
        // done the migration on its side but before JobScheduler has finished persisting
        // the updated jobs to disk. If JobScheduler hasn't persisted the update to disk,
        // then nothing that happened afterwards should have been persisted either, so there's
        // no concern over activity happening after the migration causing issues.
        boolean allSyncsMigrated = true;
        for (int i = pendingJobs.size() - 1; i >= 0; --i) {
            final JobInfo job = pendingJobs.get(i);
            final SyncOperation op = SyncOperation.maybeCreateFromJobExtras(job.getExtras());
            if (op != null) {
                // This is a sync. Move it over to SyncManager's namespace.
                mJobScheduler.schedule(job);
                jobSchedulerDefaultNamespace.cancel(job.getId());
                allSyncsMigrated = false;
            }
        }
        mSyncStorageEngine.setJobNamespaceMigrated(allSyncsMigrated);
    }

    private synchronized void verifyJobScheduler() {
    private synchronized void verifyJobScheduler() {
        if (mJobScheduler != null) {
        if (mJobScheduler != null) {
            return;
            return;
@@ -503,10 +524,12 @@ public class SyncManager {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.d(TAG, "initializing JobScheduler object.");
                Log.d(TAG, "initializing JobScheduler object.");
            }
            }
            mJobScheduler = (JobScheduler) mContext.getSystemService(
            // Use a dedicated namespace to avoid conflicts with other jobs
                    Context.JOB_SCHEDULER_SERVICE);
            // scheduled by the system process.
            mJobSchedulerInternal = getJobSchedulerInternal();
            mJobScheduler = mContext.getSystemService(JobScheduler.class)
            // Get all persisted syncs from JobScheduler
                    .forNamespace("SyncManager");
            migrateSyncJobNamespaceIfNeeded();
            // Get all persisted syncs from JobScheduler in the SyncManager namespace.
            List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();
            List<JobInfo> pendingJobs = mJobScheduler.getAllPendingJobs();


            int numPersistedPeriodicSyncs = 0;
            int numPersistedPeriodicSyncs = 0;
@@ -522,6 +545,8 @@ public class SyncManager {
                        // shown on the settings activity.
                        // shown on the settings activity.
                        mSyncStorageEngine.markPending(op.target, true);
                        mSyncStorageEngine.markPending(op.target, true);
                    }
                    }
                } else {
                    Slog.wtf(TAG, "Non-sync job inside of SyncManager namespace");
                }
                }
            }
            }
            final String summary = "Loaded persisted syncs: "
            final String summary = "Loaded persisted syncs: "
@@ -543,11 +568,6 @@ public class SyncManager {
        }
        }
    }
    }


    @VisibleForTesting
    protected JobSchedulerInternal getJobSchedulerInternal() {
        return LocalServices.getService(JobSchedulerInternal.class);
    }

    /**
    /**
     * @return whether the device most likely has some periodic syncs.
     * @return whether the device most likely has some periodic syncs.
     */
     */
+23 −1
Original line number Original line Diff line number Diff line
@@ -172,6 +172,8 @@ public class SyncStorageEngine {


    private volatile boolean mIsClockValid;
    private volatile boolean mIsClockValid;


    private volatile boolean mIsJobNamespaceMigrated;

    static {
    static {
        sAuthorityRenames = new HashMap<String, String>();
        sAuthorityRenames = new HashMap<String, String>();
        sAuthorityRenames.put("contacts", "com.android.contacts");
        sAuthorityRenames.put("contacts", "com.android.contacts");
@@ -836,6 +838,20 @@ public class SyncStorageEngine {
        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target);
        reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, target);
    }
    }


    void setJobNamespaceMigrated(boolean migrated) {
        if (mIsJobNamespaceMigrated == migrated) {
            return;
        }
        mIsJobNamespaceMigrated = migrated;
        // This isn't urgent enough to write synchronously. Post it to the handler thread so
        // SyncManager can move on with whatever it was doing.
        mHandler.sendEmptyMessageDelayed(MSG_WRITE_STATUS, WRITE_STATUS_DELAY);
    }

    boolean isJobNamespaceMigrated() {
        return mIsJobNamespaceMigrated;
    }

    public Pair<Long, Long> getBackoff(EndPoint info) {
    public Pair<Long, Long> getBackoff(EndPoint info) {
        synchronized (mAuthorities) {
        synchronized (mAuthorities) {
            AuthorityInfo authority = getAuthorityLocked(info, "getBackoff");
            AuthorityInfo authority = getAuthorityLocked(info, "getBackoff");
@@ -1585,7 +1601,6 @@ public class SyncStorageEngine {
        }
        }
    }
    }



    /**
    /**
     * Remove an authority associated with a provider. Needs to be a standalone function for
     * Remove an authority associated with a provider. Needs to be a standalone function for
     * backward compatibility.
     * backward compatibility.
@@ -2101,6 +2116,10 @@ public class SyncStorageEngine {
                        mSyncStatus.put(status.authorityId, status);
                        mSyncStatus.put(status.authorityId, status);
                    }
                    }
                    break;
                    break;
                case (int) SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED:
                    mIsJobNamespaceMigrated =
                            proto.readBoolean(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED);
                    break;
                case ProtoInputStream.NO_MORE_FIELDS:
                case ProtoInputStream.NO_MORE_FIELDS:
                    return;
                    return;
            }
            }
@@ -2368,6 +2387,9 @@ public class SyncStorageEngine {
            }
            }
            proto.end(token);
            proto.end(token);
        }
        }

        proto.write(SyncStatusProto.IS_JOB_NAMESPACE_MIGRATED, mIsJobNamespaceMigrated);

        proto.flush();
        proto.flush();
    }
    }


Loading