1616
1717package android .content ;
1818
19- import com . google . android .collect . Lists ;
19+ import android .os . SystemClock ;
2020import com .google .android .collect .Maps ;
2121
2222import android .util .Pair ;
@@ -130,33 +130,13 @@ public void remove(SyncOperation operation) {
130130 public Pair <SyncOperation , Long > nextOperation () {
131131 SyncOperation best = null ;
132132 long bestRunTime = 0 ;
133- boolean bestSyncableIsUnknownAndNotARetry = false ;
133+ boolean bestIsInitial = false ;
134134 for (SyncOperation op : mOperationsMap .values ()) {
135- long opRunTime = op .earliestRunTime ;
136- if (!op .extras .getBoolean (ContentResolver .SYNC_EXTRAS_IGNORE_BACKOFF , false )) {
137- Pair <Long , Long > backoff = mSyncStorageEngine .getBackoff (op .account , op .authority );
138- long delayUntil = mSyncStorageEngine .getDelayUntilTime (op .account , op .authority );
139- opRunTime = Math .max (
140- Math .max (opRunTime , delayUntil ),
141- backoff != null ? backoff .first : 0 );
142- }
143- // we know a sync is a retry if the intialization flag is set, since that will only
144- // be set by the sync dispatching code, thus if it is set it must have already been
145- // dispatched
146- final boolean syncableIsUnknownAndNotARetry =
147- !op .extras .getBoolean (ContentResolver .SYNC_EXTRAS_INITIALIZE , false )
148- && mSyncStorageEngine .getIsSyncable (op .account , op .authority ) < 0 ;
149- // if the unsyncable state differs, make the current the best if it is unsyncable
150- // else, if the expedited state differs, make the current the best if it is expedited
151- // else, make the current the best if it is earlier than the best
152- if (best == null
153- || ((bestSyncableIsUnknownAndNotARetry == syncableIsUnknownAndNotARetry )
154- ? (best .expedited == op .expedited
155- ? opRunTime < bestRunTime
156- : op .expedited )
157- : syncableIsUnknownAndNotARetry )) {
135+ final long opRunTime = getOpTime (op );
136+ final boolean opIsInitial = getIsInitial (op );
137+ if (isOpBetter (best , bestRunTime , bestIsInitial , op , opRunTime , opIsInitial )) {
158138 best = op ;
159- bestSyncableIsUnknownAndNotARetry = syncableIsUnknownAndNotARetry ;
139+ bestIsInitial = opIsInitial ;
160140 bestRunTime = opRunTime ;
161141 }
162142 }
@@ -166,6 +146,91 @@ public Pair<SyncOperation, Long> nextOperation() {
166146 return Pair .create (best , bestRunTime );
167147 }
168148
149+ // VisibleForTesting
150+ long getOpTime (SyncOperation op ) {
151+ long opRunTime = op .earliestRunTime ;
152+ if (!op .extras .getBoolean (ContentResolver .SYNC_EXTRAS_IGNORE_BACKOFF , false )) {
153+ Pair <Long , Long > backoff = mSyncStorageEngine .getBackoff (op .account , op .authority );
154+ long delayUntil = mSyncStorageEngine .getDelayUntilTime (op .account , op .authority );
155+ opRunTime = Math .max (
156+ Math .max (opRunTime , delayUntil ),
157+ backoff != null ? backoff .first : 0 );
158+ }
159+ return opRunTime ;
160+ }
161+
162+ // VisibleForTesting
163+ boolean getIsInitial (SyncOperation op ) {
164+ // Initial op is defined as an op with an unknown syncable that is not a retry.
165+ // We know a sync is a retry if the intialization flag is set, since that will only
166+ // be set by the sync dispatching code, thus if it is set it must have already been
167+ // dispatched
168+ return !op .extras .getBoolean (ContentResolver .SYNC_EXTRAS_INITIALIZE , false )
169+ && mSyncStorageEngine .getIsSyncable (op .account , op .authority ) < 0 ;
170+ }
171+
172+ // return true if op is a better candidate than best. Rules:
173+ // if the "Initial" state differs, make the current the best if it is "Initial".
174+ // else, if the expedited state differs, pick the expedited unless it is backed off and the
175+ // non-expedited isn't
176+ // VisibleForTesting
177+ boolean isOpBetter (
178+ SyncOperation best , long bestRunTime , boolean bestIsInitial ,
179+ SyncOperation op , long opRunTime , boolean opIsInitial ) {
180+ boolean setBest = false ;
181+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
182+ Log .v (TAG , "nextOperation: Processing op: " + op );
183+ }
184+ if (best == null ) {
185+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
186+ Log .v (TAG , " First op selected" );
187+ }
188+ setBest = true ;
189+ } else if (bestIsInitial == opIsInitial ) {
190+ if (best .expedited == op .expedited ) {
191+ if (opRunTime < bestRunTime ) {
192+ // if both have same level, earlier time wins
193+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
194+ Log .v (TAG , " Same expedite level - new op selected" );
195+ }
196+ setBest = true ;
197+ }
198+ } else {
199+ final long now = SystemClock .elapsedRealtime ();
200+ if (op .expedited ) {
201+ if (opRunTime <= now || bestRunTime > now ) {
202+ // if op is expedited, it wins unless op can't run yet and best can
203+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
204+ Log .v (TAG , " New op is expedited and can run - new op selected" );
205+ }
206+ setBest = true ;
207+ } else {
208+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
209+ Log .v (TAG , " New op is expedited but can't run and best can" );
210+ }
211+ }
212+ } else {
213+ if (bestRunTime > now && opRunTime <= now ) {
214+ // if best is expedited but can't run yet and op can run, op wins
215+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
216+ Log .v (TAG ,
217+ " New op is not expedited but can run - new op selected" );
218+ }
219+ setBest = true ;
220+ }
221+ }
222+ }
223+ } else {
224+ if (opIsInitial ) {
225+ if (Log .isLoggable (TAG , Log .VERBOSE )) {
226+ Log .v (TAG , " New op is init - new op selected" );
227+ }
228+ setBest = true ;
229+ }
230+ }
231+ return setBest ;
232+ }
233+
169234 /**
170235 * Find and return the SyncOperation that should be run next and is ready to run.
171236 * @param now the current {@link android.os.SystemClock#elapsedRealtime()}, used to
0 commit comments